mongoid-tree 0.4.0 → 0.5.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.
data/README.rdoc CHANGED
@@ -156,12 +156,17 @@ Example:
156
156
 
157
157
  == Known issues
158
158
 
159
- See http://github.com/benedikt/mongoid-tree/issues
159
+ See https://github.com/benedikt/mongoid-tree/issues
160
160
 
161
161
 
162
162
  == Repository
163
163
 
164
- See http://github.com/benedikt/mongoid-tree and feel free to fork it!
164
+ See https://github.com/benedikt/mongoid-tree and feel free to fork it!
165
+
166
+
167
+ == Contributors
168
+
169
+ See a list of all contributors at https://github.com/benedikt/mongoid-tree/contributors. Thanks a lot everyone!
165
170
 
166
171
 
167
172
  == Copyright
@@ -38,10 +38,22 @@ module Mongoid
38
38
  field :position, :type => Integer
39
39
 
40
40
  before_save :assign_default_position
41
- before_save :reposition_former_siblings, :if => :parent_id_changed?
41
+ before_save :reposition_former_siblings, :if => :sibling_reposition_required?
42
42
  after_destroy :move_lower_siblings_up
43
43
  end
44
44
 
45
+ ##
46
+ # :singleton-method: roots
47
+ # Returns all root documents ordered by position
48
+
49
+ module ClassMethods # :nodoc:
50
+
51
+ def roots
52
+ super.order_by(:position.asc)
53
+ end
54
+
55
+ end
56
+
45
57
  ##
46
58
  # Returns siblings below the current document.
47
59
  # Siblings with a position greater than this documents's position.
@@ -115,17 +127,22 @@ module Mongoid
115
127
  #
116
128
  # This method changes the node's parent if nescessary.
117
129
  def move_above(other)
118
- update_attributes!(:parent_id => other.parent_id) unless sibling_of?(other)
130
+ unless sibling_of?(other)
131
+ self.parent_id = other.parent_id
132
+ save!
133
+ end
119
134
 
120
135
  if position > other.position
121
136
  new_position = other.position
122
137
  other.lower_siblings.where(:position.lt => self.position).each { |s| s.inc(:position, 1) }
123
138
  other.inc(:position, 1)
124
- update_attributes!(:position => new_position)
139
+ self.position = new_position
140
+ save!
125
141
  else
126
142
  new_position = other.position - 1
127
143
  other.higher_siblings.where(:position.gt => self.position).each { |s| s.inc(:position, -1) }
128
- update_attributes!(:position => new_position)
144
+ self.position = new_position
145
+ save!
129
146
  end
130
147
  end
131
148
 
@@ -134,17 +151,22 @@ module Mongoid
134
151
  #
135
152
  # This method changes the node's parent if nescessary.
136
153
  def move_below(other)
137
- update_attributes!(:parent_id => other.parent_id) unless sibling_of?(other)
154
+ unless sibling_of?(other)
155
+ self.parent_id = other.parent_id
156
+ save!
157
+ end
138
158
 
139
159
  if position > other.position
140
160
  new_position = other.position + 1
141
161
  other.lower_siblings.where(:position.lt => self.position).each { |s| s.inc(:position, 1) }
142
- update_attributes!(:position => new_position)
162
+ self.position = new_position
163
+ save!
143
164
  else
144
165
  new_position = other.position
145
166
  other.higher_siblings.where(:position.gt => self.position).each { |s| s.inc(:position, -1) }
146
167
  other.inc(:position, -1)
147
- update_attributes!(:position => new_position)
168
+ self.position = new_position
169
+ save!
148
170
  end
149
171
  end
150
172
 
@@ -161,6 +183,10 @@ module Mongoid
161
183
  former_siblings.each { |s| s.inc(:position, -1) }
162
184
  end
163
185
 
186
+ def sibling_reposition_required?
187
+ parent_id_changed? && persisted?
188
+ end
189
+
164
190
  def assign_default_position
165
191
  return unless self.position.nil? || self.parent_id_changed?
166
192
 
@@ -46,6 +46,41 @@ module Mongoid # :nodoc:
46
46
  # node1, node2, node3, node4, node5, node6, node7
47
47
  #
48
48
  module Traversal
49
+ extend ActiveSupport::Concern
50
+
51
+ ##
52
+ # :singleton-method: traverse
53
+ # Traverses the entire tree, one root at a time, using the given traversal
54
+ # method (Default is :depth_first).
55
+ #
56
+ # See Mongoid::Tree::Traversal for available traversal methods.
57
+ #
58
+ # Example:
59
+ #
60
+ # # Say we have the following tree, and want to print its hierarchy:
61
+ # # root_1
62
+ # # child_1_a
63
+ # # root_2
64
+ # # child_2_a
65
+ # # child_2_a_1
66
+ #
67
+ # Node.traverse(:depth_first) do |node|
68
+ # indentation = ' ' * node.depth
69
+ #
70
+ # puts "#{indentation}#{node.name}"
71
+ # end
72
+ #
73
+
74
+ ##
75
+ # The methods in this module are class-level methods documented above.
76
+ # They're extended into the base class automatically.
77
+ module ClassMethods # :nodoc:
78
+ def traverse(type = :depth_first, &block)
79
+ raise ArgumentError, "No block given" unless block_given?
80
+ roots.each { |root| root.traverse(type, &block) }
81
+ nil
82
+ end
83
+ end
49
84
 
50
85
  ##
51
86
  # Traverses the tree using the given traversal method (Default is :depth_first)
@@ -60,7 +95,7 @@ module Mongoid # :nodoc:
60
95
  # results << node
61
96
  # end
62
97
  def traverse(type = :depth_first, &block)
63
- raise "No block given" unless block_given?
98
+ raise ArgumentError, "No block given" unless block_given?
64
99
  send("#{type}_traversal", &block)
65
100
  end
66
101
 
@@ -79,7 +114,6 @@ module Mongoid # :nodoc:
79
114
  queue += node.children
80
115
  end
81
116
  end
82
-
83
117
  end
84
118
  end
85
119
  end
data/lib/mongoid/tree.rb CHANGED
@@ -84,8 +84,12 @@ module Mongoid # :nodoc:
84
84
 
85
85
  included do
86
86
  references_many :children, :class_name => self.name, :foreign_key => :parent_id, :inverse_of => :parent do
87
- def <<(*objects)
88
- super; objects.each { |c| c.parent = @parent }
87
+ def <<(*objects) # :nodoc:
88
+ super
89
+ objects.each do |c|
90
+ c.parent = @parent
91
+ c.save
92
+ end
89
93
  end
90
94
  end
91
95
 
@@ -274,7 +278,10 @@ module Mongoid # :nodoc:
274
278
  ##
275
279
  # Moves all children to this document's parent
276
280
  def move_children_to_parent
277
- children.each { |c| c.update_attributes(:parent_id => self.parent_id) }
281
+ children.each do |c|
282
+ c.parent_id = self.parent_id
283
+ c.save
284
+ end
278
285
  end
279
286
 
280
287
  ##
@@ -2,6 +2,8 @@ require 'spec_helper'
2
2
 
3
3
  describe Mongoid::Tree::Ordering do
4
4
 
5
+ subject { OrderedNode }
6
+
5
7
  it "should store position as an Integer with a default of nil" do
6
8
  f = OrderedNode.fields['position']
7
9
  f.should_not be_nil
@@ -11,7 +13,7 @@ describe Mongoid::Tree::Ordering do
11
13
 
12
14
  describe 'when saved' do
13
15
  before(:each) do
14
- setup_ordered_tree <<-ENDTREE
16
+ setup_tree <<-ENDTREE
15
17
  - root:
16
18
  - child:
17
19
  - subchild:
@@ -60,11 +62,18 @@ describe Mongoid::Tree::Ordering do
60
62
  other_child.save!
61
63
  node(:another_child).position.should == 0
62
64
  end
65
+
66
+ it "should not reposition siblings when it's not yet saved" do
67
+ new_node = OrderedNode.new(:name => 'new')
68
+ new_node.parent = node(:root)
69
+ new_node.should_not_receive(:reposition_former_siblings)
70
+ new_node.save
71
+ end
63
72
  end
64
73
 
65
74
  describe 'destroy strategies' do
66
75
  before(:each) do
67
- setup_ordered_tree <<-ENDTREE
76
+ setup_tree <<-ENDTREE
68
77
  - root:
69
78
  - child:
70
79
  - subchild
@@ -84,7 +93,7 @@ describe Mongoid::Tree::Ordering do
84
93
 
85
94
  describe 'utility methods' do
86
95
  before(:each) do
87
- setup_ordered_tree <<-ENDTREE
96
+ setup_tree <<-ENDTREE
88
97
  - first_root:
89
98
  - first_child_of_first_root
90
99
  - second_child_of_first_root
@@ -159,7 +168,7 @@ describe Mongoid::Tree::Ordering do
159
168
 
160
169
  describe 'moving nodes around', :focus => true do
161
170
  before(:each) do
162
- setup_ordered_tree <<-ENDTREE
171
+ setup_tree <<-ENDTREE
163
172
  - first_root:
164
173
  - first_child_of_first_root
165
174
  - second_child_of_first_root
@@ -175,7 +184,6 @@ describe Mongoid::Tree::Ordering do
175
184
  describe '#move_below' do
176
185
  it 'should fix positions within the current list when moving an sibling away from its current parent' do
177
186
  node_to_move = node(:first_child_of_first_root)
178
- new_parent = node(:second_root)
179
187
  node_to_move.move_below(node(:first_child_of_second_root))
180
188
  node(:second_child_of_first_root).position.should == 0
181
189
  end
@@ -214,7 +222,6 @@ describe Mongoid::Tree::Ordering do
214
222
  describe '#move_above' do
215
223
  it 'should fix positions within the current list when moving an sibling away from its current parent' do
216
224
  node_to_move = node(:first_child_of_first_root)
217
- new_parent = node(:second_root)
218
225
  node_to_move.move_above(node(:first_child_of_second_root))
219
226
  node(:second_child_of_first_root).position.should == 0
220
227
  end
@@ -2,6 +2,8 @@ require 'spec_helper'
2
2
 
3
3
  describe Mongoid::Tree::Traversal do
4
4
 
5
+ subject { Node }
6
+
5
7
  describe '#traverse' do
6
8
 
7
9
  subject { Node.new }
@@ -10,7 +12,7 @@ describe Mongoid::Tree::Traversal do
10
12
  expect { subject.traverse }.to raise_error(/No block given/)
11
13
  end
12
14
 
13
- [:depth_first].each do |method|
15
+ [:depth_first, :breadth_first].each do |method|
14
16
  it "should support #{method} traversal" do
15
17
  expect { subject.traverse(method) {} }.to_not raise_error
16
18
  end
@@ -68,8 +70,11 @@ describe Mongoid::Tree::Traversal do
68
70
  end
69
71
 
70
72
  describe 'with reordered nodes' do
73
+
74
+ subject { OrderedNode }
75
+
71
76
  before do
72
- setup_ordered_tree <<-ENDTREE
77
+ setup_tree <<-ENDTREE
73
78
  node1:
74
79
  - node2:
75
80
  - node3
@@ -111,4 +116,60 @@ describe Mongoid::Tree::Traversal do
111
116
 
112
117
  end
113
118
 
119
+ describe '.traverse' do
120
+
121
+ describe 'when not given a block' do
122
+
123
+ it 'raises an error' do
124
+ expect {Node.traverse}.to raise_error ArgumentError, 'No block given'
125
+ end
126
+ end
127
+
128
+ before :each do
129
+ setup_tree <<-ENDTREE
130
+ - root1
131
+ - root2
132
+ ENDTREE
133
+
134
+ @block = Proc.new {}
135
+ @root1 = node(:root1)
136
+ @root2 = node(:root2)
137
+
138
+ Node.stub(:roots).and_return [@root1, @root2]
139
+ end
140
+
141
+ it 'grabs each root' do
142
+ Node.should_receive(:roots).and_return []
143
+
144
+ Node.traverse &@block
145
+ end
146
+
147
+ it 'defaults the "type" arg to :depth_first' do
148
+ @root1.should_receive(:traverse).with(:depth_first)
149
+ @root2.should_receive(:traverse).with(:depth_first)
150
+
151
+ Node.traverse &@block
152
+ end
153
+
154
+ it 'traverses each root' do
155
+ @root1.should_receive(:traverse)
156
+ @root2.should_receive(:traverse)
157
+
158
+ Node.traverse &@block
159
+ end
160
+
161
+ describe 'when the "type" arg is :breadth_first' do
162
+
163
+ it 'traverses breadth-first' do
164
+ @root1.should_receive(:traverse).with(:breadth_first)
165
+ @root2.should_receive(:traverse).with(:breadth_first)
166
+
167
+ Node.traverse :breadth_first, &@block
168
+ end
169
+ end
170
+
171
+ it 'returns nil' do
172
+ Node.traverse(&@block).should be nil
173
+ end
174
+ end
114
175
  end
@@ -2,29 +2,31 @@ require 'spec_helper'
2
2
 
3
3
  describe Mongoid::Tree do
4
4
 
5
+ subject { Node }
6
+
5
7
  it "should reference many children as inverse of parent with index" do
6
- a = Node.associations['children']
7
- a.should_not be_nil
8
- a.association.should == Mongoid::Associations::ReferencesMany
9
- a.options.class_name.should == 'Node'
10
- a.options.foreign_key.should == 'parent_id'
8
+ a = Node.reflect_on_association(:children)
9
+ a.should be
10
+ a.association.should eql(Mongoid::Associations::ReferencesMany)
11
+ a.options.class_name.should eql('Node')
12
+ a.options.foreign_key.should eql('parent_id')
11
13
  Node.index_options.should have_key('parent_id')
12
14
  end
13
15
 
14
16
  it "should be referenced in one parent as inverse of children" do
15
- a = Node.associations['parent']
16
- a.should_not be_nil
17
- a.association.should == Mongoid::Associations::ReferencedIn
18
- a.options.class_name.should == 'Node'
19
- a.options.inverse_of.should == :children
17
+ a = Node.reflect_on_association(:parent)
18
+ a.should be
19
+ a.association.should eql(Mongoid::Associations::ReferencedIn)
20
+ a.options.class_name.should eql('Node')
21
+ a.options.inverse_of.should eql(:children)
20
22
  a.options.index.should be_true
21
23
  end
22
24
 
23
25
  it "should store parent_ids as Array with [] as default with index" do
24
26
  f = Node.fields['parent_ids']
25
- f.should_not be_nil
26
- f.options[:type].should == Array
27
- f.options[:default].should == []
27
+ f.should be
28
+ f.options[:type].should eql(Array)
29
+ f.options[:default].should eql([])
28
30
  Node.index_options.should have_key(:parent_ids)
29
31
  end
30
32
 
@@ -84,7 +86,9 @@ describe Mongoid::Tree do
84
86
  end
85
87
 
86
88
  it "should rebuild its children's parent_ids when its own parent_id is removed" do
87
- node(:child).update_attributes(:parent_id => nil)
89
+ c = node(:child)
90
+ c.parent_id = nil
91
+ c.save
88
92
  node(:subchild).parent_ids.should == [node(:child).id]
89
93
  end
90
94
 
@@ -3,11 +3,7 @@ require 'yaml'
3
3
  module Mongoid::Tree::TreeMacros
4
4
 
5
5
  def setup_tree(tree)
6
- create_tree(YAML.load(tree), {:ordered => false})
7
- end
8
-
9
- def setup_ordered_tree(tree)
10
- create_tree(YAML.load(tree), {:ordered => true})
6
+ create_tree(YAML.load(tree))
11
7
  end
12
8
 
13
9
  def node(name)
@@ -25,22 +21,21 @@ module Mongoid::Tree::TreeMacros
25
21
  end
26
22
 
27
23
  private
28
- def create_tree(object, opts={})
24
+ def create_tree(object)
29
25
  case object
30
- when String: return create_node(object, opts)
31
- when Array: object.each { |tree| create_tree(tree, opts) }
32
- when Hash:
26
+ when String then return create_node(object)
27
+ when Array then object.each { |tree| create_tree(tree) }
28
+ when Hash then
33
29
  name, children = object.first
34
- node = create_node(name, opts)
35
- children.each { |c| node.children << create_tree(c, opts) }
30
+ node = create_node(name)
31
+ children.each { |c| node.children << create_tree(c) }
36
32
  return node
37
33
  end
38
34
  end
39
35
 
40
- def create_node(name, opts={})
41
- node_class = opts[:ordered] ? OrderedNode : Node
36
+ def create_node(name)
42
37
  @nodes ||= HashWithIndifferentAccess.new
43
- @nodes[name] = node_class.create(:name => name)
38
+ @nodes[name] = subject.create(:name => name)
44
39
  end
45
40
  end
46
41
 
@@ -4,6 +4,8 @@ class Node
4
4
  include Mongoid::Tree::Traversal
5
5
 
6
6
  field :name
7
+
8
+ attr_accessible :name
7
9
  end
8
10
 
9
11
  class SubclassedNode < Node
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mongoid-tree
3
3
  version: !ruby/object:Gem::Version
4
- hash: 15
4
+ hash: 11
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
- - 4
8
+ - 5
9
9
  - 0
10
- version: 0.4.0
10
+ version: 0.5.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Benedikt Deicke
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-11-03 00:00:00 +01:00
18
+ date: 2010-12-20 00:00:00 +01:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -44,11 +44,11 @@ dependencies:
44
44
  requirements:
45
45
  - - ~>
46
46
  - !ruby/object:Gem::Version
47
- hash: 3
47
+ hash: 5
48
48
  segments:
49
49
  - 2
50
- - 0
51
- version: "2.0"
50
+ - 3
51
+ version: "2.3"
52
52
  type: :development
53
53
  version_requirements: *id002
54
54
  - !ruby/object:Gem::Dependency
@@ -107,10 +107,9 @@ files:
107
107
  - README.rdoc
108
108
  - Rakefile
109
109
  - Gemfile
110
- - Gemfile.lock
111
110
  - .rspec
112
111
  has_rdoc: true
113
- homepage: http://github.com/benedikt/mongoid-tree
112
+ homepage: https://github.com/benedikt/mongoid-tree
114
113
  licenses: []
115
114
 
116
115
  post_install_message:
data/Gemfile.lock DELETED
@@ -1,57 +0,0 @@
1
- PATH
2
- remote: .
3
- specs:
4
- mongoid-tree (0.4.0)
5
- mongoid (>= 2.0.0.beta.20)
6
-
7
- GEM
8
- remote: http://rubygems.org/
9
- specs:
10
- activemodel (3.0.1)
11
- activesupport (= 3.0.1)
12
- builder (~> 2.1.2)
13
- i18n (~> 0.4.1)
14
- activesupport (3.0.1)
15
- autotest (4.4.2)
16
- bson (1.1.1)
17
- bson_ext (1.1.1)
18
- builder (2.1.2)
19
- diff-lcs (1.1.2)
20
- haml (2.2.24)
21
- hanna (0.1.12)
22
- haml (~> 2.2.8)
23
- rake (~> 0.8.2)
24
- rdoc (~> 2.3.0)
25
- i18n (0.4.2)
26
- mongo (1.1.1)
27
- bson (>= 1.1.1)
28
- mongoid (2.0.0.beta.20)
29
- activemodel (~> 3.0)
30
- mongo (~> 1.1)
31
- tzinfo (~> 0.3.22)
32
- will_paginate (~> 3.0.pre)
33
- rake (0.8.7)
34
- rdoc (2.3.0)
35
- rspec (2.0.1)
36
- rspec-core (~> 2.0.1)
37
- rspec-expectations (~> 2.0.1)
38
- rspec-mocks (~> 2.0.1)
39
- rspec-core (2.0.1)
40
- rspec-expectations (2.0.1)
41
- diff-lcs (>= 1.1.2)
42
- rspec-mocks (2.0.1)
43
- rspec-core (~> 2.0.1)
44
- rspec-expectations (~> 2.0.1)
45
- tzinfo (0.3.23)
46
- will_paginate (3.0.pre2)
47
-
48
- PLATFORMS
49
- ruby
50
-
51
- DEPENDENCIES
52
- autotest (>= 4.3.2)
53
- bson_ext (>= 1.0.4)
54
- hanna (>= 0.1.12)
55
- mongoid (>= 2.0.0.beta.20)
56
- mongoid-tree!
57
- rspec (~> 2.0)