mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Moved acts_as_tree into a plugin of the same name on the official Rails svn (closes #9514) [lifofifo]
git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@7454 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
This commit is contained in:
parent
11f4d28344
commit
ea0975a20b
7 changed files with 9 additions and 362 deletions
|
@ -1,5 +1,7 @@
|
|||
*SVN*
|
||||
|
||||
* Moved acts_as_tree into a plugin of the same name on the official Rails svn #9514 [lifofifo]
|
||||
|
||||
* Moved acts_as_nested_set into a plugin of the same name on the official Rails svn #9516 [josh]
|
||||
|
||||
* Moved acts_as_list into a plugin of the same name on the official Rails svn [josh]
|
||||
|
|
|
@ -43,7 +43,6 @@ require 'active_record/associations'
|
|||
require 'active_record/aggregations'
|
||||
require 'active_record/transactions'
|
||||
require 'active_record/timestamp'
|
||||
require 'active_record/acts/tree'
|
||||
require 'active_record/locking/optimistic'
|
||||
require 'active_record/locking/pessimistic'
|
||||
require 'active_record/migration'
|
||||
|
@ -63,7 +62,6 @@ ActiveRecord::Base.class_eval do
|
|||
include ActiveRecord::Aggregations
|
||||
include ActiveRecord::Transactions
|
||||
include ActiveRecord::Reflection
|
||||
include ActiveRecord::Acts::Tree
|
||||
include ActiveRecord::Calculations
|
||||
include ActiveRecord::XmlSerialization
|
||||
include ActiveRecord::AttributeMethods
|
||||
|
|
|
@ -1,96 +0,0 @@
|
|||
module ActiveRecord
|
||||
module Acts #:nodoc:
|
||||
module Tree #:nodoc:
|
||||
def self.included(base)
|
||||
base.extend(ClassMethods)
|
||||
end
|
||||
|
||||
# Specify this +acts_as+ extension if you want to model a tree structure by providing a parent association and a children
|
||||
# association. This requires that you have a foreign key column, which by default is called +parent_id+.
|
||||
#
|
||||
# class Category < ActiveRecord::Base
|
||||
# acts_as_tree :order => "name"
|
||||
# end
|
||||
#
|
||||
# Example:
|
||||
# root
|
||||
# \_ child1
|
||||
# \_ subchild1
|
||||
# \_ subchild2
|
||||
#
|
||||
# root = Category.create("name" => "root")
|
||||
# child1 = root.children.create("name" => "child1")
|
||||
# subchild1 = child1.children.create("name" => "subchild1")
|
||||
#
|
||||
# root.parent # => nil
|
||||
# child1.parent # => root
|
||||
# root.children # => [child1]
|
||||
# root.children.first.children.first # => subchild1
|
||||
#
|
||||
# In addition to the parent and children associations, the following instance methods are added to the class
|
||||
# after calling <tt>acts_as_tree</tt>:
|
||||
# * <tt>siblings</tt> - Returns all the children of the parent, excluding the current node (<tt>[subchild2]</tt> when called on <tt>subchild1</tt>)
|
||||
# * <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>)
|
||||
# * <tt>ancestors</tt> - Returns all the ancestors of the current node (<tt>[child1, root]</tt> when called on <tt>subchild2</tt>)
|
||||
# * <tt>root</tt> - Returns the root of the current node (<tt>root</tt> when called on <tt>subchild2</tt>)
|
||||
module ClassMethods
|
||||
# Configuration options are:
|
||||
#
|
||||
# * <tt>foreign_key</tt> - specifies the column name to use for tracking of the tree (default: +parent_id+)
|
||||
# * <tt>order</tt> - makes it possible to sort the children according to this SQL snippet.
|
||||
# * <tt>counter_cache</tt> - keeps a count in a +children_count+ column if set to +true+ (default: +false+).
|
||||
def acts_as_tree(options = {})
|
||||
configuration = { :foreign_key => "parent_id", :order => nil, :counter_cache => nil }
|
||||
configuration.update(options) if options.is_a?(Hash)
|
||||
|
||||
belongs_to :parent, :class_name => name, :foreign_key => configuration[:foreign_key], :counter_cache => configuration[:counter_cache]
|
||||
has_many :children, :class_name => name, :foreign_key => configuration[:foreign_key], :order => configuration[:order], :dependent => :destroy
|
||||
|
||||
class_eval <<-EOV
|
||||
include ActiveRecord::Acts::Tree::InstanceMethods
|
||||
|
||||
def self.roots
|
||||
find(:all, :conditions => "#{configuration[:foreign_key]} IS NULL", :order => #{configuration[:order].nil? ? "nil" : %Q{"#{configuration[:order]}"}})
|
||||
end
|
||||
|
||||
def self.root
|
||||
find(:first, :conditions => "#{configuration[:foreign_key]} IS NULL", :order => #{configuration[:order].nil? ? "nil" : %Q{"#{configuration[:order]}"}})
|
||||
end
|
||||
EOV
|
||||
end
|
||||
end
|
||||
|
||||
module InstanceMethods
|
||||
# Returns list of ancestors, starting from parent until root.
|
||||
#
|
||||
# subchild1.ancestors # => [child1, root]
|
||||
def ancestors
|
||||
node, nodes = self, []
|
||||
nodes << node = node.parent while node.parent
|
||||
nodes
|
||||
end
|
||||
|
||||
# Returns the root node of the tree.
|
||||
def root
|
||||
node = self
|
||||
node = node.parent while node.parent
|
||||
node
|
||||
end
|
||||
|
||||
# Returns all siblings of the current node.
|
||||
#
|
||||
# subchild1.siblings # => [subchild2]
|
||||
def siblings
|
||||
self_and_siblings - [self]
|
||||
end
|
||||
|
||||
# Returns all siblings and a reference to the current node.
|
||||
#
|
||||
# subchild1.self_and_siblings # => [subchild1, subchild2]
|
||||
def self_and_siblings
|
||||
parent ? parent.children : self.class.roots
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -5,7 +5,6 @@ require 'fixtures/comment'
|
|||
require 'fixtures/author'
|
||||
require 'fixtures/category'
|
||||
require 'fixtures/categorization'
|
||||
require 'fixtures/mixin'
|
||||
require 'fixtures/company'
|
||||
require 'fixtures/topic'
|
||||
require 'fixtures/reply'
|
||||
|
@ -53,16 +52,6 @@ class CascadedEagerLoadingTest < Test::Unit::TestCase
|
|||
assert_equal 5, authors[0].posts.size
|
||||
end
|
||||
|
||||
def test_eager_association_loading_with_acts_as_tree
|
||||
roots = TreeMixin.find(:all, :include=>"children", :conditions=>"mixins.parent_id IS NULL", :order=>"mixins.id")
|
||||
assert_equal mixins(:tree_1, :tree2_1, :tree3_1), roots
|
||||
assert_no_queries do
|
||||
assert_equal 2, roots[0].children.size
|
||||
assert_equal 0, roots[1].children.size
|
||||
assert_equal 0, roots[2].children.size
|
||||
end
|
||||
end
|
||||
|
||||
def test_eager_association_loading_with_cascaded_three_levels_by_ping_pong
|
||||
firms = Firm.find(:all, :include=>{:account=>{:firm=>:account}}, :order=>"companies.id")
|
||||
assert_equal 2, firms.size
|
||||
|
@ -103,24 +92,8 @@ class CascadedEagerLoadingTest < Test::Unit::TestCase
|
|||
authors.first.posts.first.special_comments.first.post.very_special_comment
|
||||
end
|
||||
end
|
||||
|
||||
def test_eager_association_loading_with_recursive_cascading_three_levels_has_many
|
||||
root_node = RecursivelyCascadedTreeMixin.find(:first, :include=>{:children=>{:children=>:children}}, :order => 'mixins.id')
|
||||
assert_equal mixins(:recursively_cascaded_tree_4), assert_no_queries { root_node.children.first.children.first.children.first }
|
||||
end
|
||||
|
||||
def test_eager_association_loading_with_recursive_cascading_three_levels_has_one
|
||||
root_node = RecursivelyCascadedTreeMixin.find(:first, :include=>{:first_child=>{:first_child=>:first_child}}, :order => 'mixins.id')
|
||||
assert_equal mixins(:recursively_cascaded_tree_4), assert_no_queries { root_node.first_child.first_child.first_child }
|
||||
end
|
||||
|
||||
def test_eager_association_loading_with_recursive_cascading_three_levels_belongs_to
|
||||
leaf_node = RecursivelyCascadedTreeMixin.find(:first, :include=>{:parent=>{:parent=>:parent}}, :order => 'mixins.id DESC')
|
||||
assert_equal mixins(:recursively_cascaded_tree_1), assert_no_queries { leaf_node.parent.parent.parent }
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
require 'fixtures/vertex'
|
||||
require 'fixtures/edge'
|
||||
class CascadedEagerLoadingTest < Test::Unit::TestCase
|
||||
|
|
73
activerecord/test/fixtures/mixin.rb
vendored
73
activerecord/test/fixtures/mixin.rb
vendored
|
@ -1,73 +0,0 @@
|
|||
class Mixin < ActiveRecord::Base
|
||||
|
||||
end
|
||||
|
||||
class TreeMixin < Mixin
|
||||
acts_as_tree :foreign_key => "parent_id", :order => "id"
|
||||
end
|
||||
|
||||
class TreeMixinWithoutOrder < Mixin
|
||||
acts_as_tree :foreign_key => "parent_id"
|
||||
end
|
||||
|
||||
class RecursivelyCascadedTreeMixin < Mixin
|
||||
acts_as_tree :foreign_key => "parent_id"
|
||||
has_one :first_child, :class_name => 'RecursivelyCascadedTreeMixin', :foreign_key => :parent_id
|
||||
end
|
||||
|
||||
|
||||
class NestedSet < Mixin
|
||||
acts_as_nested_set :scope => "root_id IS NULL"
|
||||
|
||||
def self.table_name() "mixins" end
|
||||
end
|
||||
|
||||
class NestedSetWithStringScope < Mixin
|
||||
acts_as_nested_set :scope => 'root_id = #{root_id}'
|
||||
|
||||
def self.table_name() "mixins" end
|
||||
end
|
||||
|
||||
class NestedSetWithSymbolScope < Mixin
|
||||
acts_as_nested_set :scope => :root
|
||||
|
||||
def self.table_name() "mixins" end
|
||||
end
|
||||
|
||||
class NestedSetSuperclass < Mixin
|
||||
acts_as_nested_set :scope => :root
|
||||
|
||||
def self.table_name() "mixins" end
|
||||
end
|
||||
|
||||
class NestedSetSubclass < NestedSetSuperclass
|
||||
|
||||
end
|
||||
|
||||
class NestedSet < Mixin
|
||||
acts_as_nested_set :scope => "root_id IS NULL"
|
||||
|
||||
def self.table_name() "mixins" end
|
||||
end
|
||||
|
||||
class NestedSetWithStringScope < Mixin
|
||||
acts_as_nested_set :scope => 'root_id = #{root_id}'
|
||||
|
||||
def self.table_name() "mixins" end
|
||||
end
|
||||
|
||||
class NestedSetWithSymbolScope < Mixin
|
||||
acts_as_nested_set :scope => :root
|
||||
|
||||
def self.table_name() "mixins" end
|
||||
end
|
||||
|
||||
class NestedSetSuperclass < Mixin
|
||||
acts_as_nested_set :scope => :root
|
||||
|
||||
def self.table_name() "mixins" end
|
||||
end
|
||||
|
||||
class NestedSetSubclass < NestedSetSuperclass
|
||||
|
||||
end
|
63
activerecord/test/fixtures/mixins.yml
vendored
63
activerecord/test/fixtures/mixins.yml
vendored
|
@ -1,71 +1,8 @@
|
|||
# tree mixins
|
||||
tree_1:
|
||||
id: 1001
|
||||
type: TreeMixin
|
||||
parent_id:
|
||||
|
||||
tree_2:
|
||||
id: 1002
|
||||
type: TreeMixin
|
||||
parent_id: 1001
|
||||
|
||||
tree_3:
|
||||
id: 1003
|
||||
type: TreeMixin
|
||||
parent_id: 1002
|
||||
|
||||
tree_4:
|
||||
id: 1004
|
||||
type: TreeMixin
|
||||
parent_id: 1001
|
||||
|
||||
tree2_1:
|
||||
id: 1005
|
||||
type: TreeMixin
|
||||
parent_id:
|
||||
|
||||
tree3_1:
|
||||
id: 1006
|
||||
type: TreeMixin
|
||||
parent_id:
|
||||
|
||||
tree_without_order_1:
|
||||
id: 1101
|
||||
type: TreeMixinWithoutOrder
|
||||
parent_id:
|
||||
|
||||
tree_without_order_2:
|
||||
id: 1100
|
||||
type: TreeMixinWithoutOrder
|
||||
parent_id:
|
||||
|
||||
recursively_cascaded_tree_1:
|
||||
id: 5005
|
||||
type: RecursivelyCascadedTreeMixin
|
||||
parent_id:
|
||||
|
||||
recursively_cascaded_tree_2:
|
||||
id: 5006
|
||||
type: RecursivelyCascadedTreeMixin
|
||||
parent_id: 5005
|
||||
|
||||
recursively_cascaded_tree_3:
|
||||
id: 5007
|
||||
type: RecursivelyCascadedTreeMixin
|
||||
parent_id: 5006
|
||||
|
||||
recursively_cascaded_tree_4:
|
||||
id: 5008
|
||||
type: RecursivelyCascadedTreeMixin
|
||||
parent_id: 5007
|
||||
|
||||
|
||||
# Nested set mixins
|
||||
|
||||
<% (1..10).each do |counter| %>
|
||||
set_<%= counter %>:
|
||||
id: <%= counter+3000 %>
|
||||
type: NestedSet
|
||||
<% end %>
|
||||
|
||||
# Big old set
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
require 'abstract_unit'
|
||||
require 'active_record/acts/tree'
|
||||
require 'active_record/acts/nested_set'
|
||||
require 'fixtures/mixin'
|
||||
|
||||
class Mixin < ActiveRecord::Base
|
||||
end
|
||||
|
||||
# Let us control what Time.now returns for the TouchTest suite
|
||||
class Time
|
||||
|
@ -21,98 +23,6 @@ class Time
|
|||
end
|
||||
|
||||
|
||||
class TreeTest < Test::Unit::TestCase
|
||||
fixtures :mixins
|
||||
|
||||
def test_children
|
||||
assert_equal mixins(:tree_1).children, mixins(:tree_2, :tree_4)
|
||||
assert_equal mixins(:tree_2).children, [mixins(:tree_3)]
|
||||
assert_equal mixins(:tree_3).children, []
|
||||
assert_equal mixins(:tree_4).children, []
|
||||
end
|
||||
|
||||
def test_parent
|
||||
assert_equal mixins(:tree_2).parent, mixins(:tree_1)
|
||||
assert_equal mixins(:tree_2).parent, mixins(:tree_4).parent
|
||||
assert_nil mixins(:tree_1).parent
|
||||
end
|
||||
|
||||
def test_delete
|
||||
assert_equal 6, TreeMixin.count
|
||||
mixins(:tree_1).destroy
|
||||
assert_equal 2, TreeMixin.count
|
||||
mixins(:tree2_1).destroy
|
||||
mixins(:tree3_1).destroy
|
||||
assert_equal 0, TreeMixin.count
|
||||
end
|
||||
|
||||
def test_insert
|
||||
@extra = mixins(:tree_1).children.create
|
||||
|
||||
assert @extra
|
||||
|
||||
assert_equal @extra.parent, mixins(:tree_1)
|
||||
|
||||
assert_equal 3, mixins(:tree_1).children.size
|
||||
assert mixins(:tree_1).children.include?(@extra)
|
||||
assert mixins(:tree_1).children.include?(mixins(:tree_2))
|
||||
assert mixins(:tree_1).children.include?(mixins(:tree_4))
|
||||
end
|
||||
|
||||
def test_ancestors
|
||||
assert_equal [], mixins(:tree_1).ancestors
|
||||
assert_equal [mixins(:tree_1)], mixins(:tree_2).ancestors
|
||||
assert_equal mixins(:tree_2, :tree_1), mixins(:tree_3).ancestors
|
||||
assert_equal [mixins(:tree_1)], mixins(:tree_4).ancestors
|
||||
assert_equal [], mixins(:tree2_1).ancestors
|
||||
assert_equal [], mixins(:tree3_1).ancestors
|
||||
end
|
||||
|
||||
def test_root
|
||||
assert_equal mixins(:tree_1), TreeMixin.root
|
||||
assert_equal mixins(:tree_1), mixins(:tree_1).root
|
||||
assert_equal mixins(:tree_1), mixins(:tree_2).root
|
||||
assert_equal mixins(:tree_1), mixins(:tree_3).root
|
||||
assert_equal mixins(:tree_1), mixins(:tree_4).root
|
||||
assert_equal mixins(:tree2_1), mixins(:tree2_1).root
|
||||
assert_equal mixins(:tree3_1), mixins(:tree3_1).root
|
||||
end
|
||||
|
||||
def test_roots
|
||||
assert_equal mixins(:tree_1, :tree2_1, :tree3_1), TreeMixin.roots
|
||||
end
|
||||
|
||||
def test_siblings
|
||||
assert_equal mixins(:tree2_1, :tree3_1), mixins(:tree_1).siblings
|
||||
assert_equal [mixins(:tree_4)], mixins(:tree_2).siblings
|
||||
assert_equal [], mixins(:tree_3).siblings
|
||||
assert_equal [mixins(:tree_2)], mixins(:tree_4).siblings
|
||||
assert_equal mixins(:tree_1, :tree3_1), mixins(:tree2_1).siblings
|
||||
assert_equal mixins(:tree_1, :tree2_1), mixins(:tree3_1).siblings
|
||||
end
|
||||
|
||||
def test_self_and_siblings
|
||||
assert_equal mixins(:tree_1, :tree2_1, :tree3_1), mixins(:tree_1).self_and_siblings
|
||||
assert_equal mixins(:tree_2, :tree_4), mixins(:tree_2).self_and_siblings
|
||||
assert_equal [mixins(:tree_3)], mixins(:tree_3).self_and_siblings
|
||||
assert_equal mixins(:tree_2, :tree_4), mixins(:tree_4).self_and_siblings
|
||||
assert_equal mixins(:tree_1, :tree2_1, :tree3_1), mixins(:tree2_1).self_and_siblings
|
||||
assert_equal mixins(:tree_1, :tree2_1, :tree3_1), mixins(:tree3_1).self_and_siblings
|
||||
end
|
||||
end
|
||||
|
||||
class TreeTestWithoutOrder < Test::Unit::TestCase
|
||||
fixtures :mixins
|
||||
|
||||
def test_root
|
||||
assert mixins(:tree_without_order_1, :tree_without_order_2).include?(TreeMixinWithoutOrder.root)
|
||||
end
|
||||
|
||||
def test_roots
|
||||
assert_equal [], mixins(:tree_without_order_1, :tree_without_order_2) - TreeMixinWithoutOrder.roots
|
||||
end
|
||||
end
|
||||
|
||||
class TouchTest < Test::Unit::TestCase
|
||||
fixtures :mixins
|
||||
|
||||
|
@ -170,15 +80,11 @@ class TouchTest < Test::Unit::TestCase
|
|||
def test_create_turned_off
|
||||
Mixin.record_timestamps = false
|
||||
|
||||
assert_nil mixins(:tree_1).updated_at
|
||||
mixins(:tree_1).save
|
||||
assert_nil mixins(:tree_1).updated_at
|
||||
assert_nil mixins(:set_1).updated_at
|
||||
mixins(:set_1).save
|
||||
assert_nil mixins(:set_1).updated_at
|
||||
|
||||
Mixin.record_timestamps = true
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
end
|
Loading…
Reference in a new issue