5

I have some troubles wrapping my head around the following situation..

I am trying to create a tree structure, where I will be able to give custom names to connections between nodes..

So I want to have Node and Relation models. Each

Node 
 has_many :relations

Each

Relation 
 has_many :nodes

Node can be either a parent or a child.. So far everything was easy and there are tons of examples that show how to make a self-referential has_many table... The problem is that I want to be able to give names to relations, so that I can do something like:

relation1 = node1.relations.create(:name => "relation_name", :child => node2)

and in result get something like:

relation1.name == "relation_name"
relation1.parent == node1
relation1.child == node2

All the creations are happening within the model, this activity is not really exposed to user, if that matters. Thanks!

EDIT2: Here is how it works now:

class Node < ActiveRecord::Base
  belongs_to :sentence

  has_one :parent_relation, :foreign_key => "child_id", :class_name => "Relation"
  has_many :child_relations, :foreign_key => "parent_id", :class_name => "Relation"

  has_one :parent, :through => :parent_relation
  has_many :children,  :through => :child_relations, :source => :child

  has_many :relations, :foreign_key => "child_id"
  has_many :relations, :foreign_key => "parent_id"

class Relation < ActiveRecord::Base

  has_many :videos, :as => :videoable, :dependent => :destroy
  has_many :phrases, :through => :videos

  belongs_to :parent, :class_name => "Node"#, :inverse_of => :parent_relation
  belongs_to :child, :class_name => "Node"#, :inverse_of => :child_relation
Stpn
  • 6,202
  • 7
  • 47
  • 94
  • So a `relation` can only have one `parent` and one `child`? Or can it have multiple of each? – Azolo Mar 29 '12 at 21:41
  • You keep changing your question haha, I'm going to try and just answer the original question to the best of my ability. I think that it will kinda help you solve your other questions by making your code a little more understandable. – Azolo Mar 29 '12 at 22:52

1 Answers1

6

So what you're talking about is more like a Joins Model than a Self-Reference.

Note: I changed your relation association 'labels' because I was having a hard time with your naming, so you don't have to change your 'labels' that was just for me.

So for your Node class you could do something like this

class Node < ActiveRecord::Base
   has_one  :parent_relation, :foreign_key => "child_id",
                              :class_name => "Relation"
   has_many :child_relations, :foreign_key => "parent_id", 
                              :class_name => "Relation"

   has_one  :parent, :through => :parent_relation
   has_many :children, :through => :child_relations, :source => :child
end

Then for your Relation class you could something like

class Relation < ActiveRecord::Base
  belongs_to :parent, :class_name => "Node", :inverse_of => :parent_relation
  belongs_to :child, :class_name => "Node", :inverse_of => :child_relations
end

The :inverse_of option should let you build let you build a Node based off the parent and children associations from your Node instances, this is just a caveat from the magic with :through relationships. (Documentation for this is at the bottom of the Joins Model section.)

I don't fully understand your association structure, but I think this should model it correctly. Lemme know if there are any problems though.

Side Note: Since Relation is a constant set in the ActiveRecord module you might consider changing it to something like NodeRelationship. I don't think it will interfere with your program, but it definitively caused some trouble for my thought process.

Azolo
  • 4,353
  • 1
  • 23
  • 31
  • Thanks! It worked! Although I had to comment out the :inverse_of => part.. it was giving error otherwise. I will edit the question so it reflects the current setup – Stpn Mar 30 '12 at 04:17
  • The error is: Could not find the inverse association for child – Stpn Mar 30 '12 at 04:38
  • err sorry it should be `child_relations` – Azolo Mar 30 '12 at 05:05