Changeset 7454
- Timestamp:
- 09/11/07 03:25:59 (10 months ago)
- Files:
-
- trunk/activerecord/CHANGELOG (modified) (1 diff)
- trunk/activerecord/lib/active_record.rb (modified) (2 diffs)
- trunk/activerecord/lib/active_record/acts/tree.rb (modified) (1 diff)
- trunk/activerecord/test/associations/cascaded_eager_loading_test.rb (modified) (3 diffs)
- trunk/activerecord/test/fixtures/mixin.rb (deleted)
- trunk/activerecord/test/fixtures/mixins.yml (modified) (2 diffs)
- trunk/activerecord/test/mixin_test.rb (modified) (4 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/activerecord/CHANGELOG
r7453 r7454 1 1 *SVN* 2 3 * Moved acts_as_tree into a plugin of the same name on the official Rails svn #9514 [lifofifo] 2 4 3 5 * Moved acts_as_nested_set into a plugin of the same name on the official Rails svn #9516 [josh] trunk/activerecord/lib/active_record.rb
r7453 r7454 44 44 require 'active_record/transactions' 45 45 require 'active_record/timestamp' 46 require 'active_record/acts/tree'47 46 require 'active_record/locking/optimistic' 48 47 require 'active_record/locking/pessimistic' … … 64 63 include ActiveRecord::Transactions 65 64 include ActiveRecord::Reflection 66 include ActiveRecord::Acts::Tree67 65 include ActiveRecord::Calculations 68 66 include ActiveRecord::XmlSerialization trunk/activerecord/lib/active_record/acts/tree.rb
r7366 r7454 1 module ActiveRecord2 module Acts #:nodoc:3 module Tree #:nodoc:4 def self.included(base)5 base.extend(ClassMethods)6 end7 8 # Specify this +acts_as+ extension if you want to model a tree structure by providing a parent association and a children9 # association. This requires that you have a foreign key column, which by default is called +parent_id+.10 #11 # class Category < ActiveRecord::Base12 # acts_as_tree :order => "name"13 # end14 #15 # Example:16 # root17 # \_ child118 # \_ subchild119 # \_ subchild220 #21 # root = Category.create("name" => "root")22 # child1 = root.children.create("name" => "child1")23 # subchild1 = child1.children.create("name" => "subchild1")24 #25 # root.parent # => nil26 # child1.parent # => root27 # root.children # => [child1]28 # root.children.first.children.first # => subchild129 #30 # In addition to the parent and children associations, the following instance methods are added to the class31 # after calling <tt>acts_as_tree</tt>:32 # * <tt>siblings</tt> - Returns all the children of the parent, excluding the current node (<tt>[subchild2]</tt> when called on <tt>subchild1</tt>)33 # * <tt>self_and_siblings</tt> - Returns all the children of the parent, including the current node (<tt>[subchild1, subchild2]</tt> when called on <tt>subchild1</tt>)34 # * <tt>ancestors</tt> - Returns all the ancestors of the current node (<tt>[child1, root]</tt> when called on <tt>subchild2</tt>)35 # * <tt>root</tt> - Returns the root of the current node (<tt>root</tt> when called on <tt>subchild2</tt>)36 module ClassMethods37 # Configuration options are:38 #39 # * <tt>foreign_key</tt> - specifies the column name to use for tracking of the tree (default: +parent_id+)40 # * <tt>order</tt> - makes it possible to sort the children according to this SQL snippet.41 # * <tt>counter_cache</tt> - keeps a count in a +children_count+ column if set to +true+ (default: +false+).42 def acts_as_tree(options = {})43 configuration = { :foreign_key => "parent_id", :order => nil, :counter_cache => nil }44 configuration.update(options) if options.is_a?(Hash)45 46 belongs_to :parent, :class_name => name, :foreign_key => configuration[:foreign_key], :counter_cache => configuration[:counter_cache]47 has_many :children, :class_name => name, :foreign_key => configuration[:foreign_key], :order => configuration[:order], :dependent => :destroy48 49 class_eval <<-EOV50 include ActiveRecord::Acts::Tree::InstanceMethods51 52 def self.roots53 find(:all, :conditions => "#{configuration[:foreign_key]} IS NULL", :order => #{configuration[:order].nil? ? "nil" : %Q{"#{configuration[:order]}"}})54 end55 56 def self.root57 find(:first, :conditions => "#{configuration[:foreign_key]} IS NULL", :order => #{configuration[:order].nil? ? "nil" : %Q{"#{configuration[:order]}"}})58 end59 EOV60 end61 end62 63 module InstanceMethods64 # Returns list of ancestors, starting from parent until root.65 #66 # subchild1.ancestors # => [child1, root]67 def ancestors68 node, nodes = self, []69 nodes << node = node.parent while node.parent70 nodes71 end72 73 # Returns the root node of the tree.74 def root75 node = self76 node = node.parent while node.parent77 node78 end79 80 # Returns all siblings of the current node.81 #82 # subchild1.siblings # => [subchild2]83 def siblings84 self_and_siblings - [self]85 end86 87 # Returns all siblings and a reference to the current node.88 #89 # subchild1.self_and_siblings # => [subchild1, subchild2]90 def self_and_siblings91 parent ? parent.children : self.class.roots92 end93 end94 end95 end96 endtrunk/activerecord/test/associations/cascaded_eager_loading_test.rb
r7081 r7454 6 6 require 'fixtures/category' 7 7 require 'fixtures/categorization' 8 require 'fixtures/mixin'9 8 require 'fixtures/company' 10 9 require 'fixtures/topic' … … 54 53 end 55 54 56 def test_eager_association_loading_with_acts_as_tree57 roots = TreeMixin.find(:all, :include=>"children", :conditions=>"mixins.parent_id IS NULL", :order=>"mixins.id")58 assert_equal mixins(:tree_1, :tree2_1, :tree3_1), roots59 assert_no_queries do60 assert_equal 2, roots[0].children.size61 assert_equal 0, roots[1].children.size62 assert_equal 0, roots[2].children.size63 end64 end65 66 55 def test_eager_association_loading_with_cascaded_three_levels_by_ping_pong 67 56 firms = Firm.find(:all, :include=>{:account=>{:firm=>:account}}, :order=>"companies.id") … … 104 93 end 105 94 end 106 107 def test_eager_association_loading_with_recursive_cascading_three_levels_has_many108 root_node = RecursivelyCascadedTreeMixin.find(:first, :include=>{:children=>{:children=>:children}}, :order => 'mixins.id')109 assert_equal mixins(:recursively_cascaded_tree_4), assert_no_queries { root_node.children.first.children.first.children.first }110 end111 112 def test_eager_association_loading_with_recursive_cascading_three_levels_has_one113 root_node = RecursivelyCascadedTreeMixin.find(:first, :include=>{:first_child=>{:first_child=>:first_child}}, :order => 'mixins.id')114 assert_equal mixins(:recursively_cascaded_tree_4), assert_no_queries { root_node.first_child.first_child.first_child }115 end116 117 def test_eager_association_loading_with_recursive_cascading_three_levels_belongs_to118 leaf_node = RecursivelyCascadedTreeMixin.find(:first, :include=>{:parent=>{:parent=>:parent}}, :order => 'mixins.id DESC')119 assert_equal mixins(:recursively_cascaded_tree_1), assert_no_queries { leaf_node.parent.parent.parent }120 end121 95 end 122 123 96 124 97 require 'fixtures/vertex' trunk/activerecord/test/fixtures/mixins.yml
r7453 r7454 1 # tree mixins2 tree_1:3 id: 10014 type: TreeMixin5 parent_id:6 7 tree_2:8 id: 10029 type: TreeMixin10 parent_id: 100111 12 tree_3:13 id: 100314 type: TreeMixin15 parent_id: 100216 17 tree_4:18 id: 100419 type: TreeMixin20 parent_id: 100121 22 tree2_1:23 id: 100524 type: TreeMixin25 parent_id:26 27 tree3_1:28 id: 100629 type: TreeMixin30 parent_id:31 32 tree_without_order_1:33 id: 110134 type: TreeMixinWithoutOrder35 parent_id:36 37 tree_without_order_2:38 id: 110039 type: TreeMixinWithoutOrder40 parent_id:41 42 recursively_cascaded_tree_1:43 id: 500544 type: RecursivelyCascadedTreeMixin45 parent_id:46 47 recursively_cascaded_tree_2:48 id: 500649 type: RecursivelyCascadedTreeMixin50 parent_id: 500551 52 recursively_cascaded_tree_3:53 id: 500754 type: RecursivelyCascadedTreeMixin55 parent_id: 500656 57 recursively_cascaded_tree_4:58 id: 500859 type: RecursivelyCascadedTreeMixin60 parent_id: 500761 62 63 1 # Nested set mixins 64 2 … … 66 4 set_<%= counter %>: 67 5 id: <%= counter+3000 %> 68 type: NestedSet69 6 <% end %> 70 7 trunk/activerecord/test/mixin_test.rb
r7444 r7454 2 2 require 'active_record/acts/tree' 3 3 require 'active_record/acts/nested_set' 4 require 'fixtures/mixin' 4 5 class Mixin < ActiveRecord::Base 6 end 5 7 6 8 # Let us control what Time.now returns for the TouchTest suite … … 21 23 end 22 24 23 24 class TreeTest < Test::Unit::TestCase25 fixtures :mixins26 27 def test_children28 assert_equal mixins(:tree_1).children, mixins(:tree_2, :tree_4)29 assert_equal mixins(:tree_2).children, [mixins(:tree_3)]30 assert_equal mixins(:tree_3).children, []31 assert_equal mixins(:tree_4).children, []32 end33 34 def test_parent35 assert_equal mixins(:tree_2).parent, mixins(:tree_1)36 assert_equal mixins(:tree_2).parent, mixins(:tree_4).parent37 assert_nil mixins(:tree_1).parent38 end39 40 def test_delete41 assert_equal 6, TreeMixin.count42 mixins(:tree_1).destroy43 assert_equal 2, TreeMixin.count44 mixins(:tree2_1).destroy45 mixins(:tree3_1).destroy46 assert_equal 0, TreeMixin.count47 end48 49 def test_insert50 @extra = mixins(:tree_1).children.create51 52 assert @extra53 54 assert_equal @extra.parent, mixins(:tree_1)55 56 assert_equal 3, mixins(:tree_1).children.size57 assert mixins(:tree_1).children.include?(@extra)58 assert mixins(:tree_1).children.include?(mixins(:tree_2))59 assert mixins(:tree_1).children.include?(mixins(:tree_4))60 end61 62 def test_ancestors63 assert_equal [], mixins(:tree_1).ancestors64 assert_equal [mixins(:tree_1)], mixins(:tree_2).ancestors65 assert_equal mixins(:tree_2, :tree_1), mixins(:tree_3).ancestors66 assert_equal [mixins(:tree_1)], mixins(:tree_4).ancestors67 assert_equal [], mixins(:tree2_1).ancestors68 assert_equal [], mixins(:tree3_1).ancestors69 end70 71 def test_root72 assert_equal mixins(:tree_1), TreeMixin.root73 assert_equal mixins(:tree_1), mixins(:tree_1).root74 assert_equal mixins(:tree_1), mixins(:tree_2).root75 assert_equal mixins(:tree_1), mixins(:tree_3).root76 assert_equal mixins(:tree_1), mixins(:tree_4).root77 assert_equal mixins(:tree2_1), mixins(:tree2_1).root78 assert_equal mixins(:tree3_1), mixins(:tree3_1).root79 end80 81 def test_roots82 assert_equal mixins(:tree_1, :tree2_1, :tree3_1), TreeMixin.roots83 end84 85 def test_siblings86 assert_equal mixins(:tree2_1, :tree3_1), mixins(:tree_1).siblings87 assert_equal [mixins(:tree_4)], mixins(:tree_2).siblings88 assert_equal [], mixins(:tree_3).siblings89 assert_equal [mixins(:tree_2)], mixins(:tree_4).siblings90 assert_equal mixins(:tree_1, :tree3_1), mixins(:tree2_1).siblings91 assert_equal mixins(:tree_1, :tree2_1), mixins(:tree3_1).siblings92 end93 94 def test_self_and_siblings95 assert_equal mixins(:tree_1, :tree2_1, :tree3_1), mixins(:tree_1).self_and_siblings96 assert_equal mixins(:tree_2, :tree_4), mixins(:tree_2).self_and_siblings97 assert_equal [mixins(:tree_3)], mixins(:tree_3).self_and_siblings98 assert_equal mixins(:tree_2, :tree_4), mixins(:tree_4).self_and_siblings99 assert_equal mixins(:tree_1, :tree2_1, :tree3_1), mixins(:tree2_1).self_and_siblings100 assert_equal mixins(:tree_1, :tree2_1, :tree3_1), mixins(:tree3_1).self_and_siblings101 end102 end103 104 class TreeTestWithoutOrder < Test::Unit::TestCase105 fixtures :mixins106 107 def test_root108 assert mixins(:tree_without_order_1, :tree_without_order_2).include?(TreeMixinWithoutOrder.root)109 end110 111 def test_roots112 assert_equal [], mixins(:tree_without_order_1, :tree_without_order_2) - TreeMixinWithoutOrder.roots113 end114 end115 25 116 26 class TouchTest < Test::Unit::TestCase … … 171 81 Mixin.record_timestamps = false 172 82 173 assert_nil mixins(: tree_1).updated_at174 mixins(: tree_1).save175 assert_nil mixins(: tree_1).updated_at83 assert_nil mixins(:set_1).updated_at 84 mixins(:set_1).save 85 assert_nil mixins(:set_1).updated_at 176 86 177 87 Mixin.record_timestamps = true … … 179 89 180 90 end 181 182 183 184