##// END OF EJS Templates
awesome_nested_set: import git 2-1-stable branch revision 3d5ac746542b564 (#6579)...
Toshi MARUYAMA -
r12869:e22c5c3f80df
parent child
Show More
@@ -1,19 +1,18
1 1 language: ruby
2 2 script: bundle exec rspec spec
3 3 env:
4 4 - DB=sqlite3
5 5 - DB=sqlite3mem
6 6 - DB=postgresql
7 7 - DB=mysql
8 8 rvm:
9 9 - 2.0.0
10 10 - 1.9.3
11 - rbx-19mode
11 - rbx
12 12 - jruby-19mode
13 13 - 1.8.7
14 - rbx-18mode
15 14 - jruby-18mode
16 15 gemfile:
17 16 - gemfiles/Gemfile.rails-3.0.rb
18 17 - gemfiles/Gemfile.rails-3.1.rb
19 18 - gemfiles/Gemfile.rails-3.2.rb
@@ -1,32 +1,38
1 1 gem 'combustion', :github => 'pat/combustion', :branch => 'master'
2 2
3 3 source 'https://rubygems.org'
4 4
5 5 gemspec :path => File.expand_path('../', __FILE__)
6 6
7 7 platforms :jruby do
8 8 gem 'activerecord-jdbcsqlite3-adapter'
9 9 gem 'activerecord-jdbcmysql-adapter'
10 10 gem 'jdbc-mysql'
11 11 gem 'activerecord-jdbcpostgresql-adapter'
12 12 gem 'jruby-openssl'
13 13 end
14 14
15 15 platforms :ruby do
16 16 gem 'sqlite3'
17 17 gem 'mysql2', (MYSQL2_VERSION if defined? MYSQL2_VERSION)
18 18 gem 'pg'
19 19 end
20 20
21 21 RAILS_VERSION = nil unless defined? RAILS_VERSION
22 22 gem 'railties', RAILS_VERSION
23 23 gem 'activerecord', RAILS_VERSION
24 24 gem 'actionpack', RAILS_VERSION
25 25
26 platforms :rbx do
27 gem 'rubysl', '~> 2.0'
28 gem 'rubysl-test-unit'
29 end
30
31
26 32 # Add Oracle Adapters
27 33 # gem 'ruby-oci8'
28 34 # gem 'activerecord-oracle_enhanced-adapter'
29 35
30 36 # Debuggers
31 37 gem 'pry'
32 38 gem 'pry-nav'
@@ -1,133 +1,135
1 1 require 'awesome_nested_set/columns'
2 2 require 'awesome_nested_set/model'
3 3
4 4 module CollectiveIdea #:nodoc:
5 5 module Acts #:nodoc:
6 6 module NestedSet #:nodoc:
7 7
8 8 # This acts provides Nested Set functionality. Nested Set is a smart way to implement
9 9 # an _ordered_ tree, with the added feature that you can select the children and all of their
10 10 # descendants with a single query. The drawback is that insertion or move need some complex
11 11 # sql queries. But everything is done here by this module!
12 12 #
13 13 # Nested sets are appropriate each time you want either an orderd tree (menus,
14 14 # commercial categories) or an efficient way of querying big trees (threaded posts).
15 15 #
16 16 # == API
17 17 #
18 18 # Methods names are aligned with acts_as_tree as much as possible to make replacment from one
19 19 # by another easier.
20 20 #
21 21 # item.children.create(:name => "child1")
22 22 #
23 23
24 24 # Configuration options are:
25 25 #
26 26 # * +:parent_column+ - specifies the column name to use for keeping the position integer (default: parent_id)
27 27 # * +:left_column+ - column name for left boundry data, default "lft"
28 28 # * +:right_column+ - column name for right boundry data, default "rgt"
29 29 # * +:depth_column+ - column name for the depth data, default "depth"
30 30 # * +:scope+ - restricts what is to be considered a list. Given a symbol, it'll attach "_id"
31 31 # (if it hasn't been already) and use that as the foreign key restriction. You
32 32 # can also pass an array to scope by multiple attributes.
33 33 # Example: <tt>acts_as_nested_set :scope => [:notable_id, :notable_type]</tt>
34 34 # * +:dependent+ - behavior for cascading destroy. If set to :destroy, all the
35 35 # child objects are destroyed alongside this object by calling their destroy
36 36 # method. If set to :delete_all (default), all the child objects are deleted
37 37 # without calling their destroy method.
38 38 # * +:counter_cache+ adds a counter cache for the number of children.
39 39 # defaults to false.
40 40 # Example: <tt>acts_as_nested_set :counter_cache => :children_count</tt>
41 41 # * +:order_column+ on which column to do sorting, by default it is the left_column_name
42 42 # Example: <tt>acts_as_nested_set :order_column => :position</tt>
43 43 #
44 44 # See CollectiveIdea::Acts::NestedSet::Model::ClassMethods for a list of class methods and
45 45 # CollectiveIdea::Acts::NestedSet::Model for a list of instance methods added
46 46 # to acts_as_nested_set models
47 47 def acts_as_nested_set(options = {})
48 48 acts_as_nested_set_parse_options! options
49 49
50 50 include Model
51 51 include Columns
52 52 extend Columns
53 53
54 54 acts_as_nested_set_relate_parent!
55 55 acts_as_nested_set_relate_children!
56 56
57 57 attr_accessor :skip_before_destroy
58 58
59 59 acts_as_nested_set_prevent_assignment_to_reserved_columns!
60 60 acts_as_nested_set_define_callbacks!
61 61 end
62 62
63 63 private
64 64 def acts_as_nested_set_define_callbacks!
65 65 # on creation, set automatically lft and rgt to the end of the tree
66 66 before_create :set_default_left_and_right
67 67 before_save :store_new_parent
68 68 after_save :move_to_new_parent, :set_depth!
69 69 before_destroy :destroy_descendants
70 70
71 71 define_model_callbacks :move
72 72 end
73 73
74 74 def acts_as_nested_set_relate_children!
75 75 has_many_children_options = {
76 76 :class_name => self.base_class.to_s,
77 77 :foreign_key => parent_column_name,
78 78 :order => quoted_order_column_name,
79 79 :inverse_of => (:parent unless acts_as_nested_set_options[:polymorphic]),
80 80 }
81 81
82 82 # Add callbacks, if they were supplied.. otherwise, we don't want them.
83 83 [:before_add, :after_add, :before_remove, :after_remove].each do |ar_callback|
84 84 has_many_children_options.update(ar_callback => acts_as_nested_set_options[ar_callback]) if acts_as_nested_set_options[ar_callback]
85 85 end
86 86
87 87 has_many :children, has_many_children_options
88 88 end
89 89
90 90 def acts_as_nested_set_relate_parent!
91 91 belongs_to :parent, :class_name => self.base_class.to_s,
92 92 :foreign_key => parent_column_name,
93 93 :counter_cache => acts_as_nested_set_options[:counter_cache],
94 94 :inverse_of => (:children unless acts_as_nested_set_options[:polymorphic]),
95 :polymorphic => acts_as_nested_set_options[:polymorphic]
95 :polymorphic => acts_as_nested_set_options[:polymorphic],
96 :touch => acts_as_nested_set_options[:touch]
96 97 end
97 98
98 99 def acts_as_nested_set_default_options
99 100 {
100 101 :parent_column => 'parent_id',
101 102 :left_column => 'lft',
102 103 :right_column => 'rgt',
103 104 :depth_column => 'depth',
104 105 :dependent => :delete_all, # or :destroy
105 106 :polymorphic => false,
106 :counter_cache => false
107 :counter_cache => false,
108 :touch => false
107 109 }.freeze
108 110 end
109 111
110 112 def acts_as_nested_set_parse_options!(options)
111 113 options = acts_as_nested_set_default_options.merge(options)
112 114
113 115 if options[:scope].is_a?(Symbol) && options[:scope].to_s !~ /_id$/
114 116 options[:scope] = "#{options[:scope]}_id".intern
115 117 end
116 118
117 119 class_attribute :acts_as_nested_set_options
118 120 self.acts_as_nested_set_options = options
119 121 end
120 122
121 123 def acts_as_nested_set_prevent_assignment_to_reserved_columns!
122 124 # no assignment to structure fields
123 125 [left_column_name, right_column_name, depth_column_name].each do |column|
124 126 module_eval <<-"end_eval", __FILE__, __LINE__
125 127 def #{column}=(x)
126 128 raise ActiveRecord::ActiveRecordError, "Unauthorized assignment to #{column}: it's an internal field handled by acts_as_nested_set code, use move_to_* methods instead."
127 129 end
128 130 end_eval
129 131 end
130 132 end
131 133 end
132 134 end
133 135 end
@@ -1,41 +1,43
1 1 require 'awesome_nested_set/tree'
2 2
3 3 module CollectiveIdea
4 4 module Acts
5 5 module NestedSet
6 6 module Model
7 7 module Rebuildable
8 8
9 9
10 10 # Rebuilds the left & rights if unset or invalid.
11 11 # Also very useful for converting from acts_as_tree.
12 12 def rebuild!(validate_nodes = true)
13 13 # default_scope with order may break database queries so we do all operation without scope
14 14 unscoped do
15 15 Tree.new(self, validate_nodes).rebuild!
16 16 end
17 17 end
18 18
19 19 private
20 20 def scope_for_rebuild
21 21 scope = proc {}
22 22
23 23 if acts_as_nested_set_options[:scope]
24 24 scope = proc {|node|
25 25 scope_column_names.inject("") {|str, column_name|
26 str << "AND #{connection.quote_column_name(column_name)} = #{connection.quote(node.send(column_name))} "
26 column_value = node.send(column_name)
27 cond = column_value.nil? ? "IS NULL" : "= #{connection.quote(column_value)}"
28 str << "AND #{connection.quote_column_name(column_name)} #{cond} "
27 29 }
28 30 }
29 31 end
30 32 scope
31 33 end
32 34
33 35 def order_for_rebuild
34 36 "#{quoted_left_column_full_name}, #{quoted_right_column_full_name}, #{primary_key}"
35 37 end
36 38 end
37 39
38 40 end
39 41 end
40 42 end
41 43 end
General Comments 0
You need to be logged in to leave comments. Login now