@@ -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 |
|
|
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 |
|
|
|
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