mongoid-ordering 0.1.1

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/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source :rubygems
2
+
3
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012 Douwe Maan
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,139 @@
1
+ # mongoid-ordering [![Build Status](https://secure.travis-ci.org/DouweM/mongoid-ordering.png?branch=master)](http://travis-ci.org/DouweM/mongoid-ordering)
2
+
3
+ mongoid-ordering makes it easy to keep your Mongoid documents in order.
4
+
5
+ Most of mongoid-ordering is based on the
6
+ `Mongoid::Tree::Ordering` module from the
7
+ [mongoid-tree](https://github.com/benedikt/mongoid-tree) gem. I thought
8
+ the ordering logic would be useful outside of the tree context as well, so I
9
+ extracted it into the gem you're looking at right now.
10
+
11
+ ## Features
12
+
13
+ * Automatically order your query results by a new `position` attribute.
14
+ * Allow documents to be ordered within a certain scope.
15
+ * Handle changes in position when a document is destroyed or when a document is
16
+ moved outside of this scope.
17
+ * Tons of utility methods to make working with ordered documents incredibly easy.
18
+
19
+ ## Requirements
20
+
21
+ * mongoid (~> 3.0)
22
+
23
+ ## Installation
24
+
25
+ Add the following to your Gemfile:
26
+
27
+ ```ruby
28
+ gem "mongoid-ordering"
29
+ ```
30
+
31
+ And tell Bundler to install the new gem:
32
+
33
+ ```
34
+ bundle install
35
+ ```
36
+
37
+ ## Usage
38
+
39
+ Include the `Mongoid::Ordering` module in your document class:
40
+
41
+ ```ruby
42
+ class Book
43
+ include Mongoid::Document
44
+ include Mongoid::Ordering
45
+
46
+ ...
47
+ end
48
+ ```
49
+
50
+ This will take care of everything to get you going.
51
+
52
+ If you want to specify a scope within which to keep the documents in order,
53
+ you can like this:
54
+
55
+ ```ruby
56
+ class Book
57
+ include Mongoid::Document
58
+ include Mongoid::Ordering
59
+
60
+ belongs_to :author
61
+
62
+ ordered scope: :author
63
+
64
+ ...
65
+ end
66
+ ```
67
+
68
+ You will now have access to the following methods:
69
+
70
+ ```ruby
71
+ # Retrieve siblings positioned above this document.
72
+ book.higher_siblings
73
+ # Retrieve siblings positioned below this document.
74
+ book.lower_siblings
75
+ # Retrieve the highest sibling.
76
+ book.highest_sibling
77
+ # Retrieve the lowest sibling.
78
+ book.lowest_sibling
79
+
80
+ # Is this the highest sibling?
81
+ book.at_top?
82
+ # Is this the lowest sibling?
83
+ book.at_bottom?
84
+
85
+ # Move document to the top.
86
+ book.move_to_top
87
+ # Move document to the bottom.
88
+ book.move_to_bottom
89
+ # Move document one position up.
90
+ book.move_up
91
+ # Move document one position down.
92
+ book.move_down
93
+ # Move document above another document.
94
+ book.move_above(other_book)
95
+ # Move document below another document.
96
+ book.move_below(other_book)
97
+ ```
98
+
99
+ mongoid-ordering uses [mongoid-siblings](https://github.com/DouweM/mongoid-siblings) to get all of this to work, so you'll get the following methods as a bonus:
100
+
101
+ ```ruby
102
+ # Retrieve document's siblings
103
+ book.siblings
104
+ # Retrieve document's siblings and itself
105
+ book.siblings_and_self
106
+ # Is this document a sibling of the other document?
107
+ book.sibling_of?(other_book)
108
+ # Make document a sibling of the other document.
109
+ # This will move this book to the same scope as the other book.
110
+ book.sibling_of!(other_book)
111
+ ```
112
+
113
+ ## Full documentation
114
+ See [this project's RubyDoc.info page](http://rubydoc.info/github/DouweM/mongoid-ordering/master/frames).
115
+
116
+ ## Known issues
117
+ See [the GitHub Issues page](https://github.com/DouweM/mongoid-ordering/issues).
118
+
119
+ ## License
120
+ Copyright (c) 2012 Douwe Maan
121
+
122
+ Permission is hereby granted, free of charge, to any person obtaining
123
+ a copy of this software and associated documentation files (the
124
+ "Software"), to deal in the Software without restriction, including
125
+ without limitation the rights to use, copy, modify, merge, publish,
126
+ distribute, sublicense, and/or sell copies of the Software, and to
127
+ permit persons to whom the Software is furnished to do so, subject to
128
+ the following conditions:
129
+
130
+ The above copyright notice and this permission notice shall be
131
+ included in all copies or substantial portions of the Software.
132
+
133
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
134
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
135
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
136
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
137
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
138
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
139
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,17 @@
1
+ require "rspec/core/rake_task"
2
+
3
+ spec = Gem::Specification.load("mongoid-ordering.gemspec")
4
+
5
+ RSpec::Core::RakeTask.new(:spec)
6
+
7
+ task default: :spec
8
+
9
+ desc "Build the .gem file"
10
+ task :build do
11
+ system "gem build #{spec.name}.gemspec"
12
+ end
13
+
14
+ desc "Push the .gem file to rubygems.org"
15
+ task release: :build do
16
+ system "gem push #{spec.name}-#{spec.version}.gem"
17
+ end
@@ -0,0 +1,255 @@
1
+ module Mongoid
2
+
3
+ # Add 'position' field, multiple methods and multiple callbacks to help with
4
+ # ordering your documents.
5
+ module Ordering
6
+ extend ActiveSupport::Concern
7
+ include Mongoid::Siblings
8
+
9
+ included do
10
+ cattr_accessor :ordering_scopes
11
+ self.ordering_scopes = []
12
+
13
+ field :position, type: Integer
14
+
15
+ default_scope asc(:position)
16
+
17
+ before_save :assign_default_position
18
+ before_save :reposition_former_siblings, if: :sibling_reposition_required?
19
+ after_destroy :move_lower_siblings_up
20
+ end
21
+
22
+ module ClassMethods
23
+ # Sets options used for ordering.
24
+ #
25
+ # @example Set options.
26
+ # class Book
27
+ # include Mongoid::Document
28
+ # include Mongoid::Ordering
29
+ #
30
+ # belongs_to :author
31
+ #
32
+ # ordered scope: :author
33
+ # end
34
+ #
35
+ # @param [ Hash ] options The options.
36
+ #
37
+ # @option options [ Array<Symbol>, Symbol ] scope One or more relations or
38
+ # attributes that will determine the scope within which to keep the
39
+ # documents in order.
40
+ def ordered(options = {})
41
+ self.default_sibling_scope = self.ordering_scopes = Array.wrap(options[:scope]).compact
42
+ end
43
+ end
44
+
45
+ # Returns siblings positioned above this document.
46
+ # Siblings with a position lower than this document's position.
47
+ #
48
+ # @example Retrieve siblings positioned above this document.
49
+ # book.higher_siblings
50
+ #
51
+ # @return [ Mongoid::Criteria ] Criteria to retrieve the document's higher siblings.
52
+ def higher_siblings
53
+ self.siblings.where(:position.lt => self.position)
54
+ end
55
+
56
+ # Returns siblings positioned below this document.
57
+ # Siblings with a position greater than this document's position.
58
+ #
59
+ # @example Retrieve siblings positioned below this document.
60
+ # book.lower_siblings
61
+ #
62
+ # @return [ Mongoid::Criteria ] Criteria to retrieve the document's lower siblings.
63
+ def lower_siblings
64
+ self.siblings.where(:position.gt => self.position)
65
+ end
66
+
67
+ # Returns the highest sibling (could be self).
68
+ #
69
+ # @example Retrieve the highest sibling.
70
+ # book.highest_sibling
71
+ #
72
+ # @return [ Mongoid::Document ] The highest sibling.
73
+ def highest_sibling
74
+ self.siblings_and_self.first
75
+ end
76
+
77
+ # Returns the lowest sibling (could be self).
78
+ #
79
+ # @example Retrieve the lowest sibling.
80
+ # book.lowest_sibling
81
+ #
82
+ # @return [ Mongoid::Document ] The lowest sibling
83
+ def lowest_sibling
84
+ self.siblings_and_self.last
85
+ end
86
+
87
+ # Is this the highest sibling?
88
+ #
89
+ # @example Is this the highest sibling?
90
+ # book.at_top?
91
+ #
92
+ # @return [ Boolean ] True if this document is the highest sibling.
93
+ def at_top?
94
+ self.higher_siblings.empty?
95
+ end
96
+
97
+ # Is this the lowest sibling?
98
+ #
99
+ # @example Is this the lowest sibling?
100
+ # book.at_bottom?
101
+ #
102
+ # @return [ Boolean ] True if this document is the lowest sibling.
103
+ def at_bottom?
104
+ self.lower_siblings.empty?
105
+ end
106
+
107
+ # Moves this document above all of its siblings.
108
+ #
109
+ # @example Move document to the top.
110
+ # book.move_to_top
111
+ #
112
+ # @return [ Boolean ] True if the document was moved to the top or was
113
+ # already there.
114
+ def move_to_top
115
+ return true if at_top?
116
+ self.move_above(self.highest_sibling)
117
+ end
118
+
119
+ # Moves this document below all of its siblings.
120
+ #
121
+ # @example Move document to the bottom.
122
+ # book.move_to_bottom
123
+ #
124
+ # @return [ Boolean ] True if the document was moved to the bottom or was
125
+ # already there.
126
+ def move_to_bottom
127
+ return true if at_bottom?
128
+ self.move_below(self.lowest_sibling)
129
+ end
130
+
131
+ # Moves this document one position up.
132
+ #
133
+ # @example Move document one position up.
134
+ # book.move_up
135
+ def move_up
136
+ return if at_top?
137
+ self.siblings.where(position: self.position - 1).first.inc(:position, 1)
138
+ self.inc(:position, -1)
139
+ end
140
+
141
+ # Moves this document one position down.
142
+ #
143
+ # @example Move document one position down.
144
+ # book.move_down
145
+ def move_down
146
+ return if at_bottom?
147
+ self.siblings.where(position: self.position + 1).first.inc(:position, -1)
148
+ self.inc(:position, 1)
149
+ end
150
+
151
+ # Moves this document above the specified document.
152
+ #
153
+ # This method changes this document's scope values if necessary.
154
+ #
155
+ # @example Move document above another document.
156
+ # book.move_above(other_book)
157
+ #
158
+ # @param [ Mongoid::Document ] other The document to Moves this document
159
+ # above.
160
+ def move_above(other)
161
+ return false unless self.sibling_of!(other)
162
+
163
+ if self.position > other.position
164
+ new_position = other.position
165
+ other.lower_siblings.and(:position.lt => self.position).each { |s| s.inc(:position, 1) }
166
+ other.inc(:position, 1)
167
+ else
168
+ new_position = other.position - 1
169
+ other.higher_siblings.and(:position.gt => self.position).each { |s| s.inc(:position, -1) }
170
+ end
171
+ self.position = new_position
172
+
173
+ self.save!
174
+ end
175
+
176
+ # Moves this document below the specified document.
177
+ #
178
+ # This method changes this document's scope values if necessary.
179
+ #
180
+ # @example Move document below another document.
181
+ # book.move_below(other_book)
182
+ #
183
+ # @param [ Mongoid::Document ] other The document to Moves this document
184
+ # below.
185
+ def move_below(other)
186
+ return false unless self.sibling_of!(other)
187
+
188
+ if self.position > other.position
189
+ new_position = other.position + 1
190
+ other.lower_siblings.and(:position.lt => self.position).each { |s| s.inc(:position, 1) }
191
+ else
192
+ new_position = other.position
193
+ other.higher_siblings.and(:position.gt => self.position).each { |s| s.inc(:position, -1) }
194
+ other.inc(:position, -1)
195
+ end
196
+ self.position = new_position
197
+
198
+ self.save!
199
+ end
200
+
201
+ private
202
+
203
+ def move_lower_siblings_up
204
+ self.lower_siblings.each { |s| s.inc(:position, -1) }
205
+ end
206
+
207
+ def sibling_reposition_required?
208
+ return false if self.ordering_scopes.empty?
209
+ self.ordering_scopes.any? { |scope| attribute_changed?(key_for_scope(scope)) } && persisted?
210
+ end
211
+
212
+ def reposition_former_siblings
213
+ return if self.ordering_scopes.empty?
214
+
215
+ old_scope_values = {}
216
+ self.ordering_scopes.each do |scope|
217
+ scope_metadata = self.reflect_on_association(scope)
218
+ scope_key = scope_metadata ? scope_metadata.key : scope.to_s
219
+
220
+ if attribute_changed?(scope_key)
221
+ old_value = attribute_was(scope_key)
222
+
223
+ old_scope_values[scope] = if scope_metadata && old_value
224
+ model = scope_metadata.inverse_type ? attribute_was(scope_metadata.inverse_type) : scope_metadata.klass
225
+ scope_metadata.criteria(old_value, model).first
226
+ else
227
+ old_value
228
+ end
229
+ end
230
+ end
231
+
232
+ former_siblings = self.siblings(scope_values: old_scope_values).where(:position.gt => (attribute_was("position") || 0))
233
+ former_siblings.each { |s| s.inc(:position, -1) }
234
+ end
235
+
236
+ def assign_default_position
237
+ return unless self.position.nil? ||
238
+ (self.ordering_scopes.any? { |scope| attribute_changed?(key_for_scope(scope)) } &&
239
+ !new_record?)
240
+
241
+ siblings = self.siblings
242
+ self.position = if siblings.empty? || siblings.map(&:position).compact.empty?
243
+ 0
244
+ else
245
+ siblings.max(:position).to_i + 1
246
+ end
247
+ end
248
+
249
+ def key_for_scope(scope)
250
+ return nil if scope.nil?
251
+ metadata = self.reflect_on_association(scope)
252
+ metadata ? metadata.key : scope.to_s
253
+ end
254
+ end
255
+ end
@@ -0,0 +1,291 @@
1
+ require "spec_helper.rb"
2
+
3
+ describe Mongoid::Ordering do
4
+
5
+ describe ".ordered" do
6
+
7
+ let(:test_class) {
8
+ Class.new do
9
+ include Mongoid::Document
10
+ include Mongoid::Ordering
11
+
12
+ ordered scope: [:main, :fallback]
13
+ end
14
+ }
15
+
16
+ it "sets the default sibling scope and the ordering scopes to the specified scope" do
17
+ test_class.default_sibling_scope.should eq([:main, :fallback])
18
+ test_class.ordering_scopes.should eq([:main, :fallback])
19
+ end
20
+ end
21
+
22
+ describe "Instance methods" do
23
+
24
+ let!(:sibling1) { DummyParentDocument.create }
25
+ let!(:sibling2) { DummyParentDocument.create }
26
+ let!(:sibling3) { DummyParentDocument.create }
27
+
28
+ describe "#lower_siblings" do
29
+
30
+ it "returns the siblings with a higher position" do
31
+ sibling1.lower_siblings.should eq([sibling2, sibling3])
32
+ end
33
+ end
34
+
35
+ describe "#higher_siblings" do
36
+
37
+ it "returns the siblings with a lower position" do
38
+ sibling3.higher_siblings.should eq([sibling1, sibling2])
39
+ end
40
+ end
41
+
42
+ describe "#highest_sibling" do
43
+
44
+ it "returns the first sibling" do
45
+ sibling2.highest_sibling.should eq(sibling1)
46
+ end
47
+ end
48
+
49
+ describe "#lowest_sibling" do
50
+
51
+ it "returns the last sibling" do
52
+ sibling2.lowest_sibling.should eq(sibling3)
53
+ end
54
+ end
55
+
56
+ describe "#at_top?" do
57
+
58
+ context "when the subject is the first sibling" do
59
+
60
+ it "returns true" do
61
+ sibling1.should be_at_top
62
+ end
63
+ end
64
+
65
+ context "when the subject is not the first sibling" do
66
+
67
+ it "returns false" do
68
+ sibling2.should_not be_at_top
69
+ end
70
+ end
71
+ end
72
+
73
+ describe "#at_bottom?" do
74
+
75
+ context "when the subject is the last sibling" do
76
+
77
+ it "returns true" do
78
+ sibling3.should be_at_bottom
79
+ end
80
+ end
81
+
82
+ context "when the subject is not the last sibling" do
83
+
84
+ it "returns false" do
85
+ sibling2.should_not be_at_bottom
86
+ end
87
+ end
88
+ end
89
+
90
+ describe "#move_to_top" do
91
+
92
+ it "rearranges the siblings" do
93
+ sibling3.move_to_top
94
+
95
+ sibling3.siblings_and_self.should eq([sibling3, sibling1, sibling2])
96
+ end
97
+
98
+ it "properly sets the positions" do
99
+ sibling3.move_to_top
100
+
101
+ sibling3.reload.position.should eq(0)
102
+ sibling1.reload.position.should eq(1)
103
+ sibling2.reload.position.should eq(2)
104
+ end
105
+ end
106
+
107
+ describe "#move_to_bottom" do
108
+
109
+ it "rearranges the siblings" do
110
+ sibling1.move_to_bottom
111
+
112
+ sibling1.siblings_and_self.should eq([sibling2, sibling3, sibling1])
113
+ end
114
+
115
+ it "properly sets the positions" do
116
+ sibling1.move_to_bottom
117
+
118
+ sibling2.reload.position.should eq(0)
119
+ sibling3.reload.position.should eq(1)
120
+ sibling1.reload.position.should eq(2)
121
+ end
122
+ end
123
+
124
+ describe "#move_up" do
125
+
126
+ it "rearranges the siblings" do
127
+ sibling3.move_up
128
+
129
+ sibling3.siblings_and_self.should eq([sibling1, sibling3, sibling2])
130
+ end
131
+
132
+ it "properly sets the positions" do
133
+ sibling3.move_up
134
+
135
+ sibling1.reload.position.should eq(0)
136
+ sibling3.reload.position.should eq(1)
137
+ sibling2.reload.position.should eq(2)
138
+ end
139
+ end
140
+
141
+ describe "#move_down" do
142
+
143
+ it "rearranges the siblings" do
144
+ sibling1.move_down
145
+
146
+ sibling1.siblings_and_self.should eq([sibling2, sibling1, sibling3])
147
+ end
148
+
149
+ it "properly sets the positions" do
150
+ sibling1.move_down
151
+
152
+ sibling2.reload.position.should eq(0)
153
+ sibling1.reload.position.should eq(1)
154
+ sibling3.reload.position.should eq(2)
155
+ end
156
+ end
157
+
158
+ describe "#move_above" do
159
+
160
+ context "when the subject was somewhere above the other object" do
161
+
162
+ it "rearranges the siblings" do
163
+ sibling1.move_above(sibling3)
164
+
165
+ sibling1.siblings_and_self.should eq([sibling2, sibling1, sibling3])
166
+ end
167
+
168
+ it "properly sets the positions" do
169
+ sibling1.move_above(sibling3)
170
+
171
+ sibling2.reload.position.should eq(0)
172
+ sibling1.reload.position.should eq(1)
173
+ sibling3.reload.position.should eq(2)
174
+ end
175
+ end
176
+
177
+ context "when the subject was somewhere below the other object" do
178
+
179
+ it "rearranges the siblings" do
180
+ sibling3.move_above(sibling2)
181
+
182
+ sibling3.siblings_and_self.should eq([sibling1, sibling3, sibling2])
183
+ end
184
+
185
+ it "properly sets the positions" do
186
+ sibling3.move_above(sibling2)
187
+
188
+ sibling1.reload.position.should eq(0)
189
+ sibling3.reload.position.should eq(1)
190
+ sibling2.reload.position.should eq(2)
191
+ end
192
+ end
193
+ end
194
+
195
+ describe "#move_below" do
196
+
197
+ context "when the subject was somewhere above the other object" do
198
+
199
+ it "rearranges the siblings" do
200
+ sibling1.move_below(sibling2)
201
+
202
+ sibling1.siblings_and_self.should eq([sibling2, sibling1, sibling3])
203
+ end
204
+
205
+ it "properly sets the positions" do
206
+ sibling1.move_below(sibling2)
207
+
208
+ sibling2.reload.position.should eq(0)
209
+ sibling1.reload.position.should eq(1)
210
+ sibling3.reload.position.should eq(2)
211
+ end
212
+ end
213
+ end
214
+
215
+ context "when the subject was somewhere below the other object" do
216
+
217
+ it "rearranges the siblings" do
218
+ sibling3.move_below(sibling1)
219
+
220
+ sibling3.siblings_and_self.should eq([sibling1, sibling3, sibling2])
221
+ end
222
+
223
+ it "properly sets the positions" do
224
+ sibling3.move_below(sibling1)
225
+
226
+ sibling1.reload.position.should eq(0)
227
+ sibling3.reload.position.should eq(1)
228
+ sibling2.reload.position.should eq(2)
229
+ end
230
+ end
231
+ end
232
+
233
+ describe "creating a document" do
234
+
235
+ context "when no siblings exist yet" do
236
+
237
+ it "sets the subject's position to 0" do
238
+ DummyParentDocument.create.position.should eq(0)
239
+ end
240
+ end
241
+
242
+ context "when a sibling already exist" do
243
+
244
+ let!(:sibling) { DummyParentDocument.create }
245
+
246
+ it "sets the subject's position to the highest position plus 1" do
247
+ DummyParentDocument.create.position.should eq(1)
248
+ end
249
+ end
250
+ end
251
+
252
+ describe "moving a document to another parent" do
253
+
254
+ let!(:parent1) { DummyParentDocument.create }
255
+ let!(:parent1_child1) { DummyChildDocument.create(parent: parent1) }
256
+ let!(:parent1_child2) { DummyChildDocument.create(parent: parent1) }
257
+ let!(:parent1_child3) { DummyChildDocument.create(parent: parent1) }
258
+ let!(:parent2) { DummyParentDocument.create }
259
+ let!(:parent2_child1) { DummyChildDocument.create(parent: parent2) }
260
+ let!(:parent2_child2) { DummyChildDocument.create(parent: parent2) }
261
+ let!(:parent2_child3) { DummyChildDocument.create(parent: parent2) }
262
+
263
+ before(:each) do
264
+ parent1_child2.parent = parent2
265
+ parent1_child2.save
266
+ end
267
+
268
+ it "moves lower siblings up" do
269
+ parent1_child1.reload.position.should eq(0)
270
+ parent1_child3.reload.position.should eq(1)
271
+ end
272
+
273
+ it "sets the subject's position to the highest position under the new parent plus 1" do
274
+ parent1_child2.reload.position.should eq(3)
275
+ end
276
+ end
277
+
278
+ describe "destroying a document" do
279
+
280
+ let!(:sibling1) { DummyParentDocument.create }
281
+ let!(:sibling2) { DummyParentDocument.create }
282
+ let!(:sibling3) { DummyParentDocument.create }
283
+
284
+ it "moves lower siblings up" do
285
+ sibling2.destroy
286
+
287
+ sibling1.reload.position.should eq(0)
288
+ sibling3.reload.position.should eq(1)
289
+ end
290
+ end
291
+ end
@@ -0,0 +1,20 @@
1
+ require "rubygems"
2
+ require "bundler/setup"
3
+
4
+ require "mongoid"
5
+ require "mongoid/siblings"
6
+ require "mongoid/ordering"
7
+
8
+ require "rspec"
9
+
10
+ Mongoid.configure do |config|
11
+ config.connect_to "mongoid_ordering_test"
12
+ end
13
+
14
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
15
+
16
+ RSpec.configure do |config|
17
+ config.after :each do
18
+ Mongoid::Config.purge!
19
+ end
20
+ end
@@ -0,0 +1,19 @@
1
+ class DummyParentDocument
2
+ include Mongoid::Document
3
+ include Mongoid::Ordering
4
+
5
+ ordered scope: nil
6
+
7
+ has_many :children, class_name: "DummyChildDocument",
8
+ inverse_of: :parent
9
+ end
10
+
11
+ class DummyChildDocument
12
+ include Mongoid::Document
13
+ include Mongoid::Ordering
14
+
15
+ ordered scope: :parent
16
+
17
+ belongs_to :parent, class_name: DummyParentDocument.to_s,
18
+ inverse_of: :children
19
+ end
metadata ADDED
@@ -0,0 +1,106 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mongoid-ordering
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Douwe Maan
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-09-09 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: mongoid
16
+ requirement: &70134351207600 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '3.0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *70134351207600
25
+ - !ruby/object:Gem::Dependency
26
+ name: mongoid-siblings
27
+ requirement: &70134351205960 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ~>
31
+ - !ruby/object:Gem::Version
32
+ version: 0.1.0
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *70134351205960
36
+ - !ruby/object:Gem::Dependency
37
+ name: rake
38
+ requirement: &70134351204840 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *70134351204840
47
+ - !ruby/object:Gem::Dependency
48
+ name: rspec
49
+ requirement: &70134351203100 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: *70134351203100
58
+ description: mongoid-ordering makes it easy to keep your Mongoid documents in order.
59
+ email: douwe@selenight.nl
60
+ executables: []
61
+ extensions: []
62
+ extra_rdoc_files: []
63
+ files:
64
+ - lib/mongoid/ordering.rb
65
+ - LICENSE
66
+ - README.md
67
+ - Rakefile
68
+ - Gemfile
69
+ - spec/mongoid/ordering_spec.rb
70
+ - spec/spec_helper.rb
71
+ - spec/support/models.rb
72
+ homepage: https://github.com/DouweM/mongoid-ordering
73
+ licenses:
74
+ - MIT
75
+ post_install_message:
76
+ rdoc_options: []
77
+ require_paths:
78
+ - lib
79
+ required_ruby_version: !ruby/object:Gem::Requirement
80
+ none: false
81
+ requirements:
82
+ - - ! '>='
83
+ - !ruby/object:Gem::Version
84
+ version: '0'
85
+ segments:
86
+ - 0
87
+ hash: -533634391627392618
88
+ required_rubygems_version: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ segments:
95
+ - 0
96
+ hash: -533634391627392618
97
+ requirements: []
98
+ rubyforge_project:
99
+ rubygems_version: 1.8.6
100
+ signing_key:
101
+ specification_version: 3
102
+ summary: Easy ordering of your Mongoid documents.
103
+ test_files:
104
+ - spec/mongoid/ordering_spec.rb
105
+ - spec/spec_helper.rb
106
+ - spec/support/models.rb