mongoid_monkey 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.
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