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