collectiveidea-awesome_nested_set 1.2.0 → 1.3.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,6 +1,6 @@
1
1
  = AwesomeNestedSet
2
2
 
3
- Awesome 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.
3
+ Awesome 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
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
9
  == Installation
10
10
 
11
- If you are on Rails 2.1 or later:
11
+ Install as a plugin:
12
12
 
13
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
60
  http://api.rubyonrails.com/classes/ActiveRecord/Acts/NestedSet/ClassMethods.html
61
61
  http://opensource.symetrie.com/trac/better_nested_set/
62
62
 
63
+ == How to contribute
63
64
 
64
- Copyright (c) 2008 Collective Idea, released under the MIT license
65
+ If you find what you might think is a bug:
66
+
67
+ 1. Check the GitHub issue tracker to see if anyone else has had the same issue.
68
+ http://github.com/collectiveidea/awesome_nested_set/issues/
69
+ 2. If you don't see anything, create an issue with information on how to reproduce it.
70
+
71
+ If you want to contribute an enhancement or a fix:
72
+
73
+ 1. Fork the project on github.
74
+ http://github.com/collectiveidea/awesome_nested_set/
75
+ 2. Make your changes with tests.
76
+ 3. Commit the changes without making changes to the Rakefile, VERSION, or any other files that aren't related to your enhancement or fix
77
+ 4. Send a pull request.
78
+
79
+ Copyright ©2008 Collective Idea, released under the MIT license
data/Rakefile CHANGED
@@ -28,7 +28,7 @@ task :default => :test
28
28
 
29
29
  desc 'Test the awesome_nested_set plugin.'
30
30
  Rake::TestTask.new(:test) do |t|
31
- t.libs << 'lib'
31
+ t.libs += ['lib', 'test']
32
32
  t.pattern = 'test/**/*_test.rb'
33
33
  t.verbose = true
34
34
  end
@@ -45,7 +45,7 @@ end
45
45
  namespace :test do
46
46
  desc "just rcov minus html output"
47
47
  Rcov::RcovTask.new(:coverage) do |t|
48
- # t.libs << 'test'
48
+ t.libs << 'test'
49
49
  t.test_files = FileList['test/**/*_test.rb']
50
50
  t.output_dir = 'coverage'
51
51
  t.verbose = true
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.2.0
1
+ 1.3.0
@@ -2,11 +2,11 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{awesome_nested_set}
5
- s.version = "1.2.0"
5
+ s.version = "1.3.0"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
8
  s.authors = ["Brandon Keepers", "Daniel Morrison"]
9
- s.date = %q{2009-07-15}
9
+ s.date = %q{2009-08-11}
10
10
  s.description = %q{An awesome nested set implementation for Active Record}
11
11
  s.email = %q{info@collectiveidea.com}
12
12
  s.extra_rdoc_files = [
@@ -22,10 +22,9 @@ Gem::Specification.new do |s|
22
22
  "awesome_nested_set.gemspec",
23
23
  "init.rb",
24
24
  "lib/awesome_nested_set.rb",
25
- "lib/awesome_nested_set/compatability.rb",
26
25
  "lib/awesome_nested_set/helper.rb",
27
- "lib/awesome_nested_set/named_scope.rb",
28
26
  "rails/init.rb",
27
+ "test/application.rb",
29
28
  "test/awesome_nested_set/helper_test.rb",
30
29
  "test/awesome_nested_set_test.rb",
31
30
  "test/db/database.yml",
@@ -46,6 +45,7 @@ Gem::Specification.new do |s|
46
45
  "test/fixtures/categories.yml",
47
46
  "test/fixtures/departments.yml",
48
47
  "test/fixtures/notes.yml",
48
+ "test/application.rb",
49
49
  "test/awesome_nested_set/helper_test.rb",
50
50
  "test/awesome_nested_set_test.rb",
51
51
  "test/db/schema.rb",
@@ -72,19 +72,23 @@ module CollectiveIdea #:nodoc:
72
72
  include InstanceMethods
73
73
  extend Columns
74
74
  extend ClassMethods
75
+
76
+ belongs_to :parent, :class_name => self.base_class.class_name,
77
+ :foreign_key => parent_column_name
75
78
 
76
79
  attr_accessor :skip_before_destroy
77
80
 
78
81
  # no bulk assignment
79
82
  attr_protected left_column_name.intern,
80
- right_column_name.intern,
81
- parent_column_name.intern
83
+ right_column_name.intern
82
84
 
83
- before_create :set_default_left_and_right
85
+ before_create :set_default_left_and_right
86
+ before_save :store_new_parent
87
+ after_save :move_to_new_parent
84
88
  before_destroy :destroy_descendants
85
89
 
86
90
  # no assignment to structure fields
87
- [left_column_name, right_column_name, parent_column_name].each do |column|
91
+ [left_column_name, right_column_name].each do |column|
88
92
  module_eval <<-"end_eval", __FILE__, __LINE__
89
93
  def #{column}=(x)
90
94
  raise ActiveRecord::ActiveRecordError, "Unauthorized assignment to #{column}: it's an internal field handled by acts_as_nested_set code, use move_to_* methods instead."
@@ -283,11 +287,6 @@ module CollectiveIdea #:nodoc:
283
287
  self_and_ancestors.find(:first)
284
288
  end
285
289
 
286
- # Returns the immediate parent
287
- def parent
288
- nested_set_scope.find_by_id(parent_id) if parent_id
289
- end
290
-
291
290
  # Returns the array of all parents and self
292
291
  def self_and_ancestors
293
292
  nested_set_scope.scoped :conditions => [
@@ -434,6 +433,19 @@ module CollectiveIdea #:nodoc:
434
433
  self.class.base_class.scoped options
435
434
  end
436
435
 
436
+ def store_new_parent
437
+ @move_to_new_parent_id = parent_id_changed? ? parent_id : false
438
+ true # force callback to return true
439
+ end
440
+
441
+ def move_to_new_parent
442
+ if @move_to_new_parent_id.nil?
443
+ move_to_root
444
+ elsif @move_to_new_parent_id
445
+ move_to_child_of(@move_to_new_parent_id)
446
+ end
447
+ end
448
+
437
449
  # on creation, set automatically lft and rgt to the end of the tree
438
450
  def set_default_left_and_right
439
451
  maxright = nested_set_scope.maximum(right_column_name) || 0
@@ -1,4 +1,3 @@
1
- require 'awesome_nested_set/compatability'
2
1
  require 'awesome_nested_set'
3
2
 
4
3
  ActiveRecord::Base.class_eval do
@@ -0,0 +1 @@
1
+ # This file is here to satisfy test_help from Rails < 2.3
@@ -1,9 +1,9 @@
1
- require File.dirname(__FILE__) + '/../test_helper'
1
+ require 'test_helper'
2
2
 
3
3
  module CollectiveIdea
4
4
  module Acts #:nodoc:
5
5
  module NestedSet #:nodoc:
6
- class AwesomeNestedSetTest < Test::Unit::TestCase
6
+ class AwesomeNestedSetTest < TestCaseClass
7
7
  include Helper
8
8
  fixtures :categories
9
9
 
@@ -38,4 +38,4 @@ module CollectiveIdea
38
38
  end
39
39
  end
40
40
  end
41
- end
41
+ end
@@ -1,10 +1,10 @@
1
- require File.dirname(__FILE__) + '/test_helper'
1
+ require 'test_helper'
2
2
 
3
3
  class Note < ActiveRecord::Base
4
4
  acts_as_nested_set :scope => [:notable_id, :notable_type]
5
5
  end
6
6
 
7
- class AwesomeNestedSetTest < Test::Unit::TestCase
7
+ class AwesomeNestedSetTest < TestCaseClass
8
8
 
9
9
  class Default < ActiveRecord::Base
10
10
  acts_as_nested_set
@@ -66,15 +66,10 @@ class AwesomeNestedSetTest < Test::Unit::TestCase
66
66
  assert_raises(ActiveRecord::ActiveRecordError) { Category.new.rgt = 1 }
67
67
  end
68
68
 
69
- def test_parent_column_protected_from_assignment
70
- assert_raises(ActiveRecord::ActiveRecordError) { Category.new.parent_id = 1 }
71
- end
72
-
73
69
  def test_colums_protected_on_initialize
74
- c = Category.new(:lft => 1, :rgt => 2, :parent_id => 3)
70
+ c = Category.new(:lft => 1, :rgt => 2)
75
71
  assert_nil c.lft
76
72
  assert_nil c.rgt
77
- assert_nil c.parent_id
78
73
  end
79
74
 
80
75
  def test_scoped_appends_id
@@ -619,4 +614,58 @@ class AwesomeNestedSetTest < Test::Unit::TestCase
619
614
  assert Category.valid?
620
615
  end
621
616
 
617
+ def test_assigning_parent_id_on_create
618
+ category = Category.create!(:name => "Child", :parent_id => categories(:child_2).id)
619
+ assert_equal categories(:child_2), category.parent
620
+ assert_equal categories(:child_2).id, category.parent_id
621
+ assert_not_nil category.left
622
+ assert_not_nil category.right
623
+ assert Category.valid?
624
+ end
625
+
626
+ def test_assigning_parent_on_create
627
+ category = Category.create!(:name => "Child", :parent => categories(:child_2))
628
+ assert_equal categories(:child_2), category.parent
629
+ assert_equal categories(:child_2).id, category.parent_id
630
+ assert_not_nil category.left
631
+ assert_not_nil category.right
632
+ assert Category.valid?
633
+ end
634
+
635
+ def test_assigning_parent_id_to_nil_on_create
636
+ category = Category.create!(:name => "New Root", :parent_id => nil)
637
+ assert_nil category.parent
638
+ assert_nil category.parent_id
639
+ assert_not_nil category.left
640
+ assert_not_nil category.right
641
+ assert Category.valid?
642
+ end
643
+
644
+ def test_assigning_parent_id_on_update
645
+ category = categories(:child_2_1)
646
+ category.parent_id = categories(:child_3).id
647
+ category.save
648
+ assert_equal categories(:child_3), category.parent
649
+ assert_equal categories(:child_3).id, category.parent_id
650
+ assert Category.valid?
651
+ end
652
+
653
+ def test_assigning_parent_on_update
654
+ category = categories(:child_2_1)
655
+ category.parent = categories(:child_3)
656
+ category.save
657
+ assert_equal categories(:child_3), category.parent
658
+ assert_equal categories(:child_3).id, category.parent_id
659
+ assert Category.valid?
660
+ end
661
+
662
+ def test_assigning_parent_id_to_nil_on_update
663
+ category = categories(:child_2_1)
664
+ category.parent_id = nil
665
+ category.save
666
+ assert_nil category.parent
667
+ assert_nil category.parent_id
668
+ assert Category.valid?
669
+ end
670
+
622
671
  end
@@ -1,16 +1,16 @@
1
1
  $:.unshift(File.dirname(__FILE__) + '/../lib')
2
2
  plugin_test_dir = File.dirname(__FILE__)
3
+ RAILS_ROOT = plugin_test_dir
3
4
 
4
5
  require 'rubygems'
5
6
  require 'test/unit'
6
7
  require 'multi_rails_init'
7
- require 'active_record'
8
- require 'action_controller'
9
- require 'action_view'
10
- require 'active_record/fixtures'
8
+ require 'test_help'
11
9
 
12
10
  require plugin_test_dir + '/../init.rb'
13
11
 
12
+ TestCaseClass = ActiveSupport::TestCase rescue Test::Unit::TestCase
13
+
14
14
  ActiveRecord::Base.logger = Logger.new(plugin_test_dir + "/debug.log")
15
15
 
16
16
  ActiveRecord::Base.configurations = YAML::load(IO.read(plugin_test_dir + "/db/database.yml"))
@@ -21,10 +21,10 @@ load(File.join(plugin_test_dir, "db", "schema.rb"))
21
21
  Dir["#{plugin_test_dir}/fixtures/*.rb"].each {|file| require file }
22
22
 
23
23
 
24
- class Test::Unit::TestCase #:nodoc:
24
+ class TestCaseClass #:nodoc:
25
25
  self.fixture_path = File.dirname(__FILE__) + "/fixtures/"
26
26
  self.use_transactional_fixtures = true
27
27
  self.use_instantiated_fixtures = false
28
28
 
29
29
  fixtures :categories, :notes, :departments
30
- end
30
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: collectiveidea-awesome_nested_set
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brandon Keepers
@@ -10,7 +10,7 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2009-07-15 00:00:00 -07:00
13
+ date: 2009-08-11 00:00:00 -07:00
14
14
  default_executable:
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
@@ -41,10 +41,9 @@ files:
41
41
  - awesome_nested_set.gemspec
42
42
  - init.rb
43
43
  - lib/awesome_nested_set.rb
44
- - lib/awesome_nested_set/compatability.rb
45
44
  - lib/awesome_nested_set/helper.rb
46
- - lib/awesome_nested_set/named_scope.rb
47
45
  - rails/init.rb
46
+ - test/application.rb
48
47
  - test/awesome_nested_set/helper_test.rb
49
48
  - test/awesome_nested_set_test.rb
50
49
  - test/db/database.yml
@@ -56,6 +55,7 @@ files:
56
55
  - test/test_helper.rb
57
56
  has_rdoc: false
58
57
  homepage: http://github.com/collectiveidea/awesome_nested_set
58
+ licenses:
59
59
  post_install_message:
60
60
  rdoc_options:
61
61
  - --main
@@ -79,7 +79,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
79
79
  requirements: []
80
80
 
81
81
  rubyforge_project:
82
- rubygems_version: 1.2.0
82
+ rubygems_version: 1.3.5
83
83
  signing_key:
84
84
  specification_version: 3
85
85
  summary: An awesome nested set implementation for Active Record
@@ -88,6 +88,7 @@ test_files:
88
88
  - test/fixtures/categories.yml
89
89
  - test/fixtures/departments.yml
90
90
  - test/fixtures/notes.yml
91
+ - test/application.rb
91
92
  - test/awesome_nested_set/helper_test.rb
92
93
  - test/awesome_nested_set_test.rb
93
94
  - test/db/schema.rb
@@ -1,29 +0,0 @@
1
- # Rails <2.x doesn't define #except
2
- class 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!)
14
- end
15
-
16
- # NamedScope is new to Rails 2.1
17
- unless defined? ActiveRecord::NamedScope
18
- require 'awesome_nested_set/named_scope'
19
- ActiveRecord::Base.class_eval do
20
- include CollectiveIdea::NamedScope
21
- end
22
- end
23
-
24
- # Rails 1.2.x doesn't define #quoted_table_name
25
- class 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')
29
- end
@@ -1,140 +0,0 @@
1
- # Taken from Rails 2.1
2
- module 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
140
- end