nested_set 1.5.4 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- nested_set (1.5.2)
4
+ nested_set (1.5.4)
5
5
  activerecord (>= 3.0.0)
6
6
  railties (>= 3.0.0)
7
7
 
@@ -31,11 +31,20 @@ GEM
31
31
  activesupport (3.0.0)
32
32
  arel (1.0.1)
33
33
  activesupport (~> 3.0.0)
34
+ bench_press (0.3.1)
35
+ activesupport (>= 2.3.5)
36
+ facter (>= 1.5.7)
37
+ httparty (>= 0.6.1)
38
+ jeweler (>= 1.4.0)
34
39
  builder (2.1.2)
40
+ crack (0.1.8)
35
41
  erubis (2.6.6)
36
42
  abstract (>= 1.0.0)
43
+ facter (1.5.8)
37
44
  gemcutter (0.6.1)
38
45
  git (1.2.5)
46
+ httparty (0.6.1)
47
+ crack (= 0.1.8)
39
48
  i18n (0.4.1)
40
49
  jeweler (1.4.0)
41
50
  gemcutter (>= 0.1.0)
@@ -66,6 +75,7 @@ DEPENDENCIES
66
75
  actionpack (>= 3.0.0)
67
76
  activerecord (>= 3.0.0)
68
77
  activesupport (>= 3.0.0)
78
+ bench_press (>= 0.3.1)
69
79
  jeweler
70
80
  nested_set!
71
81
  railties (>= 3.0.0)
@@ -0,0 +1,113 @@
1
+ # NestedSet
2
+
3
+ Nested Set is an implementation of the nested set pattern for ActiveRecord models. It is replacement for acts_as_nested_set and BetterNestedSet, but awesomer. It supports Rails 3.0 and later.
4
+
5
+ ## See, it's Rails 3 only.
6
+
7
+ ## Installation
8
+
9
+ The plugin is available as a gem:
10
+
11
+ gem 'nested_set'
12
+
13
+ Or install as a plugin:
14
+
15
+ rails plugin install git://github.com/skyeagle/nested_set.git
16
+
17
+ ## Usage
18
+
19
+ To make use of nested_set, your model needs to have 3 fields: lft, rgt, and parent_id:
20
+
21
+ class CreateCategories < ActiveRecord::Migration
22
+ def self.up
23
+ create_table :categories do |t|
24
+ t.string :name
25
+ t.integer :parent_id
26
+ t.integer :lft
27
+ t.integer :rgt
28
+
29
+ # Uncomment it to store item level
30
+ # t.integer :depth
31
+ end
32
+ end
33
+
34
+ def self.down
35
+ drop_table :categories
36
+ end
37
+ end
38
+
39
+ Enable the nested set functionality by declaring acts_as_nested_set on your model
40
+
41
+ class Category < ActiveRecord::Base
42
+ acts_as_nested_set
43
+ end
44
+
45
+ Run `rake rdoc` to generate the API docs and see CollectiveIdea::Acts::NestedSet::Base::SingletonMethods for more info.
46
+
47
+ ### Conversion from other trees
48
+
49
+ 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
50
+
51
+ Category.rebuild!
52
+
53
+ Your tree be converted to a valid nested set.
54
+
55
+ ## View Helper
56
+
57
+ The view helper is called #nested_set_options.
58
+
59
+ Example usage:
60
+
61
+ <%= f.select :parent_id, nested_set_options(Category, @category) {|i, level| "#{'-' * level} #{i.name}" } %>
62
+
63
+ <%= select_tag 'parent_id', options_for_select(nested_set_options(Category) {|i, level| "#{'-' * level} #{i.name}" } ) %>
64
+
65
+ or sorted select:
66
+
67
+ <%= f.select :parent_id, sorted_nested_set_options(Category, lambda(&:name)) {|i, level| "#{'-' * level} #{i.name}" } %>
68
+
69
+ <% sort_method = lambda{|x, y| x.name.downcase <=> y.name.downcase} %>
70
+
71
+ NOTE: to sort UTF-8 strings you should use `x.name.mb_chars.downcase`
72
+
73
+ <%= select_tag 'parent_id', options_for_select(sorted_nested_set_options(Category, sort_method){|i, level| "#{'-' * level} #{i.name}" } ) %>
74
+
75
+ See CollectiveIdea::Acts::NestedSet::Helper for more information about the helpers.
76
+
77
+ ## Development
78
+
79
+ bundle install
80
+
81
+ Running tests
82
+
83
+ bundle exec rake test
84
+
85
+ Benchmark tests
86
+
87
+ bundle exec ruby test/benchmark.rb
88
+
89
+ ### References
90
+
91
+ You can learn more about nested sets at:
92
+
93
+ [1](http://www.dbmsmag.com/9603d06.html)
94
+ [2](http://threebit.net/tutorials/nestedset/tutorial1.html)
95
+ [3](http://api.rubyonrails.com/classes/ActiveRecord/Acts/NestedSet/ClassMethods.html)
96
+ [4](http://opensource.symetrie.com/trac/better_nested_set/)
97
+
98
+ ## How to contribute
99
+
100
+ If you find what you might think is a bug:
101
+
102
+ 1. Check the GitHub issue tracker to see if anyone else has had the same issue.
103
+ [Issues tracker](http://github.com/skyeagle/nested_set/issues)
104
+ 2. If you don't see anything, create an issue with information on how to reproduce it.
105
+
106
+ If you want to contribute an enhancement or a fix:
107
+
108
+ 1. Fork the project on github. [http://github.com/skyeagle/nested_set](http://github.com/skyeagle/nested_set)
109
+ 2. Make your changes with tests.
110
+ 3. Commit the changes without making changes to the Rakefile, VERSION, or any other files that aren't related to your enhancement or fix
111
+ 4. Send a pull request.
112
+
113
+ Copyright ©2010 Collective Idea, released under the MIT license
data/Rakefile CHANGED
@@ -16,6 +16,7 @@ begin
16
16
  gem.add_development_dependency "sqlite3-ruby"
17
17
  gem.add_development_dependency "actionpack", ['>= 3.0.0']
18
18
  gem.add_development_dependency "activesupport", ['>= 3.0.0']
19
+ gem.add_development_dependency "bench_press", ['>= 0.3.1']
19
20
  gem.add_development_dependency "jeweler"
20
21
  end
21
22
  Jeweler::GemcutterTasks.new
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.5.4
1
+ 1.6.0
@@ -99,7 +99,7 @@ module CollectiveIdea #:nodoc:
99
99
  where("#{quoted_right_column_name} - #{quoted_left_column_name} = 1").
100
100
  order(quoted_left_column_name)
101
101
  }
102
- scope :with_depth, proc {|level| where(:depth => level).order("lft") }
102
+ scope :with_depth, proc {|level| where(:depth => level).order(quoted_left_column_name) }
103
103
 
104
104
  define_callbacks :move, :terminator => "result == false"
105
105
  end
@@ -113,6 +113,42 @@ module CollectiveIdea #:nodoc:
113
113
  roots.first
114
114
  end
115
115
 
116
+ # Returns arranged nodes hash.
117
+ # I.e. you have this tree:
118
+ #
119
+ # 1
120
+ # 2
121
+ # 3
122
+ # 4
123
+ # 5
124
+ # 6
125
+ # 7
126
+ #
127
+ # Hash will looks like:
128
+ #
129
+ # {1 => {2 => {}, 3 => {4 => {5 => {}}, 6 => {}}}, 7 => {}}
130
+ #
131
+ # == Usage:
132
+ #
133
+ # Categories.arrange
134
+ # Categories.find(42).children.arrange
135
+ # Categories.find(42).descendants.arrange
136
+ # Categories.find(42).self_and_descendants.arrange
137
+ #
138
+ # This arranged hash can be rendered with recursive render_tree helper
139
+ def arrange
140
+ arranged = ActiveSupport::OrderedHash.new
141
+ insertion_points = [arranged]
142
+ depth = 0
143
+ order(quoted_left_column_name).each_with_level do |node, level|
144
+ insertion_points.push insertion_points.last.values.last if level > depth
145
+ (depth - level).times { insertion_points.pop } if level < depth
146
+ insertion_points.last.merge! node => ActiveSupport::OrderedHash.new
147
+ depth = level
148
+ end
149
+ arranged
150
+ end
151
+
116
152
  def valid?
117
153
  left_and_rights_valid? && no_duplicates_for_columns? && all_roots_valid?
118
154
  end
@@ -210,19 +246,17 @@ module CollectiveIdea #:nodoc:
210
246
  # Example:
211
247
  # Category.each_with_level(Category.root.self_and_descendants) do |o, level|
212
248
  #
213
- def each_with_level(objects)
214
- path = [nil]
215
- objects.each do |o|
216
- if o.parent_id != path.last
217
- # we are on a new level, did we decent or ascent?
218
- if path.include?(o.parent_id)
219
- # remove wrong wrong tailing paths elements
220
- path.pop while path.last != o.parent_id
221
- else
222
- path << o.parent_id
223
- end
249
+
250
+ def each_with_level(objects = nil)
251
+ levels = []
252
+ (objects || scoped).each do |i|
253
+ if level = levels.index(i.parent_id)
254
+ levels.slice!((level + 1)..-1)
255
+ else
256
+ levels << i.parent_id
257
+ level = levels.size - 1
224
258
  end
225
- yield(o, path.length - 1)
259
+ yield(i, level)
226
260
  end
227
261
  end
228
262
 
@@ -25,19 +25,13 @@ module CollectiveIdea #:nodoc:
25
25
  class_or_item = class_or_item.roots if class_or_item.is_a?(Class)
26
26
  items = Array(class_or_item)
27
27
  result = []
28
- items.each do |root|
28
+ items.each do |item|
29
29
  levels = []
30
- result += root.self_and_descendants.map do |i|
31
- if level = levels.index(i.parent_id)
32
- levels.slice!((level + 1)..-1)
33
- else
34
- levels << i.parent_id
35
- level = levels.size - 1
36
- end
30
+ item.self_and_descendants.each_with_level do |i, level|
37
31
  if mover.nil? || mover.new_record? || mover.move_possible?(i)
38
- [yield(i, level), i.id]
32
+ result.push([yield(i, level), i.id])
39
33
  end
40
- end.compact
34
+ end
41
35
  end
42
36
  result
43
37
  end
@@ -61,7 +55,7 @@ module CollectiveIdea #:nodoc:
61
55
  #
62
56
  # OR
63
57
  #
64
- # sort_method = lambda{|x,y| x.name.downcase <=> y.name.downcase}
58
+ # sort_method = lambda{|x,y| x.name.mb_chars.downcase <=> y.name.mb_chars.downcase}
65
59
  #
66
60
  # <%= f.select :parent_id, nested_set_options(Category, sort_method) {|i, level|
67
61
  # "#{'–' * level} #{i.name}"
@@ -71,25 +65,51 @@ module CollectiveIdea #:nodoc:
71
65
  class_or_item = class_or_item.roots if class_or_item.is_a?(Class)
72
66
  items = Array(class_or_item)
73
67
  result = []
74
- items.sort_by(&sort_proc).each do |root|
75
- set = root.self_and_descendants
76
- result += build_node(set[0], set, sort_proc, mover, level){|x, level| yield(x, level)}
68
+ items.sort_by(&sort_proc).each do |item|
69
+ hash = item.self_and_descendants.arrange
70
+ result += build_node(hash, sort_proc, mover, level){|x, lvl| yield(x, lvl)}
77
71
  end
78
72
  result
79
73
  end
80
74
 
81
- def build_node(node, set, sort_proc, mover = nil, level = nil)
75
+ def build_node(hash, sort_proc, mover = nil, level = nil)
82
76
  result ||= []
83
- if mover.nil? || mover.new_record? || mover.move_possible?(i)
84
- result << [yield(node, level), node.id]
85
- unless node.leaf?
86
- set.select{|i| i.parent_id == node.id}.sort_by(&sort_proc).map{ |i|
87
- result.push(*build_node(i, set, sort_proc, mover, level.to_i + 1){|x, level| yield(x, level)})
88
- }
77
+ hash.keys.sort_by(&sort_proc).each do |node|
78
+ if mover.nil? || mover.new_record? || mover.move_possible?(node)
79
+ result << [yield(node, level.to_i), node.id]
80
+ result.push(*build_node(hash[node], sort_proc, mover, level.to_i + 1){|x, lvl| yield(x, lvl)})
89
81
  end
90
- end
82
+ end if hash.present?
91
83
  result
92
84
  end
85
+
86
+ # Recursively render arranged nodes hash
87
+ #
88
+ # == Params
89
+ # * +hash+ - Hash or arranged nodes, i.e. Category.arranged
90
+ # * +options+ - HTML options for root ul node.
91
+ # Given options with ex. :sort => lambda{|x| x.name}
92
+ # you allow node sorting by analogy with sorted_nested_set_options helper method
93
+ # * +&block+ - A block that will be used to display node
94
+ #
95
+ # == Usage
96
+ #
97
+ # arranged_nodes = Category.arranged
98
+ #
99
+ # <%= render_tree arranged_nodes do |node, child| %>
100
+ # <li><%= node.name %></li>
101
+ # <%= child %>
102
+ # <% end %>
103
+ #
104
+ def render_tree hash, options = {}, &block
105
+ sort_proc = options.delete :sort
106
+ content_tag :ul, options do
107
+ hash.keys.sort_by(&sort_proc).each do |node|
108
+ block.call node, render_tree(hash[node], :sort => sort_proc, &block)
109
+ end
110
+ end if hash.present?
111
+ end
112
+
93
113
  end
94
114
  end
95
115
  end
@@ -5,55 +5,55 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{nested_set}
8
- s.version = "1.5.4"
8
+ s.version = "1.6.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Brandon Keepers", "Daniel Morrison"]
12
- s.date = %q{2010-11-12}
12
+ s.date = %q{2010-12-11}
13
13
  s.description = %q{An awesome nested set implementation for Active Record}
14
14
  s.email = %q{info@collectiveidea.com}
15
15
  s.extra_rdoc_files = [
16
- "README.rdoc"
16
+ "README.md"
17
17
  ]
18
18
  s.files = [
19
19
  ".autotest",
20
- ".gitignore",
21
- "Gemfile",
22
- "Gemfile.lock",
23
- "MIT-LICENSE",
24
- "README.rdoc",
25
- "Rakefile",
26
- "VERSION",
27
- "init.rb",
28
- "lib/nested_set.rb",
29
- "lib/nested_set/base.rb",
30
- "lib/nested_set/depth.rb",
31
- "lib/nested_set/descendants.rb",
32
- "lib/nested_set/helper.rb",
33
- "lib/nested_set/railtie.rb",
34
- "nested_set.gemspec",
35
- "rails/init.rb",
36
- "test/db/database.yml",
37
- "test/db/schema.rb",
38
- "test/fixtures/categories.yml",
39
- "test/fixtures/category.rb",
40
- "test/fixtures/departments.yml",
41
- "test/fixtures/notes.yml",
42
- "test/nested_set/helper_test.rb",
43
- "test/nested_set_test.rb",
44
- "test/test_helper.rb"
20
+ "Gemfile",
21
+ "Gemfile.lock",
22
+ "MIT-LICENSE",
23
+ "README.md",
24
+ "Rakefile",
25
+ "VERSION",
26
+ "init.rb",
27
+ "lib/nested_set.rb",
28
+ "lib/nested_set/base.rb",
29
+ "lib/nested_set/depth.rb",
30
+ "lib/nested_set/descendants.rb",
31
+ "lib/nested_set/helper.rb",
32
+ "lib/nested_set/railtie.rb",
33
+ "nested_set.gemspec",
34
+ "rails/init.rb",
35
+ "test/benchmarks.rb",
36
+ "test/db/database.yml",
37
+ "test/db/schema.rb",
38
+ "test/fixtures/categories.yml",
39
+ "test/fixtures/category.rb",
40
+ "test/fixtures/departments.yml",
41
+ "test/fixtures/notes.yml",
42
+ "test/nested_set/helper_test.rb",
43
+ "test/nested_set_test.rb",
44
+ "test/test_helper.rb"
45
45
  ]
46
46
  s.homepage = %q{http://github.com/skyeagle/nested_set}
47
- s.rdoc_options = ["--charset=UTF-8"]
48
47
  s.require_paths = ["lib"]
49
48
  s.rubygems_version = %q{1.3.7}
50
49
  s.summary = %q{An awesome nested set implementation for Active Record}
51
50
  s.test_files = [
51
+ "test/benchmarks.rb",
52
+ "test/db/schema.rb",
53
+ "test/fixtures/category.rb",
54
+ "test/nested_set/helper_test.rb",
52
55
  "test/nested_set_test.rb",
53
- "test/nested_set/helper_test.rb",
54
- "test/test_helper.rb",
55
- "test/fixtures/category.rb",
56
- "test/db/schema.rb"
56
+ "test/test_helper.rb"
57
57
  ]
58
58
 
59
59
  if s.respond_to? :specification_version then
@@ -61,26 +61,53 @@ Gem::Specification.new do |s|
61
61
  s.specification_version = 3
62
62
 
63
63
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
64
+ s.add_runtime_dependency(%q<nested_set>, [">= 0"])
65
+ s.add_runtime_dependency(%q<railties>, [">= 3.0.0"])
66
+ s.add_runtime_dependency(%q<activerecord>, [">= 3.0.0"])
67
+ s.add_development_dependency(%q<sqlite3-ruby>, [">= 0"])
68
+ s.add_development_dependency(%q<actionpack>, [">= 3.0.0"])
69
+ s.add_development_dependency(%q<activesupport>, [">= 3.0.0"])
70
+ s.add_development_dependency(%q<bench_press>, [">= 0.3.1"])
71
+ s.add_development_dependency(%q<jeweler>, [">= 0"])
64
72
  s.add_runtime_dependency(%q<railties>, [">= 3.0.0"])
65
73
  s.add_runtime_dependency(%q<activerecord>, [">= 3.0.0"])
66
74
  s.add_development_dependency(%q<sqlite3-ruby>, [">= 0"])
67
75
  s.add_development_dependency(%q<actionpack>, [">= 3.0.0"])
68
76
  s.add_development_dependency(%q<activesupport>, [">= 3.0.0"])
77
+ s.add_development_dependency(%q<bench_press>, [">= 0.3.1"])
69
78
  s.add_development_dependency(%q<jeweler>, [">= 0"])
70
79
  else
80
+ s.add_dependency(%q<nested_set>, [">= 0"])
71
81
  s.add_dependency(%q<railties>, [">= 3.0.0"])
72
82
  s.add_dependency(%q<activerecord>, [">= 3.0.0"])
73
83
  s.add_dependency(%q<sqlite3-ruby>, [">= 0"])
74
84
  s.add_dependency(%q<actionpack>, [">= 3.0.0"])
75
85
  s.add_dependency(%q<activesupport>, [">= 3.0.0"])
86
+ s.add_dependency(%q<bench_press>, [">= 0.3.1"])
87
+ s.add_dependency(%q<jeweler>, [">= 0"])
88
+ s.add_dependency(%q<railties>, [">= 3.0.0"])
89
+ s.add_dependency(%q<activerecord>, [">= 3.0.0"])
90
+ s.add_dependency(%q<sqlite3-ruby>, [">= 0"])
91
+ s.add_dependency(%q<actionpack>, [">= 3.0.0"])
92
+ s.add_dependency(%q<activesupport>, [">= 3.0.0"])
93
+ s.add_dependency(%q<bench_press>, [">= 0.3.1"])
76
94
  s.add_dependency(%q<jeweler>, [">= 0"])
77
95
  end
78
96
  else
97
+ s.add_dependency(%q<nested_set>, [">= 0"])
98
+ s.add_dependency(%q<railties>, [">= 3.0.0"])
99
+ s.add_dependency(%q<activerecord>, [">= 3.0.0"])
100
+ s.add_dependency(%q<sqlite3-ruby>, [">= 0"])
101
+ s.add_dependency(%q<actionpack>, [">= 3.0.0"])
102
+ s.add_dependency(%q<activesupport>, [">= 3.0.0"])
103
+ s.add_dependency(%q<bench_press>, [">= 0.3.1"])
104
+ s.add_dependency(%q<jeweler>, [">= 0"])
79
105
  s.add_dependency(%q<railties>, [">= 3.0.0"])
80
106
  s.add_dependency(%q<activerecord>, [">= 3.0.0"])
81
107
  s.add_dependency(%q<sqlite3-ruby>, [">= 0"])
82
108
  s.add_dependency(%q<actionpack>, [">= 3.0.0"])
83
109
  s.add_dependency(%q<activesupport>, [">= 3.0.0"])
110
+ s.add_dependency(%q<bench_press>, [">= 0.3.1"])
84
111
  s.add_dependency(%q<jeweler>, [">= 0"])
85
112
  end
86
113
  end
@@ -0,0 +1,55 @@
1
+ if $0 == __FILE__
2
+
3
+ plugin_test_dir = File.dirname(__FILE__)
4
+
5
+ $:.unshift(plugin_test_dir + '/../lib')
6
+
7
+ #require 'logger'
8
+ require 'active_support'
9
+ require 'active_record'
10
+ require 'nested_set'
11
+ require 'bench_press'
12
+
13
+
14
+ CollectiveIdea::Acts::NestedSet::Railtie.extend_active_record
15
+ #ActiveRecord::Base.logger = Logger.new(plugin_test_dir + "/debug.log")
16
+ ActiveRecord::Base.configurations = YAML::load(IO.read(plugin_test_dir + "/db/database.yml"))
17
+ ActiveRecord::Base.establish_connection(ENV["DB"] || "sqlite3mem")
18
+ ActiveRecord::Migration.verbose = false
19
+ ActiveRecord::Schema.define(:version => 0) do
20
+ create_table :categories, :force => true do |t|
21
+ t.column :name, :string
22
+ t.column :parent_id, :integer
23
+ t.column :lft, :integer
24
+ t.column :rgt, :integer
25
+ end
26
+ end
27
+
28
+ class Category < ActiveRecord::Base
29
+ acts_as_nested_set
30
+ end
31
+
32
+ Category.delete_all
33
+ Category.create(:name => "Root Node 1")
34
+ Category.create(:name => "Root Node 2")
35
+ 50.times do |i|
36
+ node = Category.create(:name => "Node #{i}")
37
+ set = Category.roots.map{|root| root.self_and_descendants}.flatten
38
+ random_node = set[rand(set.size-1)]
39
+ node.move_to_child_of(random_node)
40
+ end
41
+
42
+ include CollectiveIdea::Acts::NestedSet::Helper
43
+ extend BenchPress
44
+
45
+ reps 100
46
+
47
+ measure "nested_set_options" do
48
+ nested_set_options(Category){|i, level| "#{'-' * level} #{i.name}" }
49
+ end
50
+
51
+ measure "sorted_nested_set_options" do
52
+ sorted_nested_set_options(Category, lambda(&:name)){|i, level| "#{'-' * level} #{i.name}" }
53
+ end
54
+
55
+ end
@@ -5,6 +5,7 @@ ActiveRecord::Schema.define(:version => 0) do
5
5
  t.column :parent_id, :integer
6
6
  t.column :lft, :integer
7
7
  t.column :rgt, :integer
8
+ t.column :depth, :integer, :default => 0
8
9
  t.column :organization_id, :integer
9
10
  end
10
11
 
@@ -12,4 +12,5 @@ class Category < ActiveRecord::Base
12
12
  end
13
13
  }
14
14
  end
15
- end
15
+
16
+ end
@@ -1,77 +1,103 @@
1
1
  require 'test_helper'
2
2
 
3
- module CollectiveIdea
4
- module Acts #:nodoc:
5
- module NestedSet #:nodoc:
6
- class NestedSetTest < ActiveSupport::TestCase
7
- include Helper
8
- fixtures :categories
3
+ class HelperTest < ActionView::TestCase
4
+ include CollectiveIdea::Acts::NestedSet::Helper
5
+ fixtures :categories
9
6
 
10
- def test_nested_set_options
11
- expected = [
12
- [" Top Level", 1],
13
- ["- Child 1", 2],
14
- ['- Child 2', 3],
15
- ['-- Child 2.1', 4],
16
- ['- Child 3', 5],
17
- [" Top Level 2", 6]
18
- ]
19
- actual = nested_set_options(Category) do |c, level|
20
- "#{'-' * level} #{c.name}"
21
- end
22
- assert_equal expected, actual
23
- end
7
+ def test_nested_set_options
8
+ expected = [
9
+ [" Top Level", 1],
10
+ ["- Child 1", 2],
11
+ ['- Child 2', 3],
12
+ ['-- Child 2.1', 4],
13
+ ['- Child 3', 5],
14
+ [" Top Level 2", 6]
15
+ ]
16
+ actual = nested_set_options(Category) do |c, level|
17
+ "#{'-' * level} #{c.name}"
18
+ end
19
+ assert_equal expected, actual
20
+ end
24
21
 
25
- def test_nested_set_options_with_mover
26
- expected = [
27
- [" Top Level", 1],
28
- ["- Child 1", 2],
29
- ['- Child 3', 5],
30
- [" Top Level 2", 6]
31
- ]
32
- actual = nested_set_options(Category, categories(:child_2)) do |c, level|
33
- "#{'-' * level} #{c.name}"
34
- end
35
- assert_equal expected, actual
36
- end
22
+ def test_nested_set_options_with_mover
23
+ expected = [
24
+ [" Top Level", 1],
25
+ ["- Child 1", 2],
26
+ ['- Child 3', 5],
27
+ [" Top Level 2", 6]
28
+ ]
29
+ actual = nested_set_options(Category, categories(:child_2)) do |c, level|
30
+ "#{'-' * level} #{c.name}"
31
+ end
32
+ assert_equal expected, actual
33
+ end
37
34
 
38
- def test_build_node
39
- set = categories(:top_level).self_and_descendants
40
- expected = set.map{|i| [i.name, i.id]}
41
- actual = build_node(set[0], set, lambda(&:lft)){|i, level| i.name }
42
- assert_equal expected, actual
43
- end
35
+ def test_build_node
36
+ set = categories(:top_level).self_and_descendants
37
+ expected = set.map{|i| [i.name, i.id]}
44
38
 
45
- def test_build_node_with_back_id_order
46
- set = categories(:top_level).self_and_descendants
47
- expected = [
48
- ["Top Level", 1],
49
- ["Child 3", 5],
50
- ["Child 2", 3],
51
- ["Child 2.1", 4],
52
- ["Child 1", 2]
53
- ]
54
- actual = build_node(set[0], set, lambda{|x| -x.id}){|i, level| i.name }
55
- assert_equal expected, actual
56
- end
39
+ hash = set.arrange
40
+ actual = build_node(hash, lambda(&:lft)){|i, level| i.name }
41
+ assert_equal expected, actual
42
+ end
57
43
 
58
- def test_sorted_nested_set
59
- expected = [
60
- [" Top Level 2", 6],
61
- [" Top Level", 1],
62
- ['- Child 3', 5],
63
- ['- Child 2', 3],
64
- ['-- Child 2.1', 4],
65
- ["- Child 1", 2]
66
- ]
44
+ def test_build_node_with_back_id_order
45
+ expected = [
46
+ ["Top Level", 1],
47
+ ["Child 3", 5],
48
+ ["Child 2", 3],
49
+ ["Child 2.1", 4],
50
+ ["Child 1", 2]
51
+ ]
67
52
 
68
- actual = sorted_nested_set_options(Category, lambda{|x| -x.id}) do |c, level|
69
- "#{'-' * level} #{c.name}"
70
- end
71
- assert_equal expected, actual
72
- end
53
+ hash = categories(:top_level).self_and_descendants.arrange
54
+ actual = build_node(hash, lambda{|x| -x.id}){|i, level| i.name }
55
+ assert_equal expected, actual
56
+ end
57
+
58
+ def test_sorted_nested_set
59
+ expected = [
60
+ [" Top Level 2", 6],
61
+ [" Top Level", 1],
62
+ ['- Child 3', 5],
63
+ ['- Child 2', 3],
64
+ ['-- Child 2.1', 4],
65
+ ["- Child 1", 2]
66
+ ]
73
67
 
74
- end
68
+ actual = sorted_nested_set_options(Category, lambda{|x| -x.id}) do |c, level|
69
+ "#{'-' * level} #{c.name}"
75
70
  end
71
+ assert_equal expected, actual
76
72
  end
73
+
74
+ def test_sorted_nested_set_with_mover
75
+ expected = [
76
+ [" Top Level 2", 6],
77
+ [" Top Level", 1],
78
+ ['- Child 3', 5],
79
+ ["- Child 1", 2]
80
+ ]
81
+
82
+ actual = sorted_nested_set_options(Category, lambda{|x| -x.id}, categories(:child_2)) do |c, level|
83
+ "#{'-' * level} #{c.name}"
84
+ end
85
+ assert_equal expected, actual
86
+ end
87
+ def test_render_tree
88
+ html = render_tree(Category.arrange) do |category, child|
89
+ concat content_tag(:li, category)
90
+ concat child
91
+ end
92
+ assert_equal html, "<ul><li>Top Level</li><ul><li>Child 1</li><li>Child 2</li><ul><li>Child 2.1</li></ul><li>Child 3</li></ul><li>Top Level 2</li></ul>"
93
+ end
94
+
95
+ def test_sorted_render_tree
96
+ html = render_tree(Category.arrange, :sort => lambda{|x| -x.id}) do |category, child|
97
+ concat content_tag(:li, category)
98
+ concat child
99
+ end
100
+ assert_equal html, "<ul><li>Top Level 2</li><li>Top Level</li><ul><li>Child 3</li><li>Child 2</li><ul><li>Child 2.1</li></ul><li>Child 1</li></ul></ul>"
101
+ end
102
+
77
103
  end
@@ -712,6 +712,15 @@ class NestedSetTest < ActiveSupport::TestCase
712
712
  end
713
713
  end
714
714
 
715
+ def check_scoped_structure(entries, structure)
716
+ structure = structure.dup
717
+ entries.each_with_level do |category, level|
718
+ expected_level, expected_name = structure.shift
719
+ assert_equal expected_name, category.name, "wrong category"
720
+ assert_equal expected_level, level, "wrong level for #{category.name}"
721
+ end
722
+ end
723
+
715
724
  def test_each_with_level
716
725
  levels = [
717
726
  [0, "Top Level"],
@@ -721,6 +730,7 @@ class NestedSetTest < ActiveSupport::TestCase
721
730
  [1, "Child 3" ]]
722
731
 
723
732
  check_structure(Category.root.self_and_descendants, levels)
733
+ check_scoped_structure(Category.root.self_and_descendants, levels)
724
734
 
725
735
  # test some deeper structures
726
736
  category = Category.find_by_name("Child 1")
@@ -746,7 +756,7 @@ class NestedSetTest < ActiveSupport::TestCase
746
756
  [2, "Child 2.1"],
747
757
  [1, "Child 3" ]]
748
758
 
749
- check_structure(Category.root.self_and_descendants, levels)
759
+ check_scoped_structure(Category.root.self_and_descendants, levels)
750
760
  end
751
761
 
752
762
  def test_model_with_attr_accessible
@@ -801,4 +811,55 @@ class NestedSetTest < ActiveSupport::TestCase
801
811
  ensure
802
812
  Category.class_eval { reset_callbacks :move }
803
813
  end
814
+
815
+ # helper to turn arranged hash to levels array
816
+ def hash_to_array hash, level = 0
817
+ array = []
818
+ hash.each do |key, value|
819
+ array.push [level, "#{key}"]
820
+ array += hash_to_array(value, level.next)
821
+ end
822
+ array
823
+ end
824
+
825
+ def test_arrangement
826
+ levels = [
827
+ [0, "Top Level"],
828
+ [1, "Child 1"],
829
+ [1, "Child 2"],
830
+ [2, "Child 2.1"],
831
+ [1, "Child 3"],
832
+ [0, "Top Level 2"]]
833
+
834
+ assert_equal hash_to_array(Category.arrange), levels
835
+
836
+ # some deeper structure
837
+
838
+ category = Category.find_by_name("Child 1")
839
+ c1 = Category.new(:name => "Child 1.1")
840
+ c2 = Category.new(:name => "Child 1.1.1")
841
+ c3 = Category.new(:name => "Child 1.1.1.1")
842
+ c4 = Category.new(:name => "Child 1.2")
843
+ [c1, c2, c3, c4].each(&:save!)
844
+
845
+ c1.move_to_child_of(category)
846
+ c2.move_to_child_of(c1)
847
+ c3.move_to_child_of(c2)
848
+ c4.move_to_child_of(category)
849
+
850
+ levels = [
851
+ [0, "Top Level"],
852
+ [1, "Child 1"],
853
+ [2, "Child 1.1"],
854
+ [3, "Child 1.1.1"],
855
+ [4, "Child 1.1.1.1"],
856
+ [2, "Child 1.2"],
857
+ [1, "Child 2"],
858
+ [2, "Child 2.1"],
859
+ [1, "Child 3" ],
860
+ [0, "Top Level 2"]]
861
+
862
+ assert_equal hash_to_array(Category.arrange), levels
863
+ end
864
+
804
865
  end
@@ -9,6 +9,10 @@ require 'active_support'
9
9
  require 'active_support/test_case'
10
10
  require 'active_record'
11
11
  require 'action_pack'
12
+ require 'action_view'
13
+ require 'action_view/base'
14
+ require 'action_view/template/handlers/erb'
15
+ require 'action_view/test_case'
12
16
  require 'nested_set'
13
17
 
14
18
  CollectiveIdea::Acts::NestedSet::Railtie.extend_active_record
metadata CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 1
7
- - 5
8
- - 4
9
- version: 1.5.4
7
+ - 6
8
+ - 0
9
+ version: 1.6.0
10
10
  platform: ruby
11
11
  authors:
12
12
  - Brandon Keepers
@@ -15,13 +15,25 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-11-12 00:00:00 +03:00
18
+ date: 2010-12-11 00:00:00 +03:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
22
- name: railties
23
- prerelease: false
22
+ name: nested_set
24
23
  requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ segments:
29
+ - 0
30
+ version: "0"
31
+ type: :runtime
32
+ prerelease: false
33
+ version_requirements: *id001
34
+ - !ruby/object:Gem::Dependency
35
+ name: railties
36
+ requirement: &id002 !ruby/object:Gem::Requirement
25
37
  none: false
26
38
  requirements:
27
39
  - - ">="
@@ -32,11 +44,11 @@ dependencies:
32
44
  - 0
33
45
  version: 3.0.0
34
46
  type: :runtime
35
- version_requirements: *id001
47
+ prerelease: false
48
+ version_requirements: *id002
36
49
  - !ruby/object:Gem::Dependency
37
50
  name: activerecord
38
- prerelease: false
39
- requirement: &id002 !ruby/object:Gem::Requirement
51
+ requirement: &id003 !ruby/object:Gem::Requirement
40
52
  none: false
41
53
  requirements:
42
54
  - - ">="
@@ -47,11 +59,11 @@ dependencies:
47
59
  - 0
48
60
  version: 3.0.0
49
61
  type: :runtime
50
- version_requirements: *id002
62
+ prerelease: false
63
+ version_requirements: *id003
51
64
  - !ruby/object:Gem::Dependency
52
65
  name: sqlite3-ruby
53
- prerelease: false
54
- requirement: &id003 !ruby/object:Gem::Requirement
66
+ requirement: &id004 !ruby/object:Gem::Requirement
55
67
  none: false
56
68
  requirements:
57
69
  - - ">="
@@ -60,11 +72,11 @@ dependencies:
60
72
  - 0
61
73
  version: "0"
62
74
  type: :development
63
- version_requirements: *id003
75
+ prerelease: false
76
+ version_requirements: *id004
64
77
  - !ruby/object:Gem::Dependency
65
78
  name: actionpack
66
- prerelease: false
67
- requirement: &id004 !ruby/object:Gem::Requirement
79
+ requirement: &id005 !ruby/object:Gem::Requirement
68
80
  none: false
69
81
  requirements:
70
82
  - - ">="
@@ -75,11 +87,11 @@ dependencies:
75
87
  - 0
76
88
  version: 3.0.0
77
89
  type: :development
78
- version_requirements: *id004
90
+ prerelease: false
91
+ version_requirements: *id005
79
92
  - !ruby/object:Gem::Dependency
80
93
  name: activesupport
81
- prerelease: false
82
- requirement: &id005 !ruby/object:Gem::Requirement
94
+ requirement: &id006 !ruby/object:Gem::Requirement
83
95
  none: false
84
96
  requirements:
85
97
  - - ">="
@@ -90,11 +102,69 @@ dependencies:
90
102
  - 0
91
103
  version: 3.0.0
92
104
  type: :development
93
- version_requirements: *id005
105
+ prerelease: false
106
+ version_requirements: *id006
107
+ - !ruby/object:Gem::Dependency
108
+ name: bench_press
109
+ requirement: &id007 !ruby/object:Gem::Requirement
110
+ none: false
111
+ requirements:
112
+ - - ">="
113
+ - !ruby/object:Gem::Version
114
+ segments:
115
+ - 0
116
+ - 3
117
+ - 1
118
+ version: 0.3.1
119
+ type: :development
120
+ prerelease: false
121
+ version_requirements: *id007
94
122
  - !ruby/object:Gem::Dependency
95
123
  name: jeweler
124
+ requirement: &id008 !ruby/object:Gem::Requirement
125
+ none: false
126
+ requirements:
127
+ - - ">="
128
+ - !ruby/object:Gem::Version
129
+ segments:
130
+ - 0
131
+ version: "0"
132
+ type: :development
96
133
  prerelease: false
97
- requirement: &id006 !ruby/object:Gem::Requirement
134
+ version_requirements: *id008
135
+ - !ruby/object:Gem::Dependency
136
+ name: railties
137
+ requirement: &id009 !ruby/object:Gem::Requirement
138
+ none: false
139
+ requirements:
140
+ - - ">="
141
+ - !ruby/object:Gem::Version
142
+ segments:
143
+ - 3
144
+ - 0
145
+ - 0
146
+ version: 3.0.0
147
+ type: :runtime
148
+ prerelease: false
149
+ version_requirements: *id009
150
+ - !ruby/object:Gem::Dependency
151
+ name: activerecord
152
+ requirement: &id010 !ruby/object:Gem::Requirement
153
+ none: false
154
+ requirements:
155
+ - - ">="
156
+ - !ruby/object:Gem::Version
157
+ segments:
158
+ - 3
159
+ - 0
160
+ - 0
161
+ version: 3.0.0
162
+ type: :runtime
163
+ prerelease: false
164
+ version_requirements: *id010
165
+ - !ruby/object:Gem::Dependency
166
+ name: sqlite3-ruby
167
+ requirement: &id011 !ruby/object:Gem::Requirement
98
168
  none: false
99
169
  requirements:
100
170
  - - ">="
@@ -103,7 +173,66 @@ dependencies:
103
173
  - 0
104
174
  version: "0"
105
175
  type: :development
106
- version_requirements: *id006
176
+ prerelease: false
177
+ version_requirements: *id011
178
+ - !ruby/object:Gem::Dependency
179
+ name: actionpack
180
+ requirement: &id012 !ruby/object:Gem::Requirement
181
+ none: false
182
+ requirements:
183
+ - - ">="
184
+ - !ruby/object:Gem::Version
185
+ segments:
186
+ - 3
187
+ - 0
188
+ - 0
189
+ version: 3.0.0
190
+ type: :development
191
+ prerelease: false
192
+ version_requirements: *id012
193
+ - !ruby/object:Gem::Dependency
194
+ name: activesupport
195
+ requirement: &id013 !ruby/object:Gem::Requirement
196
+ none: false
197
+ requirements:
198
+ - - ">="
199
+ - !ruby/object:Gem::Version
200
+ segments:
201
+ - 3
202
+ - 0
203
+ - 0
204
+ version: 3.0.0
205
+ type: :development
206
+ prerelease: false
207
+ version_requirements: *id013
208
+ - !ruby/object:Gem::Dependency
209
+ name: bench_press
210
+ requirement: &id014 !ruby/object:Gem::Requirement
211
+ none: false
212
+ requirements:
213
+ - - ">="
214
+ - !ruby/object:Gem::Version
215
+ segments:
216
+ - 0
217
+ - 3
218
+ - 1
219
+ version: 0.3.1
220
+ type: :development
221
+ prerelease: false
222
+ version_requirements: *id014
223
+ - !ruby/object:Gem::Dependency
224
+ name: jeweler
225
+ requirement: &id015 !ruby/object:Gem::Requirement
226
+ none: false
227
+ requirements:
228
+ - - ">="
229
+ - !ruby/object:Gem::Version
230
+ segments:
231
+ - 0
232
+ version: "0"
233
+ type: :development
234
+ prerelease: false
235
+ version_requirements: *id015
107
236
  description: An awesome nested set implementation for Active Record
108
237
  email: info@collectiveidea.com
109
238
  executables: []
@@ -111,14 +240,13 @@ executables: []
111
240
  extensions: []
112
241
 
113
242
  extra_rdoc_files:
114
- - README.rdoc
243
+ - README.md
115
244
  files:
116
245
  - .autotest
117
- - .gitignore
118
246
  - Gemfile
119
247
  - Gemfile.lock
120
248
  - MIT-LICENSE
121
- - README.rdoc
249
+ - README.md
122
250
  - Rakefile
123
251
  - VERSION
124
252
  - init.rb
@@ -130,6 +258,7 @@ files:
130
258
  - lib/nested_set/railtie.rb
131
259
  - nested_set.gemspec
132
260
  - rails/init.rb
261
+ - test/benchmarks.rb
133
262
  - test/db/database.yml
134
263
  - test/db/schema.rb
135
264
  - test/fixtures/categories.yml
@@ -144,8 +273,8 @@ homepage: http://github.com/skyeagle/nested_set
144
273
  licenses: []
145
274
 
146
275
  post_install_message:
147
- rdoc_options:
148
- - --charset=UTF-8
276
+ rdoc_options: []
277
+
149
278
  require_paths:
150
279
  - lib
151
280
  required_ruby_version: !ruby/object:Gem::Requirement
@@ -172,8 +301,9 @@ signing_key:
172
301
  specification_version: 3
173
302
  summary: An awesome nested set implementation for Active Record
174
303
  test_files:
175
- - test/nested_set_test.rb
304
+ - test/benchmarks.rb
305
+ - test/db/schema.rb
306
+ - test/fixtures/category.rb
176
307
  - test/nested_set/helper_test.rb
308
+ - test/nested_set_test.rb
177
309
  - test/test_helper.rb
178
- - test/fixtures/category.rb
179
- - test/db/schema.rb
data/.gitignore DELETED
@@ -1,26 +0,0 @@
1
- ## MAC OS
2
- .DS_Store
3
-
4
- ## TEXTMATE
5
- *.tmproj
6
- tmtags
7
-
8
- ## EMACS
9
- *~
10
- \#*
11
- .\#*
12
-
13
- ## VIM
14
- *.swp
15
-
16
- ## PROJECT::GENERAL
17
- coverage
18
- rdoc
19
- pkg
20
-
21
- # PROJECT::SPECIFIC
22
- awesome_nested_set.sqlite3.db
23
- test/debug.log
24
- rdoc
25
- *.sw?
26
- .bundle
@@ -1,99 +0,0 @@
1
- = NestedSet
2
-
3
- Nested Set is an implementation of the nested set pattern for ActiveRecord models. It is replacement for acts_as_nested_set and BetterNestedSet, but awesomer. It supports Rails 3.0 and later.
4
-
5
- === See, it's Rails 3 only.
6
-
7
- == Installation
8
-
9
- The plugin is available as a gem:
10
-
11
- gem 'nested_set'
12
-
13
- Or install as a plugin:
14
-
15
- rails plugin install git://github.com/skyeagle/nested_set.git
16
-
17
- == Usage
18
-
19
- To make use of nested_set, your model needs to have 3 fields: lft, rgt, and parent_id:
20
-
21
- class CreateCategories < ActiveRecord::Migration
22
- def self.up
23
- create_table :categories do |t|
24
- t.string :name
25
- t.integer :parent_id
26
- t.integer :lft
27
- t.integer :rgt
28
-
29
- # Uncomment it to store item level
30
- # t.integer :depth
31
- end
32
- end
33
-
34
- def self.down
35
- drop_table :categories
36
- end
37
- end
38
-
39
- Enable the nested set functionality by declaring acts_as_nested_set on your model
40
-
41
- class Category < ActiveRecord::Base
42
- acts_as_nested_set
43
- end
44
-
45
- Run `rake rdoc` to generate the API docs and see CollectiveIdea::Acts::NestedSet::Base::SingletonMethods for more info.
46
-
47
- == Conversion from other trees
48
-
49
- 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
50
-
51
- Category.rebuild!
52
-
53
- Your tree be converted to a valid nested set.
54
-
55
- == View Helper
56
-
57
- The view helper is called #nested_set_options.
58
-
59
- Example usage:
60
-
61
- <%= f.select :parent_id, nested_set_options(Category, @category) {|i| "#{'-' * i.level} #{i.name}" } %>
62
-
63
- <%= select_tag 'parent_id', options_for_select(nested_set_options(Category) {|i| "#{'-' * i.level} #{i.name}" } ) %>
64
-
65
- or sorted select:
66
-
67
- <%= f.select :parent_id, sorted_nested_set_options(Category, lambda(&:name)) {|i| "#{'-' * i.level} #{i.name}" } %>
68
-
69
- <% sort_method = lambda{|x, y| x.name.downcase <=> y.name.downcase} %>
70
- <%= select_tag 'parent_id', options_for_select(sorted_nested_set_options(Category, sort_method){|i| "#{'-' * i.level} #{i.name}" } ) %>
71
-
72
- See CollectiveIdea::Acts::NestedSet::Helper for more information about the helpers.
73
-
74
- == References
75
-
76
- You can learn more about nested sets at:
77
-
78
- http://www.dbmsmag.com/9603d06.html
79
- http://threebit.net/tutorials/nestedset/tutorial1.html
80
- http://api.rubyonrails.com/classes/ActiveRecord/Acts/NestedSet/ClassMethods.html
81
- http://opensource.symetrie.com/trac/better_nested_set/
82
-
83
- == How to contribute
84
-
85
- If you find what you might think is a bug:
86
-
87
- 1. Check the GitHub issue tracker to see if anyone else has had the same issue.
88
- http://github.com/skyeagle/nested_set/issues/
89
- 2. If you don't see anything, create an issue with information on how to reproduce it.
90
-
91
- If you want to contribute an enhancement or a fix:
92
-
93
- 1. Fork the project on github.
94
- http://github.com/skyeagle/nested_set/
95
- 2. Make your changes with tests.
96
- 3. Commit the changes without making changes to the Rakefile, VERSION, or any other files that aren't related to your enhancement or fix
97
- 4. Send a pull request.
98
-
99
- Copyright ©2010 Collective Idea, released under the MIT license