mongoid 3.0.14 → 3.0.15

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.
@@ -3,6 +3,46 @@
3
3
  For instructions on upgrading to newer versions, visit
4
4
  [mongoid.org](http://mongoid.org/docs/upgrading.html).
5
5
 
6
+ ## 3.0.15
7
+
8
+ ### Resolved Issues
9
+
10
+ * \#2630 Fix cascading when the metadata exists but no cascade defined.
11
+
12
+ * \#2625 Fix `Marshal.dump` and `Marshal.load` of proxies and criteria
13
+ objects.
14
+
15
+ * \#2619 Fixed the classes returned by `observed_classes` on an observer
16
+ when it is observing custom models.
17
+
18
+ * \#2612 `DocumentNotFound` errors now expose the class in the error
19
+ instance.
20
+
21
+ * \#2610 Ensure calling `first` after a `last` that had sorting options resets
22
+ the sort.
23
+
24
+ * \#2604 Check pulls and pushes for conflicting updates. (Lucas Souza)
25
+
26
+ * \#2600 Instantiate the proper class type for attributes when using
27
+ multi parameter attributes. (xxswingxx)
28
+
29
+ * \#2598 Fixed sorting on localized fields with embedded docs.
30
+
31
+ * \#2588 Block defining methods for dynamic attributes that would be invalid
32
+ ruby methods. (Matt Sanford)
33
+
34
+ * \#2587 Fix method clash with `belongs_to` proxies when resetting relation
35
+ unloaded criteria.
36
+
37
+ * \#2585 Ensure session configuration options get passed to Moped as symbols.
38
+
39
+ * \#2584 Allow map/reduce to operate on secondaries if output is set to `inline`.
40
+
41
+ * \#2582 Ensure `nil` session override can never cause to access a session with
42
+ name `nil`.
43
+
44
+ * \#2581 Use strong consistency when reloading documents. (Mark Kremer)
45
+
6
46
  ## 3.0.14
7
47
 
8
48
  ### Resolved Issues
@@ -149,7 +149,8 @@ module Mongoid
149
149
  #
150
150
  # @since 2.2.0
151
151
  def set_conflict?(field)
152
- pull_fields.has_key?(field.split(".", 2)[0])
152
+ name = field.split(".", 2)[0]
153
+ pull_fields.has_key?(name) || push_fields.has_key?(name)
153
154
  end
154
155
 
155
156
  # Is the operation going to be a conflict for a $push?
@@ -58,7 +58,12 @@ module Mongoid
58
58
  #
59
59
  # @since 1.0.0
60
60
  def read_attribute(name)
61
- attributes[name.to_s]
61
+ normalized = name.to_s
62
+ if hash_dot_syntax?(normalized)
63
+ attributes.__nested__(normalized)
64
+ else
65
+ attributes[normalized]
66
+ end
62
67
  end
63
68
  alias :[] :read_attribute
64
69
 
@@ -100,7 +105,8 @@ module Mongoid
100
105
  super || (
101
106
  Mongoid.allow_dynamic_fields &&
102
107
  attributes &&
103
- attributes.has_key?(name.to_s.reader)
108
+ attributes.has_key?(name.to_s.reader) &&
109
+ name.to_s.valid_method_name?
104
110
  )
105
111
  end
106
112
 
@@ -191,6 +197,8 @@ module Mongoid
191
197
  #
192
198
  # @since 3.0.0
193
199
  def define_dynamic_reader(name)
200
+ return unless name.valid_method_name?
201
+
194
202
  class_eval <<-READER
195
203
  def #{name}
196
204
  read_attribute(#{name.inspect})
@@ -209,6 +217,8 @@ module Mongoid
209
217
  #
210
218
  # @since 3.0.0
211
219
  def define_dynamic_writer(name)
220
+ return unless name.valid_method_name?
221
+
212
222
  class_eval <<-WRITER
213
223
  def #{name}=(value)
214
224
  write_attribute(#{name.inspect}, value)
@@ -216,6 +226,20 @@ module Mongoid
216
226
  WRITER
217
227
  end
218
228
 
229
+ # Does the string contain dot syntax for accessing hashes?
230
+ #
231
+ # @api private
232
+ #
233
+ # @example Is the string in dot syntax.
234
+ # model.hash_dot_syntax?
235
+ #
236
+ # @return [ true, false ] If the string contains a "."
237
+ #
238
+ # @since 3.0.15
239
+ def hash_dot_syntax?(string)
240
+ string =~ /\./
241
+ end
242
+
219
243
  # Used for allowing accessor methods for dynamic attributes.
220
244
  #
221
245
  # @param [ String, Symbol ] name The name of the method.
@@ -118,12 +118,13 @@ module Mongoid
118
118
  #
119
119
  # @since 2.0.0.rc.7
120
120
  def process_attribute(name, value)
121
- responds = respond_to?("#{name}=")
121
+ writer_method = "#{name}="
122
+ responds = respond_to?(writer_method)
122
123
  if Mongoid.allow_dynamic_fields && !responds
123
124
  write_attribute(name, value)
124
125
  else
125
126
  raise Errors::UnknownAttribute.new(self.class, name) unless responds
126
- send("#{name}=", value)
127
+ send(writer_method, value)
127
128
  end
128
129
  end
129
130
 
@@ -286,7 +286,27 @@ module Mongoid
286
286
  # @since 3.0.0
287
287
  def results
288
288
  raise Errors::NoMapReduceOutput.new(command) unless command[:out]
289
- @results ||= session.with(consistency: :strong).command(command)
289
+ @results ||= __session__.command(command)
290
+ end
291
+
292
+ # Get the session with the proper consistency.
293
+ #
294
+ # @api private
295
+ #
296
+ # @note We can use eventual if the output is set to inline.
297
+ #
298
+ # @example Get the session.
299
+ # map_reduce.__session__
300
+ #
301
+ # @return [ Session ] The session with consistency set.
302
+ #
303
+ # @since 3.0.15
304
+ def __session__
305
+ if command[:out][:inline] != 1
306
+ session.with(consistency: :strong)
307
+ else
308
+ session
309
+ end
290
310
  end
291
311
  end
292
312
  end
@@ -197,6 +197,7 @@ module Mongoid
197
197
  #
198
198
  # @since 3.0.0
199
199
  def first
200
+ apply_sorting
200
201
  apply_id_sorting
201
202
  with_eager_loading(query.first)
202
203
  end
@@ -1,5 +1,6 @@
1
1
  # encoding: utf-8
2
2
  require "mongoid/criterion/inspection"
3
+ require "mongoid/criterion/marshalable"
3
4
  require "mongoid/criterion/scoping"
4
5
 
5
6
  module Mongoid
@@ -15,6 +16,7 @@ module Mongoid
15
16
  include Contextual
16
17
  include Origin::Queryable
17
18
  include Criterion::Inspection
19
+ include Criterion::Marshalable
18
20
  include Criterion::Scoping
19
21
 
20
22
  attr_accessor :embedded, :klass
@@ -0,0 +1,48 @@
1
+ # encoding: utf-8
2
+ module Mongoid
3
+ module Criterion
4
+ module Marshalable
5
+
6
+ # Provides the data needed to Marshal.dump a criteria.
7
+ #
8
+ # @example Dump the criteria.
9
+ # Marshal.dump(criteria)
10
+ #
11
+ # @return [ Array<Object> ] The dumped data.
12
+ #
13
+ # @since 3.0.15
14
+ def marshal_dump
15
+ data = [ klass, driver, inclusions, documents, strategy, negating ]
16
+ data.push(scoping_options).push(dump_hash(:selector)).push(dump_hash(:options))
17
+ end
18
+
19
+ # Resets the criteria object after a Marshal.load
20
+ #
21
+ # @example Load the criteria.
22
+ # Marshal.load(criteria)
23
+ #
24
+ # @since 3.0.15
25
+ def marshal_load(data)
26
+ @scoping_options, raw_selector, raw_options = data.pop(3)
27
+ @klass, @driver, @inclusions, @documents, @strategy, @negating = data
28
+ @selector = load_hash(Origin::Selector, raw_selector)
29
+ @options = load_hash(Origin::Options, raw_options)
30
+ end
31
+
32
+ private
33
+
34
+ def dump_hash(name)
35
+ send(name).inject({}) do |raw, (key, value)|
36
+ raw[key] = value
37
+ raw
38
+ end
39
+ end
40
+
41
+ def load_hash(hash_class, raw)
42
+ hash = hash_class.new(klass.aliased_fields, klass.fields)
43
+ hash.merge!(raw)
44
+ hash
45
+ end
46
+ end
47
+ end
48
+ end
@@ -7,7 +7,7 @@ module Mongoid
7
7
  # it will display all of those.
8
8
  class DocumentNotFound < MongoidError
9
9
 
10
- attr_reader :params
10
+ attr_reader :klass, :params
11
11
 
12
12
  # Create the new error.
13
13
  #
@@ -24,7 +24,7 @@ module Mongoid
24
24
  if !unmatched && !params.is_a?(Hash)
25
25
  raise ArgumentError, 'please also supply the unmatched ids'
26
26
  end
27
- @params = params
27
+ @klass, @params = klass, params
28
28
  super(
29
29
  compose_message(
30
30
  message_key(params),
@@ -72,6 +72,25 @@ module Mongoid
72
72
  self["_id"] || self["id"] || self[:id] || self[:_id]
73
73
  end
74
74
 
75
+ # Fetch a nested value via dot syntax.
76
+ #
77
+ # @example Fetch a nested value via dot syntax.
78
+ # { "name" => { "en" => "test" }}.__nested__("name.en")
79
+ #
80
+ # @param [ String ] string the dot syntax string.
81
+ #
82
+ # @return [ Object ] The matching value.
83
+ #
84
+ # @since 3.0.15
85
+ def __nested__(string)
86
+ keys = string.split(".")
87
+ value = self
88
+ keys.each do |key|
89
+ value = value[key]
90
+ end
91
+ value
92
+ end
93
+
75
94
  # Turn the object from the ruby type we deal with to a Mongo friendly
76
95
  # type.
77
96
  #
@@ -120,6 +120,18 @@ module Mongoid
120
120
  include?("=")
121
121
  end
122
122
 
123
+ # Is this string a valid_method_name?
124
+ #
125
+ # @example Is the string a valid Ruby idenfier for use as a method name
126
+ # "model=".valid_method_name?
127
+ #
128
+ # @return [ true, false ] If the string contains a valid Ruby identifier.
129
+ #
130
+ # @since 3.0.15
131
+ def valid_method_name?
132
+ /[@$"]/ !~ to_sym.inspect
133
+ end
134
+
123
135
  # Is the object not to be converted to bson on criteria creation?
124
136
  #
125
137
  # @example Is the object unconvertable?
@@ -51,7 +51,8 @@ module Mongoid
51
51
  def process_attributes(attrs = nil, role = :default, guard_protected_attributes = true)
52
52
  if attrs
53
53
  errors = []
54
- attributes = {}
54
+ attributes = attrs.class.new
55
+ attributes.permitted = true if attrs.respond_to?(:permitted?) && attrs.permitted?
55
56
  multi_parameter_attributes = {}
56
57
 
57
58
  attrs.each_pair do |key, value|
@@ -79,7 +80,7 @@ module Mongoid
79
80
  raise Errors::MultiparameterAssignmentErrors.new(errors),
80
81
  "#{errors.size} error(s) on assignment of multiparameter attributes"
81
82
  end
82
-
83
+
83
84
  super attributes, role, guard_protected_attributes
84
85
  else
85
86
  super
@@ -176,5 +176,26 @@ module Mongoid
176
176
  return false unless klass.respond_to?(:observers)
177
177
  klass.observers.disabled_for?(self) || Mongoid.observers.disabled_for?(self)
178
178
  end
179
+
180
+ class << self
181
+
182
+ # Attaches the observer to the specified classes.
183
+ #
184
+ # @example Attach the BandObserver to the class Artist.
185
+ # class BandObserver < Mongoid::Observer
186
+ # observe :artist
187
+ # end
188
+ #
189
+ # @param [ Array<Symbol> ] models The names of the models.
190
+ #
191
+ # @since 3.0.15
192
+ def observe(*models)
193
+ models.flatten!
194
+ models.collect! do |model|
195
+ model.respond_to?(:to_sym) ? model.to_s.camelize.constantize : model
196
+ end
197
+ singleton_class.redefine_method(:observed_classes) { models }
198
+ end
199
+ end
179
200
  end
180
201
  end
@@ -69,7 +69,7 @@ module Mongoid
69
69
  # @since 3.0.14
70
70
  def reset_relation_criteria(name)
71
71
  if instance_variable_defined?("@#{name}")
72
- send(name).reset_relation_criteria
72
+ send(name).reset_unloaded
73
73
  end
74
74
  end
75
75
 
@@ -29,7 +29,7 @@ module Mongoid
29
29
  if !metadata || !metadata.versioned?
30
30
  if meta = relations[name]
31
31
  strategy = meta.cascade_strategy
32
- strategy.new(self, meta).cascade
32
+ strategy.new(self, meta).cascade if strategy
33
33
  end
34
34
  end
35
35
  end
@@ -0,0 +1,31 @@
1
+ # encoding: utf-8
2
+ module Mongoid
3
+ module Relations
4
+ module Marshalable
5
+
6
+ # Provides the data needed to Marshal.dump a relation proxy.
7
+ #
8
+ # @example Dump the proxy.
9
+ # Marshal.dump(proxy)
10
+ #
11
+ # @return [ Array<Object> ] The dumped data.
12
+ #
13
+ # @since 3.0.15
14
+ def marshal_dump
15
+ [ base, target, metadata ]
16
+ end
17
+
18
+ # Takes the provided data and sets it back on the proxy.
19
+ #
20
+ # @example Load the proxy.
21
+ # Marshal.load(proxy)
22
+ #
23
+ # @return [ Array<Object> ] The loaded data.
24
+ #
25
+ # @since 3.0.15
26
+ def marshal_load(data)
27
+ @base, @target, @metadata = data
28
+ end
29
+ end
30
+ end
31
+ end
@@ -1,4 +1,6 @@
1
1
  # encoding: utf-8
2
+ require "mongoid/relations/marshalable"
3
+
2
4
  module Mongoid
3
5
  module Relations
4
6
 
@@ -14,11 +16,12 @@ module Mongoid
14
16
  end
15
17
 
16
18
  include Threaded::Lifecycle
19
+ include Marshalable
17
20
 
18
21
  attr_accessor :base, :loaded, :metadata, :target
19
22
 
20
23
  # Backwards compatibility with Mongoid beta releases.
21
- delegate :klass, :foreign_key, :inverse_foreign_key, to: :metadata
24
+ delegate :foreign_key, :inverse_foreign_key, to: :metadata
22
25
  delegate :bind_one, :unbind_one, to: :binding
23
26
  delegate :collection_name, to: :base
24
27
 
@@ -39,6 +42,18 @@ module Mongoid
39
42
  extend_proxy(metadata.extension) if metadata.extension?
40
43
  end
41
44
 
45
+ # Get the class from the metadata, or return nil if no metadata present.
46
+ #
47
+ # @example Get the class.
48
+ # proxy.klass
49
+ #
50
+ # @return [ Class ] The relation class.
51
+ #
52
+ # @since 3.0.15
53
+ def klass
54
+ metadata ? metadata.klass : nil
55
+ end
56
+
42
57
  # Resets the criteria inside the relation proxy. Used by many to many
43
58
  # relations to keep the underlying ids array in sync.
44
59
  #
@@ -46,7 +61,7 @@ module Mongoid
46
61
  # person.preferences.reset_relation_criteria
47
62
  #
48
63
  # @since 3.0.14
49
- def reset_relation_criteria
64
+ def reset_unloaded
50
65
  target.reset_unloaded(criteria)
51
66
  end
52
67
 
@@ -173,7 +173,7 @@ module Mongoid
173
173
  unless replacement.blank?
174
174
  push(replacement.compact.uniq)
175
175
  else
176
- reset_relation_criteria
176
+ reset_unloaded
177
177
  end
178
178
  self
179
179
  end
@@ -309,6 +309,30 @@ module Mongoid
309
309
  !!@executed
310
310
  end
311
311
 
312
+ # Provides the data needed to Marshal.dump an enumerable proxy.
313
+ #
314
+ # @example Dump the proxy.
315
+ # Marshal.dump(proxy)
316
+ #
317
+ # @return [ Array<Object> ] The dumped data.
318
+ #
319
+ # @since 3.0.15
320
+ def marshal_dump
321
+ [ _added, _loaded, _unloaded ]
322
+ end
323
+
324
+ # Loads the data needed to Marshal.load an enumerable proxy.
325
+ #
326
+ # @example Load the proxy.
327
+ # Marshal.load(proxy)
328
+ #
329
+ # @return [ Array<Object> ] The dumped data.
330
+ #
331
+ # @since 3.0.15
332
+ def marshal_load(data)
333
+ @_added, @_loaded, @_unloaded = data
334
+ end
335
+
312
336
  # Reset the enumerable back to its persisted state.
313
337
  #
314
338
  # @example Reset the enumerable.
@@ -370,7 +370,7 @@ module Mongoid
370
370
  #
371
371
  # @since 3.0.0
372
372
  def __session__
373
- if name = session_override
373
+ if !(name = session_override).nil?
374
374
  Sessions.with_name(name)
375
375
  elsif storage_options && name = storage_options[:session]
376
376
  Sessions.with_name(name)
@@ -101,7 +101,7 @@ module Mongoid
101
101
  def parse(config)
102
102
  options = config[:options].try(:dup) || {}
103
103
  parsed = config.has_key?(:uri) ? MongoUri.new(config[:uri]).to_hash : config
104
- [ parsed, options ]
104
+ [ parsed, options.symbolize_keys ]
105
105
  end
106
106
  end
107
107
  end
@@ -1,4 +1,4 @@
1
1
  # encoding: utf-8
2
2
  module Mongoid
3
- VERSION = "3.0.14"
3
+ VERSION = "3.0.15"
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: 3.0.14
4
+ version: 3.0.15
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-11-24 00:00:00.000000000 Z
12
+ date: 2012-12-17 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activemodel
@@ -115,6 +115,7 @@ files:
115
115
  - lib/mongoid/copyable.rb
116
116
  - lib/mongoid/criteria.rb
117
117
  - lib/mongoid/criterion/inspection.rb
118
+ - lib/mongoid/criterion/marshalable.rb
118
119
  - lib/mongoid/criterion/scoping.rb
119
120
  - lib/mongoid/dirty.rb
120
121
  - lib/mongoid/document.rb
@@ -281,6 +282,7 @@ files:
281
282
  - lib/mongoid/relations/embedded/one.rb
282
283
  - lib/mongoid/relations/macros.rb
283
284
  - lib/mongoid/relations/many.rb
285
+ - lib/mongoid/relations/marshalable.rb
284
286
  - lib/mongoid/relations/metadata.rb
285
287
  - lib/mongoid/relations/nested_builder.rb
286
288
  - lib/mongoid/relations/one.rb
@@ -350,10 +352,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
350
352
  requirements:
351
353
  - - ! '>='
352
354
  - !ruby/object:Gem::Version
353
- version: '0'
354
- segments:
355
- - 0
356
- hash: 4134482588596477262
355
+ version: '1.9'
357
356
  required_rubygems_version: !ruby/object:Gem::Requirement
358
357
  none: false
359
358
  requirements: