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
@@ -56,7 +56,8 @@ module Mongoid #:nodoc:
56
56
  (class << self; self; end).class_eval <<-EOT
57
57
  def #{name}(*args)
58
58
  scope = scopes[:#{name}]
59
- scope.extend(criteria.fuse(scope.conditions.as_conditions(*args)))
59
+ conditions = scope.conditions.as_conditions(*args)
60
+ scope.extend(criteria.fuse(conditions))
60
61
  end
61
62
  EOT
62
63
  end
@@ -123,6 +124,21 @@ module Mongoid #:nodoc:
123
124
  end
124
125
  end
125
126
 
127
+ # When inheriting, we want to copy the scopes from the parent class and
128
+ # set the on the child to start, mimicking the behaviour of the old
129
+ # class_inheritable_accessor that was deprecated in Rails edge.
130
+ #
131
+ # @example Inherit from this class.
132
+ # Person.inherited(Doctor)
133
+ #
134
+ # @param [ Class ] subclass The inheriting class.
135
+ #
136
+ # @since 2.0.0.rc.6
137
+ def inherited(subclass)
138
+ super
139
+ subclass.scopes = scopes.dup
140
+ end
141
+
126
142
  protected
127
143
 
128
144
  # Warns or raises exception if overriding another scope or method.
@@ -12,7 +12,7 @@ module Mongoid #:nodoc:
12
12
 
13
13
  module ClassMethods #:nodoc:
14
14
 
15
- REJECT_ALL_BLANK_PROC = proc { |attributes| attributes.all? { |_, value| value.blank? } }
15
+ REJECT_ALL_BLANK_PROC = proc { |attributes| attributes.all? { |key, value| key == '_destroy' || value.blank? } }
16
16
 
17
17
  # Used when needing to update related models from a parent relation. Can
18
18
  # be used on embedded or referenced relations.
@@ -170,5 +170,12 @@ module Mongoid #:nodoc:
170
170
  end
171
171
  end
172
172
  end
173
+
174
+ def disabled_for?(object)
175
+ klass = object.class
176
+ return false unless klass.respond_to?(:observers)
177
+ klass.observers.disabled_for?(self) || Mongoid.observers.disabled_for?(self)
178
+ end
179
+
173
180
  end
174
181
  end
@@ -25,11 +25,12 @@ module Mongoid #:nodoc:
25
25
  doc.run_callbacks(:create) do
26
26
  yield(doc)
27
27
  doc.new_record = false
28
- doc.reset_persisted_children and true
28
+ true
29
29
  end
30
30
  end
31
31
 
32
32
  unless result == false
33
+ doc.reset_persisted_children
33
34
  doc.move_changes
34
35
  Threaded.clear_options!
35
36
  end
@@ -42,15 +42,6 @@ module Mongoid #:nodoc:
42
42
  def persist
43
43
  prepare do
44
44
  unless updates.empty?
45
- # @todo Durran: This is a temporary fix for #791 until we rewrite
46
- # the dirty tracking to properly flag a document as changed if
47
- # only embedded documents have changed.
48
- if document.respond_to?(:updated_at)
49
- if document.timestamping? && !document.updated_at_changed?
50
- document.updated_at = Time.now
51
- end
52
- end
53
-
54
45
  collection.update(selector, updates, options)
55
46
  conflicts.each_pair do |key, value|
56
47
  collection.update(selector, { key => value }, options)
@@ -116,8 +116,8 @@ module Mongoid #:nodoc:
116
116
  # @param [ Hash ] attributes The attributes to update.
117
117
  #
118
118
  # @return [ true, false ] True if validation passed, false if not.
119
- def update_attributes(attributes = {})
120
- write_attributes(attributes); save
119
+ def update_attributes(attributes = {}, options = {})
120
+ assign_attributes(attributes, options); save
121
121
  end
122
122
 
123
123
  # Update the document attributes in the database and raise an error if
@@ -131,8 +131,8 @@ module Mongoid #:nodoc:
131
131
  # @raise [ Errors::Validations ] If validation failed.
132
132
  #
133
133
  # @return [ true, false ] True if validation passed.
134
- def update_attributes!(attributes = {})
135
- update_attributes(attributes).tap do |result|
134
+ def update_attributes!(attributes = {}, options = {})
135
+ update_attributes(attributes, options).tap do |result|
136
136
  unless result
137
137
  self.class.fail_validate!(self) if errors.any?
138
138
  self.class.fail_callback!(self, :update_attributes!)
@@ -151,7 +151,7 @@ module Mongoid #:nodoc:
151
151
  # @return [ true, false ] True is success, false if not.
152
152
  def upsert(options = {})
153
153
  if new_record?
154
- insert(options).persisted?
154
+ !insert(options).new_record?
155
155
  else
156
156
  update(options)
157
157
  end
@@ -23,8 +23,27 @@ module Rails #:nodoc:
23
23
  config.respond_to?(:app_generators) ? :app_generators : :generators
24
24
  end
25
25
 
26
+ # Maping of rescued exceptions to HTTP responses
27
+ #
28
+ # @example
29
+ # railtie.rescue_responses
30
+ #
31
+ # @ return [Hash] rescued responses
32
+ #
33
+ # @since 2.4.3
34
+ def self.rescue_responses
35
+ {
36
+ "Mongoid::Errors::DocumentNotFound" => :not_found,
37
+ "Mongoid::Errors::Validations" => 422
38
+ }
39
+ end
40
+
26
41
  config.send(generator).orm :mongoid, :migration => false
27
42
 
43
+ if config.action_dispatch.rescue_responses
44
+ config.action_dispatch.rescue_responses.merge!(rescue_responses)
45
+ end
46
+
28
47
  rake_tasks do
29
48
  load "mongoid/railties/database.rake"
30
49
  end
@@ -81,10 +100,9 @@ module Rails #:nodoc:
81
100
  # 404s and not 500s, validation errors are 422s.
82
101
  initializer "load http errors" do |app|
83
102
  config.after_initialize do
84
- ActionDispatch::ShowExceptions.rescue_responses.update({
85
- "Mongoid::Errors::DocumentNotFound" => :not_found,
86
- "Mongoid::Errors::Validations" => 422
87
- })
103
+ unless config.action_dispatch.rescue_responses
104
+ ActionDispatch::ShowExceptions.rescue_responses.update(Railtie.rescue_responses)
105
+ end
88
106
  end
89
107
  end
90
108
 
@@ -19,17 +19,15 @@ module Mongoid # :nodoc:
19
19
  #
20
20
  # @since 2.0.0.rc.1
21
21
  def bind
22
+ base.you_must(metadata.foreign_key_setter, target.id)
23
+ if metadata.inverse_type
24
+ base.you_must(metadata.inverse_type_setter, target.class.model_name)
25
+ end
22
26
  unless _binding?
23
27
  _binding do
24
28
  inverse = metadata.inverse(target)
25
- base.you_must(metadata.foreign_key_setter, target.id)
26
- if metadata.inverse_type
27
- base.you_must(metadata.inverse_type_setter, target.class.model_name)
28
- end
29
29
  if inverse
30
- inverse_metadata = metadata.inverse_metadata(target)
31
- if inverse_metadata != metadata && !inverse_metadata.nil?
32
- base.metadata = inverse_metadata
30
+ if set_base_metadata
33
31
  if base.referenced_many?
34
32
  target.send(inverse).push(base) unless Mongoid.identity_map_enabled?
35
33
  else
@@ -51,14 +49,15 @@ module Mongoid # :nodoc:
51
49
  #
52
50
  # @since 2.0.0.rc.1
53
51
  def unbind
52
+ base.you_must(metadata.foreign_key_setter, nil)
53
+ if metadata.inverse_type
54
+ base.you_must(metadata.inverse_type_setter, nil)
55
+ end
54
56
  unless _binding?
55
57
  _binding do
56
58
  inverse = metadata.inverse(target)
57
- base.you_must(metadata.foreign_key_setter, nil)
58
- if metadata.inverse_type
59
- base.you_must(metadata.inverse_type_setter, nil)
60
- end
61
59
  if inverse
60
+ set_base_metadata
62
61
  if base.referenced_many?
63
62
  target.send(inverse).delete(base)
64
63
  else
@@ -69,6 +68,27 @@ module Mongoid # :nodoc:
69
68
  end
70
69
  end
71
70
  alias :unbind_one :unbind
71
+
72
+ private
73
+
74
+ # Ensure that the metadata on the base is correct, for the cases
75
+ # where we have multiple belongs to definitions and were are setting
76
+ # different parents in memory in order.
77
+ #
78
+ # @api private
79
+ #
80
+ # @example Set the base metadata.
81
+ # binding.set_base_metadata
82
+ #
83
+ # @return [ true, false ] If the metadata changed.
84
+ #
85
+ # @since 2.4.4
86
+ def set_base_metadata
87
+ inverse_metadata = metadata.inverse_metadata(target)
88
+ if inverse_metadata != metadata && !inverse_metadata.nil?
89
+ base.metadata = inverse_metadata
90
+ end
91
+ end
72
92
  end
73
93
  end
74
94
  end
@@ -17,12 +17,12 @@ module Mongoid # :nodoc:
17
17
  #
18
18
  # @since 2.0.0.rc.1
19
19
  def bind_one(doc)
20
+ doc.you_must(metadata.foreign_key_setter, base.id)
21
+ if metadata.type
22
+ doc.you_must(metadata.type_setter, base.class.model_name)
23
+ end
20
24
  unless _binding?
21
25
  _binding do
22
- doc.you_must(metadata.foreign_key_setter, base.id)
23
- if metadata.type
24
- doc.you_must(metadata.type_setter, base.class.model_name)
25
- end
26
26
  doc.send(metadata.inverse_setter, base)
27
27
  end
28
28
  end
@@ -37,12 +37,12 @@ module Mongoid # :nodoc:
37
37
  #
38
38
  # @since 2.0.0.rc.1
39
39
  def unbind_one(doc)
40
+ doc.you_must(metadata.foreign_key_setter, nil)
41
+ if metadata.type
42
+ doc.you_must(metadata.type_setter, nil)
43
+ end
40
44
  unless _binding?
41
45
  _binding do
42
- doc.you_must(metadata.foreign_key_setter, nil)
43
- if metadata.type
44
- doc.you_must(metadata.type_setter, nil)
45
- end
46
46
  doc.send(metadata.inverse_setter, nil)
47
47
  end
48
48
  end
@@ -19,13 +19,13 @@ module Mongoid # :nodoc:
19
19
  #
20
20
  # @since 2.0.0.rc.1
21
21
  def bind
22
+ target.you_must(metadata.foreign_key_setter, base.id)
23
+ if metadata.type
24
+ target.you_must(metadata.type_setter, base.class.model_name)
25
+ end
22
26
  unless _binding?
23
27
  _binding do
24
- target.you_must(metadata.foreign_key_setter, base.id)
25
28
  target.send(metadata.inverse_setter, base)
26
- if metadata.type
27
- target.you_must(metadata.type_setter, base.class.model_name)
28
- end
29
29
  end
30
30
  end
31
31
  end
@@ -40,13 +40,13 @@ module Mongoid # :nodoc:
40
40
  #
41
41
  # @since 2.0.0.rc.1
42
42
  def unbind
43
+ target.you_must(metadata.foreign_key_setter, nil)
44
+ if metadata.type
45
+ target.you_must(metadata.type_setter, nil)
46
+ end
43
47
  unless _binding?
44
48
  _binding do
45
- target.you_must(metadata.foreign_key_setter, nil)
46
49
  target.send(metadata.inverse_setter, nil)
47
- if metadata.type
48
- target.you_must(metadata.type_setter, nil)
49
- end
50
50
  end
51
51
  end
52
52
  end
@@ -92,7 +92,9 @@ module Mongoid # :nodoc:
92
92
  def process(parent, attrs)
93
93
  return if reject?(parent, attrs)
94
94
  if id = attrs.extract_id
95
- doc = existing.find(convert_id(existing.first.class, id))
95
+ first = existing.first
96
+ converted = first ? convert_id(first.class, id) : id
97
+ doc = existing.find(converted)
96
98
  if destroyable?(attrs)
97
99
  existing.delete(doc)
98
100
  doc.destroy unless doc.embedded?
@@ -2,7 +2,24 @@
2
2
  module Mongoid # :nodoc:
3
3
  module Relations #:nodoc:
4
4
  module Cascading #:nodoc:
5
- class Delete < Strategy
5
+ class Delete
6
+
7
+ attr_accessor :document, :relation, :metadata
8
+
9
+ # Initialize the new cascade strategy, which will set up the relation
10
+ # and the metadata.
11
+ #
12
+ # @example Instantiate the strategy
13
+ # Strategy.new(document, metadata)
14
+ #
15
+ # @param [ Document ] document The document to cascade from.
16
+ # @param [ Metadata ] metadata The relation's metadata.
17
+ #
18
+ # @return [ Strategy ] The new strategy.
19
+ def initialize(document, metadata)
20
+ @document, @metadata = document, metadata
21
+ @relation = document.send(metadata.name)
22
+ end
6
23
 
7
24
  # Execute the cascading deletion for the relation if it already exists.
8
25
  # This should be optimized in the future potentially not to load all
@@ -10,8 +27,18 @@ module Mongoid # :nodoc:
10
27
  #
11
28
  # @example Perform the cascading delete.
12
29
  # strategy.cascade
30
+ #
31
+ # @since 2.0.0
13
32
  def cascade
14
- Array.wrap(relation).each { |doc| doc.delete } if relation
33
+ if relation
34
+ if relation.cascades.empty?
35
+ safety = Threaded.safety_options
36
+ relation.clear
37
+ Threaded.safety_options = safety
38
+ else
39
+ ::Array.wrap(relation).each { |doc| doc.delete }
40
+ end
41
+ end
15
42
  end
16
43
  end
17
44
  end
@@ -2,7 +2,24 @@
2
2
  module Mongoid # :nodoc:
3
3
  module Relations #:nodoc:
4
4
  module Cascading #:nodoc:
5
- class Destroy < Strategy
5
+ class Destroy
6
+
7
+ attr_accessor :document, :relation, :metadata
8
+
9
+ # Initialize the new cascade strategy, which will set up the relation
10
+ # and the metadata.
11
+ #
12
+ # @example Instantiate the strategy
13
+ # Strategy.new(document, metadata)
14
+ #
15
+ # @param [ Document ] document The document to cascade from.
16
+ # @param [ Metadata ] metadata The relation's metadata.
17
+ #
18
+ # @return [ Strategy ] The new strategy.
19
+ def initialize(document, metadata)
20
+ @document, @metadata = document, metadata
21
+ @relation = document.send(metadata.name)
22
+ end
6
23
 
7
24
  # Execute the cascading deletion for the relation if it already exists.
8
25
  # This should be optimized in the future potentially not to load all
@@ -2,7 +2,24 @@
2
2
  module Mongoid # :nodoc:
3
3
  module Relations #:nodoc:
4
4
  module Cascading #:nodoc:
5
- class Nullify < Strategy
5
+ class Nullify
6
+
7
+ attr_accessor :document, :relation, :metadata
8
+
9
+ # Initialize the new cascade strategy, which will set up the relation
10
+ # and the metadata.
11
+ #
12
+ # @example Instantiate the strategy
13
+ # Strategy.new(document, metadata)
14
+ #
15
+ # @param [ Document ] document The document to cascade from.
16
+ # @param [ Metadata ] metadata The relation's metadata.
17
+ #
18
+ # @return [ Strategy ] The new strategy.
19
+ def initialize(document, metadata)
20
+ @document, @metadata = document, metadata
21
+ @relation = document.send(metadata.name)
22
+ end
6
23
 
7
24
  # This cascade does not delete the referenced relations, but instead
8
25
  # sets the foreign key values to nil.
@@ -1,5 +1,4 @@
1
1
  # encoding: utf-8
2
- require "mongoid/relations/cascading/strategy"
3
2
  require "mongoid/relations/cascading/delete"
4
3
  require "mongoid/relations/cascading/destroy"
5
4
  require "mongoid/relations/cascading/nullify"
@@ -55,13 +55,13 @@ module Mongoid #:nodoc:
55
55
  #
56
56
  # @since 2.0.0
57
57
  def atomically(modifier, &block)
58
- updater = Threaded.update_consumer(root_class) ||
59
- Threaded.set_update_consumer(root_class, MODIFIERS[modifier].new)
58
+ updater = Threaded.update_consumer(collection_name) ||
59
+ Threaded.set_update_consumer(collection_name, MODIFIERS[modifier].new)
60
60
  count_executions do
61
61
  block.call if block
62
62
  end.tap do
63
63
  if @executions.zero?
64
- Threaded.set_update_consumer(root_class, nil)
64
+ Threaded.set_update_consumer(collection_name, nil)
65
65
  updater.execute(collection)
66
66
  end
67
67
  end
@@ -28,6 +28,22 @@ module Mongoid # :nodoc:
28
28
  end
29
29
  alias :push :<<
30
30
 
31
+ # Get this relation as as its representation in the database.
32
+ #
33
+ # @example Convert the relation to an attributes hash.
34
+ # person.addresses.as_document
35
+ #
36
+ # @return [ Array<Hash> ] The relation as stored in the db.
37
+ #
38
+ # @since 2.0.0.rc.1
39
+ def as_document
40
+ [].tap do |attributes|
41
+ _unscoped.each do |doc|
42
+ attributes.push(doc.as_document)
43
+ end
44
+ end
45
+ end
46
+
31
47
  # Appends an array of documents to the relation. Performs a batch
32
48
  # insert of the documents instead of persisting one at a time.
33
49
  #
@@ -78,6 +94,7 @@ module Mongoid # :nodoc:
78
94
  Factory.build(type || metadata.klass, attributes, options).tap do |doc|
79
95
  doc.identify
80
96
  append(doc)
97
+ doc.apply_proc_defaults
81
98
  yield(doc) if block_given?
82
99
  doc.run_callbacks(:build) { doc }
83
100
  end
@@ -172,12 +189,16 @@ module Mongoid # :nodoc:
172
189
  target.delete_one(document).tap do |doc|
173
190
  _unscoped.delete_one(doc)
174
191
  if doc && !_binding?
175
- if _assigning? && !doc.paranoid?
176
- base.add_atomic_pull(doc)
192
+ if _assigning?
193
+ if doc.paranoid?
194
+ doc.destroy(:suppress => true)
195
+ else
196
+ base.add_atomic_pull(doc)
197
+ end
177
198
  else
178
199
  doc.delete(:suppress => true)
200
+ unbind_one(doc)
179
201
  end
180
- unbind_one(doc)
181
202
  end
182
203
  reindex
183
204
  end
@@ -283,46 +304,41 @@ module Mongoid # :nodoc:
283
304
  tap do |proxy|
284
305
  if replacement.blank?
285
306
  if _assigning? && !proxy.empty?
286
- base.atomic_unsets.push(proxy.first.atomic_path)
307
+ base.atomic_unsets.push(_unscoped.first.atomic_path)
287
308
  end
288
309
  proxy.clear
289
310
  else
290
311
  atomically(:$set) do
312
+ base.delayed_atomic_sets.clear unless _assigning?
291
313
  if replacement.first.is_a?(Hash)
292
- replacement = Many.builder(base, metadata, replacement).build
314
+ replacement = replacement.map do |doc|
315
+ attributes = { :metadata => metadata, :_parent => base }
316
+ attributes.merge!(doc)
317
+ Factory.build(klass, attributes)
318
+ end
293
319
  end
294
320
  docs = replacement.compact
295
321
  proxy.target = docs
296
322
  self._unscoped = docs.dup
297
- if _assigning?
298
- base.delayed_atomic_sets[metadata.name.to_s] = proxy.as_document
299
- end
300
323
  proxy.target.each_with_index do |doc, index|
301
324
  integrate(doc)
302
325
  doc._index = index
303
326
  doc.save if base.persisted? && !_assigning?
304
327
  end
328
+ if _assigning?
329
+ name = _unscoped.first.atomic_path
330
+ base.delayed_atomic_sets[name].try(:clear)
331
+ base._children.each do |child|
332
+ child.delayed_atomic_sets.clear
333
+ end
334
+ base.instance_variable_set(:@_children, nil)
335
+ base.delayed_atomic_sets[name] = proxy.as_document
336
+ end
305
337
  end
306
338
  end
307
339
  end
308
340
  end
309
341
 
310
- # Get this relation as as its representation in the database.
311
- #
312
- # @example Convert the relation to an attributes hash.
313
- # person.addresses.as_document
314
- #
315
- # @return [ Array<Hash> ] The relation as stored in the db.
316
- #
317
- # @since 2.0.0.rc.1
318
- def as_document
319
- [].tap do |attributes|
320
- _unscoped.each do |doc|
321
- attributes.push(doc.as_document)
322
- end
323
- end
324
- end
325
-
326
342
  # Return the relation with all previous scoping removed. This is the
327
343
  # exact representation of the docs in the database.
328
344
  #
@@ -350,10 +366,10 @@ module Mongoid # :nodoc:
350
366
  #
351
367
  # @since 2.0.0.rc.1
352
368
  def append(document)
353
- target.push(document)
369
+ target.push(*scope([document]))
354
370
  _unscoped.push(document)
355
371
  integrate(document)
356
- document._index = target.size - 1
372
+ document._index = _unscoped.size - 1
357
373
  end
358
374
 
359
375
  # Instantiate the binding associated with this relation.
@@ -383,6 +399,22 @@ module Mongoid # :nodoc:
383
399
  end
384
400
  end
385
401
 
402
+ # Deletes one document from the target and unscoped.
403
+ #
404
+ # @api private
405
+ #
406
+ # @example Delete one document.
407
+ # relation.delete_one(doc)
408
+ #
409
+ # @param [ Document ] document The document to delete.
410
+ #
411
+ # @since 2.4.7
412
+ def delete_one(document)
413
+ target.delete_one(document)
414
+ _unscoped.delete_one(document)
415
+ reindex
416
+ end
417
+
386
418
  # Integrate the document into the relation. will set its metadata and
387
419
  # attempt to bind the inverse.
388
420
  #
@@ -39,7 +39,7 @@ module Mongoid # :nodoc:
39
39
  if _assigning?
40
40
  base.atomic_unsets.push(proxy.atomic_path)
41
41
  else
42
- proxy.delete if persistable?
42
+ proxy.destroy if persistable?
43
43
  end
44
44
  proxy.unbind_one
45
45
  return nil unless replacement
@@ -717,6 +717,19 @@ module Mongoid # :nodoc:
717
717
  !!self[:versioned]
718
718
  end
719
719
 
720
+ # Returns the metadata itself. Here for compatibility with Rails
721
+ # association metadata.
722
+ #
723
+ # @example Get the options.
724
+ # metadata.options
725
+ #
726
+ # @return [ Metadata ] self.
727
+ #
728
+ # @since 2.4.6
729
+ def options
730
+ self
731
+ end
732
+
720
733
  # Returns default order for this association.
721
734
  #
722
735
  # @example Get default order
@@ -848,7 +861,7 @@ module Mongoid # :nodoc:
848
861
  #
849
862
  # @since 2.0.0.rc.1
850
863
  def determine_inverse_relation
851
- default = klass.relations[inverse_klass.name.underscore]
864
+ default = foreign_key_match || klass.relations[inverse_klass.name.underscore]
852
865
  return default.name if default
853
866
  klass.relations.each_pair do |key, meta|
854
867
  next if meta.versioned? || meta.name == name
@@ -859,6 +872,25 @@ module Mongoid # :nodoc:
859
872
  return nil
860
873
  end
861
874
 
875
+ # Return metadata where the foreign key matches the foreign key on this
876
+ # relation.
877
+ #
878
+ # @api private
879
+ #
880
+ # @example Return a foreign key match.
881
+ # meta.foreign_key_match
882
+ #
883
+ # @return [ Metadata ] A match, if any.
884
+ #
885
+ # @since 2.4.11
886
+ def foreign_key_match
887
+ if fk = self[:foreign_key]
888
+ klass.relations.values.detect do |meta|
889
+ fk == meta.foreign_key if meta.stores_foreign_key?
890
+ end
891
+ end
892
+ end
893
+
862
894
  # Determine the key for the relation in the attributes.
863
895
  #
864
896
  # @example Get the key.
@@ -6,6 +6,18 @@ module Mongoid # :nodoc:
6
6
  # behaviour or those proxies.
7
7
  class One < Proxy
8
8
 
9
+ # Clear this relation - same as calling #delete on the document.
10
+ #
11
+ # @example Clear the relation.
12
+ # relation.clear
13
+ #
14
+ # @return [ true, false ] If the delete suceeded.
15
+ #
16
+ # @since 3.0.0
17
+ def clear
18
+ target.delete
19
+ end
20
+
9
21
  # Get all the documents in the relation that are loaded into memory.
10
22
  #
11
23
  # @example Get the in memory documents.