mongoid 2.4.0 → 2.4.12

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 (107) hide show
  1. data/CHANGELOG.md +329 -1
  2. data/Rakefile +0 -9
  3. data/lib/config/locales/bg.yml +1 -0
  4. data/lib/config/locales/de.yml +1 -0
  5. data/lib/config/locales/en-GB.yml +1 -0
  6. data/lib/config/locales/en.yml +1 -0
  7. data/lib/config/locales/es.yml +1 -0
  8. data/lib/config/locales/fr.yml +1 -0
  9. data/lib/config/locales/hi.yml +1 -0
  10. data/lib/config/locales/hu.yml +1 -0
  11. data/lib/config/locales/id.yml +1 -0
  12. data/lib/config/locales/it.yml +1 -0
  13. data/lib/config/locales/ja.yml +1 -0
  14. data/lib/config/locales/kr.yml +1 -0
  15. data/lib/config/locales/nl.yml +1 -0
  16. data/lib/config/locales/pl.yml +1 -0
  17. data/lib/config/locales/pt-BR.yml +1 -0
  18. data/lib/config/locales/pt.yml +1 -0
  19. data/lib/config/locales/ro.yml +1 -0
  20. data/lib/config/locales/ru.yml +1 -0
  21. data/lib/config/locales/sv.yml +1 -0
  22. data/lib/config/locales/vi.yml +1 -0
  23. data/lib/config/locales/zh-CN.yml +1 -0
  24. data/lib/mongoid/atomic/modifiers.rb +56 -15
  25. data/lib/mongoid/atomic.rb +14 -8
  26. data/lib/mongoid/attributes.rb +1 -1
  27. data/lib/mongoid/callbacks.rb +3 -3
  28. data/lib/mongoid/collection.rb +5 -1
  29. data/lib/mongoid/collections/retry.rb +2 -1
  30. data/lib/mongoid/components.rb +2 -1
  31. data/lib/mongoid/config/database.rb +4 -7
  32. data/lib/mongoid/config/environment.rb +4 -2
  33. data/lib/mongoid/config/replset_database.rb +3 -5
  34. data/lib/mongoid/config.rb +18 -0
  35. data/lib/mongoid/contexts/enumerable.rb +26 -1
  36. data/lib/mongoid/contexts/mongo.rb +62 -14
  37. data/lib/mongoid/copyable.rb +38 -8
  38. data/lib/mongoid/criteria.rb +1 -0
  39. data/lib/mongoid/criterion/exclusion.rb +3 -1
  40. data/lib/mongoid/criterion/inclusion.rb +2 -2
  41. data/lib/mongoid/criterion/optional.rb +2 -2
  42. data/lib/mongoid/criterion/selector.rb +25 -4
  43. data/lib/mongoid/cursor.rb +4 -2
  44. data/lib/mongoid/dirty.rb +61 -18
  45. data/lib/mongoid/document.rb +6 -2
  46. data/lib/mongoid/extensions/hash/criteria_helpers.rb +3 -1
  47. data/lib/mongoid/extensions/string/inflections.rb +1 -1
  48. data/lib/mongoid/factory.rb +3 -3
  49. data/lib/mongoid/fields/internal/array.rb +4 -9
  50. data/lib/mongoid/fields/internal/big_decimal.rb +7 -1
  51. data/lib/mongoid/fields/internal/date_time.rb +1 -1
  52. data/lib/mongoid/fields/internal/false_class.rb +10 -0
  53. data/lib/mongoid/fields/internal/foreign_keys/array.rb +22 -8
  54. data/lib/mongoid/fields/internal/set.rb +1 -1
  55. data/lib/mongoid/fields/internal/timekeeping.rb +2 -1
  56. data/lib/mongoid/fields/internal/true_class.rb +10 -0
  57. data/lib/mongoid/fields/mappings.rb +14 -13
  58. data/lib/mongoid/fields/serializable.rb +41 -3
  59. data/lib/mongoid/fields.rb +154 -26
  60. data/lib/mongoid/hierarchy.rb +6 -1
  61. data/lib/mongoid/identity_map.rb +15 -0
  62. data/lib/mongoid/logger.rb +7 -1
  63. data/lib/mongoid/matchers/all.rb +8 -1
  64. data/lib/mongoid/matchers/in.rb +8 -1
  65. data/lib/mongoid/matchers/strategies.rb +6 -2
  66. data/lib/mongoid/multi_database.rb +5 -0
  67. data/lib/mongoid/named_scope.rb +17 -1
  68. data/lib/mongoid/nested_attributes.rb +1 -1
  69. data/lib/mongoid/observer.rb +7 -0
  70. data/lib/mongoid/persistence/insertion.rb +2 -1
  71. data/lib/mongoid/persistence/operations/update.rb +0 -9
  72. data/lib/mongoid/persistence.rb +5 -5
  73. data/lib/mongoid/railtie.rb +22 -4
  74. data/lib/mongoid/relations/bindings/referenced/in.rb +31 -11
  75. data/lib/mongoid/relations/bindings/referenced/many.rb +8 -8
  76. data/lib/mongoid/relations/bindings/referenced/one.rb +8 -8
  77. data/lib/mongoid/relations/builders/nested_attributes/many.rb +3 -1
  78. data/lib/mongoid/relations/cascading/delete.rb +29 -2
  79. data/lib/mongoid/relations/cascading/destroy.rb +18 -1
  80. data/lib/mongoid/relations/cascading/nullify.rb +18 -1
  81. data/lib/mongoid/relations/cascading.rb +0 -1
  82. data/lib/mongoid/relations/embedded/atomic.rb +3 -3
  83. data/lib/mongoid/relations/embedded/many.rb +58 -26
  84. data/lib/mongoid/relations/embedded/one.rb +1 -1
  85. data/lib/mongoid/relations/metadata.rb +33 -1
  86. data/lib/mongoid/relations/one.rb +12 -0
  87. data/lib/mongoid/relations/proxy.rb +6 -3
  88. data/lib/mongoid/relations/referenced/many.rb +14 -9
  89. data/lib/mongoid/relations/referenced/many_to_many.rb +3 -4
  90. data/lib/mongoid/relations/targets/enumerable.rb +9 -3
  91. data/lib/mongoid/relations.rb +3 -1
  92. data/lib/mongoid/serialization.rb +18 -6
  93. data/lib/mongoid/state.rb +1 -1
  94. data/lib/mongoid/threaded.rb +26 -0
  95. data/lib/mongoid/timestamps/updated.rb +1 -1
  96. data/lib/mongoid/validations/format.rb +20 -0
  97. data/lib/mongoid/validations/length.rb +20 -0
  98. data/lib/mongoid/validations/localizable.rb +30 -0
  99. data/lib/mongoid/validations/presence.rb +6 -2
  100. data/lib/mongoid/validations/uniqueness.rb +162 -30
  101. data/lib/mongoid/validations.rb +54 -2
  102. data/lib/mongoid/version.rb +1 -1
  103. data/lib/mongoid/versioning.rb +12 -3
  104. data/lib/mongoid.rb +1 -0
  105. data/lib/rails/mongoid.rb +18 -12
  106. metadata +58 -41
  107. data/lib/mongoid/relations/cascading/strategy.rb +0 -26
@@ -16,11 +16,13 @@ module Mongoid #:nodoc:
16
16
  # @since 1.0.0
17
17
  def expand_complex_criteria
18
18
  {}.tap do |hsh|
19
- each_pair do |k,v|
19
+ each_pair do |k, v|
20
20
  if k.respond_to?(:key) && k.respond_to?(:to_mongo_query)
21
21
  hsh[k.key] ||= {}
22
+ v = v.expand_complex_criteria if v.is_a?(::Hash)
22
23
  hsh[k.key].merge!(k.to_mongo_query(v))
23
24
  else
25
+ v.map!{|e| e.is_a?(::Hash) ? e.expand_complex_criteria : e } if v.is_a?(::Array)
24
26
  hsh[k] = v
25
27
  end
26
28
  end
@@ -100,7 +100,7 @@ module Mongoid #:nodoc:
100
100
  #
101
101
  # @return [ String ] The string stripped of "=".
102
102
  def reader
103
- writer? ? gsub("=", "") : self
103
+ delete("=")
104
104
  end
105
105
 
106
106
  # Is this string a writer?
@@ -15,7 +15,7 @@ module Mongoid #:nodoc:
15
15
  # @param [ Hash ] optiosn The mass assignment scoping options.
16
16
  #
17
17
  # @return [ Document ] The instantiated document.
18
- def build(klass, attributes = {}, options = {})
18
+ def build(klass, attributes = nil, options = {})
19
19
  type = (attributes || {})["_type"]
20
20
  if type && klass._types.include?(type)
21
21
  type.constantize.new(attributes, options)
@@ -34,8 +34,8 @@ module Mongoid #:nodoc:
34
34
  # @param [ Hash ] attributes The document attributes.
35
35
  #
36
36
  # @return [ Document ] The instantiated document.
37
- def from_db(klass, attributes = {})
38
- type = attributes["_type"]
37
+ def from_db(klass, attributes = nil)
38
+ type = (attributes || {})["_type"]
39
39
  if type.blank?
40
40
  klass.instantiate(attributes)
41
41
  else
@@ -13,20 +13,15 @@ module Mongoid #:nodoc:
13
13
  # field.add_atomic_changes(doc, "key", {}, [], [])
14
14
  #
15
15
  # @param [ Document ] document The document to add to.
16
- # @param [ String ] key The name of the field.
16
+ # @param [ String ] name The name of the field.
17
+ # @param [ String ] key The atomic location of the field.
17
18
  # @param [ Hash ] mods The current modifications.
18
19
  # @param [ Array ] new The new elements to add.
19
20
  # @param [ Array ] old The old elements getting removed.
20
21
  #
21
22
  # @since 2.4.0
22
- def add_atomic_changes(document, key, mods, new, old)
23
- if new.any? && old.any?
24
- mods[key] = document.attributes[key]
25
- elsif new.any?
26
- document.atomic_array_pushes[key] = new
27
- elsif old.any?
28
- document.atomic_array_pulls[key] = old
29
- end
23
+ def add_atomic_changes(document, name, key, mods, new, old)
24
+ mods[key] = new
30
25
  end
31
26
 
32
27
  # Array fields are resizable.
@@ -19,7 +19,13 @@ module Mongoid #:nodoc:
19
19
  #
20
20
  # @since 2.1.0
21
21
  def deserialize(object)
22
- object ? ::BigDecimal.new(object) : object
22
+ return object unless object
23
+ begin
24
+ Float(object)
25
+ ::BigDecimal.new(object)
26
+ rescue ArgumentError, TypeError
27
+ object
28
+ end
23
29
  end
24
30
 
25
31
  # Special case to serialize the object.
@@ -20,7 +20,7 @@ module Mongoid #:nodoc:
20
20
  #
21
21
  # @since 2.1.0
22
22
  def deserialize(object)
23
- object.try(:to_datetime)
23
+ super(object).try(:to_datetime)
24
24
  end
25
25
  end
26
26
  end
@@ -0,0 +1,10 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Fields #:nodoc:
4
+ module Internal #:nodoc:
5
+ # Defines the behaviour for false values.
6
+ class FalseClass < Boolean
7
+ end
8
+ end
9
+ end
10
+ end
@@ -13,20 +13,34 @@ module Mongoid #:nodoc:
13
13
  # @example Add the atomic changes.
14
14
  # field.add_atomic_changes(doc, "key", {}, [], [])
15
15
  #
16
+ # @todo: Durran: Refactor, big time.
17
+ #
16
18
  # @param [ Document ] document The document to add to.
17
- # @param [ String ] key The name of the field.
19
+ # @param [ String ] name The name of the field.
20
+ # @param [ String ] key The atomic location of the field.
18
21
  # @param [ Hash ] mods The current modifications.
19
22
  # @param [ Array ] new The new elements to add.
20
23
  # @param [ Array ] old The old elements getting removed.
21
24
  #
22
25
  # @since 2.4.0
23
- def add_atomic_changes(document, key, mods, new, old)
24
- if new.any? && old.any?
25
- mods[key] = document.attributes[key]
26
- elsif new.any?
27
- document.atomic_array_add_to_sets[key] = new
28
- elsif old.any?
29
- document.atomic_array_pulls[key] = old
26
+ def add_atomic_changes(document, name, key, mods, new_elements, old_elements)
27
+ old = (old_elements || [])
28
+ new = (new_elements || [])
29
+ if new.length > old.length
30
+ if new.first(old.length) == old
31
+ document.atomic_array_add_to_sets[key] = new.drop(old.length)
32
+ else
33
+ mods[key] = document.attributes[name]
34
+ end
35
+ elsif new.length < old.length
36
+ pulls = old - new
37
+ if new == old - pulls
38
+ document.atomic_array_pulls[key] = pulls
39
+ else
40
+ mods[key] = document.attributes[name]
41
+ end
42
+ elsif new != old
43
+ mods[key] = document.attributes[name]
30
44
  end
31
45
  end
32
46
 
@@ -33,7 +33,7 @@ module Mongoid #:nodoc:
33
33
  #
34
34
  # @since 2.4.0
35
35
  def selection(object)
36
- return object if object.is_a?(::Hash)
36
+ return object unless object.is_a?(::Set)
37
37
  serialize(object)
38
38
  end
39
39
 
@@ -32,7 +32,7 @@ module Mongoid #:nodoc:
32
32
  return nil if object.blank?
33
33
  object = object.getlocal unless Mongoid::Config.use_utc?
34
34
  if Mongoid::Config.use_activesupport_time_zone?
35
- time_zone = Mongoid::Config.use_utc? ? 'UTC' : ::Time.zone
35
+ time_zone = Mongoid::Config.use_utc? ? "UTC" : ::Time.zone
36
36
  object = object.in_time_zone(time_zone)
37
37
  end
38
38
  object
@@ -90,6 +90,7 @@ module Mongoid #:nodoc:
90
90
  when ::String
91
91
  time.parse(value)
92
92
  when ::DateTime
93
+ return value if value.utc? && Mongoid.use_utc?
93
94
  time.local(value.year, value.month, value.day, value.hour, value.min, value.sec)
94
95
  when ::Date
95
96
  time.local(value.year, value.month, value.day)
@@ -0,0 +1,10 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Fields #:nodoc:
4
+ module Internal #:nodoc:
5
+ # Defines the behaviour for true values.
6
+ class TrueClass < Boolean
7
+ end
8
+ end
9
+ end
10
+ end
@@ -2,13 +2,14 @@
2
2
  module Mongoid #:nodoc
3
3
  module Fields #:nodoc:
4
4
 
5
+ module Internal #:nodoc:
6
+ end
7
+
5
8
  # This module maps classes used in field type definitions to the custom
6
9
  # definable field in Mongoid.
7
10
  module Mappings
8
11
  extend self
9
12
 
10
- MODULE = "Mongoid::Fields::Internal"
11
-
12
13
  # Get the custom field type for the provided class used in the field
13
14
  # definition.
14
15
  #
@@ -21,21 +22,21 @@ module Mongoid #:nodoc
21
22
  #
22
23
  # @since 2.1.0
23
24
  def for(klass, foreign_key = false)
24
- return Internal::Object unless klass
25
- if foreign_key
26
- return "#{MODULE}::ForeignKeys::#{klass.to_s.demodulize}".constantize
27
- end
28
- begin
29
- modules = "#{ MODULE }::|BSON::|ActiveSupport::"
30
- if match = klass.to_s.match(Regexp.new("^(#{ modules })?(\\w+)$"))
31
- "#{MODULE}::#{ match[2] }".constantize
25
+ if klass.nil?
26
+ Internal::Object
27
+ elsif foreign_key
28
+ Internal::ForeignKeys.const_get(klass.to_s)
29
+ else
30
+ modules = "BSON::|ActiveSupport::"
31
+ match = klass.to_s.match(Regexp.new("^(#{ modules })?(\\w+)$"))
32
+ if match and Internal.const_defined?(match[2])
33
+ Internal.const_get(match[2])
32
34
  else
33
- klass.to_s.constantize
35
+ klass
34
36
  end
35
- rescue NameError
36
- klass
37
37
  end
38
38
  end
39
+
39
40
  end
40
41
  end
41
42
  end
@@ -74,10 +74,10 @@ module Mongoid #:nodoc:
74
74
  #
75
75
  # @since 2.1.8
76
76
  def eval_default(doc)
77
- if default_val.respond_to?(:call)
78
- serialize(doc.instance_exec(&default_val))
77
+ if fields = Threaded.selection
78
+ evaluated_default(doc) if included?(fields)
79
79
  else
80
- serialize(default_val.duplicable? ? default_val.dup : default_val)
80
+ evaluated_default(doc)
81
81
  end
82
82
  end
83
83
 
@@ -188,6 +188,44 @@ module Mongoid #:nodoc:
188
188
  @versioned ||= (options[:versioned].nil? ? true : options[:versioned])
189
189
  end
190
190
 
191
+ private
192
+
193
+ # Is the field included in the fields that were returned from the
194
+ # database? We can apply the default if:
195
+ # 1. The field is included in an only limitation (field: 1)
196
+ # 2. The field is not excluded in a without limitation (field: 0)
197
+ #
198
+ # @example Is the field included?
199
+ # field.included?(fields)
200
+ #
201
+ # @param [ Hash ] fields The field limitations.
202
+ #
203
+ # @return [ true, false ] If the field was included.
204
+ #
205
+ # @since 2.4.4
206
+ def included?(fields)
207
+ (fields.values.first == 1 && fields[name.to_sym] == 1) ||
208
+ (fields.values.first == 0 && !fields.has_key?(name.to_sym))
209
+ end
210
+
211
+ # Get the evaluated default.
212
+ #
213
+ # @example Get the evaluated default.
214
+ # field.evaluated_default.
215
+ #
216
+ # @param [ Document ] doc The doc being applied to.
217
+ #
218
+ # @return [ Object ] The default value.
219
+ #
220
+ # @since 2.4.4
221
+ def evaluated_default(doc)
222
+ if default_val.respond_to?(:call)
223
+ serialize(doc.instance_exec(&default_val))
224
+ else
225
+ serialize(default_val.duplicable? ? default_val.dup : default_val)
226
+ end
227
+ end
228
+
191
229
  module ClassMethods #:nodoc:
192
230
 
193
231
  # Create the new field with a name and optional additional options.
@@ -8,6 +8,7 @@ require "mongoid/fields/internal/binary"
8
8
  require "mongoid/fields/internal/boolean"
9
9
  require "mongoid/fields/internal/date"
10
10
  require "mongoid/fields/internal/date_time"
11
+ require "mongoid/fields/internal/false_class"
11
12
  require "mongoid/fields/internal/float"
12
13
  require "mongoid/fields/internal/hash"
13
14
  require "mongoid/fields/internal/integer"
@@ -23,6 +24,7 @@ require "mongoid/fields/internal/string"
23
24
  require "mongoid/fields/internal/symbol"
24
25
  require "mongoid/fields/internal/time"
25
26
  require "mongoid/fields/internal/time_with_zone"
27
+ require "mongoid/fields/internal/true_class"
26
28
  require "mongoid/fields/internal/foreign_keys/array"
27
29
  require "mongoid/fields/internal/foreign_keys/object"
28
30
 
@@ -90,8 +92,10 @@ module Mongoid #:nodoc
90
92
  unless attributes.has_key?(name)
91
93
  if field = fields[name]
92
94
  default = field.eval_default(self)
93
- attribute_will_change!(name)
94
- attributes[name] = default
95
+ unless default.nil?
96
+ attribute_will_change!(name)
97
+ attributes[name] = default
98
+ end
95
99
  end
96
100
  end
97
101
  end
@@ -276,14 +280,15 @@ module Mongoid #:nodoc
276
280
  def add_field(name, options = {})
277
281
  aliased = options[:as]
278
282
  aliased_fields[aliased.to_s] = name if aliased
279
- meth = aliased || name
280
283
  type = options[:localize] ? Fields::Internal::Localized : options[:type]
281
284
  Mappings.for(type, options[:identity]).instantiate(name, options).tap do |field|
282
285
  fields[name] = field
283
286
  add_defaults(field)
284
- create_accessors(name, meth, options)
287
+ create_accessors(name, name, options)
288
+ create_accessors(name, aliased, options) if aliased
285
289
  process_options(field)
286
- create_dirty_methods(name)
290
+ create_dirty_methods(name, name)
291
+ create_dirty_methods(name, aliased) if aliased
287
292
  end
288
293
  end
289
294
 
@@ -337,43 +342,164 @@ module Mongoid #:nodoc
337
342
  # @param [ Symbol ] name The name of the field.
338
343
  # @param [ Symbol ] meth The name of the accessor.
339
344
  # @param [ Hash ] options The options.
345
+ #
346
+ # @since 2.0.0
340
347
  def create_accessors(name, meth, options = {})
341
348
  field = fields[name]
349
+
350
+ create_field_getter(name, meth, field)
351
+ create_field_setter(name, meth)
352
+ create_field_check(name, meth)
353
+
354
+ if options[:localize]
355
+ create_translations_getter(name, meth)
356
+ create_translations_setter(name, meth)
357
+ end
358
+ end
359
+
360
+ # Create the getter method for the provided field.
361
+ #
362
+ # @example Create the getter.
363
+ # Model.create_field_getter("name", "name", field)
364
+ #
365
+ # @param [ String ] name The name of the attribute.
366
+ # @param [ String ] meth The name of the method.
367
+ # @param [ Field ] field The field.
368
+ #
369
+ # @since 2.4.0
370
+ def create_field_getter(name, meth, field)
342
371
  generated_methods.module_eval do
343
- if field.cast_on_read?
344
- class_eval <<-EOM
345
- def #{meth}
346
- fields[#{name.inspect}].deserialize(read_attribute(#{name.inspect}))
372
+ if meth =~ /\W/
373
+ if field.cast_on_read?
374
+ define_method(meth) do
375
+ fields[name].deserialize(read_attribute(name))
347
376
  end
348
- EOM
349
- else
350
- class_eval <<-EOM
351
- def #{meth}
352
- read_attribute(#{name.inspect}).tap do |value|
377
+ else
378
+ define_method(meth) do
379
+ read_attribute(name).tap do |value|
353
380
  if value.is_a?(Array) || value.is_a?(Hash)
354
- attribute_will_change!(#{name.inspect})
381
+ attribute_will_change!(name)
355
382
  end
356
383
  end
357
384
  end
358
- EOM
385
+ end
386
+ else
387
+ if field.cast_on_read?
388
+ class_eval <<-EOM
389
+ def #{meth}
390
+ fields[#{name.inspect}].deserialize(read_attribute(#{name.inspect}))
391
+ end
392
+ EOM
393
+ else
394
+ class_eval <<-EOM
395
+ def #{meth}
396
+ read_attribute(#{name.inspect}).tap do |value|
397
+ if value.is_a?(Array) || value.is_a?(Hash)
398
+ attribute_will_change!(#{name.inspect})
399
+ end
400
+ end
401
+ end
402
+ EOM
403
+ end
359
404
  end
360
- class_eval <<-EOM
361
- def #{meth}=(value)
362
- write_attribute(#{name.inspect}, value)
405
+ end
406
+ end
407
+
408
+ # Create the setter method for the provided field.
409
+ #
410
+ # @example Create the setter.
411
+ # Model.create_field_setter("name", "name")
412
+ #
413
+ # @param [ String ] name The name of the attribute.
414
+ # @param [ String ] meth The name of the method.
415
+ #
416
+ # @since 2.4.0
417
+ def create_field_setter(name, meth)
418
+ generated_methods.module_eval do
419
+ if meth =~ /\W/
420
+ define_method(meth) do |value|
421
+ write_attribute(name, value)
363
422
  end
423
+ else
424
+ class_eval <<-EOM
425
+ def #{meth}=(value)
426
+ write_attribute(#{name.inspect}, value)
427
+ end
428
+ EOM
429
+ end
430
+ end
431
+ end
364
432
 
365
- def #{meth}?
366
- attr = read_attribute(#{name.inspect})
433
+ # Create the check method for the provided field.
434
+ #
435
+ # @example Create the check.
436
+ # Model.create_field_check("name", "name")
437
+ #
438
+ # @param [ String ] name The name of the attribute.
439
+ # @param [ String ] meth The name of the method.
440
+ #
441
+ # @since 2.4.0
442
+ def create_field_check(name, meth)
443
+ generated_methods.module_eval do
444
+ if meth =~ /\W/
445
+ define_method("#{meth}?") do
446
+ attr = read_attribute(name)
367
447
  attr == true || attr.present?
368
448
  end
369
- EOM
449
+ else
450
+ class_eval <<-EOM
451
+ def #{meth}?
452
+ attr = read_attribute(#{name.inspect})
453
+ attr == true || attr.present?
454
+ end
455
+ EOM
456
+ end
457
+ end
458
+ end
370
459
 
371
- if options[:localize]
460
+ # Create the translation getter method for the provided field.
461
+ #
462
+ # @example Create the translation getter.
463
+ # Model.create_translations_getter("name", "name")
464
+ #
465
+ # @param [ String ] name The name of the attribute.
466
+ # @param [ String ] meth The name of the method.
467
+ #
468
+ # @since 2.4.0
469
+ def create_translations_getter(name, meth)
470
+ generated_methods.module_eval do
471
+ if meth =~ /\W/
472
+ define_method("#{meth}_translations") do
473
+ attributes[name]
474
+ end
475
+ else
372
476
  class_eval <<-EOM
373
477
  def #{meth}_translations
374
478
  attributes[#{name.inspect}]
375
479
  end
480
+ EOM
481
+ end
482
+ end
483
+ end
376
484
 
485
+ # Create the translation setter method for the provided field.
486
+ #
487
+ # @example Create the translation setter.
488
+ # Model.create_translations_setter("name", "name")
489
+ #
490
+ # @param [ String ] name The name of the attribute.
491
+ # @param [ String ] meth The name of the method.
492
+ #
493
+ # @since 2.4.0
494
+ def create_translations_setter(name, meth)
495
+ generated_methods.module_eval do
496
+ if meth =~ /\W/
497
+ define_method("#{meth}_translations=") do |value|
498
+ attribute_will_change!(name)
499
+ attributes[name] = value
500
+ end
501
+ else
502
+ class_eval <<-EOM
377
503
  def #{meth}_translations=(value)
378
504
  attribute_will_change!(#{name.inspect})
379
505
  attributes[#{name.inspect}] = value
@@ -387,11 +513,13 @@ module Mongoid #:nodoc
387
513
  #
388
514
  # @example Include the fields.
389
515
  # Person.generated_methods
516
+ #
517
+ # @return [ Module ] The module of generated methods.
518
+ #
519
+ # @since 2.0.0
390
520
  def generated_methods
391
521
  @generated_methods ||= begin
392
- Module.new.tap do |mod|
393
- include mod
394
- end
522
+ Module.new.tap { |mod| include(mod) }
395
523
  end
396
524
  end
397
525
  end
@@ -69,7 +69,12 @@ module Mongoid #:nodoc
69
69
  # @since 2.0.0.beta.1
70
70
  def remove_child(child)
71
71
  name = child.metadata.name
72
- child.embedded_one? ? remove_ivar(name) : send(name).delete_one(child)
72
+ if child.embedded_one?
73
+ remove_ivar(name)
74
+ else
75
+ relation = send(name)
76
+ relation.send(:delete_one, child)
77
+ end
73
78
  end
74
79
 
75
80
  # After children are persisted we can call this to move all their changes
@@ -4,6 +4,21 @@ module Mongoid #:nodoc:
4
4
  # Defines behaviour for the identity map in Mongoid.
5
5
  class IdentityMap < Hash
6
6
 
7
+ # Clear the many documents.
8
+ #
9
+ # @example Clear the docs.
10
+ # identity_map.clear_many(Post, selector)
11
+ #
12
+ # @param [ Class ] klass The klass to clear.
13
+ # @param [ Hash ] selector The selector to identify it.
14
+ #
15
+ # @return [ Array<Document> ] The documents.
16
+ #
17
+ # @since 2.4.10
18
+ def clear_many(klass, selector)
19
+ (documents_for(klass)[selector] ||= []).clear
20
+ end
21
+
7
22
  # Get a document from the identity map by its id.
8
23
  #
9
24
  # @example Get the document from the map.
@@ -4,7 +4,13 @@ module Mongoid #:nodoc:
4
4
  # The Mongoid logger which wraps some other ruby compliant logger class.
5
5
  class Logger
6
6
 
7
- delegate :info, :debug, :error, :fatal, :unknown, :to => :logger, :allow_nil => true
7
+ delegate \
8
+ :info,
9
+ :debug,
10
+ :error,
11
+ :fatal,
12
+ :level,
13
+ :unknown, :to => :logger, :allow_nil => true
8
14
 
9
15
  # Emit a warning log message.
10
16
  #
@@ -14,7 +14,14 @@ module Mongoid #:nodoc:
14
14
  #
15
15
  # @return [ true, false ] If the values match.
16
16
  def matches?(value)
17
- @attribute == value.values.first
17
+ attribute_array = Array.wrap(@attribute)
18
+ value.values.first.all? do |e|
19
+ if e.is_a?(Regexp)
20
+ attribute_array.any? { |_attribute| _attribute =~ e }
21
+ else
22
+ attribute_array.include?(e)
23
+ end
24
+ end
18
25
  end
19
26
  end
20
27
  end
@@ -14,7 +14,14 @@ module Mongoid #:nodoc:
14
14
  #
15
15
  # @return [ true, false ] If a value exists.
16
16
  def matches?(value)
17
- Array.wrap(@attribute).any? { |e| value.values.first.include?(e) }
17
+ attribute_array = Array.wrap(@attribute)
18
+ value.values.first.any? do |e|
19
+ if e.is_a?(Regexp)
20
+ attribute_array.any? { |_attribute| _attribute =~ e }
21
+ else
22
+ attribute_array.include?(e)
23
+ end
24
+ end
18
25
  end
19
26
  end
20
27
  end
@@ -81,8 +81,12 @@ module Mongoid #:nodoc:
81
81
  # @since 2.2.1
82
82
  def extract_attribute(document, key)
83
83
  if (key_string = key.to_s) =~ /.+\..+/
84
- key_string.split('.').inject(document.attributes) do |attribs, key|
85
- attribs.try(:[], key)
84
+ key_string.split('.').inject(document.attributes) do |_attribs, _key|
85
+ if _attribs.is_a?(::Array)
86
+ _attribs.map { |doc| doc.try(:[], _key) }
87
+ else
88
+ _attribs.try(:[], _key)
89
+ end
86
90
  end
87
91
  else
88
92
  document.attributes[key_string]
@@ -26,6 +26,11 @@ module Mongoid #:nodoc:
26
26
  def set_database(name)
27
27
  @database = name.to_s
28
28
  end
29
+
30
+ def inherited(subclass)
31
+ super
32
+ subclass.set_database(database.dup) if database
33
+ end
29
34
  end
30
35
  end
31
36
  end