mongoid 2.0.0.rc.6 → 2.0.0.rc.7

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 (64) hide show
  1. data/Rakefile +2 -2
  2. data/lib/config/locales/bg.yml +3 -6
  3. data/lib/config/locales/de.yml +2 -5
  4. data/lib/config/locales/en.yml +2 -5
  5. data/lib/config/locales/es.yml +2 -5
  6. data/lib/config/locales/fr.yml +2 -5
  7. data/lib/config/locales/hu.yml +2 -5
  8. data/lib/config/locales/it.yml +2 -5
  9. data/lib/config/locales/kr.yml +2 -5
  10. data/lib/config/locales/nl.yml +2 -5
  11. data/lib/config/locales/pl.yml +2 -5
  12. data/lib/config/locales/pt-br.yml +2 -5
  13. data/lib/config/locales/pt.yml +2 -5
  14. data/lib/config/locales/ro.yml +2 -5
  15. data/lib/config/locales/ru.yml +41 -0
  16. data/lib/config/locales/sv.yml +2 -5
  17. data/lib/config/locales/zh-CN.yml +3 -6
  18. data/lib/mongoid/atomicity.rb +2 -2
  19. data/lib/mongoid/attributes.rb +40 -69
  20. data/lib/mongoid/attributes/processing.rb +142 -0
  21. data/lib/mongoid/collections.rb +1 -0
  22. data/lib/mongoid/components.rb +1 -1
  23. data/lib/mongoid/config.rb +0 -1
  24. data/lib/mongoid/contexts/mongo.rb +13 -9
  25. data/lib/mongoid/criteria.rb +15 -1
  26. data/lib/mongoid/criterion/inclusion.rb +36 -11
  27. data/lib/mongoid/criterion/inspection.rb +3 -1
  28. data/lib/mongoid/criterion/optional.rb +2 -2
  29. data/lib/mongoid/dirty.rb +1 -0
  30. data/lib/mongoid/document.rb +11 -5
  31. data/lib/mongoid/extensions/integer/conversions.rb +2 -2
  32. data/lib/mongoid/extensions/object_id/conversions.rb +62 -32
  33. data/lib/mongoid/field.rb +5 -2
  34. data/lib/mongoid/identity.rb +2 -1
  35. data/lib/mongoid/matchers.rb +5 -32
  36. data/lib/mongoid/matchers/default.rb +53 -10
  37. data/lib/mongoid/matchers/or.rb +30 -0
  38. data/lib/mongoid/matchers/strategies.rb +63 -0
  39. data/lib/mongoid/multi_parameter_attributes.rb +4 -2
  40. data/lib/mongoid/nested_attributes.rb +8 -0
  41. data/lib/mongoid/persistence.rb +2 -1
  42. data/lib/mongoid/persistence/insert.rb +3 -3
  43. data/lib/mongoid/persistence/insert_embedded.rb +2 -1
  44. data/lib/mongoid/relations.rb +2 -1
  45. data/lib/mongoid/relations/bindings/referenced/many_to_many.rb +4 -2
  46. data/lib/mongoid/relations/builders/nested_attributes/many.rb +2 -0
  47. data/lib/mongoid/relations/cascading.rb +1 -1
  48. data/lib/mongoid/relations/cascading/nullify.rb +1 -1
  49. data/lib/mongoid/relations/constraint.rb +42 -0
  50. data/lib/mongoid/relations/cyclic.rb +6 -0
  51. data/lib/mongoid/relations/embedded/many.rb +4 -4
  52. data/lib/mongoid/relations/macros.rb +1 -1
  53. data/lib/mongoid/relations/many.rb +1 -0
  54. data/lib/mongoid/relations/metadata.rb +12 -4
  55. data/lib/mongoid/relations/nested_builder.rb +1 -3
  56. data/lib/mongoid/relations/referenced/many.rb +11 -18
  57. data/lib/mongoid/relations/referenced/many_to_many.rb +19 -32
  58. data/lib/mongoid/relations/referenced/one.rb +3 -1
  59. data/lib/mongoid/timestamps.rb +1 -1
  60. data/lib/mongoid/validations/associated.rb +11 -9
  61. data/lib/mongoid/validations/uniqueness.rb +1 -1
  62. data/lib/mongoid/version.rb +1 -1
  63. data/lib/mongoid/versioning.rb +2 -2
  64. metadata +9 -4
@@ -0,0 +1,142 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Attributes #:nodoc:
4
+
5
+ # This module contains the behavior for processing attributes.
6
+ module Processing
7
+
8
+ # Process the provided attributes casting them to their proper values if a
9
+ # field exists for them on the document. This will be limited to only the
10
+ # attributes provided in the suppied +Hash+ so that no extra nil values get
11
+ # put into the document's attributes.
12
+ #
13
+ # @example Process the attributes.
14
+ # person.process(:title => "sir", :age => 40)
15
+ #
16
+ # @param [ Hash ] attrs The attributes to set.
17
+ #
18
+ # @since 2.0.0.rc.7
19
+ def process(attrs = nil)
20
+ sanitize_for_mass_assignment(attrs || {}).each_pair do |key, value|
21
+ next if pending_attribute?(key, value)
22
+ process_attribute(key, value)
23
+ end
24
+ yield self if block_given?
25
+ process_pending and setup_modifications
26
+ end
27
+
28
+ protected
29
+
30
+ # If the key provided a relation or a nested attribute, where we have to
31
+ # hold off on the setting of the attribute until everything else has been
32
+ # set?
33
+ #
34
+ # @example Is the attribute pending?
35
+ # document.pending_attribute?(:name, "Durran")
36
+ #
37
+ # @param [ Synbol ] key The name of the attribute.
38
+ # @param [ Object ] value The value of the attribute.
39
+ #
40
+ # @return [ true, false ] True if pending, false if not.
41
+ #
42
+ # @since 2.0.0.rc.7
43
+ def pending_attribute?(key, value)
44
+ name = key.to_s
45
+ if relations.has_key?(name)
46
+ pending_relations[name] = value
47
+ return true
48
+ end
49
+ if nested_attributes.include?("#{name}=")
50
+ pending_nested[name] = value
51
+ return true
52
+ end
53
+ return false
54
+ end
55
+
56
+ # Get all the pending relations that need to be set.
57
+ #
58
+ # @example Get the pending relations.
59
+ # document.pending_relations
60
+ #
61
+ # @return [ Hash ] The pending relations in key/value pairs.
62
+ #
63
+ # @since 2.0.0.rc.7
64
+ def pending_relations
65
+ @pending_relations ||= {}
66
+ end
67
+
68
+ # Get all the pending nested attributes that need to be set.
69
+ #
70
+ # @example Get the pending nested attributes.
71
+ # document.pending_nested
72
+ #
73
+ # @return [ Hash ] The pending nested attributes in key/value pairs.
74
+ #
75
+ # @since 2.0.0.rc.7
76
+ def pending_nested
77
+ @pending_nested ||= {}
78
+ end
79
+
80
+ # If the attribute is dynamic, add a field for it with a type of object
81
+ # and then either way set the value.
82
+ #
83
+ # @example Process the attribute.
84
+ # document.process_attribute(name, value)
85
+ #
86
+ # @param [ Symbol ] name The name of the field.
87
+ # @param [ Object ] value The value of the field.
88
+ #
89
+ # @since 2.0.0.rc.7
90
+ def process_attribute(name, value)
91
+ if Mongoid.allow_dynamic_fields && !respond_to?("#{name}=")
92
+ write_attribute(name, value)
93
+ else
94
+ send("#{name}=", value)
95
+ end
96
+ end
97
+
98
+ # Process all the pending nested attributes that needed to wait until
99
+ # ids were set to fire off.
100
+ #
101
+ # @example Process the nested attributes.
102
+ # document.process_nested
103
+ #
104
+ # @since 2.0.0.rc.7
105
+ def process_nested
106
+ pending_nested.each_pair do |name, value|
107
+ send("#{name}=", value)
108
+ end
109
+ end
110
+
111
+ # Process all the pending items, then clear them out.
112
+ #
113
+ # @example Process the pending items.
114
+ # document.process_pending
115
+ #
116
+ # @since 2.0.0.rc.7
117
+ def process_pending
118
+ process_nested and process_relations
119
+ pending_nested.clear and pending_relations.clear
120
+ end
121
+
122
+ # Process all the pending relations that needed to wait until ids were set
123
+ # to fire off.
124
+ #
125
+ # @example Process the relations.
126
+ # document.process_relations
127
+ #
128
+ # @since 2.0.0.rc.7
129
+ def process_relations
130
+ pending_relations.each_pair do |name, value|
131
+ metadata = relations[name]
132
+ if value.is_a?(Hash)
133
+ metadata.nested_builder(value, {}).build(self)
134
+ else
135
+ send("#{name}=", value, :binding => true)
136
+ end
137
+ end
138
+ end
139
+ end
140
+ end
141
+ end
142
+
@@ -18,6 +18,7 @@ module Mongoid #:nodoc
18
18
  #
19
19
  # Returns: <tt>Mongo::Collection</tt>
20
20
  def collection
21
+ raise Errors::InvalidCollection.new(self) if embedded? && !cyclic
21
22
  self._collection || set_collection
22
23
  add_indexes; self._collection
23
24
  end
@@ -13,8 +13,8 @@ module Mongoid #:nodoc
13
13
  end
14
14
 
15
15
  include ActiveModel::Conversion
16
- include ActiveModel::Naming
17
16
  include ActiveModel::MassAssignmentSecurity
17
+ include ActiveModel::Naming
18
18
  include ActiveModel::Serializers::JSON
19
19
  include ActiveModel::Serializers::Xml
20
20
  include Mongoid::Atomicity
@@ -129,7 +129,6 @@ module Mongoid #:nodoc
129
129
  # @return [ Logger ] The newly set logger.
130
130
  def logger=(logger)
131
131
  @logger = logger
132
- @master.connection.instance_variable_set(:@logger, logger)
133
132
  end
134
133
 
135
134
  # Sets whether the times returned from the database use the ruby or
@@ -20,7 +20,12 @@ module Mongoid #:nodoc:
20
20
  #
21
21
  # A +Hash+ with field values as keys, counts as values
22
22
  def aggregate
23
- klass.collection.group(options[:fields], selector, { :count => 0 }, Javascript.aggregate, true)
23
+ klass.collection.group(
24
+ :key => options[:fields],
25
+ :cond => selector,
26
+ :initial => { :count => 0 },
27
+ :reduce => Javascript.aggregate
28
+ )
24
29
  end
25
30
 
26
31
  # Get the average value for the supplied field.
@@ -141,10 +146,10 @@ module Mongoid #:nodoc:
141
146
  # A +Hash+ with field values as keys, arrays of documents as values.
142
147
  def group
143
148
  klass.collection.group(
144
- options[:fields],
145
- selector,
146
- { :group => [] },
147
- Javascript.group
149
+ :key => options[:fields],
150
+ :cond => selector,
151
+ :initial => { :group => [] },
152
+ :reduce => Javascript.group
148
153
  ).collect do |docs|
149
154
  docs["group"] = docs["group"].collect do |attrs|
150
155
  Mongoid::Factory.build(klass, attrs)
@@ -287,10 +292,9 @@ module Mongoid #:nodoc:
287
292
  # and sum. Will gsub the field name in the supplied reduce function.
288
293
  def grouped(start, field, reduce)
289
294
  collection = klass.collection.group(
290
- nil,
291
- selector,
292
- { start => "start" },
293
- reduce.gsub("[field]", field)
295
+ :cond => selector,
296
+ :initial => { start => "start" },
297
+ :reduce => reduce.gsub("[field]", field)
294
298
  )
295
299
  collection.empty? ? nil : collection.first[start.to_s]
296
300
  end
@@ -195,6 +195,18 @@ module Mongoid #:nodoc:
195
195
  end
196
196
  alias :to_ary :to_a
197
197
 
198
+ # Needed to properly get a criteria back as json
199
+ #
200
+ # @example Get the criteria as json.
201
+ # Person.where(:title => "Sir").as_json
202
+ #
203
+ # @param [ Hash ] options Options to pass through to the serializer.
204
+ #
205
+ # @return [ String ] The JSON string.
206
+ def as_json(options = nil)
207
+ entries.as_json(options)
208
+ end
209
+
198
210
  class << self
199
211
 
200
212
  # Encaspulates the behavior of taking arguments and parsing them into a
@@ -297,6 +309,7 @@ module Mongoid #:nodoc:
297
309
  def initialize_copy(other)
298
310
  @selector = other.selector.dup
299
311
  @options = other.options.dup
312
+ @context = nil
300
313
  end
301
314
 
302
315
  # Update the selector setting the operator on the value for each key in the
@@ -307,7 +320,8 @@ module Mongoid #:nodoc:
307
320
  # <tt>criteria.update_selector({ :field => "value" }, "$in")</tt>
308
321
  def update_selector(attributes, operator)
309
322
  clone.tap do |crit|
310
- attributes.each do |key, value|
323
+ converted = BSON::ObjectId.convert(klass, attributes || {})
324
+ converted.each do |key, value|
311
325
  unless crit.selector[key]
312
326
  crit.selector[key] = { operator => value }
313
327
  else
@@ -50,26 +50,50 @@ module Mongoid #:nodoc:
50
50
  def any_of(*args)
51
51
  clone.tap do |crit|
52
52
  criterion = @selector["$or"] || []
53
- expanded = args.collect(&:expand_complex_criteria)
53
+ converted = BSON::ObjectId.convert(klass, args.flatten)
54
+ expanded = converted.collect(&:expand_complex_criteria)
54
55
  crit.selector["$or"] = criterion.concat(expanded)
55
56
  end
56
57
  end
57
58
  alias :or :any_of
58
59
 
59
- # Using the existing criteria, find a document by a single id, multiple
60
- # ids, or using a conditions hash.
60
+ # Find the matchind document in the criteria, either based on id or
61
+ # conditions.
61
62
  #
62
- # @example Find a single document by id.
63
- # Person.where(:title => "Sir").find(id)
63
+ # @todo Durran: DRY up duplicated code in a few places.
64
64
  #
65
- # @example Find multiple documents by ids.
66
- # Person.where(:title => "Sir").find([ id_one, id_two ])
65
+ # @example Find by an id.
66
+ # criteria.find(BSON::ObjectId.new)
67
67
  #
68
- # @return [ Document, Array<Document> ] The matching document(s).
68
+ # @example Find by multiple ids.
69
+ # criteria.find([ BSON::ObjectId.new, BSON::ObjectId.new ])
69
70
  #
70
- # @since 2.0.0.rc.1
71
+ # @example Conditionally find all matching documents.
72
+ # criteria.find(:all, :conditions => { :title => "Sir" })
73
+ #
74
+ # @example Conditionally find the first document.
75
+ # criteria.find(:first, :conditions => { :title => "Sir" })
76
+ #
77
+ # @example Conditionally find the last document.
78
+ # criteria.find(:last, :conditions => { :title => "Sir" })
79
+ #
80
+ # @param [ Symbol, BSON::ObjectId, Array<BSON::ObjectId> ] arg The
81
+ # argument to search with.
82
+ # @param [ Hash ] options The options to search with.
83
+ #
84
+ # @return [ Document, Criteria ] The matching document(s).
71
85
  def find(*args)
72
- id_criteria(*args)
86
+ raise Errors::InvalidOptions.new(
87
+ :calling_document_find_with_nil_is_invalid, {}
88
+ ) if args[0].nil?
89
+ type, criteria = Criteria.parse!(klass, embedded, *args)
90
+ criteria.merge(self) if criteria.is_a?(Criteria)
91
+ case type
92
+ when :first then return criteria.one
93
+ when :last then return criteria.last
94
+ else
95
+ return criteria
96
+ end
73
97
  end
74
98
 
75
99
  # Adds a criterion to the +Criteria+ that specifies values where any can
@@ -117,7 +141,8 @@ module Mongoid #:nodoc:
117
141
  clone.tap do |crit|
118
142
  selector = case selector
119
143
  when String then {"$where" => selector}
120
- else selector ? selector.expand_complex_criteria : {}
144
+ else
145
+ BSON::ObjectId.convert(klass, selector || {}).expand_complex_criteria
121
146
  end
122
147
 
123
148
  selector.each_pair do |key, value|
@@ -13,7 +13,9 @@ module Mongoid #:nodoc:
13
13
  def inspect
14
14
  "#<Mongoid::Criteria\n" <<
15
15
  " selector: #{selector.inspect},\n" <<
16
- " options: #{options.inspect}>\n"
16
+ " options: #{options.inspect},\n" <<
17
+ " class: #{klass},\n" <<
18
+ " embedded: #{embedded}>\n"
17
19
  end
18
20
  end
19
21
  end
@@ -106,12 +106,12 @@ module Mongoid #:nodoc:
106
106
  ids.flatten!
107
107
  if ids.size > 1
108
108
  any_in(
109
- :_id => ::BSON::ObjectId.cast!(klass, ids, klass.primary_key.nil?)
109
+ :_id => ::BSON::ObjectId.convert(klass, ids)
110
110
  )
111
111
  else
112
112
  clone.tap do |crit|
113
113
  crit.selector[:_id] =
114
- ::BSON::ObjectId.cast!(klass, ids.first, klass.primary_key.nil?)
114
+ ::BSON::ObjectId.convert(klass, ids.first)
115
115
  end
116
116
  end
117
117
  end
@@ -102,6 +102,7 @@ module Mongoid #:nodoc:
102
102
  #
103
103
  # <tt>person.move_changes</tt>
104
104
  def move_changes
105
+ @validated = false
105
106
  @previous_modifications = modifications.dup
106
107
  @modifications = {}
107
108
  end
@@ -117,8 +117,8 @@ module Mongoid #:nodoc:
117
117
  process(attrs) do |document|
118
118
  yield self if block_given?
119
119
  identify
120
- run_callbacks(:initialize) { document }
121
120
  end
121
+ run_callbacks(:initialize) { self }
122
122
  end
123
123
 
124
124
  # Return the attributes hash.
@@ -147,9 +147,10 @@ module Mongoid #:nodoc:
147
147
  raise Errors::DocumentNotFound.new(self.class, id) if reloaded.nil?
148
148
  end
149
149
  @attributes = {}.merge(reloaded || {})
150
+ reset_modifications
150
151
  tap do
151
152
  relations.keys.each do |name|
152
- if relation_exists?(name)
153
+ if instance_variable_defined?("@#{name}")
153
154
  remove_instance_variable("@#{name}")
154
155
  end
155
156
  end
@@ -189,15 +190,15 @@ module Mongoid #:nodoc:
189
190
  # the current document.
190
191
  #
191
192
  # @example Get the full hierarchy.
192
- # person.to_hash
193
+ # person.as_document
193
194
  #
194
195
  # @return [ Hash ] A hash of all attributes in the hierarchy.
195
- def to_hash
196
+ def as_document
196
197
  attributes = @attributes
197
198
  attributes.tap do |attrs|
198
199
  relations.select { |name, meta| meta.embedded? }.each do |name, meta|
199
200
  relation = send(name, false, :continue => false)
200
- attrs[name] = relation.to_hash unless relation.blank?
201
+ attrs[name] = relation.as_document unless relation.blank?
201
202
  end
202
203
  end
203
204
  end
@@ -248,6 +249,11 @@ module Mongoid #:nodoc:
248
249
  def _types
249
250
  @_type ||= [descendants + [self]].flatten.uniq.map(&:to_s)
250
251
  end
252
+
253
+ # Set the i18n scope to overwrite ActiveModel.
254
+ def i18n_scope
255
+ :mongoid
256
+ end
251
257
  end
252
258
  end
253
259
  end
@@ -6,8 +6,8 @@ module Mongoid #:nodoc:
6
6
  def set(value)
7
7
  return nil if value.blank?
8
8
  begin
9
- Integer(value)
10
- rescue ArgumentError => e
9
+ value.to_s =~ /\./ ? Float(value) : Integer(value)
10
+ rescue
11
11
  value
12
12
  end
13
13
  end
@@ -2,8 +2,21 @@
2
2
  module Mongoid #:nodoc:
3
3
  module Extensions #:nodoc:
4
4
  module ObjectId #:nodoc:
5
- module Conversions #:nodoc:
6
5
 
6
+ # Provides conversions to and from BSON::ObjectIds and Strings, Arrays,
7
+ # and Hashes.
8
+ module Conversions
9
+
10
+ # Set the BSON::ObjectId value.
11
+ #
12
+ # @example Set the value.
13
+ # BSON::ObjectId.set("4c52c439931a90ab29000003")
14
+ #
15
+ # @param [ String, BSON::ObjectId ] value The value to set.
16
+ #
17
+ # @return [ BSON::ObjectId ] The set value.
18
+ #
19
+ # @since 1.0
7
20
  def set(value)
8
21
  if value.is_a?(::String)
9
22
  BSON::ObjectId.from_string(value) unless value.blank?
@@ -12,41 +25,58 @@ module Mongoid #:nodoc:
12
25
  end
13
26
  end
14
27
 
28
+ # Get the BSON::ObjectId value.
29
+ #
30
+ # @example Get the value.
31
+ # BSON::ObjectId.set(BSON::ObjectId.new)
32
+ #
33
+ # @param [ BSON::ObjectId ] value The value to get.
34
+ #
35
+ # @return [ BSON::ObjectId ] The value.
36
+ #
37
+ # @since 1.0
15
38
  def get(value)
16
39
  value
17
40
  end
18
41
 
19
- # If the document is using BSON::ObjectIds the convert the argument to
20
- # either an object id or an array of them if the supplied argument is an
21
- # Array. Otherwise just return.
22
- #
23
- # Options:
24
- # args: A +String+ or an +Array+ convert to +BSON::ObjectId+
25
- # cast: A +Boolean+ define if we can or not cast to BSON::ObjectId.
26
- # If false, we use the default type of args
27
- #
28
- # Example:
29
- #
30
- # <tt>Mongoid.cast_ids!("4ab2bc4b8ad548971900005c", true)</tt>
31
- # <tt>Mongoid.cast_ids!(["4ab2bc4b8ad548971900005c"])</tt>
32
- #
33
- # Returns:
34
- #
35
- # If using object ids:
36
- # An +Array+ of +BSON::ObjectId+ of each element if params is an +Array+
37
- # A +BSON::ObjectId+ from params if params is +String+
38
- # Otherwise:
39
- # <tt>args</tt>
40
- def cast!(klass, args, cast = true)
41
- if !klass.using_object_ids? || args.is_a?(::BSON::ObjectId) || !cast
42
- return args
43
- end
44
- if args.is_a?(::String)
45
- ::BSON::ObjectId(args)
46
- elsif args.is_a?(::Array)
47
- args.map{ |a|
48
- a.is_a?(::BSON::ObjectId) ? a : ::BSON::ObjectId(a)
49
- }
42
+ # Convert the supplied arguments to object ids based on the class
43
+ # settings.
44
+ #
45
+ # @example Convert a string to an object id
46
+ # BSON::ObjectId.convert(Person, "4c52c439931a90ab29000003")
47
+ #
48
+ # @example Convert an array of strings to object ids.
49
+ # BSON::ObjectId.convert(Person, [ "4c52c439931a90ab29000003" ])
50
+ #
51
+ # @example Convert a hash of id strings to object ids.
52
+ # BSON::ObjectId.convert(Person, { :_id => "4c52c439931a90ab29000003" })
53
+ #
54
+ # @param [ Class ] klass The class to convert the ids for.
55
+ # @param [ Object, Array, Hash ] args The object to convert.
56
+ #
57
+ # @raise BSON::InvalidObjectId If using object ids and passed bad
58
+ # strings.
59
+ #
60
+ # @return [ BSON::ObjectId, Array, Hash ] The converted object ids.
61
+ #
62
+ # @since 2.0.0.rc.7
63
+ def convert(klass, args)
64
+ return args if args.is_a?(BSON::ObjectId) || !klass.using_object_ids?
65
+ case args
66
+ when ::String
67
+ BSON::ObjectId.from_string(args)
68
+ when ::Array
69
+ args.map do |arg|
70
+ convert(klass, arg)
71
+ end
72
+ when ::Hash
73
+ args.tap do |hash|
74
+ args.each_pair do |key, value|
75
+ begin
76
+ args[key] = convert(klass, value)
77
+ rescue BSON::InvalidObjectId; end
78
+ end
79
+ end
50
80
  else
51
81
  args
52
82
  end