acts-as-tree-2.4.0/ 0000755 0001750 0001750 00000000000 12734034242 012404 5 ustar alee alee acts-as-tree-2.4.0/Rakefile 0000644 0001750 0001750 00000000751 12734034242 014054 0 ustar alee alee require 'bundler/gem_tasks'
desc "Run the tests."
task :test do
$: << "lib" << "test"
Dir["test/*_test.rb"].each { |f| require f[5..-4] }
end
task :default => :test
# Run the rdoc task to generate rdocs for this gem
require 'rdoc/task'
RDoc::Task.new do |rdoc|
require "acts_as_tree/version"
version = ActsAsTree::VERSION
rdoc.rdoc_dir = 'rdoc'
rdoc.title = "acts_as_tree-rails3 #{version}"
rdoc.rdoc_files.include('README*')
rdoc.rdoc_files.include('lib/**/*.rb')
end
acts-as-tree-2.4.0/gemfiles/ 0000755 0001750 0001750 00000000000 12734034242 014177 5 ustar alee alee acts-as-tree-2.4.0/gemfiles/rails-3.2.gemfile 0000644 0001750 0001750 00000000173 12734034242 017144 0 ustar alee alee source "https://rubygems.org"
gem "rails", "~> 3.2.22"
gem "i18n", "< 0.7"
gem "rack-cache", "< 1.3"
gemspec path: "../"
acts-as-tree-2.4.0/gemfiles/rails-3.1.gemfile 0000644 0001750 0001750 00000000172 12734034242 017142 0 ustar alee alee source "https://rubygems.org"
gem "rails", "~> 3.1.0"
gem "i18n", "< 0.7"
gem "rack-cache", "< 1.3"
gemspec path: "../"
acts-as-tree-2.4.0/gemfiles/rails-4.2.gemfile 0000644 0001750 0001750 00000000114 12734034242 017140 0 ustar alee alee source "https://rubygems.org"
gem "rails", "~> 4.2.0"
gemspec path: "../"
acts-as-tree-2.4.0/gemfiles/rails-3.0.gemfile 0000644 0001750 0001750 00000000140 12734034242 017134 0 ustar alee alee source "https://rubygems.org"
gem "rails", "~> 3.0.0"
gem "i18n", "< 0.7"
gemspec path: "../"
acts-as-tree-2.4.0/gemfiles/rails-5.0.gemfile 0000644 0001750 0001750 00000000122 12734034242 017136 0 ustar alee alee source "https://rubygems.org"
gem "rails", "~> 5.0.0.beta1"
gemspec path: "../"
acts-as-tree-2.4.0/gemfiles/rails-4.0.gemfile 0000644 0001750 0001750 00000000114 12734034242 017136 0 ustar alee alee source "https://rubygems.org"
gem "rails", "~> 4.0.0"
gemspec path: "../"
acts-as-tree-2.4.0/gemfiles/rails-4.1.gemfile 0000644 0001750 0001750 00000000114 12734034242 017137 0 ustar alee alee source "https://rubygems.org"
gem "rails", "~> 4.1.0"
gemspec path: "../"
acts-as-tree-2.4.0/acts_as_tree.gemspec 0000644 0001750 0001750 00000002265 12734034242 016412 0 ustar alee alee # -*- encoding: utf-8 -*-
$:.push File.expand_path("../lib", __FILE__)
require 'acts_as_tree/version'
Gem::Specification.new do |s|
s.name = 'acts_as_tree'
s.version = ActsAsTree::VERSION
s.authors = ['Erik Dahlstrand', 'Rails Core', 'Mark Turner', 'Swanand Pagnis', 'Felix Bünemann']
s.email = ['erik.dahlstrand@gmail.com', 'mark@amerine.net', 'swanand.pagnis@gmail.com', 'felix.buenemann@gmail.com']
s.homepage = 'https://github.com/amerine/acts_as_tree'
s.summary = %q{Provides a simple tree behaviour to active_record models.}
s.description = %q{A gem that adds simple support for organizing ActiveRecord models into parent–children relationships.}
s.files = `git ls-files`.split("\n")
s.test_files = `git ls-files -- {test,features}/*`.split("\n")
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
s.require_paths = ["lib"]
s.rdoc_options = ["--charset=UTF-8"]
s.add_dependency "activerecord", ">= 3.0.0"
# Dependencies (installed via 'bundle install')...
s.add_development_dependency "sqlite3"
s.add_development_dependency "rdoc"
s.add_development_dependency "minitest", ">= 4.7.5"
end
acts-as-tree-2.4.0/lib/ 0000755 0001750 0001750 00000000000 12734034242 013152 5 ustar alee alee acts-as-tree-2.4.0/lib/acts_as_tree/ 0000755 0001750 0001750 00000000000 12734034242 015606 5 ustar alee alee acts-as-tree-2.4.0/lib/acts_as_tree/railtie.rb 0000644 0001750 0001750 00000000361 12734034242 017564 0 ustar alee alee module ActsAsTree
class Railtie < Rails::Railtie
initializer 'acts_as_tree.insert_into_active_record' do
ActiveSupport.on_load :active_record do
ActiveRecord::Base.send(:include, ActsAsTree)
end
end
end
end
acts-as-tree-2.4.0/lib/acts_as_tree/version.rb 0000644 0001750 0001750 00000000052 12734034242 017615 0 ustar alee alee module ActsAsTree
VERSION = "2.4.0"
end
acts-as-tree-2.4.0/lib/acts_as_tree/active_record/ 0000755 0001750 0001750 00000000000 12734034242 020417 5 ustar alee alee acts-as-tree-2.4.0/lib/acts_as_tree/active_record/acts/ 0000755 0001750 0001750 00000000000 12734034242 021351 5 ustar alee alee acts-as-tree-2.4.0/lib/acts_as_tree/active_record/acts/tree.rb 0000644 0001750 0001750 00000000471 12734034242 022637 0 ustar alee alee require 'acts_as_tree'
module ActiveRecord
module Acts #:nodoc:
module Tree
include ::ActsAsTree
def self.included(base)
Kernel.warn "[DEPRECATION] The module ActiveRecord::Acts::Tree has moved to ActsAsTree"
base.extend ::ActsAsTree::ClassMethods
end
end
end
end
acts-as-tree-2.4.0/lib/acts_as_tree.rb 0000644 0001750 0001750 00000023230 12734034242 016133 0 ustar alee alee require 'acts_as_tree/version'
module ActsAsTree
if defined? Rails::Railtie
require 'acts_as_tree/railtie'
elsif defined? Rails::Initializer
raise "acts_as_tree 1.0 is not compatible with Rails 2.3 or older"
end
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
# include ActsAsTree
#
# 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
# acts_as_tree:
# * siblings - Returns all the children of the parent, excluding
# the current node ([subchild2] when called
# on subchild1)
# * self_and_siblings - Returns all the children of the parent,
# including the current node ([subchild1, subchild2]
# when called on subchild1)
# * ancestors - Returns all the ancestors of the current node
# ([child1, root] when called on subchild2)
# * root - Returns the root of the current node (root
# when called on subchild2)
module ClassMethods
# Configuration options are:
#
# * foreign_key - specifies the column name to use for tracking
# of the tree (default: +parent_id+)
# * order - makes it possible to sort the children according to
# this SQL snippet.
# * counter_cache - keeps a count in a +children_count+ column
# if set to +true+ (default: +false+). Specify
# a custom column by passing a symbol or string.
def acts_as_tree(options = {})
configuration = {
foreign_key: "parent_id",
order: nil,
counter_cache: nil,
dependent: :destroy,
touch: false
}
configuration.update(options) if options.is_a?(Hash)
if configuration[:counter_cache] == true
configuration[:counter_cache] = :children_count
end
belongs_to_opts = {
class_name: name,
foreign_key: configuration[:foreign_key],
counter_cache: configuration[:counter_cache],
touch: configuration[:touch],
inverse_of: :children
}
belongs_to_opts[:optional] = true if ActiveRecord::VERSION::MAJOR >= 5
belongs_to :parent, belongs_to_opts
if ActiveRecord::VERSION::MAJOR >= 4
has_many :children, lambda { order configuration[:order] },
class_name: name,
foreign_key: configuration[:foreign_key],
dependent: configuration[:dependent],
inverse_of: :parent
else
has_many :children, class_name: name,
foreign_key: configuration[:foreign_key],
order: configuration[:order],
dependent: configuration[:dependent],
inverse_of: :parent
end
class_eval <<-EOV
include ActsAsTree::InstanceMethods
def self.default_tree_order
order_option = %Q{#{configuration.fetch :order, "nil"}}
order(order_option)
end
def self.root
self.roots.first
end
def self.roots
where(:#{configuration[:foreign_key]} => nil).default_tree_order
end
EOV
if configuration[:counter_cache]
after_update :update_parents_counter_cache
def children_counter_cache_column
reflect_on_association(:parent).counter_cache_column
end
def leaves
where(children_counter_cache_column => 0).default_tree_order
end
else
# Fallback to less efficent ways to find leaves.
class_eval <<-EOV
def self.leaves
internal_ids = select(:#{configuration[:foreign_key]}).where(arel_table[:#{configuration[:foreign_key]}].not_eq(nil))
where("id NOT IN (\#{internal_ids.to_sql})").default_tree_order
end
EOV
end
end
end
module TreeView
# show records in a tree view
# Example:
# root
# |_ child1
# | |_ subchild1
# | |_ subchild2
# |_ child2
# |_ subchild3
# |_ subchild4
#
def tree_view(label_method = :to_s, node = nil, level = -1)
if node.nil?
puts "root"
nodes = roots
else
label = "|_ #{node.send(label_method)}"
if level == 0
puts " #{label}"
else
puts " |#{" "*level}#{label}"
end
nodes = node.children
end
nodes.each do |child|
tree_view(label_method, child, level+1)
end
end
end
module TreeWalker
# Traverse the tree and call a block with the current node and current
# depth-level.
#
# options:
# algorithm:
# :dfs for depth-first search (default)
# :bfs for breadth-first search
# where: AR where statement to filter certain nodes
#
# The given block sets two parameters:
# first: The current node
# second: The current depth-level within the tree
#
# Example of acts_as_tree for model Page (ERB view):
# <% Page.walk_tree do |page, level| %>
# <%= link_to "#{' '*level}#{page.name}", page_path(page) %>
# <% end %>
#
# There is also a walk_tree instance method that starts walking from
# the node it is called on.
#
def walk_tree(options = {}, &block)
algorithm = options.fetch :algorithm, :dfs
where = options.fetch :where, {}
send("walk_tree_#{algorithm}", where, &block)
end
def self.extended(mod)
mod.class_eval do
def walk_tree(options = {}, &block)
algorithm = options.fetch :algorithm, :dfs
where = options.fetch :where, {}
self.class.send("walk_tree_#{algorithm}", where, self, &block)
end
end
end
private
def walk_tree_bfs(where = {}, node = nil, level = -1, &block)
nodes = (node.nil? ? roots : node.children).where(where)
nodes.each { |child| block.call child, level + 1 }
nodes.each { |child| walk_tree_bfs where, child, level + 1, &block }
end
def walk_tree_dfs(where = {}, node = nil, level = -1, &block)
block.call node, level unless level == -1
nodes = (node.nil? ? roots : node.children).where(where)
nodes.each { |child| walk_tree_dfs where, child, level + 1, &block }
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 list of descendants, starting from current node, not including current node.
#
# root.descendants # => [child1, child2, subchild1, subchild2, subchild3, subchild4]
def descendants
children.each_with_object(children.to_a) {|child, arr|
arr.concat child.descendants
}.uniq
end
# Returns list of descendants, starting from current node, including current node.
#
# root.self_and_descendants # => [root, child1, child2, subchild1, subchild2, subchild3, subchild4]
def self_and_descendants
[self] + descendants
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
# Returns children (without subchildren) and current node itself.
#
# root.self_and_children # => [root, child1]
def self_and_children
[self] + self.children
end
# Returns ancestors and current node itself.
#
# subchild1.self_and_ancestors # => [subchild1, child1, root]
def self_and_ancestors
[self] + self.ancestors
end
# Returns true if node has no parent, false otherwise
#
# subchild1.root? # => false
# root.root? # => true
def root?
parent.nil?
end
# Returns true if node has no children, false otherwise
#
# subchild1.leaf? # => true
# child1.leaf? # => false
def leaf?
children.size.zero?
end
private
def update_parents_counter_cache
counter_cache_column = self.class.children_counter_cache_column
if parent_id_changed?
self.class.decrement_counter(counter_cache_column, parent_id_was)
self.class.increment_counter(counter_cache_column, parent_id)
end
end
end
end
# Deprecating the following code in the future.
require 'acts_as_tree/active_record/acts/tree'
acts-as-tree-2.4.0/.gitignore 0000644 0001750 0001750 00000000100 12734034242 014363 0 ustar alee alee *.gem
.bundle
pkg/*
.rvmrc
Gemfile.lock
gemfiles/*.gemfile.lock
acts-as-tree-2.4.0/test/ 0000755 0001750 0001750 00000000000 12734034242 013363 5 ustar alee alee acts-as-tree-2.4.0/test/acts_as_tree_test.rb 0000644 0001750 0001750 00000035437 12734034242 017417 0 ustar alee alee require 'minitest/autorun'
require 'minitest/benchmark'
require 'active_record'
require 'acts_as_tree'
class ActsAsTreeTestCase < (defined?(MiniTest::Test) ? MiniTest::Test : MiniTest::Unit::TestCase)
def assert_queries(num = 1, &block)
query_count, result = count_queries(&block)
result
ensure
assert_equal num, query_count, "#{query_count} instead of #{num} queries were executed."
end
def assert_no_queries(&block)
assert_queries(0, &block)
end
def count_queries &block
count = 0
counter_f = ->(name, started, finished, unique_id, payload) {
unless %w[ CACHE SCHEMA ].include? payload[:name]
count += 1
end
}
begin
subscribed = ActiveSupport::Notifications.subscribe("sql.active_record", &counter_f)
result = block.call
ensure
ActiveSupport::Notifications.unsubscribe subscribed
end
[count, result]
end
def capture_stdout(&block)
real_stdout = $stdout
$stdout = StringIO.new
yield
$stdout.string
ensure
$stdout = real_stdout
end
end
ActiveRecord::Base.establish_connection adapter: "sqlite3", database: ":memory:"
def setup_db(counter_cache = false)
# AR keeps printing annoying schema statements
capture_stdout do
ActiveRecord::Base.logger
ActiveRecord::Schema.define(version: 1) do
create_table :mixins do |t|
t.column :type, :string
t.column :parent_id, :integer
t.column(:children_count, :integer, default: 0) if counter_cache
t.timestamps null: false
end
end
Mixin.reset_column_information
end
end
def teardown_db
# Silence tables deprecation on rails 5.0
ActiveSupport::Deprecation.silence do
ActiveRecord::Base.connection.tables.each do |table|
ActiveRecord::Base.connection.drop_table(table)
end
end
end
class Mixin < ActiveRecord::Base
include ActsAsTree
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 TreeMixinNullify < Mixin
acts_as_tree foreign_key: "parent_id", order: "id", dependent: :nullify
end
class TreeMixinWithCounterCache < Mixin
acts_as_tree foreign_key: "parent_id", order: "id", counter_cache: :children_count
end
class RecursivelyCascadedTreeMixin < Mixin
acts_as_tree foreign_key: "parent_id"
has_one :first_child, class_name: 'RecursivelyCascadedTreeMixin', foreign_key: :parent_id
end
class TreeMixinWithTouch < Mixin
acts_as_tree foreign_key: "parent_id", order: "id", touch: true
end
class TreeTest < ActsAsTreeTestCase
def setup
setup_db
@root1 = TreeMixin.create!
@root_child1 = TreeMixin.create! parent_id: @root1.id
@child1_child = TreeMixin.create! parent_id: @root_child1.id
@child1_child_child = TreeMixin.create! parent_id: @child1_child.id
@root_child2 = TreeMixin.create! parent_id: @root1.id
@root2 = TreeMixin.create!
@root3 = TreeMixin.create!
end
def teardown
teardown_db
end
def test_children
assert_equal @root1.children, [@root_child1, @root_child2]
assert_equal @root_child1.children, [@child1_child]
assert_equal @child1_child.children, [@child1_child_child]
assert_equal @child1_child_child.children, []
assert_equal @root_child2.children, []
end
def test_parent
assert_equal @root_child1.parent, @root1
assert_equal @root_child1.parent, @root_child2.parent
assert_nil @root1.parent
end
def test_delete
assert_equal 7, TreeMixin.count
@root1.destroy
assert_equal 2, TreeMixin.count
@root2.destroy
@root3.destroy
assert_equal 0, TreeMixin.count
end
def test_insert
@extra = @root1.children.create
assert @extra
assert_equal @extra.parent, @root1
assert_equal 3, @root1.reload.children.count
assert @root1.children.include?(@extra)
assert @root1.children.include?(@root_child1)
assert @root1.children.include?(@root_child2)
end
def test_ancestors
assert_equal [], @root1.ancestors
assert_equal [@root1], @root_child1.ancestors
assert_equal [@root_child1, @root1], @child1_child.ancestors
assert_equal [@root1], @root_child2.ancestors
assert_equal [], @root2.ancestors
assert_equal [], @root3.ancestors
end
def test_root
assert_equal @root1, TreeMixin.root
assert_equal @root1, @root1.root
assert_equal @root1, @root_child1.root
assert_equal @root1, @child1_child.root
assert_equal @root1, @root_child2.root
assert_equal @root2, @root2.root
assert_equal @root3, @root3.root
end
def test_roots
assert_equal [@root1, @root2, @root3], TreeMixin.roots
end
def test_leaves
assert_equal [@child1_child_child, @root_child2, @root2, @root3], TreeMixin.leaves
end
def test_default_tree_order
assert_equal [@root1, @root_child1, @child1_child, @child1_child_child, @root_child2, @root2, @root3], TreeMixin.default_tree_order
end
def test_siblings
assert_equal [@root2, @root3], @root1.siblings
assert_equal [@root_child2], @root_child1.siblings
assert_equal [], @child1_child.siblings
assert_equal [@root_child1], @root_child2.siblings
assert_equal [@root1, @root3], @root2.siblings
assert_equal [@root1, @root2], @root3.siblings
end
def test_self_and_siblings
assert_equal [@root1, @root2, @root3], @root1.self_and_siblings
assert_equal [@root_child1, @root_child2], @root_child1.self_and_siblings
assert_equal [@child1_child], @child1_child.self_and_siblings
assert_equal [@root_child1, @root_child2], @root_child2.self_and_siblings
assert_equal [@root1, @root2, @root3], @root2.self_and_siblings
assert_equal [@root1, @root2, @root3], @root3.self_and_siblings
end
def test_self_and_children
assert_equal [@root1, @root_child1, @root_child2], @root1.self_and_children
assert_equal [@root2], @root2.self_and_children
end
def test_self_and_ancestors
assert_equal [@child1_child, @root_child1, @root1], @child1_child.self_and_ancestors
assert_equal [@root2], @root2.self_and_ancestors
end
def test_self_and_descendants
assert_equal [@root1, @root_child1, @root_child2, @child1_child, @child1_child_child], @root1.self_and_descendants
assert_equal [@root2], @root2.self_and_descendants
end
def test_descendants
assert_equal [@root_child1, @root_child2, @child1_child, @child1_child_child], @root1.descendants
assert_equal [], @root2.descendants
end
def test_nullify
root4 = TreeMixinNullify.create!
root4_child = TreeMixinNullify.create! parent_id: root4.id
assert_equal 2, TreeMixinNullify.count
assert_equal root4.id, root4_child.parent_id
root4.destroy
assert_equal 1, TreeMixinNullify.count
assert_nil root4_child.reload.parent_id
end
def test_is_root
assert_equal true, @root1.root?
assert_equal true, @root2.root?
assert_equal true, @root3.root?
assert_equal false, @root_child1.root?
assert_equal false, @child1_child.root?
assert_equal false, @child1_child_child.root?
assert_equal false, @root_child2.root?
end
def test_is_leaf
assert_equal true, @root2.leaf?
assert_equal true, @root3.leaf?
assert_equal true, @child1_child_child.leaf?
assert_equal true, @root_child2.leaf?
assert_equal false, @root1.leaf?
assert_equal false, @root_child1.leaf?
assert_equal false, @child1_child.leaf?
end
def test_tree_view
assert_equal false, Mixin.respond_to?(:tree_view)
Mixin.extend ActsAsTree::TreeView
assert_equal true, TreeMixin.respond_to?(:tree_view)
tree_view_outputs = <<-END.gsub(/^ {6}/, '')
root
|_ 1
| |_ 2
| |_ 3
| |_ 4
| |_ 5
|_ 6
|_ 7
END
assert_equal tree_view_outputs, capture_stdout { TreeMixin.tree_view(:id) }
end
def test_tree_walker
assert_equal false, TreeMixin.respond_to?(:walk_tree)
assert_equal false, TreeMixin.new.respond_to?(:walk_tree)
TreeMixin.extend ActsAsTree::TreeWalker
assert_equal true, TreeMixin.respond_to?(:walk_tree)
assert_equal true, TreeMixin.new.respond_to?(:walk_tree)
walk_tree_dfs_output = <<-END.gsub(/^\s+/, '')
1
-2
--3
---4
-5
6
7
END
assert_equal walk_tree_dfs_output, capture_stdout { TreeMixin.walk_tree{|elem, level| puts "#{'-'*level}#{elem.id}"} }
walk_tree_dfs_sub_output = <<-END.gsub(/^\s+/, '')
2
-3
--4
5
END
assert_equal walk_tree_dfs_sub_output, capture_stdout { @root1.walk_tree{|elem, level| puts "#{'-'*level}#{elem.id}"} }
walk_tree_bfs_output = <<-END.gsub(/^\s+/, '')
1
6
7
-2
-5
--3
---4
END
assert_equal walk_tree_bfs_output, capture_stdout { TreeMixin.walk_tree(:algorithm => :bfs){|elem, level| puts "#{'-'*level}#{elem.id}"} }
walk_tree_bfs_sub_output = <<-END.gsub(/^\s+/, '')
2
5
-3
--4
END
assert_equal walk_tree_bfs_sub_output, capture_stdout { @root1.walk_tree(:algorithm => :bfs){|elem, level| puts "#{'-'*level}#{elem.id}"} }
end
end
class TestDeepDescendantsPerformance < ActsAsTreeTestCase
def setup
teardown_db
setup_db
@root1 = TreeMixin.create!
create_cascade_children @root1, "root1", 10
@root2 = TreeMixin.create!
create_cascade_children @root2, "root2", 20
@root3 = TreeMixin.create!
create_cascade_children @root3, "root3", 30
@root4 = TreeMixin.create!
create_cascade_children @root4, "root4", 40
@root5 = TreeMixin.create!
create_cascade_children @root5, "root5", 50
end
def teardown
teardown_db
end
def self.bench_range
[1, 2, 3, 4, 5]
end
def bench_descendants
skip("until I deal with the performance difference on travis")
assert_performance_linear 0.99 do |x|
obj = instance_variable_get "@root#{x}"
obj.descendants
end
end
def create_cascade_children parent, parent_name, count
first_child_name = "@#{parent_name}_child1"
first_record = TreeMixin.create! parent_id: parent.id
instance_variable_set first_child_name, first_record
(2...count).each do |child_count|
name = "@#{parent_name}_child#{child_count}"
prev = instance_variable_get "@#{parent_name}_child#{child_count - 1}"
new_record = TreeMixin.create! parent_id: prev.id
instance_variable_set name, new_record
end
end
end
class TreeTestWithEagerLoading < ActsAsTreeTestCase
def setup
teardown_db
setup_db
@root1 = TreeMixin.create!
@root_child1 = TreeMixin.create! parent_id: @root1.id
@child1_child = TreeMixin.create! parent_id: @root_child1.id
@root_child2 = TreeMixin.create! parent_id: @root1.id
@root2 = TreeMixin.create!
@root3 = TreeMixin.create!
@rc1 = RecursivelyCascadedTreeMixin.create!
@rc2 = RecursivelyCascadedTreeMixin.create! parent_id: @rc1.id
@rc3 = RecursivelyCascadedTreeMixin.create! parent_id: @rc2.id
@rc4 = RecursivelyCascadedTreeMixin.create! parent_id: @rc3.id
end
def teardown
teardown_db
end
def test_eager_association_loading
roots = TreeMixin.includes(:children)
.where('mixins.parent_id IS NULL')
.order('mixins.id')
assert_equal [@root1, @root2, @root3], 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_recursive_cascading_three_levels_has_many
root_node = RecursivelyCascadedTreeMixin.includes({children: {children: :children}})
.order('mixins.id')
.first
assert_equal @rc4, 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.includes({first_child: {first_child: :first_child}})
.order('mixins.id')
.first
assert_equal @rc4, 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.includes({parent: {parent: :parent}})
.order('mixins.id DESC')
.first
assert_equal @rc1, assert_no_queries { leaf_node.parent.parent.parent }
end
end
class TreeTestWithoutOrder < ActsAsTreeTestCase
def setup
setup_db
@root1 = TreeMixinWithoutOrder.create!
@root2 = TreeMixinWithoutOrder.create!
end
def teardown
teardown_db
end
def test_root
assert [@root1, @root2].include? TreeMixinWithoutOrder.root
end
def test_roots
assert_equal [], [@root1, @root2] - TreeMixinWithoutOrder.roots
end
end
class UnsavedTreeTest < ActsAsTreeTestCase
def setup
setup_db
@root = TreeMixin.new
@root_child = @root.children.build
end
def teardown
teardown_db
end
def test_inverse_of
# We want children to be aware of their parent before saving either
assert_equal @root, @root_child.parent
end
end
class TreeTestWithCounterCache < ActsAsTreeTestCase
def setup
teardown_db
setup_db(true)
@root = TreeMixinWithCounterCache.create!
@child1 = TreeMixinWithCounterCache.create! parent_id: @root.id
@child1_child1 = TreeMixinWithCounterCache.create! parent_id: @child1.id
@child2 = TreeMixinWithCounterCache.create! parent_id: @root.id
[@root, @child1, @child1_child1, @child2].map(&:reload)
end
def teardown
teardown_db
end
def test_counter_cache
assert_equal 2, @root.children_count
assert_equal 1, @child1.children_count
end
def test_update_parents_counter_cache
@child1_child1.update_attributes(:parent_id => @root.id)
assert_equal 3, @root.reload.children_count
assert_equal 0, @child1.reload.children_count
end
def test_leaves
assert_equal [@child1_child1, @child2], TreeMixinWithCounterCache.leaves
assert !@root.leaf?
assert @child2.leaf?
end
def test_counter_cache_being_used
assert_no_queries { @root.leaf? }
assert_no_queries { @child2.leaf? }
end
end
class TreeTestWithTouch < ActsAsTreeTestCase
def setup
teardown_db
setup_db
@root = TreeMixinWithTouch.create!
@child = TreeMixinWithTouch.create! parent_id: @root.id
end
def teardown
teardown_db
end
def test_updated_at
previous_root_updated_at = @root.updated_at
@child.update_attributes(:type => "new_type")
@root.reload
assert @root.updated_at != previous_root_updated_at
end
end
acts-as-tree-2.4.0/.travis.yml 0000644 0001750 0001750 00000002331 12734034242 014514 0 ustar alee alee language: ruby
sudo: false
cache: bundler
rvm:
- 1.9.2
- 1.9.3
- 2.0.0
- 2.1.8
- 2.2.4
gemfile:
- gemfiles/rails-3.0.gemfile
- gemfiles/rails-3.1.gemfile
- gemfiles/rails-3.2.gemfile
- gemfiles/rails-4.0.gemfile
- gemfiles/rails-4.1.gemfile
- gemfiles/rails-4.2.gemfile
- gemfiles/rails-5.0.gemfile
matrix:
exclude:
# rails 4.x requries ruby 1.9.3 or newer
- rvm: 1.9.2
gemfile: gemfiles/rails-4.0.gemfile
- rvm: 1.9.2
gemfile: gemfiles/rails-4.1.gemfile
- rvm: 1.9.2
gemfile: gemfiles/rails-4.2.gemfile
# rails < 3.2 is unsupported on ruby 2.0+
- rvm: 2.0.0
gemfile: gemfiles/rails-3.0.gemfile
- rvm: 2.0.0
gemfile: gemfiles/rails-3.1.gemfile
- rvm: 2.1.8
gemfile: gemfiles/rails-3.0.gemfile
- rvm: 2.1.8
gemfile: gemfiles/rails-3.1.gemfile
- rvm: 2.2.4
gemfile: gemfiles/rails-3.0.gemfile
- rvm: 2.2.4
gemfile: gemfiles/rails-3.1.gemfile
# rails 5.0 requires ruby 2.2.2+
- rvm: 1.9.2
gemfile: gemfiles/rails-5.0.gemfile
- rvm: 1.9.3
gemfile: gemfiles/rails-5.0.gemfile
- rvm: 2.0.0
gemfile: gemfiles/rails-5.0.gemfile
- rvm: 2.1.8
gemfile: gemfiles/rails-5.0.gemfile
acts-as-tree-2.4.0/README.md 0000644 0001750 0001750 00000014722 12734034242 013671 0 ustar alee alee # ActsAsTree
[](http://travis-ci.org/amerine/acts\_as\_tree)
[](http://badge.fury.io/rb/acts\_as\_tree)
ActsAsTree extends ActiveRecord to add simple support for organizing items into parent–children relationships. By default, ActsAsTree expects a foreign key column called `parent_id`.
## Example
```ruby
class Category < ActiveRecord::Base
acts_as_tree order: "name"
end
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
```
We also have a convenient `TreeView` module you can mixin if you want a little visual representation of the tree strucuture. Example:
```ruby
class Category < ActiveRecord::Base
extend ActsAsTree::TreeView
acts_as_tree order: 'name'
end
> Category.tree_view(:name)
root
|_ child1
| |_ subchild1
| |_ subchild2
|_ child2
|_ subchild3
|_ subchild4
=> nil
```
And there's a `TreeWalker` module (traversing the tree using depth-first search (default) or breadth-first search) as well. Example given the Model `Page` as
```ruby
class Page < ActiveRecord::Base
extend ActsAsTree::TreeWalker
acts_as_tree order: 'rank'
end
```
In your view you could traverse the tree using
```erb
<% Page.walk_tree do |page, level| %>
<%= link_to "#{'-'*level}#{page.name}", page_path(page) %>
<% end %>
```
You also could use walk\_tree as an instance method such as:
```erb
<% Page.first.walk_tree do |page, level| %>
<%= link_to "#{'-'*level}#{page.name}", page_path(page) %>
<% end %>
```
## Compatibility
We no longer support Ruby 1.8 or versions of Rails/ActiveRecord older than 3.0. If you're using a version of ActiveRecord older than 3.0 please use 0.1.1.
Moving forward we will do our best to support the latest versions of ActiveRecord and Ruby.
## Change Log
* 2.4.0 - January 12, 2016
* Added support for rails 5.0, see #46 -- klacointe
* 2.3.0 - November 6, 2015
* Added touch option to acts\_as\_tree relation. See #40 -- mbenitezm
* Fix tests on rails 3.x with ruby 1.9.2. See #41 -- felixbuenemann
* 2.2.0 - June 15, 2015
* Added TreeWalker.walk\_tree instance method. See #32, #37, #38 -- felixbuenemann, genewoo
* Fix tests on rails 3.x. See #36 -- marshall-lee
* 2.1.0 - September 25, 2014
* Added TreeWalker. See #30 -- 545ch4
* 2.0.0 - July 3, 2014
* Renamed Presentation module to TreeView, see #27, #28 -- felixbuenemann
* 1.6.1 - May 29, 2014
* Readme Improvements, see #26 -- schlick
* Improvements and Fixes for counter cache (fix counter\_cache: true). See #24, #25 -- dv
* Cleanup and fix tests, see #24.
* 1.6.0 - April 21, 2014
* Added new `leaves` method. See #23 -- MichalPokorny
* 1.5.1 - March 28, 2014
* Fixing descendants modification bug. See #20 -- amerine, tmuerell
* 1.5.0 - December 16, 2013
* Added new `descendants` method -- adamkleingit
* Fixed warning message -- akicho8
* 1.4.0 - June 25, 2013
* `Presentation#tree_view` -- rainchen
* `root?` && `leaf?` methods. -- xuanxu
* 1.3.0 - March 29, 2013
* Rails 4.0 Support! -- mischa78
* Readme Fixes -- mischa78 & seanhussey
* 1.2.0 - October 29, 2012
* Adding new `self_with_ancestors` accessor -- felixbuenemann
* `roots` is now a scope.
* 1.1.0 - April 24, 2012
* Deprecate the ActiveRecord::Acts::Tree module in favor of ActsAsTree
* 1.0.1 - April 18, 2012
* Include the Railtie for easier loading in Rails. Will reassess the forced module inclusion in the future.
* 1.0.0 - April 14, 2012
* Official 1.0 release. Force users to include the ActiveRecord::Acts::Tree module.
* 0.2.0 - April 9, 2012
* Rails 3 Support
* 0.1.1 - February 3, 2010
* Bug Fixes
* 0.1.0 - October 9, 2009
* First Gem Release
## Note on Patches/Pull Requests
1. Fork the project.
2. Make your feature addition or bug fix.
3. Add tests for it. This is important so we don't break it in a future version
unintentionally.
4. Commit, do not mess with rakefile, version, or history. (if you want to have
your own version, that is fine but bump version in a commit by itself so we can
ignore when we pull)
5. Send us a pull request. Bonus points for topic branches.
6. All contributors to this project, after their first accepted patch, are given push
access to the repository and are welcome as full contributors to ActsAsTree. All
we ask is that all changes go through CI and a Pull Request before merging.
## Releasing new versions
1. We follow Semver. So if you're shipping interface breaking changes then bump
the major version. We don't care if we ship version 1101.1.1, as long as
people know that 1101.1.1 has breaking differences from 1100.0. If you're
adding new features, but not changing existing functionality bump the minor
version, if you're shipping a bugfix, just bump the patch.
2. Following the above rules, change the version found in lib/acts_as_tree/version.rb.
3. Make sure the Change log in the README includes a brief summary of the versions
changes, with credit to the contributors.
4. Commit these changes in one "release-prep" commit (on the master branch).
5. Push that commit up to the repo.
6. Run `rake release`
This will create and push a tag to Github, then generate a gem and push it to
Rubygems.
7. Profit.
## License (MIT)
Copyright (c) 2007 David Heinemeier Hansson
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the 'Software'), to deal in the
Software without restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
acts-as-tree-2.4.0/Gemfile 0000644 0001750 0001750 00000000110 12734034242 013667 0 ustar alee alee source 'https://rubygems.org'
gemspec
group :test do
gem 'rake'
end
acts-as-tree-2.4.0/metadata.yml 0000644 0001750 0001750 00000006043 12734034242 014712 0 ustar alee alee --- !ruby/object:Gem::Specification
name: acts_as_tree
version: !ruby/object:Gem::Version
version: 2.4.0
platform: ruby
authors:
- Erik Dahlstrand
- Rails Core
- Mark Turner
- Swanand Pagnis
- Felix Bünemann
autorequire:
bindir: bin
cert_chain: []
date: 2016-01-11 00:00:00.000000000 Z
dependencies:
- !ruby/object:Gem::Dependency
name: activerecord
requirement: !ruby/object:Gem::Requirement
requirements:
- - ">="
- !ruby/object:Gem::Version
version: 3.0.0
type: :runtime
prerelease: false
version_requirements: !ruby/object:Gem::Requirement
requirements:
- - ">="
- !ruby/object:Gem::Version
version: 3.0.0
- !ruby/object:Gem::Dependency
name: sqlite3
requirement: !ruby/object:Gem::Requirement
requirements:
- - ">="
- !ruby/object:Gem::Version
version: '0'
type: :development
prerelease: false
version_requirements: !ruby/object:Gem::Requirement
requirements:
- - ">="
- !ruby/object:Gem::Version
version: '0'
- !ruby/object:Gem::Dependency
name: rdoc
requirement: !ruby/object:Gem::Requirement
requirements:
- - ">="
- !ruby/object:Gem::Version
version: '0'
type: :development
prerelease: false
version_requirements: !ruby/object:Gem::Requirement
requirements:
- - ">="
- !ruby/object:Gem::Version
version: '0'
- !ruby/object:Gem::Dependency
name: minitest
requirement: !ruby/object:Gem::Requirement
requirements:
- - ">="
- !ruby/object:Gem::Version
version: 4.7.5
type: :development
prerelease: false
version_requirements: !ruby/object:Gem::Requirement
requirements:
- - ">="
- !ruby/object:Gem::Version
version: 4.7.5
description: A gem that adds simple support for organizing ActiveRecord models into
parent–children relationships.
email:
- erik.dahlstrand@gmail.com
- mark@amerine.net
- swanand.pagnis@gmail.com
- felix.buenemann@gmail.com
executables: []
extensions: []
extra_rdoc_files: []
files:
- ".gitignore"
- ".travis.yml"
- Gemfile
- README.md
- Rakefile
- acts_as_tree.gemspec
- gemfiles/rails-3.0.gemfile
- gemfiles/rails-3.1.gemfile
- gemfiles/rails-3.2.gemfile
- gemfiles/rails-4.0.gemfile
- gemfiles/rails-4.1.gemfile
- gemfiles/rails-4.2.gemfile
- gemfiles/rails-5.0.gemfile
- lib/acts_as_tree.rb
- lib/acts_as_tree/active_record/acts/tree.rb
- lib/acts_as_tree/railtie.rb
- lib/acts_as_tree/version.rb
- test/acts_as_tree_test.rb
homepage: https://github.com/amerine/acts_as_tree
licenses: []
metadata: {}
post_install_message:
rdoc_options:
- "--charset=UTF-8"
require_paths:
- lib
required_ruby_version: !ruby/object:Gem::Requirement
requirements:
- - ">="
- !ruby/object:Gem::Version
version: '0'
required_rubygems_version: !ruby/object:Gem::Requirement
requirements:
- - ">="
- !ruby/object:Gem::Version
version: '0'
requirements: []
rubyforge_project:
rubygems_version: 2.4.8
signing_key:
specification_version: 4
summary: Provides a simple tree behaviour to active_record models.
test_files:
- test/acts_as_tree_test.rb