|
Rails Cookbook Authors: Orsini R Published year: 2007 Pages: 50/250 |
Recipe 3.17. Creating a Directory of Nested Topics with acts_as_treeProblemDatabase tables are simply a set of rows. However, you often want those rows to behave in some other way. If the data in your table represents a tree structure, how do you work with it as a tree? For example, you have a web site organized by topic. Topics can have subtopics, as can the subtopics themselves . You want to model these topics as a tree structure and store them in a single database table. SolutionFirst, create a topics table that includes a parent_id column, and populate it with some topics. Use the following migration for this: db/migrate/001_create_topics.rb :
class CreateTopics < ActiveRecord::Migration
def self.up
create_table :topics do t
t.column :parent_id, :integer
t.column :name, :string
end
Topic.create :name => 'Programming and Development'
Topic.create :parent_id => 1, :name => 'Algorithms'
Topic.create :parent_id => 1, :name => 'Methodologies'
Topic.create :parent_id => 3, :name => 'Extreme Programming'
Topic.create :parent_id => 3, :name => 'Object-Oriented Programming'
Topic.create :parent_id => 3, :name => 'Functional Languages'
Topic.create :parent_id => 2, :name => 'Sorting'
Topic.create :parent_id => 7, :name => 'Bubble sort'
Topic.create :parent_id => 7, :name => 'Heap sort'
Topic.create :parent_id => 7, :name => 'Merge sort'
Topic.create :parent_id => 7, :name => 'Quick sort'
Topic.create :parent_id => 7, :name => 'Shell sort'
end
def self.down
drop_table :topics
end
end
Declare that this model is to act as a tree structure: app/models/topic.rb : class Topic < ActiveRecord::Base acts_as_tree :order => "name" end DiscussionCalling the acts_as_tree class method on a model gives instances of that model some additional methods for inspecting the their relationships within the tree. These methods include:
Let's open up a Rails console session and inspect the topics tree that was created by the solution. First, get the root node, which we know has a parent_id of null :
>>
root = Topic.find(:first, :conditions => "parent_id is null")
=> #<Topic:0x4092ae74 @attributes={"name"=>"Programming and Development",
"id"=>"1", "parent_id"=>nil}>
We can show the root node's children with:
>>
root.children
=> [#<Topic:0x4090da04 @attributes={"name"=>"Algorithms", "id"=>"2",
"parent_id"=>"1"}>, #<Topic:0x4090d9c8
@attributes={"name"=>"Methodologies", "id"=>"3", "parent_id"=>"1"}>]
The following returns a hash of the attributes of the first node in the root node's array of children:
>>
root.children.first.attributes
=> {"name"=>"Algorithms", "id"=>2, "parent_id"=>1}
We can find a leaf node from the root by alternating calls to children and first . From the leaf node, a single call to root finds the root node:
>>
leaf = root.children.first.children.first.children.first
=> #<Topic:0x408dd804 @attributes={"name"=>"Bubble sort", "id"=>"8",
"parent_id"=>"7"}, @children=[]>
>>
leaf.root
=> #<Topic:0x408cffd8 @attributes={"name"=>"Programming and Development",
"id"=>"1", "parent_id"=>nil}, @parent=nil>
In addition to the topics loaded in the solution, we can create more directly from the Rails console. Let's create another root node named Shapes and give it two children nodes of its own:
>>
r = Topic.create(:name => "Shapes")
=> #<Topic:0x4092e9d4 @attributes={"name"=>"Shapes", "id"=>13,
"parent_id"=>nil}, @new_record_before_save=false,
@errors=#<ActiveRecord::Errors:0x4092baf4 @errors=,
@base=#<Topic:0x4092e9d4 ...>>, @new_record=false>
>>
r.siblings
=> [#<Topic:0x4092508c @attributes={"name"=>"Programming and Development",
"id"=>"1", "parent_id"=>nil}>]
>>
r.children.create(:name => "circle")
=> #<Topic:0x40921ab8 @attributes={"name"=>"circle", "id"=>14,
"parent_id"=>13}, @new_record_before_save=false,
@errors=#<ActiveRecord::Errors:0x40921108 @errors=,
@base=#<Topic:0x40921ab8 ...>>, @new_record=false>
>>
r.children.create(:name => "square")
=> #<Topic:0x4091c57c @attributes={"name"=>"square", "id"=>15,
"parent_id"=>13}, @new_record_before_save=false,
@errors=#<ActiveRecord::Errors:0x4091bbcc @errors=,
@base=#<Topic:0x4091c57c ...>>, @new_record=false>
From mysql , we can verify that the three new elements were added to the database as expected. mysql> select * from topics; +----+-----------+-----------------------------+ id parent_id name +----+-----------+-----------------------------+ 1 NULL Programming and Development 2 1 Algorithms 3 1 Methodologies 4 3 Extreme Programming 5 3 Object-Oriented Programming 6 3 Functional Languages 7 2 Sorting 8 7 Bubble sort 9 7 Heap sort 10 7 Merge sort 11 7 Quick sort 12 7 Shell sort 13 NULL Shapes 14 13 circle 15 13 square +----+-----------+-----------------------------+ 15 rows in set (0.00 sec) acts_as_tree and acts_as_nested_set are significantly different from each other, even though they appear to do the same thing. acts_as_tree scales much better with a big table because each row does not have to be updated when a new row is added. With acts_as_nested_set , position information for each record has to be updated whenever an item is added or removed. The default behavior is to use a column named parent_id to store parent nodes in the tree. You can change this behavior by specifying a different column name with the :foreign_key option of the acts_as_tree options hash. See Also
|
|
|
|
Rails Cookbook Authors: Orsini R Published year: 2007 Pages: 50/250 |
![]() Ruby on Rails 3 Tutorial: Learn Rails by Example (Addison-Wesley Professional Ruby Series) | ![]() Advanced Rails | ![]() The Ruby Programming Language | ![]() Ruby Cookbook (Cookbooks (O'Reilly)) | ![]() Ajax on Rails |
![]() Ruby on Rails 3 Tutorial: Learn Rails by Example (Addison-Wesley Professional Ruby Series) | ![]() Advanced Rails |
![]() The Ruby Programming Language | ![]() Ruby Cookbook (Cookbooks (O'Reilly)) |
![]() Ajax on Rails |