mongoid-siblings 0.1.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/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.
data/README.md ADDED
@@ -0,0 +1,83 @@
1
+ # mongoid-siblings
2
+
3
+ mongoid-siblings adds methods to enable you to easily access your Mongoid
4
+ document's siblings.
5
+
6
+ ## Requirements
7
+
8
+ * mongoid (~> 3.0)
9
+
10
+ ## Installation
11
+
12
+ Add the following to your Gemfile:
13
+
14
+ ```ruby
15
+ gem "mongoid-siblings"
16
+ ```
17
+
18
+ And tell Bundler to install the new gem:
19
+
20
+ ```
21
+ bundle install
22
+ ```
23
+
24
+ ## Usage
25
+
26
+ Include the `Mongoid::Siblings` module in your document class:
27
+
28
+ ```ruby
29
+ class Book
30
+ include Mongoid::Document
31
+ include Mongoid::Siblings
32
+
33
+ belongs_to :publisher
34
+ belongs_to :author
35
+
36
+ ...
37
+ end
38
+ ```
39
+
40
+ You will now have access to the following methods:
41
+
42
+ ```ruby
43
+ # Find all books by this book's author, not including this book.
44
+ book.siblings(scope: :author)
45
+
46
+ # Find all books by this book's author.
47
+ book.siblings_and_self(scope: :author)
48
+
49
+ # Check whether a certain book was published by the same publisher as this book.
50
+ book.sibling_of?(other_book, scope: :publisher)
51
+
52
+ # Make this book a sibling of a book with another author and publisher.
53
+ # This will set this books author and publisher to match that of the other book.
54
+ book.sibling_of!(other_book, scope: [:author, :publisher])
55
+ ```
56
+
57
+ ## Full documentation
58
+ See [this project's RubyDoc.info page](http://rubydoc.info/github/DouweM/mongoid-siblings/master/frames).
59
+
60
+ ## Known issues
61
+ See [the GitHub Issues page](https://github.com/DouweM/mongoid-siblings/issues).
62
+
63
+ ## License
64
+ Copyright (c) 2012 Douwe Maan
65
+
66
+ Permission is hereby granted, free of charge, to any person obtaining
67
+ a copy of this software and associated documentation files (the
68
+ "Software"), to deal in the Software without restriction, including
69
+ without limitation the rights to use, copy, modify, merge, publish,
70
+ distribute, sublicense, and/or sell copies of the Software, and to
71
+ permit persons to whom the Software is furnished to do so, subject to
72
+ the following conditions:
73
+
74
+ The above copyright notice and this permission notice shall be
75
+ included in all copies or substantial portions of the Software.
76
+
77
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
78
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
79
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
80
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
81
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
82
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
83
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,17 @@
1
+ require "rspec/core/rake_task"
2
+
3
+ spec = Gem::Specification.load("mongoid-siblings.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,202 @@
1
+ module Mongoid
2
+
3
+ # Adds methods to easily access your document's siblings.
4
+ module Siblings
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ cattr_accessor :default_sibling_scope
9
+ end
10
+
11
+ # Returns this document's siblings.
12
+ #
13
+ # @example Retrieve document's siblings
14
+ # book.siblings
15
+ #
16
+ # @see {#siblings_and_self}
17
+ def siblings(options = {})
18
+ self.siblings_and_self(options).excludes(id: self.id)
19
+ end
20
+
21
+ # Returns this document's siblings and itself.
22
+ #
23
+ # @example Retrieve document's siblings and itself within a certain scope.
24
+ # book.siblings_and_self(scope: :author)
25
+ #
26
+ # @example Retrieve what would be document's siblings if it had another scope value.
27
+ #
28
+ # book.siblings_and_self(
29
+ # scope: :author,
30
+ # scope_values: {
31
+ # author: other_author
32
+ # }
33
+ # )
34
+ #
35
+ # @param [ Hash ] options The options.
36
+ #
37
+ # @option options [ Array<Symbol>, Symbol ] scope One or more relations or
38
+ # attributes that siblings of this object need to have in common.
39
+ # @option options [ Hash<Symbol, Object> ] scope_values Optional alternative
40
+ # values to use to determine siblingship.
41
+ #
42
+ # @return [ Mongoid::Criteria ] Criteria to retrieve the document's siblings.
43
+ def siblings_and_self(options = {})
44
+ scopes = options[:scope] || self.default_sibling_scope
45
+ scope_values = options[:scope_values] || {}
46
+
47
+ scopes = Array.wrap(scopes).compact
48
+
49
+
50
+ criteria = base_document_class.all
51
+
52
+ detail_scopes = []
53
+
54
+ # Find out what scope determines the root criteria. This can be
55
+ # [klass].all or self.[relation].
56
+ # It is assumed that for `scopes: [:rel1, :rel2]`, sibling objects always
57
+ # have the same `rel1` *and* `rel2`, and that two objects with the same
58
+ # `rel1` will always have the same `rel2`.
59
+ scopes.reverse_each do |scope|
60
+ scope_value = scope_values.fetch(scope) { self.send(scope) }
61
+
62
+ relation_metadata = self.reflect_on_association(scope)
63
+ if relation_metadata && scope_value
64
+ proxy = self.siblings_through_relation(scope, scope_value)
65
+ next if proxy.nil?
66
+ criteria = proxy.criteria
67
+ else
68
+ detail_scopes << scope
69
+ end
70
+ end
71
+
72
+ # Apply detail criteria, to make sure siblings share every simple
73
+ # attribute or nil-relation.
74
+ detail_scopes.each do |scope|
75
+ scope_value = scope_values.fetch(scope) { self.send(scope) }
76
+
77
+ relation_metadata = self.reflect_on_association(scope)
78
+ scope_key = relation_metadata ? relation_metadata.key : scope
79
+
80
+ criteria = criteria.where(scope_key => scope_value)
81
+ end
82
+
83
+ criteria
84
+ end
85
+
86
+ # Is this document a sibling of the other document?
87
+ #
88
+ # @example Is this document a sibling of the other document?
89
+ # book.sibling_of?(other_book, scope: :author)
90
+ #
91
+ # @param [ Document ] other The document to check against.
92
+ # @param [ Hash ] options The options.
93
+ #
94
+ # @option options [ Array<Symbol>, Symbol ] scope One or more relations and
95
+ # attributes that siblings of this object need to have in common.
96
+ # @option options [ Hash<Symbol, Object> ] scope_values Optional alternative
97
+ # values for this document to use to determine siblings.
98
+ # @option options [ Hash<Symbol, Object> ] other_scope_values Optional
99
+ # alternative values for the other document to use to determine
100
+ # siblingship.
101
+ #
102
+ # @return [ Boolean ] True if the document is a sibling of the other
103
+ # document.
104
+ def sibling_of?(other, options = {})
105
+ scopes = options[:scope] || self.default_sibling_scope
106
+ scope_values = options[:scope_values] || {}
107
+ other_scope_values = options[:other_scope_values] || {}
108
+
109
+ scopes = Array.wrap(scopes).compact
110
+
111
+
112
+ return false if base_document_class != base_document_class(other)
113
+
114
+ scopes.each do |scope|
115
+ scope_value = scope_values.fetch(scope) { self.send(scope) }
116
+ other_scope_value = other_scope_values.fetch(scope) { other.send(scope) }
117
+
118
+ return false if scope_value != other_scope_value
119
+ end
120
+
121
+ true
122
+ end
123
+
124
+ # Makes this document a sibling of the other document.
125
+ #
126
+ # This is done by copying over the values used to determine siblingship
127
+ # from the other document.
128
+ #
129
+ # @example Make document a sibling of the other document.
130
+ # book.sibling_of!(book_of_other_author, scope: :author)
131
+ #
132
+ # @param [ Document ] other The document to become a sibling of.
133
+ # @param [ Hash ] options The options.
134
+ #
135
+ # @option options [ Array<Symbol>, Symbol ] scope One or more relations and
136
+ # attributes that siblings of this object need to have in common.
137
+ # @option options [ Hash<Symbol, Object> ] other_scope_values Optional
138
+ # alternative values to use to determine siblingship.
139
+ #
140
+ # @return [ Boolean ] True if the document was made a sibling of the other
141
+ # document.
142
+ def sibling_of!(other, options = {})
143
+ return true if self.sibling_of?(other, options)
144
+
145
+ scopes = options[:scope] || self.default_sibling_scope
146
+ other_scope_values = options[:other_scope_values] || {}
147
+
148
+ scopes = Array.wrap(scopes).compact
149
+
150
+
151
+ return false if base_document_class != base_document_class(other)
152
+
153
+ scopes.each do |scope|
154
+ other_scope_value = other_scope_values.fetch(scope) { other.send(scope) }
155
+
156
+ relation_metadata = self.reflect_on_association(scope)
157
+ if relation_metadata && other_scope_value
158
+ other.siblings_through_relation(scope, other_scope_value) << self
159
+ else
160
+ self.send("#{scope}=", other_scope_value)
161
+ end
162
+ end
163
+
164
+ self.save!
165
+ end
166
+
167
+ protected
168
+
169
+ def siblings_through_relation(name, other = nil)
170
+ other ||= self.send(name)
171
+
172
+ relation_metadata = self.reflect_on_association(name)
173
+ inverses = relation_metadata.inverses(other)
174
+
175
+ return nil if inverses.nil? || inverses.empty?
176
+
177
+ if inverses.length == 1
178
+ inverse = inverses.first
179
+ elsif relation_metadata.polymorphic?
180
+ inverse = inverses.find { |inverse|
181
+ inverse == self.send(relation_metadata.inverse_of_field)
182
+ }
183
+ else
184
+ inverse = inverses.find { |inverse|
185
+ other.send(inverse).include?(self)
186
+ }
187
+ end
188
+
189
+ return nil if inverse.nil?
190
+
191
+ other.send(inverse)
192
+ end
193
+
194
+ def base_document_class(doc = self)
195
+ base_document_klass = doc.class
196
+ while base_document_klass.superclass.include?(Mongoid::Document)
197
+ base_document_klass = base_document_klass.superclass
198
+ end
199
+ base_document_klass
200
+ end
201
+ end
202
+ end
@@ -0,0 +1,679 @@
1
+ require "spec_helper.rb"
2
+
3
+ describe Mongoid::Siblings do
4
+
5
+ let(:parent) { DummyParentDocument.create }
6
+
7
+ describe "#siblings" do
8
+
9
+ context "when using a fallback scope" do
10
+
11
+ let(:super_parent) { DummySuperParentDocument.create }
12
+ let(:parent) { DummyParentDocument.create }
13
+ subject { DummyReferencedChildDocument.create }
14
+ let!(:main_sibling) { DummyReferencedChildDocument.create(parent: parent, super_parent: super_parent) }
15
+ let!(:fallback_sibling) { DummyReferencedChildDocument.create(super_parent: super_parent) }
16
+ let!(:ultimate_sibling) { DummyReferencedChildDocument.create }
17
+
18
+ context "when providing scope values" do
19
+
20
+ let(:old_super_parent) { DummySuperParentDocument.create }
21
+ let(:old_parent) { DummyParentDocument.create }
22
+
23
+ before(:each) do
24
+ subject.parent = parent
25
+ subject.super_parent = super_parent
26
+ subject.save
27
+ end
28
+
29
+ context "when providing one scope value" do
30
+
31
+ let!(:old_main_sibling) { DummyReferencedChildDocument.create(parent: old_parent, super_parent: super_parent) }
32
+
33
+ it "returns the subject's siblings" do
34
+ subject.siblings(
35
+ scope: [:parent, :super_parent],
36
+ scope_values: {
37
+ parent: old_parent
38
+ }
39
+ ).to_a.should eq([old_main_sibling])
40
+ end
41
+ end
42
+
43
+ context "when providing multiple scope values" do
44
+
45
+ let!(:old_main_sibling) { DummyReferencedChildDocument.create(parent: old_parent, super_parent: old_super_parent) }
46
+ let!(:old_fallback_sibling) { DummyReferencedChildDocument.create(super_parent: old_super_parent) }
47
+
48
+ context "when the main sibling value was nil" do
49
+
50
+ it "returns the subject's siblings" do
51
+ subject.siblings(
52
+ scope: [:parent, :super_parent],
53
+ scope_values: {
54
+ parent: nil,
55
+ super_parent: old_super_parent
56
+ }
57
+ ).to_a.should eq([old_fallback_sibling])
58
+ end
59
+ end
60
+
61
+ context "when the main sibling value wasn't nil" do
62
+
63
+ it "returns the subject's siblings" do
64
+ subject.siblings(
65
+ scope: [:parent, :super_parent],
66
+ scope_values: {
67
+ parent: old_parent,
68
+ super_parent: old_super_parent
69
+ }
70
+ ).to_a.should eq([old_main_sibling])
71
+ end
72
+ end
73
+ end
74
+ end
75
+
76
+ context "when not providing scope values" do
77
+
78
+ context "when the document has a main scope document" do
79
+
80
+ before(:each) do
81
+ subject.parent = parent
82
+ subject.super_parent = super_parent
83
+ subject.save
84
+ end
85
+
86
+ it "returns the subject's siblings" do
87
+ subject.siblings(scope: [:parent, :super_parent]).to_a.should eq([main_sibling])
88
+ end
89
+ end
90
+
91
+ context "when the document has a fallback scope document but not a main scope document" do
92
+
93
+ before(:each) do
94
+ subject.super_parent = super_parent
95
+ subject.save
96
+ end
97
+
98
+ it "returns the subject's siblings" do
99
+ subject.siblings(scope: [:parent, :super_parent]).to_a.should eq([fallback_sibling])
100
+ end
101
+ end
102
+
103
+ context "when the document has neither a main scope document or a fallback scope document" do
104
+
105
+ it "returns the subject's siblings" do
106
+ subject.siblings(scope: [:parent, :super_parent]).to_a.should eq([ultimate_sibling])
107
+ end
108
+ end
109
+ end
110
+ end
111
+
112
+ context "when using a single scope" do
113
+
114
+ context "when providing a scope value" do
115
+
116
+ let(:old_parent) { DummyParentDocument.create }
117
+ subject { DummyReferencedChildDocument.create(parent: parent) }
118
+ let!(:old_sibling) { DummyReferencedChildDocument.create(parent: old_parent) }
119
+ let!(:sibling) { DummyReferencedChildDocument.create(parent: parent) }
120
+
121
+ it "returns the subject's siblings" do
122
+ subject.siblings(scope: :parent, scope_values: { parent: old_parent }).to_a.should eq([old_sibling])
123
+ end
124
+ end
125
+
126
+ context "when not providing a scope value" do
127
+
128
+ context "when using a referenced relation" do
129
+
130
+ subject { DummyReferencedChildDocument.create }
131
+
132
+ context "when using a non-polymorphic relation" do
133
+
134
+ let!(:sibling) { DummyReferencedChildDocument.create(parent: parent) }
135
+
136
+ before(:each) do
137
+ subject.parent = parent
138
+ subject.save
139
+ end
140
+
141
+ it "returns the subject's siblings" do
142
+ subject.siblings(scope: :parent).to_a.should eq([sibling])
143
+ end
144
+ end
145
+
146
+ context "when using multiple polymorphic relations" do
147
+
148
+ let!(:sibling) { DummyReferencedChildDocument.create.tap { |doc| parent.referenced_polymorphic_children1 << doc} }
149
+
150
+ before(:each) do
151
+ parent.referenced_polymorphic_children1 << subject
152
+ subject.save
153
+ end
154
+
155
+ it "returns the subject's siblings" do
156
+ subject.siblings(scope: :polymorphic_parent).to_a.should eq([sibling])
157
+ end
158
+ end
159
+ end
160
+
161
+ context "when using an embedded relation" do
162
+
163
+ context "when using a non-polymorphic relation" do
164
+
165
+ subject { DummyEmbeddedChildDocument.create(parent: parent) }
166
+ let!(:sibling) { DummyEmbeddedChildDocument.create(parent: parent) }
167
+
168
+ it "returns the subject's siblings" do
169
+ subject.siblings(scope: :parent).to_a.should eq([sibling])
170
+ end
171
+ end
172
+
173
+ context "when using a polymorphic relation" do
174
+
175
+ subject { DummyPolymorphicEmbeddedChildDocument.create(parent: parent) }
176
+ let!(:sibling) { DummyPolymorphicEmbeddedChildDocument.create(parent: parent) }
177
+
178
+ it "returns the subject's siblings" do
179
+ subject.siblings(scope: :parent).to_a.should eq([sibling])
180
+ end
181
+ end
182
+ end
183
+
184
+ context "when using a simple attribute" do
185
+
186
+ subject { DummyReferencedChildDocument.create(parent: parent) }
187
+ let!(:sibling) { DummyReferencedChildDocument.create(parent: parent) }
188
+
189
+ it "returns the subject's siblings" do
190
+ subject.siblings(scope: :parent_id).to_a.should eq([sibling])
191
+ end
192
+ end
193
+ end
194
+ end
195
+
196
+ context "when not using a scope" do
197
+
198
+ context "when a default sibling scope is set" do
199
+
200
+ before(:each) do
201
+ DummyReferencedChildDocument.default_sibling_scope = :parent
202
+ end
203
+
204
+ after(:each) do
205
+ DummyReferencedChildDocument.default_sibling_scope = nil
206
+ end
207
+
208
+ subject { DummyReferencedChildDocument.create(parent: parent) }
209
+ let!(:sibling) { DummyReferencedChildDocument.create(parent: parent) }
210
+
211
+ it "returns the subject's siblings through the default sibling scope" do
212
+ subject.siblings.to_a.should eq([sibling])
213
+ end
214
+ end
215
+
216
+ context "when no default sibling scope is set" do
217
+
218
+ subject { DummyReferencedChildDocument.create }
219
+ let!(:sibling) { DummyReferencedChildDocument.create }
220
+
221
+ it "returns all other documents of the subject's type" do
222
+ subject.siblings.to_a.should eq([sibling])
223
+ end
224
+ end
225
+ end
226
+ end
227
+
228
+ describe "#siblings_and_self" do
229
+
230
+ subject { DummyReferencedChildDocument.create(parent: parent) }
231
+ let!(:sibling) { DummyReferencedChildDocument.create(parent: parent) }
232
+
233
+ it "returns the subject's siblings and the subject itself" do
234
+ subject.siblings_and_self(scope: :parent).to_a.sort.should eq([subject, sibling].sort)
235
+ end
236
+ end
237
+
238
+ describe "#sibling_of?" do
239
+
240
+ context "when using a fallback scope" do
241
+
242
+ let(:super_parent) { DummySuperParentDocument.create }
243
+ let(:parent) { DummyParentDocument.create }
244
+ subject { DummyReferencedChildDocument.create }
245
+ let!(:main_sibling) { DummyReferencedChildDocument.create(parent: parent, super_parent: super_parent) }
246
+ let!(:fallback_sibling) { DummyReferencedChildDocument.create(super_parent: super_parent) }
247
+ let!(:ultimate_sibling) { DummyReferencedChildDocument.create }
248
+
249
+ context "when providing scope values" do
250
+
251
+ let(:old_super_parent) { DummySuperParentDocument.create }
252
+ let(:old_parent) { DummyParentDocument.create }
253
+
254
+ before(:each) do
255
+ subject.parent = parent
256
+ subject.super_parent = super_parent
257
+ subject.save
258
+ end
259
+
260
+ context "when providing one scope value" do
261
+
262
+ let!(:old_main_sibling) { DummyReferencedChildDocument.create(parent: old_parent, super_parent: super_parent) }
263
+
264
+ context "when called with a sibling" do
265
+
266
+ it "returns true" do
267
+ subject.should be_sibling_of(
268
+ old_main_sibling,
269
+ scope: [:parent, :super_parent],
270
+ scope_values: {
271
+ parent: old_parent
272
+ }
273
+ )
274
+ end
275
+ end
276
+
277
+ context "when not called with a sibling" do
278
+
279
+ it "returns false" do
280
+ subject.should_not be_sibling_of(
281
+ main_sibling,
282
+ scope: [:parent, :super_parent],
283
+ scope_values: {
284
+ parent: old_parent
285
+ }
286
+ )
287
+ subject.should_not be_sibling_of(
288
+ fallback_sibling,
289
+ scope: [:parent, :super_parent],
290
+ scope_values: {
291
+ parent: old_parent
292
+ }
293
+ )
294
+ subject.should_not be_sibling_of(
295
+ ultimate_sibling,
296
+ scope: [:parent, :super_parent],
297
+ scope_values: {
298
+ parent: old_parent
299
+ }
300
+ )
301
+ end
302
+ end
303
+ end
304
+
305
+ context "when providing multiple scope values" do
306
+
307
+ let!(:old_main_sibling) { DummyReferencedChildDocument.create(parent: old_parent, super_parent: old_super_parent) }
308
+ let!(:old_fallback_sibling) { DummyReferencedChildDocument.create(super_parent: old_super_parent) }
309
+
310
+ context "when the main sibling value was nil" do
311
+
312
+ context "when called with a sibling" do
313
+
314
+ it "returns true" do
315
+ subject.should be_sibling_of(
316
+ old_fallback_sibling,
317
+ scope: [:parent, :super_parent],
318
+ scope_values: {
319
+ parent: nil,
320
+ super_parent: old_super_parent
321
+ }
322
+ )
323
+ end
324
+ end
325
+
326
+ context "when not called with a sibling" do
327
+
328
+ it "returns false" do
329
+ subject.should_not be_sibling_of(
330
+ old_main_sibling,
331
+ scope: [:parent, :super_parent],
332
+ scope_values: {
333
+ parent: nil,
334
+ super_parent: old_super_parent
335
+ }
336
+ )
337
+ subject.should_not be_sibling_of(
338
+ main_sibling,
339
+ scope: [:parent, :super_parent],
340
+ scope_values: {
341
+ parent: nil,
342
+ super_parent: old_super_parent
343
+ }
344
+ )
345
+ subject.should_not be_sibling_of(
346
+ fallback_sibling,
347
+ scope: [:parent, :super_parent],
348
+ scope_values: {
349
+ parent: nil,
350
+ super_parent: old_super_parent
351
+ }
352
+ )
353
+ subject.should_not be_sibling_of(
354
+ ultimate_sibling,
355
+ scope: [:parent, :super_parent],
356
+ scope_values: {
357
+ parent: nil,
358
+ super_parent: old_super_parent
359
+ }
360
+ )
361
+ end
362
+ end
363
+ end
364
+
365
+ context "when the main sibling value was not nil" do
366
+
367
+ context "when called with a sibling" do
368
+
369
+ it "returns true" do
370
+ subject.should be_sibling_of(
371
+ old_main_sibling,
372
+ scope: [:parent, :super_parent],
373
+ scope_values: {
374
+ parent: old_parent,
375
+ super_parent: old_super_parent
376
+ }
377
+ )
378
+ end
379
+ end
380
+
381
+ context "when not called with a sibling" do
382
+
383
+ it "returns false" do
384
+ subject.should_not be_sibling_of(
385
+ old_fallback_sibling,
386
+ scope: [:parent, :super_parent],
387
+ scope_values: {
388
+ parent: old_parent,
389
+ super_parent: old_super_parent
390
+ }
391
+ )
392
+ subject.should_not be_sibling_of(
393
+ main_sibling,
394
+ scope: [:parent, :super_parent],
395
+ scope_values: {
396
+ parent: old_parent,
397
+ super_parent: old_super_parent
398
+ }
399
+ )
400
+ subject.should_not be_sibling_of(
401
+ fallback_sibling,
402
+ scope: [:parent, :super_parent],
403
+ scope_values: {
404
+ parent: old_parent,
405
+ super_parent: old_super_parent
406
+ }
407
+ )
408
+ subject.should_not be_sibling_of(
409
+ ultimate_sibling,
410
+ scope: [:parent, :super_parent],
411
+ scope_values: {
412
+ parent: old_parent,
413
+ super_parent: old_super_parent
414
+ }
415
+ )
416
+ end
417
+ end
418
+ end
419
+ end
420
+ end
421
+
422
+ context "when providing other scope values" do
423
+
424
+ let(:old_super_parent) { DummySuperParentDocument.create }
425
+ let(:old_parent) { DummyParentDocument.create }
426
+
427
+ before(:each) do
428
+ subject.parent = parent
429
+ subject.super_parent = super_parent
430
+ subject.save
431
+ end
432
+
433
+ context "when providing one other scope value" do
434
+
435
+ let!(:old_main_sibling) { DummyReferencedChildDocument.create(parent: old_parent, super_parent: super_parent) }
436
+
437
+ context "when called with a sibling" do
438
+
439
+ it "returns true" do
440
+ subject.should be_sibling_of(
441
+ old_main_sibling,
442
+ scope: [:parent, :super_parent],
443
+ other_scope_values: {
444
+ parent: parent
445
+ }
446
+ )
447
+ end
448
+ end
449
+ end
450
+
451
+ context "when providing multiple other scope values" do
452
+
453
+ let!(:old_main_sibling) { DummyReferencedChildDocument.create(parent: old_parent, super_parent: old_super_parent) }
454
+ let!(:old_fallback_sibling) { DummyReferencedChildDocument.create(super_parent: old_super_parent) }
455
+
456
+ context "when the other main sibling value was nil" do
457
+
458
+ before(:each) do
459
+ subject.parent = nil
460
+ subject.save
461
+ end
462
+
463
+ context "when called with a sibling" do
464
+
465
+ it "returns true" do
466
+ subject.should be_sibling_of(
467
+ old_fallback_sibling,
468
+ scope: [:parent, :super_parent],
469
+ other_scope_values: {
470
+ parent: nil,
471
+ super_parent: super_parent
472
+ }
473
+ )
474
+ end
475
+ end
476
+ end
477
+
478
+ context "when the other main sibling value was not nil" do
479
+
480
+ context "when called with a sibling" do
481
+
482
+ it "returns true" do
483
+ subject.should be_sibling_of(
484
+ old_main_sibling,
485
+ scope: [:parent, :super_parent],
486
+ other_scope_values: {
487
+ parent: parent,
488
+ super_parent: super_parent
489
+ }
490
+ )
491
+ end
492
+ end
493
+ end
494
+ end
495
+ end
496
+
497
+ context "when not providing scope values" do
498
+
499
+ context "when the document has a main scope document" do
500
+
501
+ before(:each) do
502
+ subject.parent = parent
503
+ subject.super_parent = super_parent
504
+ subject.save
505
+ end
506
+
507
+ context "when called with a sibling" do
508
+
509
+ it "returns true" do
510
+ subject.should be_sibling_of(main_sibling, scope: [:parent, :super_parent])
511
+ end
512
+ end
513
+
514
+ context "when not called with a sibling" do
515
+
516
+ it "returns false" do
517
+ subject.should_not be_sibling_of(fallback_sibling, scope: [:parent, :super_parent])
518
+ subject.should_not be_sibling_of(ultimate_sibling, scope: [:parent, :super_parent])
519
+ end
520
+ end
521
+ end
522
+
523
+ context "when the document has a fallback scope document but not a main scope document" do
524
+
525
+ before(:each) do
526
+ subject.super_parent = super_parent
527
+ subject.save
528
+ end
529
+
530
+ context "when called with a sibling" do
531
+
532
+ it "returns true" do
533
+ subject.should be_sibling_of(fallback_sibling, scope: [:parent, :super_parent])
534
+ end
535
+ end
536
+
537
+ context "when not called with a sibling" do
538
+
539
+ it "returns false" do
540
+ subject.should_not be_sibling_of(main_sibling, scope: [:parent, :super_parent])
541
+ subject.should_not be_sibling_of(ultimate_sibling, scope: [:parent, :super_parent])
542
+ end
543
+ end
544
+ end
545
+
546
+ context "when the document has neither a main scope document or a fallback scope document" do
547
+
548
+ context "when called with a sibling" do
549
+
550
+ it "returns true" do
551
+ subject.should be_sibling_of(ultimate_sibling, scope: [:parent, :super_parent])
552
+ end
553
+ end
554
+
555
+ context "when not called with a sibling" do
556
+
557
+ it "returns false" do
558
+ subject.should_not be_sibling_of(main_sibling, scope: [:parent, :super_parent])
559
+ subject.should_not be_sibling_of(fallback_sibling, scope: [:parent, :super_parent])
560
+ end
561
+ end
562
+ end
563
+ end
564
+ end
565
+
566
+ context "when using a scope" do
567
+
568
+ context "when called with a sibling" do
569
+
570
+ subject { DummyReferencedChildDocument.create(parent: parent) }
571
+ let(:sibling) { DummyReferencedChildDocument.create(parent: parent) }
572
+
573
+ it "returns true" do
574
+ subject.should be_sibling_of(sibling, scope: :parent)
575
+ end
576
+ end
577
+
578
+ context "when not called with a sibling" do
579
+
580
+ subject { DummyReferencedChildDocument.create(parent: parent) }
581
+ let(:other_parent) { DummyParentDocument.create }
582
+ let(:non_sibling) { DummyReferencedChildDocument.create(parent: other_parent) }
583
+
584
+ it "returns false" do
585
+ subject.should_not be_sibling_of(non_sibling, scope: :parent)
586
+ end
587
+ end
588
+ end
589
+
590
+ context "when not using a scope" do
591
+
592
+ context "when called with a sibling" do
593
+
594
+ subject { DummyReferencedChildDocument.create }
595
+ let(:sibling) { DummyReferencedChildDocument.create }
596
+
597
+ it "returns true" do
598
+ subject.should be_sibling_of(sibling)
599
+ end
600
+ end
601
+
602
+ context "when not called with a sibling" do
603
+
604
+ subject { DummyReferencedChildDocument.create }
605
+
606
+ it "returns false" do
607
+ subject.should_not be_sibling_of(parent)
608
+ end
609
+ end
610
+ end
611
+ end
612
+
613
+ describe "#sibling_of!" do
614
+
615
+ context "when called with a sibling" do
616
+
617
+ let(:parent) { DummyParentDocument.create }
618
+ subject { DummyReferencedChildDocument.create(parent: parent) }
619
+ let!(:sibling) { DummyReferencedChildDocument.create(parent: parent) }
620
+
621
+ it "returns true" do
622
+ subject.sibling_of!(sibling, scope: :parent).should be_true
623
+ end
624
+
625
+ it "doesn't save the subject" do
626
+ subject.should_not_receive(:save!)
627
+
628
+ subject.sibling_of!(sibling, scope: :parent)
629
+ end
630
+ end
631
+
632
+ context "when not called with a sibling" do
633
+
634
+ context "when called with an object that can never be a sibling" do
635
+
636
+ let(:parent) { DummyParentDocument.create }
637
+ subject { DummyReferencedChildDocument.create(parent: parent) }
638
+ let!(:non_sibling) { DummyParentDocument.create }
639
+
640
+ it "returns false" do
641
+ subject.sibling_of!(non_sibling, scope: :parent).should be_false
642
+ end
643
+ end
644
+
645
+ context "when called with an object that could be a sibling" do
646
+
647
+ let(:super_parent) { DummySuperParentDocument.create }
648
+ let(:parent) { DummyParentDocument.create }
649
+ subject { DummyReferencedChildDocument.create }
650
+ let!(:new_sibling) { DummyReferencedChildDocument.create(parent_id: parent.id, super_parent: super_parent) }
651
+
652
+ let(:options) { [new_sibling, scope: [:parent_id, :super_parent]] }
653
+
654
+ it "copies the scope values from the object to the subject" do
655
+ subject.sibling_of!(*options)
656
+
657
+ subject.parent_id.should eq(new_sibling.parent_id)
658
+ subject.super_parent.should eq(new_sibling.super_parent)
659
+ end
660
+
661
+ it "saves the subject" do
662
+ subject.should_receive(:save!)
663
+
664
+ subject.sibling_of!(*options)
665
+ end
666
+
667
+ it "returns true" do
668
+ subject.sibling_of!(*options).should be_true
669
+ end
670
+
671
+ it "makes the subject a sibling of the object" do
672
+ subject.sibling_of!(*options).should be_true
673
+
674
+ subject.should be_sibling_of(*options)
675
+ end
676
+ end
677
+ end
678
+ end
679
+ end
@@ -0,0 +1,19 @@
1
+ require "rubygems"
2
+ require "bundler/setup"
3
+
4
+ require "mongoid"
5
+ require "mongoid/siblings"
6
+
7
+ require "rspec"
8
+
9
+ Mongoid.configure do |config|
10
+ config.connect_to "mongoid_siblings_test"
11
+ end
12
+
13
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
14
+
15
+ RSpec.configure do |config|
16
+ config.after :each do
17
+ Mongoid::Config.purge!
18
+ end
19
+ end
@@ -0,0 +1,49 @@
1
+ class DummySuperParentDocument
2
+ include Mongoid::Document
3
+
4
+ has_many :children, class_name: "DummyReferencedChildDocument",
5
+ inverse_of: :super_parent
6
+ end
7
+
8
+ class DummyParentDocument
9
+ include Mongoid::Document
10
+
11
+ has_many :referenced_children, class_name: "DummyReferencedChildDocument",
12
+ inverse_of: :parent
13
+ has_many :referenced_polymorphic_children1, class_name: "DummyReferencedChildDocument",
14
+ as: :polymorphic_parent
15
+ has_many :referenced_polymorphic_children2, class_name: "DummyReferencedChildDocument",
16
+ as: :polymorphic_parent
17
+
18
+ embeds_many :embedded_children, class_name: "DummyEmbeddedChildDocument",
19
+ inverse_of: :parent
20
+ embeds_many :embedded_polymorphic_children, class_name: "DummyPolymorphicEmbeddedChildDocument",
21
+ as: :parent
22
+ end
23
+
24
+ class DummyReferencedChildDocument
25
+ include Mongoid::Document
26
+ include Mongoid::Siblings
27
+
28
+ belongs_to :super_parent, class_name: DummySuperParentDocument.to_s,
29
+ inverse_of: :children
30
+
31
+ belongs_to :parent, class_name: DummyParentDocument.to_s,
32
+ inverse_of: :referenced_children
33
+ belongs_to :polymorphic_parent, polymorphic: true
34
+ end
35
+
36
+ class DummyEmbeddedChildDocument
37
+ include Mongoid::Document
38
+ include Mongoid::Siblings
39
+
40
+ embedded_in :parent, class_name: DummyParentDocument.to_s,
41
+ inverse_of: :embedded_children
42
+ end
43
+
44
+ class DummyPolymorphicEmbeddedChildDocument
45
+ include Mongoid::Document
46
+ include Mongoid::Siblings
47
+
48
+ embedded_in :parent, polymorphic: true
49
+ end
metadata ADDED
@@ -0,0 +1,96 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mongoid-siblings
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
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: &70235283618700 !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: *70235283618700
25
+ - !ruby/object:Gem::Dependency
26
+ name: rake
27
+ requirement: &70235283618220 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *70235283618220
36
+ - !ruby/object:Gem::Dependency
37
+ name: rspec
38
+ requirement: &70235283617640 !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: *70235283617640
47
+ description: mongoid-siblings adds methods to enable you to easily access your Mongoid
48
+ document's siblings.
49
+ email: douwe@selenight.nl
50
+ executables: []
51
+ extensions: []
52
+ extra_rdoc_files: []
53
+ files:
54
+ - lib/mongoid/siblings.rb
55
+ - LICENSE
56
+ - README.md
57
+ - Rakefile
58
+ - Gemfile
59
+ - spec/mongoid/siblings_spec.rb
60
+ - spec/spec_helper.rb
61
+ - spec/support/models.rb
62
+ homepage: https://github.com/DouweM/mongoid-siblings
63
+ licenses:
64
+ - MIT
65
+ post_install_message:
66
+ rdoc_options: []
67
+ require_paths:
68
+ - lib
69
+ required_ruby_version: !ruby/object:Gem::Requirement
70
+ none: false
71
+ requirements:
72
+ - - ! '>='
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ segments:
76
+ - 0
77
+ hash: -1413753754356574329
78
+ required_rubygems_version: !ruby/object:Gem::Requirement
79
+ none: false
80
+ requirements:
81
+ - - ! '>='
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ segments:
85
+ - 0
86
+ hash: -1413753754356574329
87
+ requirements: []
88
+ rubyforge_project:
89
+ rubygems_version: 1.8.6
90
+ signing_key:
91
+ specification_version: 3
92
+ summary: Easy access to your Mongoid document's siblings.
93
+ test_files:
94
+ - spec/mongoid/siblings_spec.rb
95
+ - spec/spec_helper.rb
96
+ - spec/support/models.rb