loyal_awesome_nested_set 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG ADDED
@@ -0,0 +1,57 @@
1
+ 2.1.6
2
+ * Fixed rebuild! when there is a default_scope with order [Adrian Serafin]
3
+ * Testing with stable bundler, ruby 2.0, MySQL and PostgreSQL [Philip Arndt]
4
+ * Optimized move_to for large trees [ericsmith66]
5
+
6
+ 2.1.5
7
+ * Worked around issues where AR#association wasn't present on Rails 3.0.x. [Philip Arndt]
8
+ * Adds option 'order_column' which defaults to 'left_column_name'. [gudata]
9
+ * Added moving with order functionality. [Sytse Sijbrandij]
10
+ * Use tablename in all select queries. [Mikhail Dieterle]
11
+ * Made sure all descendants' depths are updated when moving parent, not just immediate child. [Phil Thompson]
12
+ * Add documentation of the callbacks. [Tobias Maier]
13
+
14
+ 2.1.4
15
+ * nested_set_options accept both Class & AR Relation. [Semyon Perepelitsa]
16
+ * Reduce the number of queries triggered by the canonical usage of `i.level` in the `nested_set` helpers. [thedarkone]
17
+ * Specifically require active_record [Bogdan Gusiev]
18
+ * compute_level now checks for a non nil association target. [Joel Nimety]
19
+
20
+ 2.1.3
21
+ * Update child depth when parent node is moved. [Amanda Wagener]
22
+ * Added move_to_child_with_index. [Ben Zhang]
23
+ * Optimised self_and_descendants for when there's an index on lft. [Mark Torrance]
24
+ * Added support for an unsaved record to return the right 'root'. [Philip Arndt]
25
+
26
+ 2.1.2
27
+ * Fixed regressions introduced. [Philip Arndt]
28
+
29
+ 2.1.1
30
+ * Added 'depth' which indicates how many levels deep the node is.
31
+ This only works when you have a column called 'depth' in your table,
32
+ otherwise it doesn't set itself. [Philip Arndt]
33
+ * Rails 3.2 support added. [Gabriel Sobrinho]
34
+ * Oracle compatibility added. [Pikender Sharma]
35
+ * Adding row locking to deletion, locking source of pivot values, and adding retry on collisions. [Markus J. Q. Roberts]
36
+ * Added method and helper for sorting children by column. [bluegod]
37
+ * Fixed .all_roots_valid? to work with Postgres. [Joshua Clayton]
38
+ * Made compatible with polymorphic belongs_to. [Graham Randall]
39
+ * Added in the association callbacks to the children :has_many association. [Michael Deering]
40
+ * Modified helper to allow using array of objects as argument. [Rahmat Budiharso]
41
+ * Fixed cases where we were calling attr_protected. [Jacob Swanner]
42
+ * Fixed nil cases involving lft and rgt. [Stuart Coyle] and [Patrick Morgan]
43
+
44
+ 2.0.2
45
+ * Fixed deprecation warning under Rails 3.1 [Philip Arndt]
46
+ * Converted Test::Unit matchers to RSpec. [Uģis Ozols]
47
+ * Added inverse_of to associations to improve performance rendering trees. [Sergio Cambra]
48
+ * Added row locking and fixed some race conditions. [Markus J. Q. Roberts]
49
+
50
+ 2.0.1
51
+ * Fixed a bug with move_to not using nested_set_scope [Andreas Sekine]
52
+
53
+ 2.0.0.pre
54
+ * Expect Rails 3
55
+ * Changed how callbacks work. Returning false in a before_move action does not block save operations. Use a validation or exception in the callback if you need that.
56
+ * Switched to RSpec
57
+ * Remove use of Comparable
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2007-2011 Collective Idea
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,163 @@
1
+ # AwesomeNestedSet
2
+
3
+ Awesome Nested Set is an implementation of the nested set pattern for ActiveRecord models.
4
+ It is a replacement for acts_as_nested_set and BetterNestedSet, but more awesome.
5
+
6
+ Version 2 supports Rails 3. Gem versions prior to 2.0 support Rails 2.
7
+
8
+ ## What makes this so awesome?
9
+
10
+ This is a new implementation of nested set based off of BetterNestedSet that fixes some bugs, removes tons of duplication, adds a few useful methods, and adds STI support.
11
+
12
+ [![Code Climate](https://codeclimate.com/github/collectiveidea/awesome_nested_set.png)](https://codeclimate.com/github/collectiveidea/awesome_nested_set)
13
+
14
+ ## Installation
15
+
16
+ Add to your Gemfile:
17
+
18
+ ```ruby
19
+ gem 'awesome_nested_set'
20
+ ```
21
+
22
+ ## Usage
23
+
24
+ To make use of `awesome_nested_set`, your model needs to have 3 fields:
25
+ `lft`, `rgt`, and `parent_id`. The names of these fields are configurable.
26
+ You can also have an optional field, `depth`:
27
+
28
+ ```ruby
29
+ class CreateCategories < ActiveRecord::Migration
30
+ def self.up
31
+ create_table :categories do |t|
32
+ t.string :name
33
+ t.integer :parent_id
34
+ t.integer :lft
35
+ t.integer :rgt
36
+ t.integer :depth # this is optional.
37
+ end
38
+ end
39
+
40
+ def self.down
41
+ drop_table :categories
42
+ end
43
+ end
44
+ ```
45
+
46
+ Enable the nested set functionality by declaring `acts_as_nested_set` on your model
47
+
48
+ ```ruby
49
+ class Category < ActiveRecord::Base
50
+ acts_as_nested_set
51
+ end
52
+ ```
53
+
54
+ Run `rake rdoc` to generate the API docs and see [CollectiveIdea::Acts::NestedSet](lib/awesome_nested_set/awesome_nested_set.rb) for more information.
55
+
56
+ ## Callbacks
57
+
58
+ There are three callbacks called when moving a node:
59
+ `before_move`, `after_move` and `around_move`.
60
+
61
+ ```ruby
62
+ class Category < ActiveRecord::Base
63
+ acts_as_nested_set
64
+
65
+ after_move :rebuild_slug
66
+ around_move :da_fancy_things_around
67
+
68
+ private
69
+
70
+ def rebuild_slug
71
+ # do whatever
72
+ end
73
+
74
+ def da_fancy_things_around
75
+ # do something...
76
+ yield # actually moves
77
+ # do something else...
78
+ end
79
+ end
80
+ ```
81
+
82
+ Beside this there are also hooks to act on the newly added or removed children.
83
+
84
+ ```ruby
85
+ class Category < ActiveRecord::Base
86
+ acts_as_nested_set :before_add => :do_before_add_stuff,
87
+ :after_add => :do_after_add_stuff,
88
+ :before_remove => :do_before_remove_stuff,
89
+ :after_remove => :do_after_remove_stuff
90
+
91
+ private
92
+
93
+ def do_before_add_stuff(child_node)
94
+ # do whatever with the child
95
+ end
96
+
97
+ def do_after_add_stuff(child_node)
98
+ # do whatever with the child
99
+ end
100
+
101
+ def do_before_remove_stuff(child_node)
102
+ # do whatever with the child
103
+ end
104
+
105
+ def do_after_remove_stuff(child_node)
106
+ # do whatever with the child
107
+ end
108
+ end
109
+ ```
110
+
111
+ ## Protecting attributes from mass assignment
112
+
113
+ It's generally best to "whitelist" the attributes that can be used in mass assignment:
114
+
115
+ ```ruby
116
+ class Category < ActiveRecord::Base
117
+ acts_as_nested_set
118
+ attr_accessible :name, :parent_id
119
+ end
120
+ ```
121
+
122
+ If for some reason that is not possible, you will probably want to protect the `lft` and `rgt` attributes:
123
+
124
+ ```ruby
125
+ class Category < ActiveRecord::Base
126
+ acts_as_nested_set
127
+ attr_protected :lft, :rgt
128
+ end
129
+ ```
130
+
131
+ ## Conversion from other trees
132
+
133
+ Coming from acts_as_tree or another system where you only have a parent_id? No problem. Simply add the lft & rgt fields as above, and then run:
134
+
135
+ ```ruby
136
+ Category.rebuild!
137
+ ```
138
+
139
+ Your tree will be converted to a valid nested set. Awesome!
140
+
141
+ ## View Helper
142
+
143
+ The view helper is called #nested_set_options.
144
+
145
+ Example usage:
146
+
147
+ ```erb
148
+ <%= f.select :parent_id, nested_set_options(Category, @category) {|i| "#{'-' * i.level} #{i.name}" } %>
149
+
150
+ <%= select_tag 'parent_id', options_for_select(nested_set_options(Category) {|i| "#{'-' * i.level} #{i.name}" } ) %>
151
+ ```
152
+
153
+ See [CollectiveIdea::Acts::NestedSet::Helper](lib/awesome_nested_set/helper.rb) for more information about the helpers.
154
+
155
+ ## References
156
+
157
+ You can learn more about nested sets at: http://threebit.net/tutorials/nestedset/tutorial1.html
158
+
159
+ ## How to contribute
160
+
161
+ Please see the ['Contributing' document](CONTRIBUTING.md).
162
+
163
+ Copyright © 2008 - 2013 Collective Idea, released under the MIT license
@@ -0,0 +1,8 @@
1
+ require 'awesome_nested_set/awesome_nested_set'
2
+ require 'active_record'
3
+ ActiveRecord::Base.send :extend, CollectiveIdea::Acts::NestedSet
4
+
5
+ if defined?(ActionView)
6
+ require 'awesome_nested_set/helper'
7
+ ActionView::Base.send :include, CollectiveIdea::Acts::NestedSet::Helper
8
+ end
@@ -0,0 +1,134 @@
1
+ require 'awesome_nested_set/columns'
2
+ require 'awesome_nested_set/model'
3
+
4
+ module CollectiveIdea #:nodoc:
5
+ module Acts #:nodoc:
6
+ module NestedSet #:nodoc:
7
+
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
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!
12
+ #
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).
15
+ #
16
+ # == API
17
+ #
18
+ # Methods names are aligned with acts_as_tree as much as possible to make replacment from one
19
+ # by another easier.
20
+ #
21
+ # item.children.create(:name => "child1")
22
+ #
23
+
24
+ # Configuration options are:
25
+ #
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"
28
+ # * +:right_column+ - column name for right boundry data, default "rgt"
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"
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.
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
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
37
+ # without calling their destroy method.
38
+ # * +:counter_cache+ adds a counter cache for the number of children.
39
+ # defaults to false.
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
42
+ # Example: <tt>acts_as_nested_set :order_column => :position</tt>
43
+ #
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
46
+ # to acts_as_nested_set models
47
+ def acts_as_nested_set(options = {})
48
+ acts_as_nested_set_parse_options! options
49
+
50
+ include Model
51
+ include Columns
52
+ extend Columns
53
+
54
+ acts_as_nested_set_relate_parent!
55
+ acts_as_nested_set_relate_children!
56
+
57
+ attr_accessor :skip_before_destroy
58
+
59
+ acts_as_nested_set_prevent_assignment_to_reserved_columns!
60
+ acts_as_nested_set_define_callbacks!
61
+ end
62
+
63
+ private
64
+ def acts_as_nested_set_define_callbacks!
65
+ # on creation, set automatically lft and rgt to the end of the tree
66
+ before_create :set_default_left_and_right
67
+ before_save :store_new_parent
68
+ after_save :move_to_new_parent, :set_depth!
69
+ before_destroy :destroy_descendants
70
+
71
+ define_model_callbacks :move
72
+ end
73
+
74
+ def acts_as_nested_set_relate_children!
75
+ has_many_children_options = {
76
+ :class_name => self.base_class.to_s,
77
+ :foreign_key => parent_column_name,
78
+ :order => quoted_order_column_name,
79
+ :inverse_of => (:parent unless acts_as_nested_set_options[:polymorphic]),
80
+ }
81
+
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|
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
86
+
87
+ order_condition = has_many_children_options.delete(:order)
88
+ has_many :children, -> { order(order_condition) }, has_many_children_options
89
+ end
90
+
91
+ def acts_as_nested_set_relate_parent!
92
+ belongs_to :parent, :class_name => self.base_class.to_s,
93
+ :foreign_key => parent_column_name,
94
+ :counter_cache => acts_as_nested_set_options[:counter_cache],
95
+ :inverse_of => (:children unless acts_as_nested_set_options[:polymorphic]),
96
+ :polymorphic => acts_as_nested_set_options[:polymorphic]
97
+ end
98
+
99
+ def acts_as_nested_set_default_options
100
+ {
101
+ :parent_column => 'parent_id',
102
+ :left_column => 'lft',
103
+ :right_column => 'rgt',
104
+ :depth_column => 'depth',
105
+ :dependent => :delete_all, # or :destroy
106
+ :polymorphic => false,
107
+ :counter_cache => false
108
+ }.freeze
109
+ end
110
+
111
+ def acts_as_nested_set_parse_options!(options)
112
+ options = acts_as_nested_set_default_options.merge(options)
113
+
114
+ if options[:scope].is_a?(Symbol) && options[:scope].to_s !~ /_id$/
115
+ options[:scope] = "#{options[:scope]}_id".intern
116
+ end
117
+
118
+ class_attribute :acts_as_nested_set_options
119
+ self.acts_as_nested_set_options = options
120
+ end
121
+
122
+ def acts_as_nested_set_prevent_assignment_to_reserved_columns!
123
+ # no assignment to structure fields
124
+ [left_column_name, right_column_name, depth_column_name].each do |column|
125
+ module_eval <<-"end_eval", __FILE__, __LINE__
126
+ def #{column}=(x)
127
+ 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
+ end
129
+ end_eval
130
+ end
131
+ end
132
+ end
133
+ end
134
+ end
@@ -0,0 +1,72 @@
1
+ # Mixed into both classes and instances to provide easy access to the column names
2
+ module CollectiveIdea #:nodoc:
3
+ module Acts #:nodoc:
4
+ module NestedSet #:nodoc:
5
+ module Columns
6
+ def left_column_name
7
+ acts_as_nested_set_options[:left_column]
8
+ end
9
+
10
+ def right_column_name
11
+ acts_as_nested_set_options[:right_column]
12
+ end
13
+
14
+ def depth_column_name
15
+ acts_as_nested_set_options[:depth_column]
16
+ end
17
+
18
+ def parent_column_name
19
+ acts_as_nested_set_options[:parent_column]
20
+ end
21
+
22
+ def order_column
23
+ acts_as_nested_set_options[:order_column] || left_column_name
24
+ end
25
+
26
+ def scope_column_names
27
+ Array(acts_as_nested_set_options[:scope])
28
+ end
29
+
30
+ def quoted_left_column_name
31
+ ActiveRecord::Base.connection.quote_column_name(left_column_name)
32
+ end
33
+
34
+ def quoted_right_column_name
35
+ ActiveRecord::Base.connection.quote_column_name(right_column_name)
36
+ end
37
+
38
+ def quoted_depth_column_name
39
+ ActiveRecord::Base.connection.quote_column_name(depth_column_name)
40
+ end
41
+
42
+ def quoted_parent_column_name
43
+ ActiveRecord::Base.connection.quote_column_name(parent_column_name)
44
+ end
45
+
46
+ def quoted_scope_column_names
47
+ scope_column_names.collect {|column_name| connection.quote_column_name(column_name) }
48
+ end
49
+
50
+ def quoted_order_column_name
51
+ ActiveRecord::Base.connection.quote_column_name(order_column)
52
+ end
53
+
54
+ def quoted_primary_key_column_full_name
55
+ "#{quoted_table_name}.#{ActiveRecord::Base.connection.quote_column_name('id')}"
56
+ end
57
+
58
+ def quoted_left_column_full_name
59
+ "#{quoted_table_name}.#{quoted_left_column_name}"
60
+ end
61
+
62
+ def quoted_right_column_full_name
63
+ "#{quoted_table_name}.#{quoted_right_column_name}"
64
+ end
65
+
66
+ def quoted_parent_column_full_name
67
+ "#{quoted_table_name}.#{quoted_parent_column_name}"
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end