mongoid-tree 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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)