mongoid 6.0.0.beta → 6.0.0.rc0

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.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/lib/mongoid/attributes.rb +2 -10
  5. data/lib/mongoid/contextual/mongo.rb +1 -2
  6. data/lib/mongoid/copyable.rb +14 -3
  7. data/lib/mongoid/criteria/queryable/optional.rb +6 -2
  8. data/lib/mongoid/criteria/queryable/options.rb +7 -6
  9. data/lib/mongoid/criteria/queryable/selector.rb +2 -2
  10. data/lib/mongoid/criteria/queryable/smash.rb +5 -4
  11. data/lib/mongoid/matchable.rb +7 -1
  12. data/lib/mongoid/matchable/elem_match.rb +28 -0
  13. data/lib/mongoid/persistable/settable.rb +4 -2
  14. data/lib/mongoid/relations/eager/has_and_belongs_to_many.rb +1 -1
  15. data/lib/mongoid/relations/proxy.rb +1 -1
  16. data/lib/mongoid/relations/touchable.rb +6 -3
  17. data/lib/mongoid/version.rb +1 -1
  18. data/spec/app/models/person.rb +0 -2
  19. data/spec/mongoid/clients/factory_spec.rb +28 -0
  20. data/spec/mongoid/clients/options_spec.rb +4 -0
  21. data/spec/mongoid/clients_spec.rb +36 -0
  22. data/spec/mongoid/config_spec.rb +4 -0
  23. data/spec/mongoid/contextual/mongo_spec.rb +0 -5
  24. data/spec/mongoid/copyable_spec.rb +12 -1
  25. data/spec/mongoid/criteria_spec.rb +128 -4
  26. data/spec/mongoid/indexable_spec.rb +8 -0
  27. data/spec/mongoid/matchable/elem_match_spec.rb +86 -0
  28. data/spec/mongoid/matchable_spec.rb +120 -0
  29. data/spec/mongoid/persistable/creatable_spec.rb +5 -0
  30. data/spec/mongoid/persistable/savable_spec.rb +5 -0
  31. data/spec/mongoid/persistable/settable_spec.rb +29 -0
  32. data/spec/mongoid/persistence_context_spec.rb +5 -0
  33. data/spec/mongoid/relations/eager/has_and_belongs_to_many_spec.rb +16 -0
  34. data/spec/mongoid/relations/metadata_spec.rb +15 -0
  35. data/spec/mongoid/relations/proxy_spec.rb +24 -0
  36. metadata +5 -2
  37. metadata.gz.sig +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 57db37f3ffe123d41531a6c93d9ae52e37cb81d2
4
- data.tar.gz: bdd70c57cb017f481fa796c8a86dc34599c1a05c
3
+ metadata.gz: 27ea8a75a0efde500417b73f4c4d38ab25f60c0b
4
+ data.tar.gz: 998d18056b79086719e963a84be38ca6d3682c6d
5
5
  SHA512:
6
- metadata.gz: 7c6b4a55f532af6dfd8c2593bb8bd05244a5916235ed9512487b7d45952e37a06e9eb50f61958d4b63da826d9631ca6422e0febb638da45e122246ead03e1461
7
- data.tar.gz: 15cbe1acb92704217bb2f2f084d14c076dce7c6c7aa5afcd6955f724825f2e6e72a89c6e0c62170c4ba4d60d04866052ce041d37ee3ea4f1be8a4d15d38070b9
6
+ metadata.gz: 1b948f4ba63c9fa1d749a71d5bb33856894736c3173bf52781702102ea90bdcb0b5b9f0547cc90d906589f1b1d088eef39b3a7f829c3dd7522142cb9f9113c51
7
+ data.tar.gz: 1e6feb48bf957d9596f6b3f150c1054f393f2ee0562e0be41e5827bc0518e73bd2d86c1344379729eea9c9f2660b05014a197c5721d2b40733eba9891cc69b2e
Binary file
data.tar.gz.sig CHANGED
Binary file
@@ -245,19 +245,11 @@ module Mongoid
245
245
  private
246
246
 
247
247
  def selection_excluded?(name, selection, field)
248
- if field && field.localized?
249
- selection["#{name}.#{::I18n.locale}"] == 0
250
- else
251
- selection[name] == 0
252
- end
248
+ selection[name] == 0
253
249
  end
254
250
 
255
251
  def selection_included?(name, selection, field)
256
- if field && field.localized?
257
- selection.key?("#{name}.#{::I18n.locale}")
258
- else
259
- selection.key?(name) || selection.keys.collect { |k| k.partition('.').first }.include?(name)
260
- end
252
+ selection.key?(name) || selection.keys.collect { |k| k.partition('.').first }.include?(name)
261
253
  end
262
254
 
263
255
  # Does the string contain dot syntax for accessing hashes?
@@ -305,8 +305,7 @@ module Mongoid
305
305
  if block_given?
306
306
  super(&block)
307
307
  else
308
- field = field.to_sym
309
- criteria.only(field).map(&field.to_proc)
308
+ criteria.pluck(field)
310
309
  end
311
310
  end
312
311
 
@@ -52,7 +52,7 @@ module Mongoid
52
52
  # @since 3.0.22
53
53
  def clone_document
54
54
  attrs = as_document.__deep_copy__
55
- process_localized_attributes(attrs)
55
+ process_localized_attributes(self, attrs)
56
56
  attrs
57
57
  end
58
58
 
@@ -67,12 +67,23 @@ module Mongoid
67
67
  # @param [ Hash ] attrs The attributes.
68
68
  #
69
69
  # @since 3.0.20
70
- def process_localized_attributes(attrs)
71
- localized_fields.keys.each do |name|
70
+ def process_localized_attributes(klass, attrs)
71
+ klass.localized_fields.keys.each do |name|
72
72
  if value = attrs.delete(name)
73
73
  attrs["#{name}_translations"] = value
74
74
  end
75
75
  end
76
+ klass.embedded_relations.each do |_, metadata|
77
+ next unless attrs.present? && attrs[metadata.key].present?
78
+
79
+ if metadata.macro == :embeds_many
80
+ attrs[metadata.key].each do |attr|
81
+ process_localized_attributes(metadata.klass, attr)
82
+ end
83
+ else
84
+ process_localized_attributes(metadata.klass, attrs[metadata.key])
85
+ end
86
+ end
76
87
  end
77
88
  end
78
89
  end
@@ -147,7 +147,9 @@ module Mongoid
147
147
  args = args.flatten
148
148
  option(*args) do |options|
149
149
  options.store(
150
- :fields, args.inject(options[:fields] || {}){ |sub, field| sub.tap { sub[field] = 1 }}
150
+ :fields,
151
+ args.inject(options[:fields] || {}){ |sub, field| sub.tap { sub[field] = 1 }},
152
+ false
151
153
  )
152
154
  end
153
155
  end
@@ -279,7 +281,9 @@ module Mongoid
279
281
  args = args.flatten
280
282
  option(*args) do |options|
281
283
  options.store(
282
- :fields, args.inject(options[:fields] || {}){ |sub, field| sub.tap { sub[field] = 0 }}
284
+ :fields,
285
+ args.inject(options[:fields] || {}){ |sub, field| sub.tap { sub[field] = 0 }},
286
+ false
283
287
  )
284
288
  end
285
289
  end
@@ -67,8 +67,8 @@ module Mongoid
67
67
  # @return [ Object ] The stored object.
68
68
  #
69
69
  # @since 1.0.0
70
- def store(key, value)
71
- super(key, evolve(value))
70
+ def store(key, value, localize = true)
71
+ super(key, evolve(value, localize))
72
72
  end
73
73
  alias :[]= :store
74
74
 
@@ -102,10 +102,10 @@ module Mongoid
102
102
  # @return [ Object ] The serialized object.
103
103
  #
104
104
  # @since 1.0.0
105
- def evolve(value)
105
+ def evolve(value, localize = true)
106
106
  case value
107
107
  when Hash
108
- evolve_hash(value)
108
+ evolve_hash(value, localize)
109
109
  else
110
110
  value
111
111
  end
@@ -123,10 +123,11 @@ module Mongoid
123
123
  # @return [ Object ] The serialized hash.
124
124
  #
125
125
  # @since 1.0.0
126
- def evolve_hash(value)
126
+ def evolve_hash(value, localize = true)
127
127
  value.inject({}) do |hash, (field, _value)|
128
128
  name, serializer = storage_pair(field)
129
- hash[normalized_key(name, serializer)] = _value
129
+ name = localized_key(name, serializer) if localize
130
+ hash[name] = _value
130
131
  hash
131
132
  end
132
133
  end
@@ -48,7 +48,7 @@ module Mongoid
48
48
  if multi_selection?(name)
49
49
  super(name, evolve_multi(value))
50
50
  else
51
- super(normalized_key(name, serializer), evolve(serializer, value))
51
+ super(localized_key(name, serializer), evolve(serializer, value))
52
52
  end
53
53
  end
54
54
  alias :[]= :store
@@ -87,7 +87,7 @@ module Mongoid
87
87
  Hash[val.map do |key, _value|
88
88
  _value = evolve_multi(_value) if multi_selection?(key)
89
89
  name, serializer = storage_pair(key)
90
- [ normalized_key(name, serializer), evolve(serializer, _value) ]
90
+ [ localized_key(name, serializer), evolve(serializer, _value) ]
91
91
  end]
92
92
  end.uniq
93
93
  end
@@ -60,13 +60,14 @@ module Mongoid
60
60
 
61
61
  private
62
62
 
63
- # Get the normalized value for the key. If localization is in play the
64
- # current locale will be appended to the key in MongoDB dot notation.
63
+ # Get the localized value for the key if needed. If the field uses
64
+ # localization the current locale will be appended to the key in
65
+ # MongoDB dot notation.
65
66
  #
66
67
  # @api private
67
68
  #
68
69
  # @example Get the normalized key name.
69
- # smash.normalized_key("field", serializer)
70
+ # smash.localized_key("field", serializer)
70
71
  #
71
72
  # @param [ String ] name The name of the field.
72
73
  # @param [ Object ] serializer The optional field serializer.
@@ -74,7 +75,7 @@ module Mongoid
74
75
  # @return [ String ] The normalized key.
75
76
  #
76
77
  # @since 1.0.0
77
- def normalized_key(name, serializer)
78
+ def localized_key(name, serializer)
78
79
  serializer && serializer.localized? ? "#{name}.#{::I18n.locale}" : name
79
80
  end
80
81
 
@@ -12,6 +12,7 @@ require "mongoid/matchable/ne"
12
12
  require "mongoid/matchable/nin"
13
13
  require "mongoid/matchable/or"
14
14
  require "mongoid/matchable/size"
15
+ require "mongoid/matchable/elem_match"
15
16
 
16
17
  module Mongoid
17
18
 
@@ -27,6 +28,7 @@ module Mongoid
27
28
  # @since 1.0.0
28
29
  MATCHERS = {
29
30
  "$all" => All,
31
+ "$elemMatch" => ElemMatch,
30
32
  "$and" => And,
31
33
  "$exists" => Exists,
32
34
  "$gt" => Gt,
@@ -149,7 +151,11 @@ module Mongoid
149
151
  end
150
152
  end
151
153
  else
152
- document.attributes[key_string]
154
+ if document.is_a?(Hash)
155
+ document[key_string]
156
+ else
157
+ document.attributes[key_string]
158
+ end
153
159
  end
154
160
  end
155
161
  end
@@ -0,0 +1,28 @@
1
+ # encoding: utf-8
2
+ module Mongoid
3
+ module Matchable
4
+
5
+ class ElemMatch < Default
6
+
7
+ # Return true if a given predicate matches a sub document entirely
8
+ #
9
+ # @example Do the values match?
10
+ # matcher.matches?({"$elemMatch" => {"a" => 1, "b" => 2}})
11
+ #
12
+ # @param [ Hash ] value The values to check.
13
+ #
14
+ # @return [ true, false ] If the values match.
15
+ def matches?(value)
16
+ if !@attribute.is_a?(Array) || !value.kind_of?(Hash) || !value["$elemMatch"].kind_of?(Hash)
17
+ return false
18
+ end
19
+
20
+ return @attribute.any? do |sub_document|
21
+ value["$elemMatch"].all? do |k, v|
22
+ Matchable.matcher(sub_document, k, v).matches?(v)
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -23,9 +23,11 @@ module Mongoid
23
23
  prepare_atomic_operation do |ops|
24
24
  process_atomic_operations(setters) do |field, value|
25
25
  process_attribute(field.to_s, value)
26
- ops[atomic_attribute_name(field)] = attributes[field]
26
+ unless relations.include?(field.to_s)
27
+ ops[atomic_attribute_name(field)] = attributes[field]
28
+ end
27
29
  end
28
- { "$set" => ops }
30
+ { "$set" => ops } unless ops.empty?
29
31
  end
30
32
  end
31
33
  end
@@ -17,7 +17,7 @@ module Mongoid
17
17
 
18
18
  @docs.each do |d|
19
19
  keys = d.send(group_by_key)
20
- docs = entries.values_at(*keys)
20
+ docs = entries.values_at(*keys).compact
21
21
  set_relation(d, docs)
22
22
  end
23
23
  end
@@ -12,7 +12,7 @@ module Mongoid
12
12
  # We undefine most methods to get them sent through to the target.
13
13
  instance_methods.each do |method|
14
14
  undef_method(method) unless
15
- method =~ /(^__|^send|^object_id|^respond_to|^tap|^public_send|extend_proxy|extend_proxies)/
15
+ method =~ /^(__.*|send|object_id|equal\?|respond_to\?|tap|public_send|extend_proxy|extend_proxies)$/
16
16
  end
17
17
 
18
18
  include Threaded::Lifecycle
@@ -53,7 +53,8 @@ module Mongoid
53
53
  def touchable(metadata)
54
54
  if metadata.touchable?
55
55
  name = metadata.name
56
- method_name = define_relation_touch_method(name)
56
+ field = metadata[:touch].is_a?(Symbol) ? metadata[:touch] : nil
57
+ method_name = define_relation_touch_method(name, field)
57
58
  after_save method_name
58
59
  after_destroy method_name
59
60
  after_touch method_name
@@ -70,19 +71,21 @@ module Mongoid
70
71
  #
71
72
  # @example Define the touch relation.
72
73
  # Model.define_relation_touch_method(:band)
74
+ # Model.define_relation_touch_method(:band, :band_updated_at)
73
75
  #
74
76
  # @param [ Symbol ] name The name of the relation.
77
+ # @param [ Symbol ] extra_field Additional timestamp field to update.
75
78
  #
76
79
  # @since 3.1.0
77
80
  #
78
81
  # @return [ Symbol ] The method name.
79
- def define_relation_touch_method(name)
82
+ def define_relation_touch_method(name, extra_field = nil)
80
83
  method_name = "touch_#{name}_after_create_or_destroy"
81
84
  class_eval <<-TOUCH
82
85
  def #{method_name}
83
86
  without_autobuild do
84
87
  relation = __send__(:#{name})
85
- relation.touch if relation
88
+ relation.touch #{":#{extra_field}" if extra_field} if relation
86
89
  end
87
90
  end
88
91
  TOUCH
@@ -1,4 +1,4 @@
1
1
  # encoding: utf-8
2
2
  module Mongoid
3
- VERSION = "6.0.0.beta"
3
+ VERSION = "6.0.0.rc0"
4
4
  end
@@ -44,8 +44,6 @@ class Person
44
44
  index name: 1
45
45
  index title: 1
46
46
 
47
- index({ ssn: 1 }, { unique: true })
48
-
49
47
  attr_reader :rescored
50
48
 
51
49
  embeds_many :favorites, order: :title.desc, inverse_of: :perp, validate: false
@@ -21,6 +21,10 @@ describe Mongoid::Clients::Factory do
21
21
  Mongoid::Config.send(:clients=, config)
22
22
  end
23
23
 
24
+ after do
25
+ client.close
26
+ end
27
+
24
28
  let(:client) do
25
29
  described_class.create(:secondary)
26
30
  end
@@ -51,6 +55,10 @@ describe Mongoid::Clients::Factory do
51
55
  Mongoid::Config.send(:clients=, config)
52
56
  end
53
57
 
58
+ after do
59
+ client.close
60
+ end
61
+
54
62
  let(:client) do
55
63
  described_class.create(:secondary)
56
64
  end
@@ -91,6 +99,10 @@ describe Mongoid::Clients::Factory do
91
99
  Mongoid::Config.send(:clients=, config)
92
100
  end
93
101
 
102
+ after do
103
+ client.close
104
+ end
105
+
94
106
  let(:client) do
95
107
  described_class.create(:secondary)
96
108
  end
@@ -125,6 +137,10 @@ describe Mongoid::Clients::Factory do
125
137
  Mongoid::Config.send(:clients=, config)
126
138
  end
127
139
 
140
+ after do
141
+ client.close
142
+ end
143
+
128
144
  let(:client) do
129
145
  described_class.create(:secondary)
130
146
  end
@@ -168,6 +184,10 @@ describe Mongoid::Clients::Factory do
168
184
  Mongoid::Config.send(:clients=, config)
169
185
  end
170
186
 
187
+ after do
188
+ client.close
189
+ end
190
+
171
191
  let(:client) do
172
192
  described_class.create
173
193
  end
@@ -213,6 +233,10 @@ describe Mongoid::Clients::Factory do
213
233
  Mongoid::Config.send(:clients=, config)
214
234
  end
215
235
 
236
+ after do
237
+ client.close
238
+ end
239
+
216
240
  let(:client) do
217
241
  described_class.default
218
242
  end
@@ -253,6 +277,10 @@ describe Mongoid::Clients::Factory do
253
277
  Mongoid::Config.send(:clients=, config)
254
278
  end
255
279
 
280
+ after do
281
+ client.close
282
+ end
283
+
256
284
  let(:client) do
257
285
  described_class.default
258
286
  end
@@ -143,6 +143,10 @@ describe Mongoid::Clients::Options do
143
143
  Mongoid::Config.send(:clients=, config)
144
144
  end
145
145
 
146
+ after do
147
+ persistence_context.client.close
148
+ end
149
+
146
150
  let(:persistence_context) do
147
151
  Band.with(client: :secondary) do |klass|
148
152
  klass.persistence_context
@@ -318,6 +318,10 @@ describe Mongoid::Clients do
318
318
 
319
319
  shared_examples_for "an overridden database name" do
320
320
 
321
+ after do
322
+ class_mongo_client.close
323
+ end
324
+
321
325
  context "when accessing from the instance" do
322
326
 
323
327
  it "returns the overridden value" do
@@ -380,6 +384,7 @@ describe Mongoid::Clients do
380
384
 
381
385
  after do
382
386
  Band.reset_storage_options!
387
+ class_mongo_client.close
383
388
  end
384
389
 
385
390
  it_behaves_like "an overridden database name"
@@ -411,6 +416,7 @@ describe Mongoid::Clients do
411
416
 
412
417
  after do
413
418
  Band.reset_storage_options!
419
+ class_mongo_client.close
414
420
  end
415
421
 
416
422
  it_behaves_like "an overridden database name"
@@ -471,6 +477,7 @@ describe Mongoid::Clients do
471
477
 
472
478
  after do
473
479
  Band.reset_storage_options!
480
+ class_mongo_client.close
474
481
  end
475
482
 
476
483
  it_behaves_like "an overridden database name"
@@ -502,6 +509,10 @@ describe Mongoid::Clients do
502
509
  Mongoid.clients[:default][:database] = database_id
503
510
  end
504
511
 
512
+ after do
513
+ mongo_client.close
514
+ end
515
+
505
516
  let!(:band) do
506
517
  Band.new
507
518
  end
@@ -728,10 +739,15 @@ describe Mongoid::Clients do
728
739
  describe ".create" do
729
740
 
730
741
  before do
742
+ Person.index({ ssn: 1 }, { unique: true })
731
743
  Person.create_indexes
732
744
  Person.create(ssn: "432-97-1111")
733
745
  end
734
746
 
747
+ after do
748
+ Person.collection.drop
749
+ end
750
+
735
751
  context "when no error occurs" do
736
752
 
737
753
  it "inserts the document" do
@@ -755,6 +771,10 @@ describe Mongoid::Clients do
755
771
  Person.create!(ssn: "432-97-1112")
756
772
  end
757
773
 
774
+ after do
775
+ Person.collection.drop
776
+ end
777
+
758
778
  context "when no error occurs" do
759
779
 
760
780
  it "inserts the document" do
@@ -765,9 +785,14 @@ describe Mongoid::Clients do
765
785
  context "when a mongodb error occurs" do
766
786
 
767
787
  before do
788
+ Person.index({ ssn: 1 }, { unique: true })
768
789
  Person.create_indexes
769
790
  end
770
791
 
792
+ after do
793
+ Person.collection.drop
794
+ end
795
+
771
796
  it "bubbles up to the caller" do
772
797
  expect {
773
798
  Person.create!(ssn: "432-97-1112")
@@ -798,9 +823,14 @@ describe Mongoid::Clients do
798
823
  end
799
824
 
800
825
  before do
826
+ Person.index({ ssn: 1 }, { unique: true })
801
827
  Person.create_indexes
802
828
  end
803
829
 
830
+ after do
831
+ Person.collection.drop
832
+ end
833
+
804
834
  it "bubbles up to the caller" do
805
835
  expect {
806
836
  person.save
@@ -822,9 +852,14 @@ describe Mongoid::Clients do
822
852
  end
823
853
 
824
854
  before do
855
+ Person.index({ ssn: 1 }, { unique: true })
825
856
  Person.create_indexes
826
857
  end
827
858
 
859
+ after do
860
+ Person.collection.drop
861
+ end
862
+
828
863
  it "bubbles up to the caller" do
829
864
  expect {
830
865
  person.save!
@@ -866,6 +901,7 @@ describe Mongoid::Clients do
866
901
 
867
902
  after do
868
903
  Band.delete_all
904
+ Band.mongo_client.close
869
905
  Mongoid.override_database(nil)
870
906
  end
871
907
 
@@ -163,6 +163,10 @@ describe Mongoid::Config do
163
163
  described_class.load!(file, :test)
164
164
  end
165
165
 
166
+ after do
167
+ client.close
168
+ end
169
+
166
170
  it "clears the previous clients" do
167
171
  expect(Mongoid::Clients.clients[:test]).to be_nil
168
172
  end
@@ -1089,11 +1089,6 @@ describe Mongoid::Contextual::Mongo do
1089
1089
 
1090
1090
  context "when passed the symbol field name" do
1091
1091
 
1092
- it "limits query to that field" do
1093
- expect(criteria).to receive(:only).with(:name).and_call_original
1094
- context.map(:name)
1095
- end
1096
-
1097
1092
  it "performs mapping" do
1098
1093
  expect(context.map(:name)).to eq ["Depeche Mode", "New Order"]
1099
1094
  end
@@ -20,7 +20,7 @@ describe Mongoid::Copyable do
20
20
  end
21
21
 
22
22
  let!(:address) do
23
- person.addresses.build(street: "Bond")
23
+ person.addresses.build(street: "Bond", name: "Bond")
24
24
  end
25
25
 
26
26
  let!(:name) do
@@ -97,6 +97,7 @@ describe Mongoid::Copyable do
97
97
  I18n.enforce_available_locales = false
98
98
  I18n.locale = 'pt_BR'
99
99
  person.desc = "descrição"
100
+ person.addresses.first.name = "descrição"
100
101
  person.save
101
102
  end
102
103
 
@@ -126,6 +127,16 @@ describe Mongoid::Copyable do
126
127
  I18n.locale = :fr
127
128
  expect(copy.desc).to be_nil
128
129
  end
130
+
131
+ it 'sets embedded translations' do
132
+ I18n.locale = 'pt_BR'
133
+ expect(copy.addresses.first.name).to eq("descrição")
134
+ end
135
+
136
+ it 'sets embedded english version' do
137
+ I18n.locale = :en
138
+ expect(copy.addresses.first.name).to eq("Bond")
139
+ end
129
140
  end
130
141
 
131
142
  context "when cloning a loaded document" do
@@ -2802,6 +2802,93 @@ describe Mongoid::Criteria do
2802
2802
  end
2803
2803
  end
2804
2804
  end
2805
+
2806
+ context 'when the field is localized' do
2807
+
2808
+ before do
2809
+ I18n.locale = :en
2810
+ d = Dictionary.create(description: 'english-text')
2811
+ I18n.locale = :de
2812
+ d.description = 'deutsch-text'
2813
+ d.save
2814
+ end
2815
+
2816
+ after do
2817
+ I18n.locale = :en
2818
+ end
2819
+
2820
+ context 'when entire field is included' do
2821
+
2822
+ let(:dictionary) do
2823
+ Dictionary.only(:description).first
2824
+
2825
+ end
2826
+
2827
+ it 'loads all translations' do
2828
+ expect(dictionary.description_translations.keys).to include('de', 'en')
2829
+ end
2830
+
2831
+ it 'returns the field value for the current locale' do
2832
+ I18n.locale = :en
2833
+ expect(dictionary.description).to eq('english-text')
2834
+ I18n.locale = :de
2835
+ expect(dictionary.description).to eq('deutsch-text')
2836
+ end
2837
+ end
2838
+
2839
+ context 'when a specific locale is included' do
2840
+
2841
+ let(:dictionary) do
2842
+ Dictionary.only(:'description.de').first
2843
+ end
2844
+
2845
+ it 'loads translations only for the included locale' do
2846
+ expect(dictionary.description_translations.keys).to include('de')
2847
+ expect(dictionary.description_translations.keys).to_not include('en')
2848
+ end
2849
+
2850
+ it 'returns the field value for the included locale' do
2851
+ I18n.locale = :en
2852
+ expect(dictionary.description).to be_nil
2853
+ I18n.locale = :de
2854
+ expect(dictionary.description).to eq('deutsch-text')
2855
+ end
2856
+ end
2857
+
2858
+ context 'when entire field is excluded' do
2859
+
2860
+ let(:dictionary) do
2861
+ Dictionary.without(:description).first
2862
+ end
2863
+
2864
+ it 'does not load all translations' do
2865
+ expect(dictionary.description_translations.keys).to_not include('de', 'en')
2866
+ end
2867
+
2868
+ it 'raises an ActiveModel::MissingAttributeError when attempting to access the field' do
2869
+ expect{dictionary.description}.to raise_error ActiveModel::MissingAttributeError
2870
+ end
2871
+ end
2872
+
2873
+ context 'when a specific locale is excluded' do
2874
+
2875
+ let(:dictionary) do
2876
+ Dictionary.without(:'description.de').first
2877
+ end
2878
+
2879
+ it 'does not load excluded translations' do
2880
+ expect(dictionary.description_translations.keys).to_not include('de')
2881
+ expect(dictionary.description_translations.keys).to include('en')
2882
+ end
2883
+
2884
+ it 'returns nil for excluded translations' do
2885
+ I18n.locale = :en
2886
+ expect(dictionary.description).to eq('english-text')
2887
+ I18n.locale = :de
2888
+ expect(dictionary.description).to be_nil
2889
+ end
2890
+ end
2891
+ end
2805
2892
  end
2806
2893
 
2807
2894
  [ :or, :any_of ].each do |method|
@@ -2889,7 +2976,7 @@ describe Mongoid::Criteria do
2889
2976
  end
2890
2977
 
2891
2978
  it "returns the values" do
2892
- expect(plucked).to eq([ "Depeche Mode", "Tool", "Photek" ])
2979
+ expect(plucked).to contain_exactly("Depeche Mode", "Tool", "Photek")
2893
2980
  end
2894
2981
 
2895
2982
  context "when subsequently executing the criteria without a pluck" do
@@ -2954,14 +3041,14 @@ describe Mongoid::Criteria do
2954
3041
  end
2955
3042
  end
2956
3043
 
2957
- context "when plucking mult-fields" do
3044
+ context "when plucking multi-fields" do
2958
3045
 
2959
3046
  let(:plucked) do
2960
3047
  Band.where(:name.exists => true).pluck(:name, :likes)
2961
3048
  end
2962
3049
 
2963
3050
  it "returns the values" do
2964
- expect(plucked).to eq([ ["Depeche Mode", 3], ["Tool", 3], ["Photek", 1] ])
3051
+ expect(plucked).to contain_exactly(["Depeche Mode", 3], ["Tool", 3], ["Photek", 1])
2965
3052
  end
2966
3053
  end
2967
3054
 
@@ -2972,7 +3059,7 @@ describe Mongoid::Criteria do
2972
3059
  end
2973
3060
 
2974
3061
  it "returns the duplicates" do
2975
- expect(plucked).to eq([ 3, 3, 1 ])
3062
+ expect(plucked).to contain_exactly(3, 3, 1)
2976
3063
  end
2977
3064
  end
2978
3065
  end
@@ -3034,6 +3121,43 @@ describe Mongoid::Criteria do
3034
3121
  end
3035
3122
  end
3036
3123
  end
3124
+
3125
+ context 'when plucking a localized field' do
3126
+
3127
+ before do
3128
+ I18n.locale = :en
3129
+ d = Dictionary.create(description: 'english-text')
3130
+ I18n.locale = :de
3131
+ d.description = 'deutsch-text'
3132
+ d.save
3133
+ end
3134
+
3135
+ after do
3136
+ I18n.locale = :en
3137
+ end
3138
+
3139
+ context 'when plucking the entire field' do
3140
+
3141
+ let(:plucked) do
3142
+ Dictionary.all.pluck(:description)
3143
+ end
3144
+
3145
+ it 'returns all translations' do
3146
+ expect(plucked.first).to eq({'en' => 'english-text', 'de' => 'deutsch-text'})
3147
+ end
3148
+ end
3149
+
3150
+ context 'when plucking a specific locale' do
3151
+
3152
+ let(:plucked) do
3153
+ Dictionary.all.pluck(:'description.de')
3154
+ end
3155
+
3156
+ it 'returns the specific translations' do
3157
+ expect(plucked.first).to eq({'de' => 'deutsch-text'})
3158
+ end
3159
+ end
3160
+ end
3037
3161
  end
3038
3162
 
3039
3163
  describe "#respond_to?" do
@@ -2,6 +2,10 @@ require "spec_helper"
2
2
 
3
3
  describe Mongoid::Indexable do
4
4
 
5
+ after do
6
+ Person.collection.drop
7
+ end
8
+
5
9
  describe ".included" do
6
10
 
7
11
  let(:klass) do
@@ -156,6 +160,10 @@ describe Mongoid::Indexable do
156
160
  end
157
161
  end
158
162
 
163
+ after do
164
+ klass.collection.drop
165
+ end
166
+
159
167
  context "when indexing a field that is aliased" do
160
168
 
161
169
  before do
@@ -0,0 +1,86 @@
1
+ require "spec_helper"
2
+
3
+ describe Mongoid::Matchable::ElemMatch do
4
+
5
+ let(:attribute) {[{"a" => 1, "b" => 2}, {"a" => 2, "b" => 2}, {"a" => 3, "b" => 1}]}
6
+ let(:matcher) do
7
+ described_class.new(attribute)
8
+ end
9
+
10
+ describe "#matches?" do
11
+
12
+ context "when the attribute is not an array" do
13
+
14
+ let(:attribute) {"string"}
15
+
16
+ it "returns false" do
17
+ expect(matcher.matches?("$elemMatch" => {"a" => 1})).to be false
18
+ end
19
+ end
20
+
21
+ context "when the $elemMatch is not a hash" do
22
+
23
+ let(:attribute) {"string"}
24
+
25
+ it "returns false" do
26
+ expect(matcher.matches?("$elemMatch" => [])).to be false
27
+ end
28
+ end
29
+
30
+ context "when there is a sub document that matches the criteria" do
31
+
32
+ it "returns true" do
33
+ expect(matcher.matches?("$elemMatch" => {"a" => 1})).to be true
34
+ end
35
+
36
+ context "when evaluating multiple fields of the subdocument" do
37
+
38
+ it "returns true" do
39
+ expect(matcher.matches?("$elemMatch" => {"a" => 1, "b" => 2})).to be true
40
+ end
41
+
42
+ context "when the $elemMatch document keys are out of order" do
43
+
44
+ it "returns true" do
45
+ expect(matcher.matches?("$elemMatch" => {"b" => 2, "a" => 1})).to be true
46
+ end
47
+ end
48
+ end
49
+
50
+ context "when using other operators that match" do
51
+
52
+ it "returns true" do
53
+ expect(matcher.matches?("$elemMatch" => {"a" => {"$in" => [1]}, "b" => {"$gt" => 1}})).to be true
54
+ end
55
+ end
56
+ end
57
+
58
+ context "when there is not a sub document that matches the criteria" do
59
+
60
+ it "returns false" do
61
+ expect(matcher.matches?("$elemMatch" => {"a" => 10})).to be false
62
+ end
63
+
64
+ context "when evaluating multiple fields of the subdocument" do
65
+
66
+ it "returns false" do
67
+ expect(matcher.matches?("$elemMatch" => {"a" => 1, "b" => 3})).to be false
68
+ end
69
+ end
70
+
71
+ context "when using other operators that do not match" do
72
+
73
+ it "returns true" do
74
+ expect(matcher.matches?("$elemMatch" => {"a" => {"$in" => [1]}, "b" => {"$gt" => 10}})).to be false
75
+ end
76
+ end
77
+ end
78
+
79
+ context "when using a criteria that matches partially but not a single sub document" do
80
+
81
+ it "returns false" do
82
+ expect(matcher.matches?("$elemMatch" => {"a" => 3, "b" => 2})).to be false
83
+ end
84
+ end
85
+ end
86
+ end
@@ -112,6 +112,126 @@ describe Mongoid::Matchable do
112
112
  end
113
113
  end
114
114
 
115
+ context "when using $elemMatch" do
116
+
117
+ context "one predicate" do
118
+
119
+ let(:selector) do
120
+ {
121
+ "occupants" => {"$elemMatch" => {"name" => "Tim"}}
122
+ }
123
+ end
124
+
125
+ context "and there is a matching sub document" do
126
+
127
+ it "returns true" do
128
+ expect(document.locations.first.matches?(selector)).to be true
129
+ end
130
+
131
+ context "using $in" do
132
+ let(:selector) do
133
+ {
134
+ "occupants" => {"$elemMatch" => {"name" => {"$in" => ["Tim", "Alice"]}}}
135
+ }
136
+ end
137
+
138
+ it "returns true" do
139
+ expect(document.locations.first.matches?(selector)).to be true
140
+ end
141
+ end
142
+ end
143
+
144
+ context "and there is not a matching sub document" do
145
+
146
+ let(:selector) do
147
+ {
148
+ "occupants" => {"$elemMatch" => {"name" => "Lyle"}}
149
+ }
150
+ end
151
+
152
+ it "returns false" do
153
+ expect(document.locations.first.matches?(selector)).to be false
154
+ end
155
+
156
+ context "using $in" do
157
+ let(:selector) do
158
+ {
159
+ "occupants" => {"$elemMatch" => {"name" => {"$in" => ["Lyfe", "Alice"]}}}
160
+ }
161
+ end
162
+
163
+ it "returns false" do
164
+ expect(document.locations.first.matches?(selector)).to be false
165
+ end
166
+ end
167
+ end
168
+ end
169
+
170
+ context "multiple predicates" do
171
+
172
+ before do
173
+ document.locations = [
174
+ Location.new(
175
+ name: 'No.1',
176
+ info: { 'door' => 'Red'},
177
+ occupants: [{'name' => 'Tim', 'eye_color' => 'brown'}, {'name' => 'Alice', 'eye_color' => 'blue'}]
178
+ )
179
+ ]
180
+ end
181
+
182
+ let(:selector) do
183
+ {
184
+ "occupants" => {"$elemMatch" => {"name" => "Tim", "eye_color" => "brown"}}
185
+ }
186
+ end
187
+
188
+ context "and there is a matching sub document" do
189
+
190
+ it "returns true" do
191
+ expect(document.locations.first.matches?(selector)).to be true
192
+ end
193
+
194
+ context "using $in and $ne in the $elemMatch to include the element" do
195
+
196
+ let(:selector) do
197
+ {
198
+ "occupants" => {"$elemMatch" => {"name" => {"$in" => ["Tim"]}, "eye_color" => {"$ne" => "blue"}}}
199
+ }
200
+ end
201
+
202
+ it "returns true" do
203
+ expect(document.locations.first.matches?(selector)).to be true
204
+ end
205
+ end
206
+ end
207
+
208
+ context "and there is not a matching sub document" do
209
+
210
+ let(:selector) do
211
+ {
212
+ "occupants" => {"$elemMatch" => {"name" => "Tim", "eye_color" => "blue"}}
213
+ }
214
+ end
215
+
216
+ it "returns false" do
217
+ expect(document.locations.first.matches?(selector)).to be false
218
+ end
219
+
220
+ context "using $ne in the $elemMatch to exclude the element" do
221
+
222
+ let(:selector) do
223
+ {
224
+ "occupants" => {"$elemMatch" => {"name" => {"$in" => ["Tim"]}, "eye_color" => {"$ne" => "brown"}}}
225
+ }
226
+ end
227
+
228
+ it "returns false" do
229
+ expect(document.locations.first.matches?(selector)).to be false
230
+ end
231
+ end
232
+ end
233
+ end
234
+ end
115
235
  end
116
236
 
117
237
  context "when performing simple matching" do
@@ -523,9 +523,14 @@ describe Mongoid::Persistable::Creatable do
523
523
  context "when a unique index exists" do
524
524
 
525
525
  before do
526
+ Person.index({ ssn: 1 }, { unique: true })
526
527
  Person.create_indexes
527
528
  end
528
529
 
530
+ after do
531
+ Person.collection.drop
532
+ end
533
+
529
534
  it "raises an error" do
530
535
  expect {
531
536
  4.times { Person.create!(ssn: "555-55-1029") }
@@ -370,10 +370,15 @@ describe Mongoid::Persistable::Savable do
370
370
  end
371
371
 
372
372
  before do
373
+ Person.index({ ssn: 1 }, { unique: true })
373
374
  Person.create_indexes
374
375
  Person.create!(ssn: "555-55-9999")
375
376
  end
376
377
 
378
+ after do
379
+ Person.collection.drop
380
+ end
381
+
377
382
  it "raises an OperationFailure" do
378
383
  expect { person.save! }.to raise_error(Mongo::Error::OperationFailure)
379
384
  end
@@ -134,6 +134,35 @@ describe Mongoid::Persistable::Settable do
134
134
 
135
135
  it_behaves_like "a settable embedded document"
136
136
  end
137
+
138
+ context 'when the field is a relation' do
139
+
140
+ let(:person) do
141
+ Person.create
142
+ end
143
+
144
+ let(:pet) do
145
+ Animal.new(name: "somepet")
146
+ end
147
+
148
+ let(:home_phone) do
149
+ Phone.new(number: "555-555-5555")
150
+ end
151
+
152
+ let(:office_phone) do
153
+ Phone.new(number: "666-666-6666")
154
+ end
155
+
156
+ it "should persist changes of embeds_one field" do
157
+ person.set(pet: pet)
158
+ expect(person.reload.pet).to eq(pet)
159
+ end
160
+
161
+ it "should persist changes of embeds_many fields" do
162
+ person.set({ phone_numbers: [home_phone, office_phone].map { |p| p.as_document} })
163
+ expect(person.reload.phone_numbers).to eq([home_phone, office_phone])
164
+ end
165
+ end
137
166
  end
138
167
  end
139
168
 
@@ -514,6 +514,10 @@ describe Mongoid::PersistenceContext do
514
514
  { client: :alternative }
515
515
  end
516
516
 
517
+ after do
518
+ persistence_context.client.close
519
+ end
520
+
517
521
  it 'uses the client option' do
518
522
  expect(persistence_context.client).to eq(Mongoid::Clients.with_name(:alternative))
519
523
  end
@@ -525,6 +529,7 @@ describe Mongoid::PersistenceContext do
525
529
  end
526
530
 
527
531
  after do
532
+ persistence_context.client.close
528
533
  Mongoid::Threaded.client_override = nil
529
534
  end
530
535
 
@@ -113,5 +113,21 @@ describe Mongoid::Relations::Eager::HasAndBelongsToMany do
113
113
  end
114
114
  end
115
115
  end
116
+
117
+ context "when some related documents no longer exist" do
118
+ before do
119
+ # Deleting the first one to meet Builders::Referenced::ManyToMany#query?
120
+ House.collection.find(_id: Person.first.house_ids.first).delete_one
121
+ end
122
+
123
+ it "does not accidentally trigger an extra query" do
124
+ expect_query(2) do
125
+ Person.asc(:_id).includes(:houses).each do |person|
126
+ expect(person.houses).to_not be_nil
127
+ expect(person.houses.length).to be(2)
128
+ end
129
+ end
130
+ end
131
+ end
116
132
  end
117
133
  end
@@ -1896,6 +1896,21 @@ describe Mongoid::Relations::Metadata do
1896
1896
  end
1897
1897
  end
1898
1898
 
1899
+ context "when touch specifies additional field" do
1900
+
1901
+ let(:metadata) do
1902
+ described_class.new(
1903
+ name: :versions,
1904
+ relation: Mongoid::Relations::Referenced::In,
1905
+ touch: :changed_at
1906
+ )
1907
+ end
1908
+
1909
+ it "returns true" do
1910
+ expect(metadata).to be_touchable
1911
+ end
1912
+ end
1913
+
1899
1914
  context "when touch is nil" do
1900
1915
 
1901
1916
  let(:metadata) do
@@ -97,4 +97,28 @@ describe Mongoid::Relations::Proxy do
97
97
  end
98
98
  end
99
99
  end
100
+
101
+ describe "equality" do
102
+ let(:messages) do
103
+ Person.create.messages
104
+ end
105
+
106
+ it "is #equal? to itself" do
107
+ expect(messages.equal?(messages)).to eq(true)
108
+ end
109
+
110
+ it "is == to itself" do
111
+ expect(messages == messages).to eq(true)
112
+ end
113
+
114
+ it "is not #equal? to its target" do
115
+ expect(messages.equal?(messages.target)).to eq(false)
116
+ expect(messages.target.equal?(messages)).to eq(false)
117
+ end
118
+
119
+ it "is == to its target" do
120
+ expect(messages == messages.target).to eq(true)
121
+ expect(messages.target == messages).to eq(true)
122
+ end
123
+ end
100
124
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mongoid
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.0.0.beta
4
+ version: 6.0.0.rc0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Durran Jordan
@@ -30,7 +30,7 @@ cert_chain:
30
30
  ZIvvwAhgCjVW5QCi2I1noxXLmtZ3XDawWu8kaGtu8giHXcwL3941m8hvFZ/Wr9Yi
31
31
  JvcXJt2a4/JvwnIs2hmKuyfhZmB9HEE5wQQaCMnnC14=
32
32
  -----END CERTIFICATE-----
33
- date: 2016-07-13 00:00:00.000000000 Z
33
+ date: 2016-08-09 00:00:00.000000000 Z
34
34
  dependencies:
35
35
  - !ruby/object:Gem::Dependency
36
36
  name: activemodel
@@ -240,6 +240,7 @@ files:
240
240
  - lib/mongoid/matchable/all.rb
241
241
  - lib/mongoid/matchable/and.rb
242
242
  - lib/mongoid/matchable/default.rb
243
+ - lib/mongoid/matchable/elem_match.rb
243
244
  - lib/mongoid/matchable/exists.rb
244
245
  - lib/mongoid/matchable/gt.rb
245
246
  - lib/mongoid/matchable/gte.rb
@@ -721,6 +722,7 @@ files:
721
722
  - spec/mongoid/matchable/all_spec.rb
722
723
  - spec/mongoid/matchable/and_spec.rb
723
724
  - spec/mongoid/matchable/default_spec.rb
725
+ - spec/mongoid/matchable/elem_match_spec.rb
724
726
  - spec/mongoid/matchable/exists_spec.rb
725
727
  - spec/mongoid/matchable/gt_spec.rb
726
728
  - spec/mongoid/matchable/gte_spec.rb
@@ -1210,6 +1212,7 @@ test_files:
1210
1212
  - spec/mongoid/matchable/all_spec.rb
1211
1213
  - spec/mongoid/matchable/and_spec.rb
1212
1214
  - spec/mongoid/matchable/default_spec.rb
1215
+ - spec/mongoid/matchable/elem_match_spec.rb
1213
1216
  - spec/mongoid/matchable/exists_spec.rb
1214
1217
  - spec/mongoid/matchable/gt_spec.rb
1215
1218
  - spec/mongoid/matchable/gte_spec.rb
metadata.gz.sig CHANGED
Binary file