mongoid 2.1.5 → 2.1.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -21,7 +21,9 @@ module Mongoid #:nodoc:
21
21
  {}.tap do |hash|
22
22
  changed.each do |name|
23
23
  change = attribute_change(name)
24
- hash[name] = change if change[0] != change[1]
24
+ if change
25
+ hash[name] = change if change[0] != change[1]
26
+ end
25
27
  end
26
28
  end
27
29
  end
@@ -89,5 +91,20 @@ module Mongoid #:nodoc:
89
91
  def attribute_change(attr)
90
92
  [changed_attributes[attr], attributes[attr]] if attribute_changed?(attr)
91
93
  end
94
+
95
+ # Determine if a specific attribute has changed.
96
+ #
97
+ # @note Overriding AM::Dirty once again since their implementation is not
98
+ # friendly to fields that can be changed in place.
99
+ #
100
+ # @param [ String ] attr The name of the attribute.
101
+ #
102
+ # @return [ true, false ] Whether the attribute has changed.
103
+ #
104
+ # @since 2.1.6
105
+ def attribute_changed?(attr)
106
+ return false unless changed_attributes.include?(attr)
107
+ changed_attributes[attr] != attributes[attr]
108
+ end
92
109
  end
93
110
  end
@@ -154,11 +154,7 @@ module Mongoid #:nodoc:
154
154
  changed_attributes.clear
155
155
  apply_default_attributes
156
156
  tap do
157
- relations.keys.each do |name|
158
- if instance_variable_defined?("@#{name}")
159
- remove_instance_variable("@#{name}")
160
- end
161
- end
157
+ reload_relations
162
158
  run_callbacks(:initialize)
163
159
  end
164
160
  end
@@ -6,6 +6,7 @@ require "mongoid/extensions/hash/scoping"
6
6
  require "mongoid/extensions/nil/collectionization"
7
7
  require "mongoid/extensions/object/checks"
8
8
  require "mongoid/extensions/object/reflections"
9
+ require "mongoid/extensions/object/substitutable"
9
10
  require "mongoid/extensions/object/yoda"
10
11
  require "mongoid/extensions/proc/scoping"
11
12
  require "mongoid/extensions/string/conversions"
@@ -37,6 +38,7 @@ end
37
38
  class Object #:nodoc:
38
39
  include Mongoid::Extensions::Object::Checks
39
40
  include Mongoid::Extensions::Object::Reflections
41
+ include Mongoid::Extensions::Object::Substitutable
40
42
  include Mongoid::Extensions::Object::Yoda
41
43
  end
42
44
 
@@ -0,0 +1,15 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Extensions #:nodoc:
4
+ module Object #:nodoc:
5
+
6
+ # This module is for defining base substitutable behaviour.
7
+ module Substitutable #:nodoc:
8
+
9
+ def substitutable
10
+ self
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -242,11 +242,11 @@ module Mongoid #:nodoc
242
242
  end
243
243
  else
244
244
  define_method(meth) do
245
- value = read_attribute(name)
246
- if value.is_a?(Array) || value.is_a?(Hash)
247
- changed_attributes[name] = value.clone unless attribute_changed?(name)
245
+ read_attribute(name).tap do |value|
246
+ if value.is_a?(Array) || value.is_a?(Hash)
247
+ changed_attributes[name] = value.clone unless attribute_changed?(name)
248
+ end
248
249
  end
249
- value
250
250
  end
251
251
  end
252
252
  define_method("#{meth}=") do |value|
@@ -33,7 +33,7 @@ module Mongoid #:nodoc:
33
33
  #
34
34
  # @since 2.1.0
35
35
  def serialize(object)
36
- object.blank? ? [] : constraint.convert(object)
36
+ object ? constraint.convert(object) : []
37
37
  end
38
38
 
39
39
  protected
@@ -107,5 +107,22 @@ module Mongoid # :nodoc:
107
107
  def referenced_one?
108
108
  metadata && metadata.macro == :references_one
109
109
  end
110
+
111
+ # Convenience method for iterating through the loaded relations and
112
+ # reloading them.
113
+ #
114
+ # @example Reload the relations.
115
+ # document.reload_relations
116
+ #
117
+ # @return [ Hash ] The relations metadata.
118
+ #
119
+ # @since 2.1.6
120
+ def reload_relations
121
+ relations.each_pair do |name, meta|
122
+ if instance_variable_defined?("@#{name}")
123
+ remove_instance_variable("@#{name}")
124
+ end
125
+ end
126
+ end
110
127
  end
111
128
  end
@@ -74,20 +74,6 @@ module Mongoid # :nodoc:
74
74
  instance_variable_set("@#{name}", relation)
75
75
  end
76
76
 
77
- # Replace an existing relation with a new one.
78
- #
79
- # @example Replace the relation.
80
- # document.substitute("addresses", Address.new)
81
- #
82
- # @param [ String ] name The name of the relation.
83
- # @param [ Document ] object The document to replace with.
84
- # @param [ Hash ] options The options.
85
- #
86
- # @since 2.0.0
87
- def substitute(name, object)
88
- set_relation(name, ivar(name).substitute(object))
89
- end
90
-
91
77
  module ClassMethods #:nodoc:
92
78
 
93
79
  # Defines the getter for the relation. Nothing too special here: just
@@ -134,16 +120,11 @@ module Mongoid # :nodoc:
134
120
  # @since 2.0.0.rc.1
135
121
  def setter(name, metadata)
136
122
  tap do
137
- define_method("#{name}=") do |*args|
138
- object = args.first
139
- if relation_exists?(name) && !object.is_a?(Hash)
140
- substitute(name, object)
123
+ define_method("#{name}=") do |object|
124
+ if relation_exists?(name) || metadata.many?
125
+ set_relation(name, send(name).substitute(object.substitutable))
141
126
  else
142
- if metadata.embedded? && object.blank? && send(name)
143
- substitute(name, object)
144
- else
145
- build(name, object, metadata)
146
- end
127
+ set_relation(name, build(name, object.substitutable, metadata))
147
128
  end
148
129
  end
149
130
  end
@@ -26,6 +26,18 @@ module Mongoid # :nodoc:
26
26
  end
27
27
  end
28
28
 
29
+ # This substituable does not clone.
30
+ #
31
+ # @example Get the substitutable.
32
+ # proxy.substitutable
33
+ #
34
+ # @return [ Object ] The target.
35
+ #
36
+ # @since 2.1.6
37
+ def substitutable
38
+ target
39
+ end
40
+
29
41
  # Substitutes the supplied target documents for the existing document
30
42
  # in the relation.
31
43
  #
@@ -44,6 +56,7 @@ module Mongoid # :nodoc:
44
56
  base.delete unless binding?
45
57
  return nil
46
58
  end
59
+ base.new_record = true
47
60
  proxy.target = replacement
48
61
  proxy.bind_one
49
62
  end
@@ -231,6 +231,9 @@ module Mongoid # :nodoc:
231
231
  proxy.clear
232
232
  else
233
233
  atomically(:$set) do
234
+ if replacement.first.is_a?(Hash)
235
+ replacement = Many.builder(metadata, replacement).build
236
+ end
234
237
  proxy.target = replacement.compact
235
238
  proxy.target.each_with_index do |doc, index|
236
239
  integrate(doc)
@@ -375,7 +375,7 @@ module Mongoid # :nodoc:
375
375
  # @since 2.0.0.rc.1
376
376
  def inverse_foreign_key
377
377
  @inverse_foreign_key ||=
378
- ( inverse_of ? inverse_of.to_s.singularize : inverse_class_name.underscore ) <<
378
+ ( inverse_of ? inverse_of.to_s.singularize : inverse_class_name.demodulize.underscore ) <<
379
379
  relation.foreign_key_suffix
380
380
  end
381
381
 
@@ -496,6 +496,18 @@ module Mongoid # :nodoc:
496
496
  @klass ||= class_name.constantize
497
497
  end
498
498
 
499
+ # Is this metadata representing a one to many or many to many relation?
500
+ #
501
+ # @example Is the relation a many?
502
+ # metadata.many?
503
+ #
504
+ # @return [ true, false ] If the relation is a many.
505
+ #
506
+ # @since 2.1.6
507
+ def many?
508
+ @many ||= relation.macro =~ /many/
509
+ end
510
+
499
511
  # Returns the macro for the relation of this metadata.
500
512
  #
501
513
  # @example Get the macro.
@@ -16,7 +16,6 @@ module Mongoid # :nodoc:
16
16
 
17
17
  # Backwards compatibility with Mongoid beta releases.
18
18
  delegate :klass, :to => :metadata
19
-
20
19
  delegate :bind_one, :unbind_one, :to => :binding
21
20
 
22
21
  # Convenience for setting the target and the metadata properties since
@@ -36,6 +35,19 @@ module Mongoid # :nodoc:
36
35
  extend metadata.extension if metadata.extension?
37
36
  end
38
37
 
38
+ # The default substitutable object for a relation proxy is the clone of
39
+ # the target.
40
+ #
41
+ # @example Get the substitutable.
42
+ # proxy.substitutable
43
+ #
44
+ # @return [ Object ] A clone of the target.
45
+ #
46
+ # @since 2.1.6
47
+ def substitutable
48
+ target.clone
49
+ end
50
+
39
51
  protected
40
52
 
41
53
  # Is the current thread in binding mode?
@@ -25,6 +25,18 @@ module Mongoid # :nodoc:
25
25
  end
26
26
  end
27
27
 
28
+ # This substituable does not clone.
29
+ #
30
+ # @example Get the substitutable.
31
+ # proxy.substitutable
32
+ #
33
+ # @return [ Object ] The target.
34
+ #
35
+ # @since 2.1.6
36
+ def substitutable
37
+ target
38
+ end
39
+
28
40
  # Substitutes the supplied target documents for the existing document
29
41
  # in the relation.
30
42
  #
@@ -214,15 +214,8 @@ module Mongoid #:nodoc:
214
214
  #
215
215
  # @since 2.0.0.beta.1
216
216
  def initialize(base, target, metadata)
217
- init(base, Targets::Enumerable.new(target), metadata) do |proxy|
217
+ init(base, Targets::Enumerable.new(target), metadata) do
218
218
  raise_mixed if klass.embedded?
219
- batched do
220
- proxy.in_memory do |doc|
221
- characterize_one(doc)
222
- bind_one(doc)
223
- doc.save if persistable?
224
- end
225
- end
226
219
  end
227
220
  end
228
221
 
@@ -275,7 +268,7 @@ module Mongoid #:nodoc:
275
268
  def substitute(replacement)
276
269
  tap do |proxy|
277
270
  proxy.purge
278
- proxy.push(replacement) if replacement
271
+ proxy.push(replacement.compact) if replacement
279
272
  end
280
273
  end
281
274
 
@@ -132,36 +132,6 @@ module Mongoid # :nodoc:
132
132
  end
133
133
  end
134
134
 
135
- # Instantiate a new references_many relation. Will set the foreign key
136
- # and the base on the inverse object.
137
- #
138
- # @example Create the new relation.
139
- # Referenced::Many.new(base, target, metadata)
140
- #
141
- # @param [ Document ] base The document this relation hangs off of.
142
- # @param [ Array<Document> ] target The target of the relation.
143
- # @param [ Metadata ] metadata The relation's metadata.
144
- #
145
- # @since 2.0.0.beta.1
146
- def initialize(base, target, metadata)
147
- init(base, Targets::Enumerable.new(target), metadata) do |proxy|
148
- raise_mixed if klass.embedded?
149
- batched do
150
- proxy.in_memory do |doc|
151
- characterize_one(doc)
152
- bind_one(doc)
153
- if persistable?
154
- base.push(metadata.foreign_key, doc.id)
155
- base.synced[metadata.foreign_key] = false
156
- doc.save
157
- else
158
- base.send(metadata.foreign_key).push(doc.id)
159
- end
160
- end
161
- end
162
- end
163
- end
164
-
165
135
  # Removes all associations between the base document and the target
166
136
  # documents by deleting the foreign keys and the references, orphaning
167
137
  # the target documents in the process.
@@ -185,6 +155,27 @@ module Mongoid # :nodoc:
185
155
  alias :nullify_all :nullify
186
156
  alias :clear :nullify
187
157
 
158
+ # Clear the relation. Will delete the documents from the db if they are
159
+ # already persisted.
160
+ #
161
+ # @example Clear the relation.
162
+ # person.posts.clear
163
+ #
164
+ # @return [ Many ] The relation emptied.
165
+ #
166
+ # @since 2.0.0.beta.1
167
+ def purge
168
+ criteria.delete_all
169
+ base.set(
170
+ metadata.foreign_key,
171
+ base.send(metadata.foreign_key).clear
172
+ )
173
+ target.clear do |doc|
174
+ unbind_one(doc)
175
+ doc.destroyed = true
176
+ end
177
+ end
178
+
188
179
  private
189
180
 
190
181
  # Appends the document to the target array, updating the index on the
@@ -244,6 +235,19 @@ module Mongoid # :nodoc:
244
235
  Builders::Referenced::ManyToMany.new(meta, object, loading)
245
236
  end
246
237
 
238
+ # Create the standard criteria for this relation given the supplied
239
+ # metadata and object.
240
+ #
241
+ # @example Get the criteria.
242
+ # Proxy.criteria(meta, object)
243
+ #
244
+ # @param [ Metadata ] metadata The relation metadata.
245
+ # @param [ Object ] object The object for the criteria.
246
+ # @param [ Class ] type The criteria class.
247
+ #
248
+ # @return [ Criteria ] The criteria.
249
+ #
250
+ # @since 2.1.0
247
251
  def criteria(metadata, object, type = nil)
248
252
  metadata.klass.any_in(:_id => object)
249
253
  end
@@ -61,6 +61,7 @@ module Mongoid # :nodoc:
61
61
  #
62
62
  # @since 2.1.0
63
63
  def update_inverse_keys(meta)
64
+ return unless changes.has_key?(meta.foreign_key)
64
65
  old, new = changes[meta.foreign_key]
65
66
  meta.criteria(new - old).add_to_set(meta.inverse_foreign_key, id)
66
67
  meta.criteria(old - new).pull(meta.inverse_foreign_key, id)
@@ -67,6 +67,20 @@ module Mongoid #:nodoc:
67
67
  loaded.clear and added.clear
68
68
  end
69
69
 
70
+ # Clones each document in the enumerable.
71
+ #
72
+ # @note This loads all documents into memory.
73
+ #
74
+ # @example Clone the enumerable.
75
+ # enumerable.clone
76
+ #
77
+ # @return [ Array<Document> ] An array clone of the enumerable.
78
+ #
79
+ # @since 2.1.6
80
+ def clone
81
+ collect { |doc| doc.clone }
82
+ end
83
+
70
84
  # Delete the supplied document from the enumerable.
71
85
  #
72
86
  # @example Delete the document.
@@ -1,4 +1,4 @@
1
1
  # encoding: utf-8
2
2
  module Mongoid #:nodoc
3
- VERSION = "2.1.5"
3
+ VERSION = "2.1.6"
4
4
  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: 2.1.5
4
+ version: 2.1.6
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-08-05 00:00:00.000000000Z
12
+ date: 2011-08-08 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activemodel
16
- requirement: &70194026936900 !ruby/object:Gem::Requirement
16
+ requirement: &70184069667540 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '3.0'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70194026936900
24
+ version_requirements: *70184069667540
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: tzinfo
27
- requirement: &70194026936180 !ruby/object:Gem::Requirement
27
+ requirement: &70184069666660 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ~>
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: 0.3.22
33
33
  type: :runtime
34
34
  prerelease: false
35
- version_requirements: *70194026936180
35
+ version_requirements: *70184069666660
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: mongo
38
- requirement: &70194026935100 !ruby/object:Gem::Requirement
38
+ requirement: &70184069665680 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ~>
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: '1.3'
44
44
  type: :runtime
45
45
  prerelease: false
46
- version_requirements: *70194026935100
46
+ version_requirements: *70184069665680
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: rdoc
49
- requirement: &70194026934200 !ruby/object:Gem::Requirement
49
+ requirement: &70184069664720 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ~>
@@ -54,10 +54,10 @@ dependencies:
54
54
  version: 3.5.0
55
55
  type: :development
56
56
  prerelease: false
57
- version_requirements: *70194026934200
57
+ version_requirements: *70184069664720
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: bson_ext
60
- requirement: &70194026933020 !ruby/object:Gem::Requirement
60
+ requirement: &70184069663720 !ruby/object:Gem::Requirement
61
61
  none: false
62
62
  requirements:
63
63
  - - ~>
@@ -65,10 +65,10 @@ dependencies:
65
65
  version: '1.3'
66
66
  type: :development
67
67
  prerelease: false
68
- version_requirements: *70194026933020
68
+ version_requirements: *70184069663720
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: mocha
71
- requirement: &70194026932080 !ruby/object:Gem::Requirement
71
+ requirement: &70184069662540 !ruby/object:Gem::Requirement
72
72
  none: false
73
73
  requirements:
74
74
  - - ~>
@@ -76,10 +76,10 @@ dependencies:
76
76
  version: 0.9.8
77
77
  type: :development
78
78
  prerelease: false
79
- version_requirements: *70194026932080
79
+ version_requirements: *70184069662540
80
80
  - !ruby/object:Gem::Dependency
81
81
  name: rspec
82
- requirement: &70194026931080 !ruby/object:Gem::Requirement
82
+ requirement: &70184069661380 !ruby/object:Gem::Requirement
83
83
  none: false
84
84
  requirements:
85
85
  - - ~>
@@ -87,10 +87,10 @@ dependencies:
87
87
  version: '2.6'
88
88
  type: :development
89
89
  prerelease: false
90
- version_requirements: *70194026931080
90
+ version_requirements: *70184069661380
91
91
  - !ruby/object:Gem::Dependency
92
92
  name: watchr
93
- requirement: &70194026929800 !ruby/object:Gem::Requirement
93
+ requirement: &70184069660280 !ruby/object:Gem::Requirement
94
94
  none: false
95
95
  requirements:
96
96
  - - ~>
@@ -98,7 +98,7 @@ dependencies:
98
98
  version: '0.6'
99
99
  type: :development
100
100
  prerelease: false
101
- version_requirements: *70194026929800
101
+ version_requirements: *70184069660280
102
102
  description: Mongoid is an ODM (Object Document Mapper) Framework for MongoDB, written
103
103
  in Ruby.
104
104
  email:
@@ -186,6 +186,7 @@ files:
186
186
  - lib/mongoid/extensions/nil/collectionization.rb
187
187
  - lib/mongoid/extensions/object/checks.rb
188
188
  - lib/mongoid/extensions/object/reflections.rb
189
+ - lib/mongoid/extensions/object/substitutable.rb
189
190
  - lib/mongoid/extensions/object/yoda.rb
190
191
  - lib/mongoid/extensions/object_id/conversions.rb
191
192
  - lib/mongoid/extensions/proc/scoping.rb
@@ -378,7 +379,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
378
379
  version: '0'
379
380
  segments:
380
381
  - 0
381
- hash: 3142306228316146123
382
+ hash: -4360740221674892894
382
383
  required_rubygems_version: !ruby/object:Gem::Requirement
383
384
  none: false
384
385
  requirements: