mongoid_monkey 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 35ce26c7c1fa842774c1c8724d3060e63d14bcc3
4
+ data.tar.gz: 73b16e660f5088a4ef8f7a07b0ad37a1731ead1c
5
+ SHA512:
6
+ metadata.gz: 1b98576cffc2d498853d06b1dbffaef9a5ea53d5c184c6884e7fe146261a5da5cea89a90b2d633d0d7d0264edea9cfd1b3d2a49bcf63a2fa34319e1e045cad55
7
+ data.tar.gz: f07e78cc1905d110a4042dac8f8f15448dc5d8d39dd7412c1ad1f8a693c59d5f39e12ffebe449a0da7030651fad95500308bd156970f5fec3e07b4846b6fc38b
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Johnny Shields
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,40 @@
1
+ # Mongoid Monkey
2
+
3
+ ### Monkey Patches for Mongoid
4
+
5
+ Mongoid Monkey is a collection of monkey patches for Mongoid 3, 4, 5, including feature
6
+ backports, fixes, and forward compatibility.
7
+
8
+ ### Warning
9
+
10
+ The patches in this gem will change/override the behavior of Mongoid. While effort has been
11
+ made to be as backward compatible as possible, use at your own risk.
12
+
13
+ ### Installation
14
+
15
+ In your Gemfile, require this gem **after** requiring `mongoid`.
16
+
17
+ ### Version Requirement
18
+
19
+ * Works with Mongoid 3, 4, 5
20
+ * Due to the `list_collections` patch, this gem requires **at least MongoDB 3.0.** Other patches will work on older MongoDB versions.
21
+
22
+ ### Patch List
23
+
24
+ Installing this gem will apply all monkey patches for the Mongoid version you are using.
25
+ If you would only like some of the patches, please copy and paste the code to your app directly
26
+ (e.g. `/config/initializers` if using Rails.)
27
+
28
+ | File | Description | 3 | 4 | 5 |
29
+ | --- | --- | --- | --- | --- |
30
+ | `atomic.rb` | Backport syntax change of atomic query methods. | ● | | |
31
+ | `big_decimal.rb` | Fixes buggy BigDecimal behavior. | ● | ● | ● |
32
+ | `instrument.rb` | Backport instrumentation change to Moped 1. | ● | | |
33
+ | `list_collections.rb` | Fetch collections via `listCollections` command; required for WiredTiger. | ● | ● | |
34
+ | `reorder.rb` | Backport `Criteria#reorder` method. | ● | | |
35
+
36
+ ### License
37
+
38
+ * This project is licensed under the MIT License.
39
+ * Some code in this repo is adapted from the fantastic work of Durran Jordan, et. al. on Mongoid.
40
+ * (c) Copyright 2016 Johnny Shields.
@@ -0,0 +1,15 @@
1
+ require 'version'
2
+
3
+ if Mongoid::VERSION =~ /\A3\./
4
+ require 'patches/atomic'
5
+ require 'patches/reorder'
6
+ end
7
+
8
+ if Mongoid::VERSION =~ /\A[345]\./
9
+ require 'patches/big_decimal'
10
+ end
11
+
12
+ if defined?(Moped)
13
+ require 'patches/instrument' if Moped::VERSION =~ /\A1\./
14
+ require 'patches/list_collections'
15
+ end
@@ -0,0 +1,109 @@
1
+ module Mongoid
2
+ module Contextual
3
+ module Atomic
4
+
5
+ def add_to_set_with_mongoid4(*args)
6
+ if args.length == 1 && args.first.is_a?(Hash)
7
+ query.update_all("$addToSet" => collect_operations(args.first))
8
+ else
9
+ add_to_set_without_mongoid4(*args)
10
+ end
11
+ end
12
+ alias_method_chain :add_to_set, :mongoid4
13
+
14
+ def bit_with_mongoid4(*args)
15
+ if args.length == 1 && args.first.is_a?(Hash)
16
+ query.update_all("$bit" => collect_operations(args.first))
17
+ else
18
+ bit_without_mongoid4(*args)
19
+ end
20
+ end
21
+ alias_method_chain :bit, :mongoid4
22
+
23
+ def inc_with_mongoid4(*args)
24
+ if args.length == 1 && args.first.is_a?(Hash)
25
+ query.update_all("$inc" => collect_operations(args.first))
26
+ else
27
+ inc_without_mongoid4(*args)
28
+ end
29
+ end
30
+ alias_method_chain :inc, :mongoid4
31
+
32
+ def pop_with_mongoid4(*args)
33
+ if args.length == 1 && args.first.is_a?(Hash)
34
+ query.update_all("$pop" => collect_operations(args.first))
35
+ else
36
+ pop_without_mongoid4(*args)
37
+ end
38
+ end
39
+ alias_method_chain :pop, :mongoid4
40
+
41
+ def pull_with_mongoid4(*args)
42
+ if args.length == 1 && args.first.is_a?(Hash)
43
+ query.update_all("$pull" => collect_operations(args.first))
44
+ else
45
+ pull_without_mongoid4(*args)
46
+ end
47
+ end
48
+ alias_method_chain :pull, :mongoid4
49
+
50
+ def pull_all_with_mongoid4(*args)
51
+ if args.length == 1 && args.first.is_a?(Hash)
52
+ query.update_all("$pullAll" => collect_operations(args.first))
53
+ else
54
+ pull_all_without_mongoid4(*args)
55
+ end
56
+ end
57
+ alias_method_chain :pull_all, :mongoid4
58
+
59
+ def push_with_mongoid4(*args)
60
+ if args.length == 1 && args.first.is_a?(Hash)
61
+ query.update_all("$push" => collect_operations(args.first))
62
+ else
63
+ push_without_mongoid4(*args)
64
+ end
65
+ end
66
+ alias_method_chain :push, :mongoid4
67
+
68
+ def push_all_with_mongoid4(*args)
69
+ if args.length == 1 && args.first.is_a?(Hash)
70
+ query.update_all("$pushAll" => collect_operations(args.first))
71
+ else
72
+ push_all_without_mongoid4(*args)
73
+ end
74
+ end
75
+ alias_method_chain :push_all, :mongoid4
76
+
77
+ def rename_with_mongoid4(*args)
78
+ if args.length == 1 && args.first.is_a?(Hash)
79
+ operations = args.first.inject({}) do |ops, (old_name, new_name)|
80
+ ops[old_name] = new_name.to_s
81
+ ops
82
+ end
83
+ query.update_all("$rename" => collect_operations(operations))
84
+ else
85
+ rename_without_mongoid4(*args)
86
+ end
87
+ end
88
+ alias_method_chain :rename, :mongoid4
89
+
90
+ def set_with_mongoid4(*args)
91
+ if args.length == 1 && args.first.is_a?(Hash)
92
+ query.update_all("$set" => collect_operations(args.first))
93
+ else
94
+ set_without_mongoid4(*args)
95
+ end
96
+ end
97
+ alias_method_chain :set, :mongoid4
98
+
99
+ private
100
+
101
+ def collect_operations(ops)
102
+ ops.inject({}) do |operations, (field, value)|
103
+ operations[database_field_name(field)] = value.mongoize
104
+ operations
105
+ end
106
+ end
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,38 @@
1
+ # Fixes inconsistent behavior of BigDecimal. This can be removed after
2
+ # https://github.com/mongodb/mongoid/pull/4164 is merged, planned for Mongoid 6.
3
+
4
+ module MongoidMonkey
5
+ module Mongoid
6
+ module Extensions
7
+ module BigDecimal
8
+
9
+ def numeric?
10
+ true
11
+ end
12
+
13
+ module ClassMethods
14
+
15
+ def demongoize(object)
16
+ object && object.numeric? ? ::BigDecimal.new(object.to_s) : nil
17
+ end
18
+
19
+ def mongoize(object)
20
+ object && object.numeric? ? object.to_s : nil
21
+ end
22
+ end
23
+ end
24
+
25
+ module String
26
+
27
+ def numeric?
28
+ true if Float(self) rescue (self =~ /^NaN|\-?Infinity$/)
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+
35
+ ::BigDecimal.__send__(:include, MongoidMonkey::Mongoid::Extensions::BigDecimal)
36
+ ::BigDecimal.extend(MongoidMonkey::Mongoid::Extensions::BigDecimal::ClassMethods)
37
+
38
+ ::String.__send__(:include, MongoidMonkey::Mongoid::Extensions::String)
@@ -0,0 +1,45 @@
1
+ # Instrument Moped 1.x same as Moped 2.x.
2
+ # Useful for integration with third-party services.
3
+
4
+ module Moped
5
+ module Instrumentable
6
+ class Noop
7
+
8
+ class << self
9
+
10
+ # Do not instrument anything.
11
+ def instrument(name, payload = {})
12
+ yield payload if block_given?
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+
19
+ module Moped
20
+ module Instrumentable
21
+
22
+ TOPIC = "query.moped"
23
+
24
+ def instrumenter
25
+ @instrumenter ||= Moped::Instrumentable::Noop
26
+ end
27
+
28
+ def instrument(name, payload = {}, &block)
29
+ instrumenter.instrument(name, payload, &block)
30
+ end
31
+ end
32
+ end
33
+
34
+ module Moped
35
+ class Node
36
+ include Moped::Instrumentable
37
+
38
+ def logging_with_instrument(operations, &block)
39
+ instrument(TOPIC, prefix: " MOPED: #{resolved_address}", ops: operations) do
40
+ logging_without_instrument(operations, &block)
41
+ end
42
+ end
43
+ alias_method_chain :logging, :instrument
44
+ end
45
+ end
@@ -0,0 +1,14 @@
1
+ # Get collection_names via for MongoDB 3.0+ listCollections command.
2
+ # Required to use Moped (Mongoid 3/4) with WiredTiger.
3
+
4
+ module Moped
5
+ class Database
6
+
7
+ def collection_names
8
+ namespaces = Collection.new(self, "$cmd").find(listCollections: 1, filter: { name: { "$not" => /system\.|\$/ } }).first
9
+ namespaces["cursor"]["firstBatch"].map do |doc|
10
+ doc["name"]
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,11 @@
1
+ # Backport of Criteria#reorder method from Mongoid 4 to Mongoid 3.
2
+
3
+ module Origin
4
+ module Optional
5
+
6
+ def reorder(*spec)
7
+ options.delete(:sort)
8
+ order_by(*spec)
9
+ end
10
+ end
11
+ end
data/lib/version.rb ADDED
@@ -0,0 +1,3 @@
1
+ module MongoidMonkey
2
+ VERSION = '0.1.0'
3
+ end
@@ -0,0 +1,2 @@
1
+ storage:
2
+ engine: mmapv1
@@ -0,0 +1,2 @@
1
+ storage:
2
+ engine: wiredTiger
@@ -0,0 +1,5 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec path: "../../"
4
+
5
+ gem 'mongoid', '~> 3'
@@ -0,0 +1,5 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec path: "../../"
4
+
5
+ gem 'mongoid', '~> 4'
@@ -0,0 +1,5 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec path: "../../"
4
+
5
+ gem 'mongoid', '~> 5'
@@ -0,0 +1,23 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+
4
+ require 'mongoid'
5
+ require 'mongoid_monkey'
6
+ require 'rspec'
7
+
8
+ Mongoid.configure do |config|
9
+ config.connect_to 'mongoid_monkey_test'
10
+ end
11
+
12
+ Mongoid.logger.level = Logger::INFO
13
+ Mongo::Logger.logger.level = Logger::INFO unless Mongoid::VERSION =~ /\A[34]\./
14
+
15
+ RSpec.configure do |config|
16
+ config.after(:all) do
17
+ if Mongoid::VERSION =~ /\A[34]\./
18
+ Mongoid.default_session.drop
19
+ else
20
+ Mongoid::Clients.default.database.drop
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,1067 @@
1
+ require "spec_helper"
2
+
3
+ if Mongoid::VERSION =~ /\A3\./
4
+
5
+ class Band
6
+ include Mongoid::Document
7
+ field :name, type: String
8
+ field :genres, type: Array
9
+ field :mems, as: :members, type: Array
10
+ field :likes, type: Integer
11
+ field :y, as: :years, type: Integer
12
+ end
13
+
14
+ # Mongoid 3
15
+ describe Mongoid::Contextual::Atomic do
16
+
17
+ describe "#add_to_set" do
18
+
19
+ let!(:depeche_mode) do
20
+ Band.create(members: [ "Dave" ])
21
+ end
22
+
23
+ let!(:new_order) do
24
+ Band.create(members: [ "Peter" ])
25
+ end
26
+
27
+ let!(:smiths) do
28
+ Band.create
29
+ end
30
+
31
+ context "when the criteria has no sorting" do
32
+
33
+ let(:criteria) do
34
+ Band.all
35
+ end
36
+
37
+ let(:context) do
38
+ Mongoid::Contextual::Mongo.new(criteria)
39
+ end
40
+
41
+ before do
42
+ context.add_to_set(:members, "Dave")
43
+ end
44
+
45
+ it "does not add duplicates" do
46
+ depeche_mode.reload.members.should eq([ "Dave" ])
47
+ end
48
+
49
+ it "adds unique values" do
50
+ new_order.reload.members.should eq([ "Peter", "Dave" ])
51
+ end
52
+
53
+ it "adds to non initialized fields" do
54
+ smiths.reload.members.should eq([ "Dave" ])
55
+ end
56
+ end
57
+
58
+ context "when the criteria has sorting" do
59
+
60
+ let(:criteria) do
61
+ Band.asc(:name)
62
+ end
63
+
64
+ let(:context) do
65
+ Mongoid::Contextual::Mongo.new(criteria)
66
+ end
67
+
68
+ before do
69
+ context.add_to_set(:members, "Dave")
70
+ end
71
+
72
+ it "does not add duplicates" do
73
+ depeche_mode.reload.members.should eq([ "Dave" ])
74
+ end
75
+
76
+ it "adds unique values" do
77
+ new_order.reload.members.should eq([ "Peter", "Dave" ])
78
+ end
79
+
80
+ it "adds to non initialized fields" do
81
+ smiths.reload.members.should eq([ "Dave" ])
82
+ end
83
+ end
84
+ end
85
+
86
+ describe "#bit" do
87
+
88
+ let!(:depeche_mode) do
89
+ Band.create(likes: 60)
90
+ end
91
+
92
+ let!(:smiths) do
93
+ Band.create
94
+ end
95
+
96
+ let(:criteria) do
97
+ Band.all
98
+ end
99
+
100
+ let(:context) do
101
+ Mongoid::Contextual::Mongo.new(criteria)
102
+ end
103
+
104
+ context "when performing a bitwise and" do
105
+
106
+ before do
107
+ context.bit(:likes, { and: 13 })
108
+ end
109
+
110
+ it "performs the bitwise operation on initialized fields" do
111
+ depeche_mode.reload.likes.should eq(12)
112
+ end
113
+
114
+ it "does not error on non initialized fields" do
115
+ smiths.reload.likes.should eq(0)
116
+ end
117
+ end
118
+
119
+ context "when performing a bitwise or" do
120
+
121
+ before do
122
+ context.bit(:likes, { or: 13 })
123
+ end
124
+
125
+ it "performs the bitwise operation on initialized fields" do
126
+ depeche_mode.reload.likes.should eq(61)
127
+ end
128
+
129
+ it "does not error on non initialized fields" do
130
+ smiths.reload.likes.should eq(13)
131
+ end
132
+ end
133
+
134
+ context "when chaining bitwise operations" do
135
+
136
+ before do
137
+ context.bit(:likes, { and: 13, or: 10 })
138
+ end
139
+
140
+ it "performs the bitwise operation on initialized fields" do
141
+ depeche_mode.reload.likes.should eq(14)
142
+ end
143
+
144
+ it "does not error on non initialized fields" do
145
+ smiths.reload.likes.should eq(10)
146
+ end
147
+ end
148
+ end
149
+
150
+ describe "#inc" do
151
+
152
+ let!(:depeche_mode) do
153
+ Band.create(likes: 60)
154
+ end
155
+
156
+ let!(:smiths) do
157
+ Band.create
158
+ end
159
+
160
+ let!(:beatles) do
161
+ Band.create(years: 2)
162
+ end
163
+
164
+ let(:criteria) do
165
+ Band.all
166
+ end
167
+
168
+ let(:context) do
169
+ Mongoid::Contextual::Mongo.new(criteria)
170
+ end
171
+
172
+ before do
173
+ context.inc(:likes, 10)
174
+ end
175
+
176
+ context "when the field exists" do
177
+
178
+ it "incs the value" do
179
+ depeche_mode.reload.likes.should eq(70)
180
+ end
181
+ end
182
+
183
+ context "when the field does not exist" do
184
+
185
+ it "does not error on the inc" do
186
+ smiths.likes.should be_nil
187
+ end
188
+ end
189
+
190
+ context "when using the alias" do
191
+
192
+ before do
193
+ context.inc(:years, 1)
194
+ end
195
+
196
+ it "incs the value and read from alias" do
197
+ beatles.reload.years.should eq(3)
198
+ end
199
+
200
+ it "incs the value and read from field" do
201
+ beatles.reload.y.should eq(3)
202
+ end
203
+ end
204
+ end
205
+
206
+ describe "#pop" do
207
+
208
+ let!(:depeche_mode) do
209
+ Band.create(members: [ "Dave", "Martin" ])
210
+ end
211
+
212
+ let!(:smiths) do
213
+ Band.create
214
+ end
215
+
216
+ let(:criteria) do
217
+ Band.all
218
+ end
219
+
220
+ let(:context) do
221
+ Mongoid::Contextual::Mongo.new(criteria)
222
+ end
223
+
224
+ context "when popping from the front" do
225
+
226
+ before do
227
+ context.pop(:members, -1)
228
+ end
229
+
230
+ it "pops the first element off the array" do
231
+ depeche_mode.reload.members.should eq([ "Martin" ])
232
+ end
233
+
234
+ it "does not error on uninitialized fields" do
235
+ smiths.reload.members.should be_nil
236
+ end
237
+ end
238
+
239
+ context "when popping from the rear" do
240
+
241
+ before do
242
+ context.pop(:members, 1)
243
+ end
244
+
245
+ it "pops the last element off the array" do
246
+ depeche_mode.reload.members.should eq([ "Dave" ])
247
+ end
248
+
249
+ it "does not error on uninitialized fields" do
250
+ smiths.reload.members.should be_nil
251
+ end
252
+ end
253
+ end
254
+
255
+ describe "#pull" do
256
+
257
+ let!(:depeche_mode) do
258
+ Band.create(members: [ "Dave", "Alan" ])
259
+ end
260
+
261
+ let!(:smiths) do
262
+ Band.create
263
+ end
264
+
265
+ let(:criteria) do
266
+ Band.all
267
+ end
268
+
269
+ let(:context) do
270
+ Mongoid::Contextual::Mongo.new(criteria)
271
+ end
272
+
273
+ before do
274
+ context.pull(:members, "Alan")
275
+ end
276
+
277
+ it "pulls when the value is found" do
278
+ depeche_mode.reload.members.should eq([ "Dave" ])
279
+ end
280
+
281
+ it "does not error on non existant fields" do
282
+ smiths.reload.members.should be_nil
283
+ end
284
+ end
285
+
286
+ describe "#pull_all" do
287
+
288
+ let!(:depeche_mode) do
289
+ Band.create(members: [ "Dave", "Alan", "Fletch" ])
290
+ end
291
+
292
+ let!(:smiths) do
293
+ Band.create
294
+ end
295
+
296
+ let(:criteria) do
297
+ Band.all
298
+ end
299
+
300
+ let(:context) do
301
+ Mongoid::Contextual::Mongo.new(criteria)
302
+ end
303
+
304
+ before do
305
+ context.pull_all(:members, [ "Alan", "Dave" ])
306
+ end
307
+
308
+ it "pulls when the values are found" do
309
+ depeche_mode.reload.members.should eq([ "Fletch" ])
310
+ end
311
+
312
+ it "does not error on non existant fields" do
313
+ smiths.reload.members.should be_nil
314
+ end
315
+ end
316
+
317
+ describe "#push" do
318
+
319
+ let!(:depeche_mode) do
320
+ Band.create(members: [ "Dave" ])
321
+ end
322
+
323
+ let!(:smiths) do
324
+ Band.create
325
+ end
326
+
327
+ let(:criteria) do
328
+ Band.all
329
+ end
330
+
331
+ let(:context) do
332
+ Mongoid::Contextual::Mongo.new(criteria)
333
+ end
334
+
335
+ before do
336
+ context.push(:members, "Alan")
337
+ end
338
+
339
+ it "pushes the value to existing arrays" do
340
+ depeche_mode.reload.members.should eq([ "Dave", "Alan" ])
341
+ end
342
+
343
+ it "pushes to non existant fields" do
344
+ smiths.reload.members.should eq([ "Alan" ])
345
+ end
346
+ end
347
+
348
+ describe "#push_all" do
349
+
350
+ let!(:depeche_mode) do
351
+ Band.create(members: [ "Dave" ])
352
+ end
353
+
354
+ let!(:smiths) do
355
+ Band.create
356
+ end
357
+
358
+ let(:criteria) do
359
+ Band.all
360
+ end
361
+
362
+ let(:context) do
363
+ Mongoid::Contextual::Mongo.new(criteria)
364
+ end
365
+
366
+ before do
367
+ context.push_all(:members, [ "Alan", "Fletch" ])
368
+ end
369
+
370
+ it "pushes the values to existing arrays" do
371
+ depeche_mode.reload.members.should eq([ "Dave", "Alan", "Fletch" ])
372
+ end
373
+
374
+ it "pushes to non existant fields" do
375
+ smiths.reload.members.should eq([ "Alan", "Fletch" ])
376
+ end
377
+ end
378
+
379
+ describe "#rename" do
380
+
381
+ let!(:depeche_mode) do
382
+ Band.create(members: [ "Dave" ])
383
+ end
384
+
385
+ let!(:smiths) do
386
+ Band.create
387
+ end
388
+
389
+ let(:criteria) do
390
+ Band.all
391
+ end
392
+
393
+ let(:context) do
394
+ Mongoid::Contextual::Mongo.new(criteria)
395
+ end
396
+
397
+ before do
398
+ context.rename(:members, :artists)
399
+ end
400
+
401
+ it "renames existing fields" do
402
+ depeche_mode.reload.artists.should eq([ "Dave" ])
403
+ end
404
+
405
+ it "does not rename non existant fields" do
406
+ smiths.reload.should_not respond_to(:artists)
407
+ end
408
+ end
409
+
410
+ describe "#set" do
411
+
412
+ let!(:depeche_mode) do
413
+ Band.create(name: "Depeche Mode")
414
+ end
415
+
416
+ let!(:smiths) do
417
+ Band.create
418
+ end
419
+
420
+ let(:criteria) do
421
+ Band.all
422
+ end
423
+
424
+ let(:context) do
425
+ Mongoid::Contextual::Mongo.new(criteria)
426
+ end
427
+
428
+ before do
429
+ context.set(:name, "Recoil")
430
+ end
431
+
432
+ it "sets existing fields" do
433
+ depeche_mode.reload.name.should eq("Recoil")
434
+ end
435
+
436
+ it "sets non existant fields" do
437
+ smiths.reload.name.should eq("Recoil")
438
+ end
439
+ end
440
+
441
+ describe "#unset" do
442
+
443
+ context "when unsetting a single field" do
444
+
445
+ let!(:depeche_mode) do
446
+ Band.create(name: "Depeche Mode", years: 10)
447
+ end
448
+
449
+ let!(:new_order) do
450
+ Band.create(name: "New Order", years: 10)
451
+ end
452
+
453
+ let(:criteria) do
454
+ Band.all
455
+ end
456
+
457
+ let(:context) do
458
+ Mongoid::Contextual::Mongo.new(criteria)
459
+ end
460
+
461
+ context "when the field is not aliased" do
462
+
463
+ before do
464
+ context.unset(:name)
465
+ end
466
+
467
+ it "unsets the first existing field" do
468
+ depeche_mode.reload.name.should be_nil
469
+ end
470
+
471
+ it "unsets the last existing field" do
472
+ new_order.reload.name.should be_nil
473
+ end
474
+ end
475
+
476
+ context "when the field is aliased" do
477
+
478
+ before do
479
+ context.unset(:years)
480
+ end
481
+
482
+ it "unsets the first existing field" do
483
+ depeche_mode.reload.years.should be_nil
484
+ end
485
+
486
+ it "unsets the last existing field" do
487
+ new_order.reload.years.should be_nil
488
+ end
489
+ end
490
+ end
491
+
492
+ context "when unsetting multiple fields" do
493
+
494
+ let!(:new_order) do
495
+ Band.create(name: "New Order", genres: [ "electro", "dub" ], years: 10)
496
+ end
497
+
498
+ let(:criteria) do
499
+ Band.all
500
+ end
501
+
502
+ let(:context) do
503
+ Mongoid::Contextual::Mongo.new(criteria)
504
+ end
505
+
506
+ context "when the field is not aliased" do
507
+
508
+ before do
509
+ context.unset(:name, :genres)
510
+ end
511
+
512
+ it "unsets name field" do
513
+ new_order.reload.name.should be_nil
514
+ end
515
+
516
+ it "unsets genres field" do
517
+ new_order.reload.genres.should be_nil
518
+ end
519
+ end
520
+
521
+ context "when the field is aliased" do
522
+
523
+ before do
524
+ context.unset(:name, :years)
525
+ end
526
+
527
+ it "unsets the unaliased field" do
528
+ new_order.reload.name.should be_nil
529
+ end
530
+
531
+ it "unsets the aliased field" do
532
+ new_order.reload.years.should be_nil
533
+ end
534
+ end
535
+ end
536
+ end
537
+ end
538
+
539
+ # Mongoid 4
540
+ describe Mongoid::Contextual::Atomic do
541
+
542
+ describe "#add_to_set" do
543
+
544
+ let!(:depeche_mode) do
545
+ Band.create(members: [ "Dave" ])
546
+ end
547
+
548
+ let!(:new_order) do
549
+ Band.create(members: [ "Peter" ])
550
+ end
551
+
552
+ let!(:smiths) do
553
+ Band.create
554
+ end
555
+
556
+ context "when the criteria has no sorting" do
557
+
558
+ let(:criteria) do
559
+ Band.all
560
+ end
561
+
562
+ let(:context) do
563
+ Mongoid::Contextual::Mongo.new(criteria)
564
+ end
565
+
566
+ before do
567
+ context.add_to_set(members: "Dave")
568
+ end
569
+
570
+ it "does not add duplicates" do
571
+ expect(depeche_mode.reload.members).to eq([ "Dave" ])
572
+ end
573
+
574
+ it "adds unique values" do
575
+ expect(new_order.reload.members).to eq([ "Peter", "Dave" ])
576
+ end
577
+
578
+ it "adds to non initialized fields" do
579
+ expect(smiths.reload.members).to eq([ "Dave" ])
580
+ end
581
+ end
582
+
583
+ context "when the criteria has sorting" do
584
+
585
+ let(:criteria) do
586
+ Band.asc(:name)
587
+ end
588
+
589
+ let(:context) do
590
+ Mongoid::Contextual::Mongo.new(criteria)
591
+ end
592
+
593
+ before do
594
+ context.add_to_set(members: "Dave", genres: "Electro")
595
+ end
596
+
597
+ it "does not add duplicates" do
598
+ expect(depeche_mode.reload.members).to eq([ "Dave" ])
599
+ end
600
+
601
+ it "adds multiple operations" do
602
+ expect(depeche_mode.reload.genres).to eq([ "Electro" ])
603
+ end
604
+
605
+ it "adds unique values" do
606
+ expect(new_order.reload.members).to eq([ "Peter", "Dave" ])
607
+ end
608
+
609
+ it "adds to non initialized fields" do
610
+ expect(smiths.reload.members).to eq([ "Dave" ])
611
+ end
612
+ end
613
+ end
614
+
615
+ describe "#bit" do
616
+
617
+ let!(:depeche_mode) do
618
+ Band.create(likes: 60)
619
+ end
620
+
621
+ let!(:smiths) do
622
+ Band.create
623
+ end
624
+
625
+ let(:criteria) do
626
+ Band.all
627
+ end
628
+
629
+ let(:context) do
630
+ Mongoid::Contextual::Mongo.new(criteria)
631
+ end
632
+
633
+ context "when performing a bitwise and" do
634
+
635
+ before do
636
+ context.bit(likes: { and: 13 })
637
+ end
638
+
639
+ it "performs the bitwise operation on initialized fields" do
640
+ expect(depeche_mode.reload.likes).to eq(12)
641
+ end
642
+
643
+ it "does not error on non initialized fields" do
644
+ expect(smiths.reload.likes).to eq(0)
645
+ end
646
+ end
647
+
648
+ context "when performing a bitwise or" do
649
+
650
+ before do
651
+ context.bit(likes: { or: 13 })
652
+ end
653
+
654
+ it "performs the bitwise operation on initialized fields" do
655
+ expect(depeche_mode.reload.likes).to eq(61)
656
+ end
657
+
658
+ it "does not error on non initialized fields" do
659
+ expect(smiths.reload.likes).to eq(13)
660
+ end
661
+ end
662
+
663
+ context "when chaining bitwise operations" do
664
+
665
+ before do
666
+ context.bit(likes: { and: 13, or: 10 })
667
+ end
668
+
669
+ it "performs the bitwise operation on initialized fields" do
670
+ expect(depeche_mode.reload.likes).to eq(14)
671
+ end
672
+
673
+ it "does not error on non initialized fields" do
674
+ expect(smiths.reload.likes).to eq(10)
675
+ end
676
+ end
677
+ end
678
+
679
+ describe "#inc" do
680
+
681
+ let!(:depeche_mode) do
682
+ Band.create(likes: 60)
683
+ end
684
+
685
+ let!(:smiths) do
686
+ Band.create
687
+ end
688
+
689
+ let!(:beatles) do
690
+ Band.create(years: 2)
691
+ end
692
+
693
+ let(:criteria) do
694
+ Band.all
695
+ end
696
+
697
+ let(:context) do
698
+ Mongoid::Contextual::Mongo.new(criteria)
699
+ end
700
+
701
+ before do
702
+ context.inc(likes: 10)
703
+ end
704
+
705
+ context "when the field exists" do
706
+
707
+ it "incs the value" do
708
+ expect(depeche_mode.reload.likes).to eq(70)
709
+ end
710
+ end
711
+
712
+ context "when the field does not exist" do
713
+
714
+ it "does not error on the inc" do
715
+ expect(smiths.likes).to be_nil
716
+ end
717
+ end
718
+
719
+ context "when using the alias" do
720
+
721
+ before do
722
+ context.inc(years: 1)
723
+ end
724
+
725
+ it "incs the value and read from alias" do
726
+ expect(beatles.reload.years).to eq(3)
727
+ end
728
+
729
+ it "incs the value and read from field" do
730
+ expect(beatles.reload.y).to eq(3)
731
+ end
732
+ end
733
+ end
734
+
735
+ describe "#pop" do
736
+
737
+ let!(:depeche_mode) do
738
+ Band.create(members: [ "Dave", "Martin" ])
739
+ end
740
+
741
+ let!(:smiths) do
742
+ Band.create
743
+ end
744
+
745
+ let(:criteria) do
746
+ Band.all
747
+ end
748
+
749
+ let(:context) do
750
+ Mongoid::Contextual::Mongo.new(criteria)
751
+ end
752
+
753
+ context "when popping from the front" do
754
+
755
+ before do
756
+ context.pop(members: -1)
757
+ end
758
+
759
+ it "pops the first element off the array" do
760
+ expect(depeche_mode.reload.members).to eq([ "Martin" ])
761
+ end
762
+
763
+ it "does not error on uninitialized fields" do
764
+ expect(smiths.reload.members).to be_nil
765
+ end
766
+ end
767
+
768
+ context "when popping from the rear" do
769
+
770
+ before do
771
+ context.pop(members: 1)
772
+ end
773
+
774
+ it "pops the last element off the array" do
775
+ expect(depeche_mode.reload.members).to eq([ "Dave" ])
776
+ end
777
+
778
+ it "does not error on uninitialized fields" do
779
+ expect(smiths.reload.members).to be_nil
780
+ end
781
+ end
782
+ end
783
+
784
+ describe "#pull" do
785
+
786
+ let!(:depeche_mode) do
787
+ Band.create(members: [ "Dave", "Alan" ])
788
+ end
789
+
790
+ let!(:smiths) do
791
+ Band.create
792
+ end
793
+
794
+ let(:criteria) do
795
+ Band.all
796
+ end
797
+
798
+ let(:context) do
799
+ Mongoid::Contextual::Mongo.new(criteria)
800
+ end
801
+
802
+ before do
803
+ context.pull(members: "Alan")
804
+ end
805
+
806
+ it "pulls when the value is found" do
807
+ expect(depeche_mode.reload.members).to eq([ "Dave" ])
808
+ end
809
+
810
+ it "does not error on non existant fields" do
811
+ expect(smiths.reload.members).to be_nil
812
+ end
813
+ end
814
+
815
+ describe "#pull_all" do
816
+
817
+ let!(:depeche_mode) do
818
+ Band.create(members: [ "Dave", "Alan", "Fletch" ])
819
+ end
820
+
821
+ let!(:smiths) do
822
+ Band.create
823
+ end
824
+
825
+ let(:criteria) do
826
+ Band.all
827
+ end
828
+
829
+ let(:context) do
830
+ Mongoid::Contextual::Mongo.new(criteria)
831
+ end
832
+
833
+ before do
834
+ context.pull_all(members: [ "Alan", "Dave" ])
835
+ end
836
+
837
+ it "pulls when the values are found" do
838
+ expect(depeche_mode.reload.members).to eq([ "Fletch" ])
839
+ end
840
+
841
+ it "does not error on non existant fields" do
842
+ expect(smiths.reload.members).to be_nil
843
+ end
844
+ end
845
+
846
+ describe "#push" do
847
+
848
+ let!(:depeche_mode) do
849
+ Band.create(members: [ "Dave" ])
850
+ end
851
+
852
+ let!(:smiths) do
853
+ Band.create
854
+ end
855
+
856
+ let(:criteria) do
857
+ Band.all
858
+ end
859
+
860
+ let(:context) do
861
+ Mongoid::Contextual::Mongo.new(criteria)
862
+ end
863
+
864
+ before do
865
+ context.push(members: "Alan")
866
+ end
867
+
868
+ it "pushes the value to existing arrays" do
869
+ expect(depeche_mode.reload.members).to eq([ "Dave", "Alan" ])
870
+ end
871
+
872
+ it "pushes to non existant fields" do
873
+ expect(smiths.reload.members).to eq([ "Alan" ])
874
+ end
875
+ end
876
+
877
+ describe "#push_all" do
878
+
879
+ let!(:depeche_mode) do
880
+ Band.create(members: [ "Dave" ])
881
+ end
882
+
883
+ let!(:smiths) do
884
+ Band.create
885
+ end
886
+
887
+ let(:criteria) do
888
+ Band.all
889
+ end
890
+
891
+ let(:context) do
892
+ Mongoid::Contextual::Mongo.new(criteria)
893
+ end
894
+
895
+ before do
896
+ context.push_all(members: [ "Alan", "Fletch" ])
897
+ end
898
+
899
+ it "pushes the values to existing arrays" do
900
+ expect(depeche_mode.reload.members).to eq([ "Dave", "Alan", "Fletch" ])
901
+ end
902
+
903
+ it "pushes to non existant fields" do
904
+ expect(smiths.reload.members).to eq([ "Alan", "Fletch" ])
905
+ end
906
+ end
907
+
908
+ describe "#rename" do
909
+
910
+ let!(:depeche_mode) do
911
+ Band.create(members: [ "Dave" ])
912
+ end
913
+
914
+ let!(:smiths) do
915
+ Band.create
916
+ end
917
+
918
+ let(:criteria) do
919
+ Band.all
920
+ end
921
+
922
+ let(:context) do
923
+ Mongoid::Contextual::Mongo.new(criteria)
924
+ end
925
+
926
+ before do
927
+ context.rename(members: :artists)
928
+ end
929
+
930
+ it "renames existing fields" do
931
+ expect(depeche_mode.reload.artists).to eq([ "Dave" ])
932
+ end
933
+
934
+ it "does not rename non existant fields" do
935
+ expect(smiths.reload).to_not respond_to(:artists)
936
+ end
937
+ end
938
+
939
+ describe "#set" do
940
+
941
+ let!(:depeche_mode) do
942
+ Band.create(name: "Depeche Mode")
943
+ end
944
+
945
+ let!(:smiths) do
946
+ Band.create
947
+ end
948
+
949
+ let(:criteria) do
950
+ Band.all
951
+ end
952
+
953
+ let(:context) do
954
+ Mongoid::Contextual::Mongo.new(criteria)
955
+ end
956
+
957
+ before do
958
+ context.set(name: "Recoil")
959
+ end
960
+
961
+ it "sets existing fields" do
962
+ expect(depeche_mode.reload.name).to eq("Recoil")
963
+ end
964
+
965
+ it "sets non existant fields" do
966
+ expect(smiths.reload.name).to eq("Recoil")
967
+ end
968
+ end
969
+
970
+ describe "#unset" do
971
+
972
+ context "when unsetting a single field" do
973
+
974
+ let!(:depeche_mode) do
975
+ Band.create(name: "Depeche Mode", years: 10)
976
+ end
977
+
978
+ let!(:new_order) do
979
+ Band.create(name: "New Order", years: 10)
980
+ end
981
+
982
+ let(:criteria) do
983
+ Band.all
984
+ end
985
+
986
+ let(:context) do
987
+ Mongoid::Contextual::Mongo.new(criteria)
988
+ end
989
+
990
+ context "when the field is not aliased" do
991
+
992
+ before do
993
+ context.unset(:name)
994
+ end
995
+
996
+ it "unsets the first existing field" do
997
+ expect(depeche_mode.reload.name).to be_nil
998
+ end
999
+
1000
+ it "unsets the last existing field" do
1001
+ expect(new_order.reload.name).to be_nil
1002
+ end
1003
+ end
1004
+
1005
+ context "when the field is aliased" do
1006
+
1007
+ before do
1008
+ context.unset(:years)
1009
+ end
1010
+
1011
+ it "unsets the first existing field" do
1012
+ expect(depeche_mode.reload.years).to be_nil
1013
+ end
1014
+
1015
+ it "unsets the last existing field" do
1016
+ expect(new_order.reload.years).to be_nil
1017
+ end
1018
+ end
1019
+ end
1020
+
1021
+ context "when unsetting multiple fields" do
1022
+
1023
+ let!(:new_order) do
1024
+ Band.create(name: "New Order", genres: [ "electro", "dub" ], years: 10)
1025
+ end
1026
+
1027
+ let(:criteria) do
1028
+ Band.all
1029
+ end
1030
+
1031
+ let(:context) do
1032
+ Mongoid::Contextual::Mongo.new(criteria)
1033
+ end
1034
+
1035
+ context "when the field is not aliased" do
1036
+
1037
+ before do
1038
+ context.unset(:name, :genres)
1039
+ end
1040
+
1041
+ it "unsets name field" do
1042
+ expect(new_order.reload.name).to be_nil
1043
+ end
1044
+
1045
+ it "unsets genres field" do
1046
+ expect(new_order.reload.genres).to be_nil
1047
+ end
1048
+ end
1049
+
1050
+ context "when the field is aliased" do
1051
+
1052
+ before do
1053
+ context.unset(:name, :years)
1054
+ end
1055
+
1056
+ it "unsets the unaliased field" do
1057
+ expect(new_order.reload.name).to be_nil
1058
+ end
1059
+
1060
+ it "unsets the aliased field" do
1061
+ expect(new_order.reload.years).to be_nil
1062
+ end
1063
+ end
1064
+ end
1065
+ end
1066
+ end
1067
+ end