mongoid 6.0.0.beta → 6.0.0.rc0

Sign up to get free protection for your applications and to get access to all the features.
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