summaryrefslogtreecommitdiff
path: root/vendor/plugins/awesome_nested_set
diff options
context:
space:
mode:
authorhukl <contact@smyck.org>2009-09-08 11:30:11 +0200
committerhukl <contact@smyck.org>2009-09-08 11:30:11 +0200
commit03bde9fb42235af7147d6312e2cf6d928111dee1 (patch)
tree973056e923d5c1b147c16cc9ee5b50a0180c5630 /vendor/plugins/awesome_nested_set
parent1ab4397274dfbed2cd35bcad0c023cefc25c7365 (diff)
updated awesome_nested_set plugin
Diffstat (limited to 'vendor/plugins/awesome_nested_set')
-rw-r--r--vendor/plugins/awesome_nested_set/.gitignore5
-rw-r--r--vendor/plugins/awesome_nested_set/README.rdoc21
-rw-r--r--vendor/plugins/awesome_nested_set/Rakefile32
-rw-r--r--vendor/plugins/awesome_nested_set/VERSION1
-rw-r--r--vendor/plugins/awesome_nested_set/awesome_nested_set.gemspec84
-rw-r--r--vendor/plugins/awesome_nested_set/lib/awesome_nested_set.rb173
-rw-r--r--vendor/plugins/awesome_nested_set/lib/awesome_nested_set/compatability.rb29
-rw-r--r--vendor/plugins/awesome_nested_set/lib/awesome_nested_set/named_scope.rb140
-rw-r--r--vendor/plugins/awesome_nested_set/rails/init.rb1
-rw-r--r--vendor/plugins/awesome_nested_set/test/application.rb1
-rw-r--r--vendor/plugins/awesome_nested_set/test/awesome_nested_set/helper_test.rb6
-rw-r--r--vendor/plugins/awesome_nested_set/test/awesome_nested_set_test.rb165
-rw-r--r--vendor/plugins/awesome_nested_set/test/test_helper.rb14
13 files changed, 363 insertions, 309 deletions
diff --git a/vendor/plugins/awesome_nested_set/.gitignore b/vendor/plugins/awesome_nested_set/.gitignore
deleted file mode 100644
index df112b0..0000000
--- a/vendor/plugins/awesome_nested_set/.gitignore
+++ /dev/null
@@ -1,5 +0,0 @@
1awesome_nested_set.sqlite3.db
2test/debug.log
3rdoc
4coverage
5pkg
diff --git a/vendor/plugins/awesome_nested_set/README.rdoc b/vendor/plugins/awesome_nested_set/README.rdoc
index c093f75..884016d 100644
--- a/vendor/plugins/awesome_nested_set/README.rdoc
+++ b/vendor/plugins/awesome_nested_set/README.rdoc
@@ -1,6 +1,6 @@
1= AwesomeNestedSet 1= AwesomeNestedSet
2 2
3Awesome 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. 3Awesome 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 2.1 and later.
4 4
5== What makes this so awesome? 5== What makes this so awesome?
6 6
@@ -8,7 +8,7 @@ This is a new implementation of nested set based off of BetterNestedSet that fix
8 8
9== Installation 9== Installation
10 10
11If you are on Rails 2.1 or later: 11Install as a plugin:
12 12
13 script/plugin install git://github.com/collectiveidea/awesome_nested_set.git 13 script/plugin install git://github.com/collectiveidea/awesome_nested_set.git
14 14
@@ -60,5 +60,20 @@ You can learn more about nested sets at:
60 http://api.rubyonrails.com/classes/ActiveRecord/Acts/NestedSet/ClassMethods.html 60 http://api.rubyonrails.com/classes/ActiveRecord/Acts/NestedSet/ClassMethods.html
61 http://opensource.symetrie.com/trac/better_nested_set/ 61 http://opensource.symetrie.com/trac/better_nested_set/
62 62
63== How to contribute
63 64
64Copyright (c) 2008 Collective Idea, released under the MIT license \ No newline at end of file 65If you find what you might think is a bug:
66
671. Check the GitHub issue tracker to see if anyone else has had the same issue.
68 http://github.com/collectiveidea/awesome_nested_set/issues/
692. If you don't see anything, create an issue with information on how to reproduce it.
70
71If you want to contribute an enhancement or a fix:
72
731. Fork the project on github.
74 http://github.com/collectiveidea/awesome_nested_set/
752. Make your changes with tests.
763. Commit the changes without making changes to the Rakefile, VERSION, or any other files that aren't related to your enhancement or fix
774. Send a pull request.
78
79Copyright ©2008 Collective Idea, released under the MIT license \ No newline at end of file
diff --git a/vendor/plugins/awesome_nested_set/Rakefile b/vendor/plugins/awesome_nested_set/Rakefile
index 53906f6..ce70813 100644
--- a/vendor/plugins/awesome_nested_set/Rakefile
+++ b/vendor/plugins/awesome_nested_set/Rakefile
@@ -1,26 +1,34 @@
1require 'rake' 1begin
2 require 'jeweler'
3rescue LoadError
4 puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
5 exit 1
6end
2require 'rake/testtask' 7require 'rake/testtask'
3require 'rake/rdoctask' 8require 'rake/rdoctask'
4require 'rake/gempackagetask'
5require 'rcov/rcovtask' 9require 'rcov/rcovtask'
6require "load_multi_rails_rake_tasks" 10require "load_multi_rails_rake_tasks"
7 11
8spec = eval(File.read("#{File.dirname(__FILE__)}/awesome_nested_set.gemspec")) 12Jeweler::Tasks.new do |s|
9PKG_NAME = spec.name 13 s.name = "awesome_nested_set"
10PKG_VERSION = spec.version 14 s.summary = "An awesome nested set implementation for Active Record"
11 15 s.description = s.summary
12Rake::GemPackageTask.new(spec) do |pkg| 16 s.email = "info@collectiveidea.com"
13 pkg.need_zip = true 17 s.homepage = "http://github.com/collectiveidea/awesome_nested_set"
14 pkg.need_tar = true 18 s.authors = ["Brandon Keepers", "Daniel Morrison"]
19 s.add_dependency "activerecord", ['>= 1.1']
20 s.has_rdoc = true
21 s.extra_rdoc_files = [ "README.rdoc"]
22 s.rdoc_options = ["--main", "README.rdoc", "--inline-source", "--line-numbers"]
23 s.test_files = Dir['test/**/*.{yml,rb}']
15end 24end
16 25
17
18desc 'Default: run unit tests.' 26desc 'Default: run unit tests.'
19task :default => :test 27task :default => :test
20 28
21desc 'Test the awesome_nested_set plugin.' 29desc 'Test the awesome_nested_set plugin.'
22Rake::TestTask.new(:test) do |t| 30Rake::TestTask.new(:test) do |t|
23 t.libs << 'lib' 31 t.libs += ['lib', 'test']
24 t.pattern = 'test/**/*_test.rb' 32 t.pattern = 'test/**/*_test.rb'
25 t.verbose = true 33 t.verbose = true
26end 34end
@@ -37,7 +45,7 @@ end
37namespace :test do 45namespace :test do
38 desc "just rcov minus html output" 46 desc "just rcov minus html output"
39 Rcov::RcovTask.new(:coverage) do |t| 47 Rcov::RcovTask.new(:coverage) do |t|
40 # t.libs << 'test' 48 t.libs << 'test'
41 t.test_files = FileList['test/**/*_test.rb'] 49 t.test_files = FileList['test/**/*_test.rb']
42 t.output_dir = 'coverage' 50 t.output_dir = 'coverage'
43 t.verbose = true 51 t.verbose = true
diff --git a/vendor/plugins/awesome_nested_set/VERSION b/vendor/plugins/awesome_nested_set/VERSION
new file mode 100644
index 0000000..347f583
--- /dev/null
+++ b/vendor/plugins/awesome_nested_set/VERSION
@@ -0,0 +1 @@
1.4.1
diff --git a/vendor/plugins/awesome_nested_set/awesome_nested_set.gemspec b/vendor/plugins/awesome_nested_set/awesome_nested_set.gemspec
index c5a1d49..dbbca9e 100644
--- a/vendor/plugins/awesome_nested_set/awesome_nested_set.gemspec
+++ b/vendor/plugins/awesome_nested_set/awesome_nested_set.gemspec
@@ -1,20 +1,72 @@
1# Generated by jeweler
2# DO NOT EDIT THIS FILE
3# Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
4# -*- encoding: utf-8 -*-
5
1Gem::Specification.new do |s| 6Gem::Specification.new do |s|
2 s.name = "awesome_nested_set" 7 s.name = %q{awesome_nested_set}
3 s.version = "1.1.1" 8 s.version = "1.4.1"
4 s.summary = "An awesome replacement for acts_as_nested_set and better_nested_set." 9
5 s.description = s.summary 10 s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
6 11 s.authors = ["Brandon Keepers", "Daniel Morrison"]
7 s.files = %w(init.rb MIT-LICENSE Rakefile README.rdoc lib/awesome_nested_set.rb lib/awesome_nested_set/compatability.rb lib/awesome_nested_set/helper.rb lib/awesome_nested_set/named_scope.rb rails/init.rb test/awesome_nested_set_test.rb test/test_helper.rb test/awesome_nested_set/helper_test.rb test/db/database.yml test/db/schema.rb test/fixtures/categories.yml test/fixtures/category.rb test/fixtures/departments.yml test/fixtures/notes.yml) 12 s.date = %q{2009-09-06}
8 13 s.description = %q{An awesome nested set implementation for Active Record}
9 s.add_dependency "activerecord", ['>= 1.1'] 14 s.email = %q{info@collectiveidea.com}
10 15 s.extra_rdoc_files = [
16 "README.rdoc"
17 ]
18 s.files = [
19 ".autotest",
20 ".gitignore",
21 "MIT-LICENSE",
22 "README.rdoc",
23 "Rakefile",
24 "VERSION",
25 "awesome_nested_set.gemspec",
26 "init.rb",
27 "lib/awesome_nested_set.rb",
28 "lib/awesome_nested_set/helper.rb",
29 "rails/init.rb",
30 "test/application.rb",
31 "test/awesome_nested_set/helper_test.rb",
32 "test/awesome_nested_set_test.rb",
33 "test/db/database.yml",
34 "test/db/schema.rb",
35 "test/fixtures/categories.yml",
36 "test/fixtures/category.rb",
37 "test/fixtures/departments.yml",
38 "test/fixtures/notes.yml",
39 "test/test_helper.rb"
40 ]
11 s.has_rdoc = true 41 s.has_rdoc = true
12 s.extra_rdoc_files = [ "README.rdoc"] 42 s.homepage = %q{http://github.com/collectiveidea/awesome_nested_set}
13 s.rdoc_options = ["--main", "README.rdoc", "--inline-source", "--line-numbers"] 43 s.rdoc_options = ["--main", "README.rdoc", "--inline-source", "--line-numbers"]
14 44 s.require_paths = ["lib"]
15 s.test_files = %w(test/awesome_nested_set_test.rb test/test_helper.rb test/awesome_nested_set/helper_test.rb test/db/database.yml test/db/schema.rb test/fixtures/categories.yml test/fixtures/category.rb test/fixtures/departments.yml test/fixtures/notes.yml) 45 s.rubygems_version = %q{1.3.1}
16 s.require_path = 'lib' 46 s.summary = %q{An awesome nested set implementation for Active Record}
17 s.author = "Collective Idea" 47 s.test_files = [
18 s.email = "info@collectiveidea.com" 48 "test/db/database.yml",
19 s.homepage = "http://collectiveidea.com" 49 "test/fixtures/categories.yml",
50 "test/fixtures/departments.yml",
51 "test/fixtures/notes.yml",
52 "test/application.rb",
53 "test/awesome_nested_set/helper_test.rb",
54 "test/awesome_nested_set_test.rb",
55 "test/db/schema.rb",
56 "test/fixtures/category.rb",
57 "test/test_helper.rb"
58 ]
59
60 if s.respond_to? :specification_version then
61 current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
62 s.specification_version = 2
63
64 if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
65 s.add_runtime_dependency(%q<activerecord>, [">= 1.1"])
66 else
67 s.add_dependency(%q<activerecord>, [">= 1.1"])
68 end
69 else
70 s.add_dependency(%q<activerecord>, [">= 1.1"])
71 end
20end 72end
diff --git a/vendor/plugins/awesome_nested_set/lib/awesome_nested_set.rb b/vendor/plugins/awesome_nested_set/lib/awesome_nested_set.rb
index 3e10891..bafdf4c 100644
--- a/vendor/plugins/awesome_nested_set/lib/awesome_nested_set.rb
+++ b/vendor/plugins/awesome_nested_set/lib/awesome_nested_set.rb
@@ -15,24 +15,11 @@ module CollectiveIdea #:nodoc:
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, except for the creation: 19 # by another easier.
20 # 20 #
21 # in acts_as_tree:
22 # item.children.create(:name => "child1") 21 # item.children.create(:name => "child1")
23 # 22 #
24 # in acts_as_nested_set:
25 # # adds a new item at the "end" of the tree, i.e. with child.left = max(tree.right)+1
26 # child = MyClass.new(:name => "child1")
27 # child.save
28 # # now move the item to its right place
29 # child.move_to_child_of my_item
30 #
31 # You can pass an id or an object to:
32 # * <tt>#move_to_child_of</tt>
33 # * <tt>#move_to_right_of</tt>
34 # * <tt>#move_to_left_of</tt>
35 #
36 module SingletonMethods 23 module SingletonMethods
37 # Configuration options are: 24 # Configuration options are:
38 # 25 #
@@ -66,35 +53,43 @@ module CollectiveIdea #:nodoc:
66 write_inheritable_attribute :acts_as_nested_set_options, options 53 write_inheritable_attribute :acts_as_nested_set_options, options
67 class_inheritable_reader :acts_as_nested_set_options 54 class_inheritable_reader :acts_as_nested_set_options
68 55
69 include Comparable 56 unless self.is_a?(ClassMethods)
70 include Columns 57 include Comparable
71 include InstanceMethods 58 include Columns
72 extend Columns 59 include InstanceMethods
73 extend ClassMethods 60 extend Columns
61 extend ClassMethods
62
63 belongs_to :parent, :class_name => self.base_class.class_name,
64 :foreign_key => parent_column_name
65 has_many :children, :class_name => self.base_class.class_name,
66 :foreign_key => parent_column_name
74 67
75 # no bulk assignment 68 attr_accessor :skip_before_destroy
76 attr_protected left_column_name.intern, 69
77 right_column_name.intern, 70 # no bulk assignment
78 parent_column_name.intern 71 attr_protected left_column_name.intern,
72 right_column_name.intern
79 73
80 before_create :set_default_left_and_right 74 before_create :set_default_left_and_right
81 before_destroy :prune_from_tree 75 before_save :store_new_parent
76 after_save :move_to_new_parent
77 before_destroy :destroy_descendants
82 78
83 # no assignment to structure fields 79 # no assignment to structure fields
84 [left_column_name, right_column_name, parent_column_name].each do |column| 80 [left_column_name, right_column_name].each do |column|
85 module_eval <<-"end_eval", __FILE__, __LINE__ 81 module_eval <<-"end_eval", __FILE__, __LINE__
86 def #{column}=(x) 82 def #{column}=(x)
87 raise ActiveRecord::ActiveRecordError, "Unauthorized assignment to #{column}: it's an internal field handled by acts_as_nested_set code, use move_to_* methods instead." 83 raise ActiveRecord::ActiveRecordError, "Unauthorized assignment to #{column}: it's an internal field handled by acts_as_nested_set code, use move_to_* methods instead."
88 end 84 end
89 end_eval 85 end_eval
90 end 86 end
91 87
92 named_scope :roots, :conditions => {parent_column_name => nil}, :order => quoted_left_column_name 88 named_scope :roots, :conditions => {parent_column_name => nil}, :order => quoted_left_column_name
93 named_scope :leaves, :conditions => "#{quoted_right_column_name} - #{quoted_left_column_name} = 1", :order => quoted_left_column_name 89 named_scope :leaves, :conditions => "#{quoted_right_column_name} - #{quoted_left_column_name} = 1", :order => quoted_left_column_name
94 if self.respond_to?(:define_callbacks)
95 define_callbacks("before_move", "after_move")
96 end
97 90
91 define_callbacks("before_move", "after_move") if self.respond_to?(:define_callbacks)
92 end
98 93
99 end 94 end
100 95
@@ -192,6 +187,30 @@ module CollectiveIdea #:nodoc:
192 set_left_and_rights.call(root_node) 187 set_left_and_rights.call(root_node)
193 end 188 end
194 end 189 end
190
191 # Iterates over tree elements and determines the current level in the tree.
192 # Only accepts default ordering, odering by an other column than lft
193 # does not work. This method is much more efficent than calling level
194 # because it doesn't require any additional database queries.
195 #
196 # Example:
197 # Category.each_with_level(Category.root.self_and_descendants) do |o, level|
198 #
199 def each_with_level(objects)
200 path = [nil]
201 objects.each do |o|
202 if o.parent_id != path.last
203 # we are on a new level, did we decent or ascent?
204 if path.include?(o.parent_id)
205 # remove wrong wrong tailing paths elements
206 path.pop while path.last != o.parent_id
207 else
208 path << o.parent_id
209 end
210 end
211 yield(o, path.length - 1)
212 end
213 end
195 end 214 end
196 215
197 # Mixed into both classes and instances to provide easy access to the column names 216 # Mixed into both classes and instances to provide easy access to the column names
@@ -255,7 +274,7 @@ module CollectiveIdea #:nodoc:
255 end 274 end
256 275
257 def leaf? 276 def leaf?
258 right - left == 1 277 !new_record? && right - left == 1
259 end 278 end
260 279
261 # Returns true is this is a child node 280 # Returns true is this is a child node
@@ -281,15 +300,10 @@ module CollectiveIdea #:nodoc:
281 self_and_ancestors.find(:first) 300 self_and_ancestors.find(:first)
282 end 301 end
283 302
284 # Returns the immediate parent
285 def parent
286 nested_set_scope.find_by_id(parent_id) if parent_id
287 end
288
289 # Returns the array of all parents and self 303 # Returns the array of all parents and self
290 def self_and_ancestors 304 def self_and_ancestors
291 nested_set_scope.scoped :conditions => [ 305 nested_set_scope.scoped :conditions => [
292 "#{self.class.table_name}.#{quoted_left_column_name} <= ? AND #{self.class.table_name}.#{quoted_right_column_name} >= ?", left, right 306 "#{self.class.quoted_table_name}.#{quoted_left_column_name} <= ? AND #{self.class.quoted_table_name}.#{quoted_right_column_name} >= ?", left, right
293 ] 307 ]
294 end 308 end
295 309
@@ -310,7 +324,7 @@ module CollectiveIdea #:nodoc:
310 324
311 # Returns a set of all of its nested children which do not have children 325 # Returns a set of all of its nested children which do not have children
312 def leaves 326 def leaves
313 descendants.scoped :conditions => "#{self.class.table_name}.#{quoted_right_column_name} - #{self.class.table_name}.#{quoted_left_column_name} = 1" 327 descendants.scoped :conditions => "#{self.class.quoted_table_name}.#{quoted_right_column_name} - #{self.class.quoted_table_name}.#{quoted_left_column_name} = 1"
314 end 328 end
315 329
316 # Returns the level of this object in the tree 330 # Returns the level of this object in the tree
@@ -322,7 +336,7 @@ module CollectiveIdea #:nodoc:
322 # Returns a set of itself and all of its nested children 336 # Returns a set of itself and all of its nested children
323 def self_and_descendants 337 def self_and_descendants
324 nested_set_scope.scoped :conditions => [ 338 nested_set_scope.scoped :conditions => [
325 "#{self.class.table_name}.#{quoted_left_column_name} >= ? AND #{self.class.table_name}.#{quoted_right_column_name} <= ?", left, right 339 "#{self.class.quoted_table_name}.#{quoted_left_column_name} >= ? AND #{self.class.quoted_table_name}.#{quoted_right_column_name} <= ?", left, right
326 ] 340 ]
327 end 341 end
328 342
@@ -331,11 +345,6 @@ module CollectiveIdea #:nodoc:
331 without_self self_and_descendants 345 without_self self_and_descendants
332 end 346 end
333 347
334 # Returns a set of only this entry's immediate children
335 def children
336 nested_set_scope.scoped :conditions => {parent_column_name => self}
337 end
338
339 def is_descendant_of?(other) 348 def is_descendant_of?(other)
340 other.left < self.left && self.left < other.right && same_scope?(other) 349 other.left < self.left && self.left < other.right && same_scope?(other)
341 end 350 end
@@ -361,13 +370,13 @@ module CollectiveIdea #:nodoc:
361 370
362 # Find the first sibling to the left 371 # Find the first sibling to the left
363 def left_sibling 372 def left_sibling
364 siblings.find(:first, :conditions => ["#{self.class.table_name}.#{quoted_left_column_name} < ?", left], 373 siblings.find(:first, :conditions => ["#{self.class.quoted_table_name}.#{quoted_left_column_name} < ?", left],
365 :order => "#{self.class.table_name}.#{quoted_left_column_name} DESC") 374 :order => "#{self.class.quoted_table_name}.#{quoted_left_column_name} DESC")
366 end 375 end
367 376
368 # Find the first sibling to the right 377 # Find the first sibling to the right
369 def right_sibling 378 def right_sibling
370 siblings.find(:first, :conditions => ["#{self.class.table_name}.#{quoted_left_column_name} > ?", left]) 379 siblings.find(:first, :conditions => ["#{self.class.quoted_table_name}.#{quoted_left_column_name} > ?", left])
371 end 380 end
372 381
373 # Shorthand method for finding the left sibling and moving to the left of it. 382 # Shorthand method for finding the left sibling and moving to the left of it.
@@ -417,7 +426,7 @@ module CollectiveIdea #:nodoc:
417 protected 426 protected
418 427
419 def without_self(scope) 428 def without_self(scope)
420 scope.scoped :conditions => ["#{self.class.table_name}.#{self.class.primary_key} != ?", self] 429 scope.scoped :conditions => ["#{self.class.quoted_table_name}.#{self.class.primary_key} != ?", self]
421 end 430 end
422 431
423 # All nested set queries should use this nested_set_scope, which performs finds on 432 # All nested set queries should use this nested_set_scope, which performs finds on
@@ -432,6 +441,19 @@ module CollectiveIdea #:nodoc:
432 self.class.base_class.scoped options 441 self.class.base_class.scoped options
433 end 442 end
434 443
444 def store_new_parent
445 @move_to_new_parent_id = parent_id_changed? ? parent_id : false
446 true # force callback to return true
447 end
448
449 def move_to_new_parent
450 if @move_to_new_parent_id.nil?
451 move_to_root
452 elsif @move_to_new_parent_id
453 move_to_child_of(@move_to_new_parent_id)
454 end
455 end
456
435 # on creation, set automatically lft and rgt to the end of the tree 457 # on creation, set automatically lft and rgt to the end of the tree
436 def set_default_left_and_right 458 def set_default_left_and_right
437 maxright = nested_set_scope.maximum(right_column_name) || 0 459 maxright = nested_set_scope.maximum(right_column_name) || 0
@@ -442,29 +464,38 @@ module CollectiveIdea #:nodoc:
442 464
443 # Prunes a branch off of the tree, shifting all of the elements on the right 465 # Prunes a branch off of the tree, shifting all of the elements on the right
444 # back to the left so the counts still work. 466 # back to the left so the counts still work.
445 def prune_from_tree 467 def destroy_descendants
446 return if right.nil? || left.nil? 468 return if right.nil? || left.nil? || skip_before_destroy
447 diff = right - left + 1 469
448
449 delete_method = acts_as_nested_set_options[:dependent] == :destroy ?
450 :destroy_all : :delete_all
451
452 self.class.base_class.transaction do 470 self.class.base_class.transaction do
453 nested_set_scope.send(delete_method, 471 if acts_as_nested_set_options[:dependent] == :destroy
454 ["#{quoted_left_column_name} > ? AND #{quoted_right_column_name} < ?", 472 descendants.each do |model|
455 left, right] 473 model.skip_before_destroy = true
456 ) 474 model.destroy
475 end
476 else
477 nested_set_scope.delete_all(
478 ["#{quoted_left_column_name} > ? AND #{quoted_right_column_name} < ?",
479 left, right]
480 )
481 end
482
483 # update lefts and rights for remaining nodes
484 diff = right - left + 1
457 nested_set_scope.update_all( 485 nested_set_scope.update_all(
458 ["#{quoted_left_column_name} = (#{quoted_left_column_name} - ?)", diff], 486 ["#{quoted_left_column_name} = (#{quoted_left_column_name} - ?)", diff],
459 ["#{quoted_left_column_name} >= ?", right] 487 ["#{quoted_left_column_name} > ?", right]
460 ) 488 )
461 nested_set_scope.update_all( 489 nested_set_scope.update_all(
462 ["#{quoted_right_column_name} = (#{quoted_right_column_name} - ?)", diff], 490 ["#{quoted_right_column_name} = (#{quoted_right_column_name} - ?)", diff],
463 ["#{quoted_right_column_name} >= ?", right] 491 ["#{quoted_right_column_name} > ?", right]
464 ) 492 )
493
494 # Don't allow multiple calls to destroy to corrupt the set
495 self.skip_before_destroy = true
465 end 496 end
466 end 497 end
467 498
468 # reload left, right, and parent 499 # reload left, right, and parent
469 def reload_nested_set 500 def reload_nested_set
470 reload(:select => "#{quoted_left_column_name}, " + 501 reload(:select => "#{quoted_left_column_name}, " +
diff --git a/vendor/plugins/awesome_nested_set/lib/awesome_nested_set/compatability.rb b/vendor/plugins/awesome_nested_set/lib/awesome_nested_set/compatability.rb
deleted file mode 100644
index 2d11da3..0000000
--- a/vendor/plugins/awesome_nested_set/lib/awesome_nested_set/compatability.rb
+++ /dev/null
@@ -1,29 +0,0 @@
1# Rails <2.x doesn't define #except
2class Hash #:nodoc:
3 # Returns a new hash without the given keys.
4 def except(*keys)
5 clone.except!(*keys)
6 end unless method_defined?(:except)
7
8 # Replaces the hash without the given keys.
9 def except!(*keys)
10 keys.map! { |key| convert_key(key) } if respond_to?(:convert_key)
11 keys.each { |key| delete(key) }
12 self
13 end unless method_defined?(:except!)
14end
15
16# NamedScope is new to Rails 2.1
17unless defined? ActiveRecord::NamedScope
18 require 'awesome_nested_set/named_scope'
19 ActiveRecord::Base.class_eval do
20 include CollectiveIdea::NamedScope
21 end
22end
23
24# Rails 1.2.x doesn't define #quoted_table_name
25class ActiveRecord::Base #:nodoc:
26 def self.quoted_table_name
27 self.connection.quote_column_name(self.table_name)
28 end unless methods.include?('quoted_table_name')
29end \ No newline at end of file
diff --git a/vendor/plugins/awesome_nested_set/lib/awesome_nested_set/named_scope.rb b/vendor/plugins/awesome_nested_set/lib/awesome_nested_set/named_scope.rb
deleted file mode 100644
index 1836498..0000000
--- a/vendor/plugins/awesome_nested_set/lib/awesome_nested_set/named_scope.rb
+++ /dev/null
@@ -1,140 +0,0 @@
1# Taken from Rails 2.1
2module CollectiveIdea #:nodoc:
3 module NamedScope #:nodoc:
4 # All subclasses of ActiveRecord::Base have two named_scopes:
5 # * <tt>all</tt>, which is similar to a <tt>find(:all)</tt> query, and
6 # * <tt>scoped</tt>, which allows for the creation of anonymous scopes, on the fly:
7 #
8 # Shirt.scoped(:conditions => {:color => 'red'}).scoped(:include => :washing_instructions)
9 #
10 # These anonymous scopes tend to be useful when procedurally generating complex queries, where passing
11 # intermediate values (scopes) around as first-class objects is convenient.
12 def self.included(base)
13 base.class_eval do
14 extend ClassMethods
15 named_scope :scoped, lambda { |scope| scope }
16 end
17 end
18
19 module ClassMethods #:nodoc:
20 def scopes
21 read_inheritable_attribute(:scopes) || write_inheritable_attribute(:scopes, {})
22 end
23
24 # Adds a class method for retrieving and querying objects. A scope represents a narrowing of a database query,
25 # such as <tt>:conditions => {:color => :red}, :select => 'shirts.*', :include => :washing_instructions</tt>.
26 #
27 # class Shirt < ActiveRecord::Base
28 # named_scope :red, :conditions => {:color => 'red'}
29 # named_scope :dry_clean_only, :joins => :washing_instructions, :conditions => ['washing_instructions.dry_clean_only = ?', true]
30 # end
31 #
32 # The above calls to <tt>named_scope</tt> define class methods <tt>Shirt.red</tt> and <tt>Shirt.dry_clean_only</tt>. <tt>Shirt.red</tt>,
33 # in effect, represents the query <tt>Shirt.find(:all, :conditions => {:color => 'red'})</tt>.
34 #
35 # Unlike Shirt.find(...), however, the object returned by <tt>Shirt.red</tt> is not an Array; it resembles the association object
36 # constructed by a <tt>has_many</tt> declaration. For instance, you can invoke <tt>Shirt.red.find(:first)</tt>, <tt>Shirt.red.count</tt>,
37 # <tt>Shirt.red.find(:all, :conditions => {:size => 'small'})</tt>. Also, just
38 # as with the association objects, name scopes acts like an Array, implementing Enumerable; <tt>Shirt.red.each(&block)</tt>,
39 # <tt>Shirt.red.first</tt>, and <tt>Shirt.red.inject(memo, &block)</tt> all behave as if Shirt.red really were an Array.
40 #
41 # These named scopes are composable. For instance, <tt>Shirt.red.dry_clean_only</tt> will produce all shirts that are both red and dry clean only.
42 # Nested finds and calculations also work with these compositions: <tt>Shirt.red.dry_clean_only.count</tt> returns the number of garments
43 # for which these criteria obtain. Similarly with <tt>Shirt.red.dry_clean_only.average(:thread_count)</tt>.
44 #
45 # All scopes are available as class methods on the ActiveRecord descendent upon which the scopes were defined. But they are also available to
46 # <tt>has_many</tt> associations. If,
47 #
48 # class Person < ActiveRecord::Base
49 # has_many :shirts
50 # end
51 #
52 # then <tt>elton.shirts.red.dry_clean_only</tt> will return all of Elton's red, dry clean
53 # only shirts.
54 #
55 # Named scopes can also be procedural.
56 #
57 # class Shirt < ActiveRecord::Base
58 # named_scope :colored, lambda { |color|
59 # { :conditions => { :color => color } }
60 # }
61 # end
62 #
63 # In this example, <tt>Shirt.colored('puce')</tt> finds all puce shirts.
64 #
65 # Named scopes can also have extensions, just as with <tt>has_many</tt> declarations:
66 #
67 # class Shirt < ActiveRecord::Base
68 # named_scope :red, :conditions => {:color => 'red'} do
69 # def dom_id
70 # 'red_shirts'
71 # end
72 # end
73 # end
74 #
75 #
76 # For testing complex named scopes, you can examine the scoping options using the
77 # <tt>proxy_options</tt> method on the proxy itself.
78 #
79 # class Shirt < ActiveRecord::Base
80 # named_scope :colored, lambda { |color|
81 # { :conditions => { :color => color } }
82 # }
83 # end
84 #
85 # expected_options = { :conditions => { :colored => 'red' } }
86 # assert_equal expected_options, Shirt.colored('red').proxy_options
87 def named_scope(name, options = {}, &block)
88 scopes[name] = lambda do |parent_scope, *args|
89 Scope.new(parent_scope, case options
90 when Hash
91 options
92 when Proc
93 options.call(*args)
94 end, &block)
95 end
96 (class << self; self end).instance_eval do
97 define_method name do |*args|
98 scopes[name].call(self, *args)
99 end
100 end
101 end
102 end
103
104 class Scope #:nodoc:
105 attr_reader :proxy_scope, :proxy_options
106 [].methods.each { |m| delegate m, :to => :proxy_found unless m =~ /(^__|^nil\?|^send|class|extend|find|count|sum|average|maximum|minimum|paginate)/ }
107 delegate :scopes, :with_scope, :to => :proxy_scope
108
109 def initialize(proxy_scope, options, &block)
110 [options[:extend]].flatten.each { |extension| extend extension } if options[:extend]
111 extend Module.new(&block) if block_given?
112 @proxy_scope, @proxy_options = proxy_scope, options.except(:extend)
113 end
114
115 def reload
116 load_found; self
117 end
118
119 protected
120 def proxy_found
121 @found || load_found
122 end
123
124 private
125 def method_missing(method, *args, &block)
126 if scopes.include?(method)
127 scopes[method].call(self, *args)
128 else
129 with_scope :find => proxy_options do
130 proxy_scope.send(method, *args, &block)
131 end
132 end
133 end
134
135 def load_found
136 @found = find(:all)
137 end
138 end
139 end
140end \ No newline at end of file
diff --git a/vendor/plugins/awesome_nested_set/rails/init.rb b/vendor/plugins/awesome_nested_set/rails/init.rb
index e0a4e8b..2ff1336 100644
--- a/vendor/plugins/awesome_nested_set/rails/init.rb
+++ b/vendor/plugins/awesome_nested_set/rails/init.rb
@@ -1,4 +1,3 @@
1require 'awesome_nested_set/compatability'
2require 'awesome_nested_set' 1require 'awesome_nested_set'
3 2
4ActiveRecord::Base.class_eval do 3ActiveRecord::Base.class_eval do
diff --git a/vendor/plugins/awesome_nested_set/test/application.rb b/vendor/plugins/awesome_nested_set/test/application.rb
new file mode 100644
index 0000000..0e6eacf
--- /dev/null
+++ b/vendor/plugins/awesome_nested_set/test/application.rb
@@ -0,0 +1 @@
# This file is here to satisfy test_help from Rails < 2.3 \ No newline at end of file
diff --git a/vendor/plugins/awesome_nested_set/test/awesome_nested_set/helper_test.rb b/vendor/plugins/awesome_nested_set/test/awesome_nested_set/helper_test.rb
index 6122a0e..888323c 100644
--- a/vendor/plugins/awesome_nested_set/test/awesome_nested_set/helper_test.rb
+++ b/vendor/plugins/awesome_nested_set/test/awesome_nested_set/helper_test.rb
@@ -1,9 +1,9 @@
1require File.dirname(__FILE__) + '/../test_helper' 1require 'test_helper'
2 2
3module CollectiveIdea 3module CollectiveIdea
4 module Acts #:nodoc: 4 module Acts #:nodoc:
5 module NestedSet #:nodoc: 5 module NestedSet #:nodoc:
6 class AwesomeNestedSetTest < Test::Unit::TestCase 6 class AwesomeNestedSetTest < TestCaseClass
7 include Helper 7 include Helper
8 fixtures :categories 8 fixtures :categories
9 9
@@ -38,4 +38,4 @@ module CollectiveIdea
38 end 38 end
39 end 39 end
40 end 40 end
41end \ No newline at end of file 41end
diff --git a/vendor/plugins/awesome_nested_set/test/awesome_nested_set_test.rb b/vendor/plugins/awesome_nested_set/test/awesome_nested_set_test.rb
index 5252d80..7da4504 100644
--- a/vendor/plugins/awesome_nested_set/test/awesome_nested_set_test.rb
+++ b/vendor/plugins/awesome_nested_set/test/awesome_nested_set_test.rb
@@ -1,19 +1,18 @@
1require File.dirname(__FILE__) + '/test_helper' 1require 'test_helper'
2 2
3class Note < ActiveRecord::Base 3class Note < ActiveRecord::Base
4 acts_as_nested_set :scope => [:notable_id, :notable_type] 4 acts_as_nested_set :scope => [:notable_id, :notable_type]
5end 5end
6class Default < ActiveRecord::Base
7 acts_as_nested_set
8 set_table_name 'categories'
9end
10class ScopedCategory < ActiveRecord::Base
11 acts_as_nested_set :scope => :organization
12 set_table_name 'categories'
13end
6 14
7class AwesomeNestedSetTest < Test::Unit::TestCase 15class AwesomeNestedSetTest < TestCaseClass
8
9 class Default < ActiveRecord::Base
10 acts_as_nested_set
11 set_table_name 'categories'
12 end
13 class Scoped < ActiveRecord::Base
14 acts_as_nested_set :scope => :organization
15 set_table_name 'categories'
16 end
17 16
18 def test_left_column_default 17 def test_left_column_default
19 assert_equal 'lft', Default.acts_as_nested_set_options[:left_column] 18 assert_equal 'lft', Default.acts_as_nested_set_options[:left_column]
@@ -66,19 +65,14 @@ class AwesomeNestedSetTest < Test::Unit::TestCase
66 assert_raises(ActiveRecord::ActiveRecordError) { Category.new.rgt = 1 } 65 assert_raises(ActiveRecord::ActiveRecordError) { Category.new.rgt = 1 }
67 end 66 end
68 67
69 def test_parent_column_protected_from_assignment
70 assert_raises(ActiveRecord::ActiveRecordError) { Category.new.parent_id = 1 }
71 end
72
73 def test_colums_protected_on_initialize 68 def test_colums_protected_on_initialize
74 c = Category.new(:lft => 1, :rgt => 2, :parent_id => 3) 69 c = Category.new(:lft => 1, :rgt => 2)
75 assert_nil c.lft 70 assert_nil c.lft
76 assert_nil c.rgt 71 assert_nil c.rgt
77 assert_nil c.parent_id
78 end 72 end
79 73
80 def test_scoped_appends_id 74 def test_scoped_appends_id
81 assert_equal :organization_id, Scoped.acts_as_nested_set_options[:scope] 75 assert_equal :organization_id, ScopedCategory.acts_as_nested_set_options[:scope]
82 end 76 end
83 77
84 def test_roots_class_method 78 def test_roots_class_method
@@ -115,7 +109,9 @@ class AwesomeNestedSetTest < Test::Unit::TestCase
115 109
116 assert !categories(:top_level).leaf? 110 assert !categories(:top_level).leaf?
117 assert !categories(:child_2).leaf? 111 assert !categories(:child_2).leaf?
112 assert !Category.new.leaf?
118 end 113 end
114
119 115
120 def test_parent 116 def test_parent
121 assert_equal categories(:child_2), categories(:child_2_1).parent 117 assert_equal categories(:child_2), categories(:child_2_1).parent
@@ -218,7 +214,7 @@ class AwesomeNestedSetTest < Test::Unit::TestCase
218 end 214 end
219 215
220 def test_is_or_is_ancestor_of_with_scope 216 def test_is_or_is_ancestor_of_with_scope
221 root = Scoped.root 217 root = ScopedCategory.root
222 child = root.children.first 218 child = root.children.first
223 assert root.is_or_is_ancestor_of?(child) 219 assert root.is_or_is_ancestor_of?(child)
224 child.update_attribute :organization_id, 'different' 220 child.update_attribute :organization_id, 'different'
@@ -244,7 +240,7 @@ class AwesomeNestedSetTest < Test::Unit::TestCase
244 end 240 end
245 241
246 def test_is_or_is_descendant_of_with_scope 242 def test_is_or_is_descendant_of_with_scope
247 root = Scoped.root 243 root = ScopedCategory.root
248 child = root.children.first 244 child = root.children.first
249 assert child.is_or_is_descendant_of?(root) 245 assert child.is_or_is_descendant_of?(root)
250 child.update_attribute :organization_id, 'different' 246 child.update_attribute :organization_id, 'different'
@@ -252,7 +248,7 @@ class AwesomeNestedSetTest < Test::Unit::TestCase
252 end 248 end
253 249
254 def test_same_scope? 250 def test_same_scope?
255 root = Scoped.root 251 root = ScopedCategory.root
256 child = root.children.first 252 child = root.children.first
257 assert child.same_scope?(root) 253 assert child.same_scope?(root)
258 child.update_attribute :organization_id, 'different' 254 child.update_attribute :organization_id, 'different'
@@ -600,4 +596,131 @@ class AwesomeNestedSetTest < Test::Unit::TestCase
600 assert_not_equal notes(:scope1), notes(:scope2) 596 assert_not_equal notes(:scope1), notes(:scope2)
601 end 597 end
602 598
599 def test_delete_does_not_invalidate
600 Category.acts_as_nested_set_options[:dependent] = :delete
601 categories(:child_2).destroy
602 assert Category.valid?
603 end
604
605 def test_destroy_does_not_invalidate
606 Category.acts_as_nested_set_options[:dependent] = :destroy
607 categories(:child_2).destroy
608 assert Category.valid?
609 end
610
611 def test_destroy_multiple_times_does_not_invalidate
612 Category.acts_as_nested_set_options[:dependent] = :destroy
613 categories(:child_2).destroy
614 categories(:child_2).destroy
615 assert Category.valid?
616 end
617
618 def test_assigning_parent_id_on_create
619 category = Category.create!(:name => "Child", :parent_id => categories(:child_2).id)
620 assert_equal categories(:child_2), category.parent
621 assert_equal categories(:child_2).id, category.parent_id
622 assert_not_nil category.left
623 assert_not_nil category.right
624 assert Category.valid?
625 end
626
627 def test_assigning_parent_on_create
628 category = Category.create!(:name => "Child", :parent => categories(:child_2))
629 assert_equal categories(:child_2), category.parent
630 assert_equal categories(:child_2).id, category.parent_id
631 assert_not_nil category.left
632 assert_not_nil category.right
633 assert Category.valid?
634 end
635
636 def test_assigning_parent_id_to_nil_on_create
637 category = Category.create!(:name => "New Root", :parent_id => nil)
638 assert_nil category.parent
639 assert_nil category.parent_id
640 assert_not_nil category.left
641 assert_not_nil category.right
642 assert Category.valid?
643 end
644
645 def test_assigning_parent_id_on_update
646 category = categories(:child_2_1)
647 category.parent_id = categories(:child_3).id
648 category.save
649 assert_equal categories(:child_3), category.parent
650 assert_equal categories(:child_3).id, category.parent_id
651 assert Category.valid?
652 end
653
654 def test_assigning_parent_on_update
655 category = categories(:child_2_1)
656 category.parent = categories(:child_3)
657 category.save
658 assert_equal categories(:child_3), category.parent
659 assert_equal categories(:child_3).id, category.parent_id
660 assert Category.valid?
661 end
662
663 def test_assigning_parent_id_to_nil_on_update
664 category = categories(:child_2_1)
665 category.parent_id = nil
666 category.save
667 assert_nil category.parent
668 assert_nil category.parent_id
669 assert Category.valid?
670 end
671
672 def test_creating_child_from_parent
673 category = categories(:child_2).children.create!(:name => "Child")
674 assert_equal categories(:child_2), category.parent
675 assert_equal categories(:child_2).id, category.parent_id
676 assert_not_nil category.left
677 assert_not_nil category.right
678 assert Category.valid?
679 end
680
681 def check_structure(entries, structure)
682 structure = structure.dup
683 Category.each_with_level(entries) do |category, level|
684 expected_level, expected_name = structure.shift
685 assert_equal expected_name, category.name, "wrong category"
686 assert_equal expected_level, level, "wrong level for #{category.name}"
687 end
688 end
689
690 def test_each_with_level
691 levels = [
692 [0, "Top Level"],
693 [1, "Child 1"],
694 [1, "Child 2"],
695 [2, "Child 2.1"],
696 [1, "Child 3" ]]
697
698 check_structure(Category.root.self_and_descendants, levels)
699
700 # test some deeper structures
701 category = Category.find_by_name("Child 1")
702 c1 = Category.new(:name => "Child 1.1")
703 c2 = Category.new(:name => "Child 1.1.1")
704 c3 = Category.new(:name => "Child 1.1.1.1")
705 c4 = Category.new(:name => "Child 1.2")
706 [c1, c2, c3, c4].each(&:save!)
707
708 c1.move_to_child_of(category)
709 c2.move_to_child_of(c1)
710 c3.move_to_child_of(c2)
711 c4.move_to_child_of(category)
712
713 levels = [
714 [0, "Top Level"],
715 [1, "Child 1"],
716 [2, "Child 1.1"],
717 [3, "Child 1.1.1"],
718 [4, "Child 1.1.1.1"],
719 [2, "Child 1.2"],
720 [1, "Child 2"],
721 [2, "Child 2.1"],
722 [1, "Child 3" ]]
723
724 check_structure(Category.root.self_and_descendants, levels)
725 end
603end 726end
diff --git a/vendor/plugins/awesome_nested_set/test/test_helper.rb b/vendor/plugins/awesome_nested_set/test/test_helper.rb
index 6939822..05d8855 100644
--- a/vendor/plugins/awesome_nested_set/test/test_helper.rb
+++ b/vendor/plugins/awesome_nested_set/test/test_helper.rb
@@ -1,17 +1,16 @@
1$:.unshift(File.dirname(__FILE__) + '/../lib') 1$:.unshift(File.dirname(__FILE__) + '/../lib')
2plugin_test_dir = File.dirname(__FILE__) 2plugin_test_dir = File.dirname(__FILE__)
3RAILS_ROOT = plugin_test_dir
3 4
4require 'rubygems' 5require 'rubygems'
5require 'test/unit' 6require 'test/unit'
6require 'multi_rails_init' 7require 'multi_rails_init'
7# gem 'activerecord', '>= 2.0' 8require 'test_help'
8require 'active_record'
9require 'action_controller'
10require 'action_view'
11require 'active_record/fixtures'
12 9
13require plugin_test_dir + '/../init.rb' 10require plugin_test_dir + '/../init.rb'
14 11
12TestCaseClass = ActiveSupport::TestCase rescue Test::Unit::TestCase
13
15ActiveRecord::Base.logger = Logger.new(plugin_test_dir + "/debug.log") 14ActiveRecord::Base.logger = Logger.new(plugin_test_dir + "/debug.log")
16 15
17ActiveRecord::Base.configurations = YAML::load(IO.read(plugin_test_dir + "/db/database.yml")) 16ActiveRecord::Base.configurations = YAML::load(IO.read(plugin_test_dir + "/db/database.yml"))
@@ -21,11 +20,10 @@ load(File.join(plugin_test_dir, "db", "schema.rb"))
21 20
22Dir["#{plugin_test_dir}/fixtures/*.rb"].each {|file| require file } 21Dir["#{plugin_test_dir}/fixtures/*.rb"].each {|file| require file }
23 22
24 23class TestCaseClass #:nodoc:
25class Test::Unit::TestCase #:nodoc:
26 self.fixture_path = File.dirname(__FILE__) + "/fixtures/" 24 self.fixture_path = File.dirname(__FILE__) + "/fixtures/"
27 self.use_transactional_fixtures = true 25 self.use_transactional_fixtures = true
28 self.use_instantiated_fixtures = false 26 self.use_instantiated_fixtures = false
29 27
30 fixtures :categories, :notes, :departments 28 fixtures :categories, :notes, :departments
31end \ No newline at end of file 29end