mongoid 3.0.14 → 3.0.15

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