Deep nested associations on the same Model

This could be a lot simpler than I think and I'm just missing something obvious!

I'm working on a creative collaboration app whereby one user could submit a Story and this can then be forked by another user and worked on seperately. To acheive this I have a has_many association within the same Story model as such:

class Story < ActiveRecord::Base

  has_many :forked_stories, :class_name => 'Story', :foreign_key => :parent_story_id

  belongs_to :parent_story

end

In this way if a single story is an original it's parent_story_id will be nil. Similarly, if I call @story.forked_stories I can get a collection of all the stories that have been forked from the current one, or @story.parent_story will return the story from which it has been forked.

What I want is to be able to return all the forked_stories and their forked_stories for a given Story and I'm finding this very difficult since they're all based on the same model. I keep running into dead ends. If I can return them to a certain depth as well that'd be great, so I've been trying to write a method that accepts a depth parameter but the crazy loops are frying my brain.

Perhaps there's something I can do with routes that would solve the problem?

Jon Hope wrote:

This could be a lot simpler than I think and I'm just missing something obvious!

I'm working on a creative collaboration app whereby one user could submit a Story and this can then be forked by another user and worked on seperately. To acheive this I have a has_many association within the same Story model as such:

class Story < ActiveRecord::Base

  has_many :forked_stories, :class_name => 'Story', :foreign_key => :parent_story_id

  belongs_to :parent_story

end

In this way if a single story is an original it's parent_story_id will be nil. Similarly, if I call @story.forked_stories I can get a collection of all the stories that have been forked from the current one, or @story.parent_story will return the story from which it has been forked.

So you have a tree structure of arbitrary depth?

What I want is to be able to return all the forked_stories and their forked_stories for a given Story and I'm finding this very difficult since they're all based on the same model. I keep running into dead ends. If I can return them to a certain depth as well that'd be great, so I've been trying to write a method that accepts a depth parameter but the crazy loops are frying my brain.

There is a less obvious but ultimately much simpler way to do this. Use a nested-set data structure, which will make what you're talking about pretty easy. The awesome_nested_set plugin would be just what you need.

Perhaps there's something I can do with routes that would solve the problem?

No. Your issues have to do with data structure, not routing.

Best,

As far as I can tell you have an algorithmical difficulty, not one related to Rails. To get back all the forked stories for a given story the following code snippet would do the job:

  def fork_tree     forked = forked_stories     forked_from_children = forked.inject() do |fstories, fstory|       fstories + fstory.fork_tree     end     forked + forked_from_children   end

(you can see the whole thing here: http://gist.github.com/179186)

Note1: This has been tested under Ruby, not Rails Note2: The method is badly named, since it just returns an array, not a tree-like structure of all the stories forked directly from the story or from its children, or grandchildren, etc.

Hope this helps, Bálint

Balint Erdi wrote:

Note 3: Unless the whole array has already been loaded into memory, that's horribly inefficient. Nested sets are the way to go here.

Best, -- Marnen Laibow-Koserhttp://www.marnen.org mar...@marnen.org -- Posted viahttp://www.ruby-forum.com/.

Seems interesting, could you sketch up how that would work?

Balint

Balint Erdi wrote:

Note 3: Unless the whole array has already been loaded into memory, that's horribly inefficient. �Nested sets are the way to go here.

Best, -- Marnen Laibow-Koserhttp://www.marnen.org mar...@marnen.org -- Posted viahttp://www.ruby-forum.com/.

Seems interesting, could you sketch up how that would work?

There are plenty of good articles available on nested sets (notably those by Joe Celko and Vadim Tropashko). Check 'em out, and also look at the awesome_nested_set rdoc!

Balint

Best,