mongoid 2.0.0.beta.9 → 2.0.0.beta.10

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 (37) hide show
  1. data/README.rdoc +2 -4
  2. data/lib/mongoid.rb +2 -1
  3. data/lib/mongoid/associations.rb +2 -3
  4. data/lib/mongoid/associations/embeds_many.rb +6 -2
  5. data/lib/mongoid/associations/embeds_one.rb +1 -3
  6. data/lib/mongoid/associations/options.rb +11 -10
  7. data/lib/mongoid/associations/references_many.rb +27 -4
  8. data/lib/mongoid/associations/references_many_as_array.rb +24 -4
  9. data/lib/mongoid/associations/references_one.rb +1 -2
  10. data/lib/mongoid/atomicity.rb +17 -4
  11. data/lib/mongoid/config.rb +97 -20
  12. data/lib/mongoid/contexts/enumerable.rb +9 -4
  13. data/lib/mongoid/contexts/mongo.rb +1 -1
  14. data/lib/mongoid/criteria.rb +7 -1
  15. data/lib/mongoid/criterion/inclusion.rb +12 -5
  16. data/lib/mongoid/criterion/optional.rb +26 -3
  17. data/lib/mongoid/cursor.rb +0 -1
  18. data/lib/mongoid/deprecation.rb +1 -2
  19. data/lib/mongoid/document.rb +25 -3
  20. data/lib/mongoid/extensions.rb +2 -1
  21. data/lib/mongoid/extensions/float/conversions.rb +1 -1
  22. data/lib/mongoid/extensions/integer/conversions.rb +1 -1
  23. data/lib/mongoid/extensions/symbol/conversions.rb +21 -0
  24. data/lib/mongoid/finders.rb +3 -2
  25. data/lib/mongoid/indexes.rb +17 -6
  26. data/lib/mongoid/logger.rb +19 -0
  27. data/lib/mongoid/paranoia.rb +12 -14
  28. data/lib/mongoid/persistence/remove_embedded.rb +0 -4
  29. data/lib/mongoid/persistence/update.rb +3 -0
  30. data/lib/mongoid/railtie.rb +37 -4
  31. data/lib/mongoid/railties/database.rake +19 -1
  32. data/lib/mongoid/validations/associated.rb +1 -1
  33. data/lib/mongoid/validations/locale/en.yml +4 -3
  34. data/lib/mongoid/validations/uniqueness.rb +19 -8
  35. data/lib/mongoid/version.rb +1 -1
  36. metadata +50 -18
  37. data/lib/mongoid/extensions/array/aliasing.rb +0 -4
@@ -6,14 +6,12 @@ Mongoid is an ODM (Object-Document-Mapper) framework for MongoDB in Ruby.
6
6
 
7
7
  == Project Tracking
8
8
 
9
- * {Mongoid on Pivotal Tracker}[http://www.pivotaltracker.com/projects/27482]
10
9
  * {Mongoid Google Group}[http://groups.google.com/group/mongoid]
11
- * {Mongoid on CI Joe}[http://ci.mongoid.org/]
12
10
  * {Mongoid Website and Documentation}[http://mongoid.org]
13
11
 
14
12
  == Compatibility
15
13
 
16
- Mongoid is developed against Ruby 1.8.6, 1.8.7, 1.9.1, 1.9.2
14
+ Mongoid is developed against Ruby 1.8.7, 1.9.1, 1.9.2
17
15
 
18
16
  = Documentation
19
17
 
@@ -22,7 +20,7 @@ Please see the new Mongoid website for up-to-date documentation:
22
20
 
23
21
  = License
24
22
 
25
- Copyright (c) 2009 Durran Jordan
23
+ Copyright (c) 2009, 2010 Durran Jordan
26
24
 
27
25
  Permission is hereby granted, free of charge, to any person obtaining
28
26
  a copy of this software and associated documentation files (the
@@ -63,6 +63,7 @@ require "mongoid/hierarchy"
63
63
  require "mongoid/identity"
64
64
  require "mongoid/indexes"
65
65
  require "mongoid/javascript"
66
+ require "mongoid/logger"
66
67
  require "mongoid/matchers"
67
68
  require "mongoid/memoization"
68
69
  require "mongoid/named_scope"
@@ -111,7 +112,7 @@ module Mongoid #:nodoc
111
112
  block_given? ? yield(config) : config
112
113
  end
113
114
 
114
- # Easy convenience method for having an alert generated from the
115
+ # Easy convenience method for generating an alert from the
115
116
  # deprecation module.
116
117
  #
117
118
  # Example:
@@ -229,8 +229,8 @@ module Mongoid # :nodoc:
229
229
 
230
230
  alias :has_one_related :references_one
231
231
 
232
- # Returns the macro associated with the supplied association name. This
233
- # will return embeds_on, embeds_many, embedded_in or nil.
232
+ # Returns the association reflection object with the supplied association
233
+ # name.
234
234
  #
235
235
  # Options:
236
236
  #
@@ -241,7 +241,6 @@ module Mongoid # :nodoc:
241
241
  # <tt>Person.reflect_on_association(:addresses)</tt>
242
242
  def reflect_on_association(name)
243
243
  association = associations[name.to_s]
244
- association ? association.macro : nil
245
244
  end
246
245
 
247
246
  protected
@@ -175,9 +175,10 @@ module Mongoid #:nodoc:
175
175
  #
176
176
  # The newly build target Document.
177
177
  def nested_build(attributes, options = {})
178
+ @parent.instance_variable_set(:@building_nested, true)
178
179
  attributes.each do |index, attrs|
179
180
  if document = detect { |document| document._index == index.to_i }
180
- if options && options[:allow_destroy] && attrs['_destroy']
181
+ if options && options[:allow_destroy] && Boolean.set(attrs['_destroy'])
181
182
  @target.delete(document)
182
183
  document.destroy
183
184
  else
@@ -186,7 +187,10 @@ module Mongoid #:nodoc:
186
187
  else
187
188
  build(attrs)
188
189
  end
189
- end; self
190
+ end
191
+ @target.each_with_index { |document, index| document._index = index }
192
+ @parent.instance_variable_set(:@building_nested, false)
193
+ self
190
194
  end
191
195
 
192
196
  # Paginate the association. Will create a new criteria, set the documents
@@ -44,9 +44,7 @@ module Mongoid #:nodoc:
44
44
  # A new target document.
45
45
  def nested_build(attributes, options = nil)
46
46
  unless @target.blank? && options[:update_only]
47
- (attributes || {}).each do |key, value|
48
- @target.write_attribute(key, value)
49
- end
47
+ @target.write_attributes(attributes)
50
48
  end; @target
51
49
  end
52
50
 
@@ -1,17 +1,17 @@
1
1
  # encoding: utf-8
2
2
  module Mongoid #:nodoc:
3
3
  module Associations #:nodoc:
4
- class Options #:nodoc:
4
+ class Options < Hash #:nodoc:
5
5
 
6
6
  # Create the new +Options+ object, which provides convenience methods for
7
7
  # accessing values out of an options +Hash+.
8
8
  def initialize(attributes = {})
9
- @attributes = attributes
9
+ self.merge!(attributes)
10
10
  end
11
11
 
12
12
  # Returns the extension if it exists, nil if not.
13
13
  def extension
14
- @attributes[:extend]
14
+ self[:extend]
15
15
  end
16
16
 
17
17
  # Returns true is the options have extensions.
@@ -19,15 +19,16 @@ module Mongoid #:nodoc:
19
19
  !extension.nil?
20
20
  end
21
21
 
22
- # Return the foreign key based off the association name.
22
+ # Return the foreign key if it exists, otherwise inflect it from the
23
+ # associated class name.
23
24
  def foreign_key
24
- key = @attributes[:foreign_key] || klass.name.to_s.foreign_key
25
+ key = self[:foreign_key] || klass.name.to_s.foreign_key
25
26
  key.to_s
26
27
  end
27
28
 
28
29
  # Returns the name of the inverse_of association
29
30
  def inverse_of
30
- @attributes[:inverse_of]
31
+ self[:inverse_of]
31
32
  end
32
33
 
33
34
  # Return a +Class+ for the options. See #class_name
@@ -39,22 +40,22 @@ module Mongoid #:nodoc:
39
40
  # was provided, then the constantized class_name will be returned. If not,
40
41
  # a constant based on the association name will be returned.
41
42
  def class_name
42
- @attributes[:class_name] || name.to_s.classify
43
+ self[:class_name] || name.to_s.classify
43
44
  end
44
45
 
45
46
  # Returns the association name of the options.
46
47
  def name
47
- @attributes[:name].to_s
48
+ self[:name].to_s
48
49
  end
49
50
 
50
51
  # Returns whether or not this association is polymorphic.
51
52
  def polymorphic
52
- @attributes[:polymorphic] == true
53
+ self[:polymorphic] == true
53
54
  end
54
55
 
55
56
  # Used with references_many to save as array of ids.
56
57
  def stored_as
57
- @attributes[:stored_as]
58
+ self[:stored_as]
58
59
  end
59
60
  end
60
61
  end
@@ -26,7 +26,7 @@ module Mongoid #:nodoc:
26
26
  # Returns the newly created object.
27
27
  def build(attributes = nil)
28
28
  load_target
29
- name = @parent.class.to_s.underscore
29
+ name = determine_name
30
30
  object = @klass.instantiate((attributes || {}).merge(name => @parent))
31
31
  @target << object
32
32
  object
@@ -38,7 +38,7 @@ module Mongoid #:nodoc:
38
38
  # the new object will then be saved.
39
39
  #
40
40
  # Returns the newly created object.
41
- def create(attributes)
41
+ def create(attributes = nil)
42
42
  build(attributes).tap(&:save)
43
43
  end
44
44
 
@@ -46,7 +46,7 @@ module Mongoid #:nodoc:
46
46
  # validation fails an error is raised.
47
47
  #
48
48
  # Returns the newly created object.
49
- def create!(attributes)
49
+ def create!(attributes = nil)
50
50
  build(attributes).tap(&:save!)
51
51
  end
52
52
 
@@ -144,6 +144,11 @@ module Mongoid #:nodoc:
144
144
  @target = @target.entries if @parent.new_record?
145
145
  end
146
146
 
147
+ def determine_name
148
+ @proxy ||= class << self; self; end
149
+ @proxy.send(:determine_name, @parent, @options)
150
+ end
151
+
147
152
  # The default query used for retrieving the documents from the database.
148
153
  # In this case we use the common API between Mongoid, ActiveRecord, and
149
154
  # DataMapper so we can do one-to-many relationships with data in other
@@ -212,10 +217,28 @@ module Mongoid #:nodoc:
212
217
  #
213
218
  # <tt>RelatesToOne.update(game, person, options)</tt>
214
219
  def update(target, document, options)
215
- name = document.class.to_s.underscore
220
+ name = determine_name(document, options)
216
221
  target.each { |child| child.send("#{name}=", document) }
217
222
  instantiate(document, options, target)
218
223
  end
224
+
225
+ protected
226
+ def determine_name(document, options)
227
+ target = document.class
228
+
229
+ if (inverse = options.inverse_of) && inverse.is_a?(Array)
230
+ inverse = [*inverse].detect { |name| target.respond_to?(name) }
231
+ end
232
+
233
+ if !inverse
234
+ association = options.klass.associations.values.detect do |metadata|
235
+ metadata.options.klass == target
236
+ end
237
+ inverse = association.name if association
238
+ end
239
+
240
+ inverse || target.to_s.underscore
241
+ end
219
242
  end
220
243
  end
221
244
  end
@@ -22,7 +22,15 @@ module Mongoid #:nodoc:
22
22
  # clean way to handle this with new documents - we want to set the
23
23
  # actual objects as well, but dont want to get in an infinite loop
24
24
  # while doing so.
25
- object.send(reverse_key(object)) << @parent.id
25
+ if inverse?
26
+ reverse_key = reverse_key(object)
27
+ case inverse_of(object).macro
28
+ when :references_many
29
+ object.send(reverse_key) << @parent.id
30
+ when :referenced_in
31
+ object.send("#{reverse_key}=", @parent.id)
32
+ end
33
+ end
26
34
  @target << object
27
35
  end
28
36
  end
@@ -42,9 +50,22 @@ module Mongoid #:nodoc:
42
50
  end
43
51
 
44
52
  protected
53
+
45
54
  # Find the inverse key for the supplied document.
46
55
  def reverse_key(document)
47
- document.send(@options.inverse_of).options.foreign_key
56
+ inverse_of(document).options.foreign_key
57
+ end
58
+
59
+ # Returns +true+ if there is an inverse association on the referenced
60
+ # model.
61
+ def inverse?
62
+ !!@options.inverse_of
63
+ end
64
+
65
+ # Returns the association on +document+ which is the inverse of this
66
+ # association.
67
+ def inverse_of(document)
68
+ document.class.associations[@options.inverse_of.to_s]
48
69
  end
49
70
 
50
71
  # The default query used for retrieving the documents from the database.
@@ -67,8 +88,7 @@ module Mongoid #:nodoc:
67
88
  # <tt>RelatesToManyAsArray.update(preferences, person, options)</tt>
68
89
  def update(target, document, options)
69
90
  target.each do |child|
70
- name = child.associations[options.inverse_of.to_s].options.name
71
- child.send(name) << document
91
+ document.send(options.name) << child
72
92
  end
73
93
  instantiate(document, options, target)
74
94
  end
@@ -24,7 +24,7 @@ module Mongoid #:nodoc:
24
24
  # newly created document.
25
25
  #
26
26
  # Returns the newly created object.
27
- def create(attributes)
27
+ def create(attributes = {})
28
28
  build(attributes).tap(&:save)
29
29
  end
30
30
 
@@ -93,7 +93,6 @@ module Mongoid #:nodoc:
93
93
  target
94
94
  end
95
95
  end
96
-
97
96
  end
98
97
  end
99
98
  end
@@ -4,7 +4,7 @@ module Mongoid #:nodoc:
4
4
  extend ActiveSupport::Concern
5
5
 
6
6
  # Get all the atomic updates that need to happen for the current
7
- # +Document+. This will include all changes that need to happen in the
7
+ # +Document+. This includes all changes that need to happen in the
8
8
  # entire hierarchy that exists below where the save call was made.
9
9
  #
10
10
  # Example:
@@ -15,9 +15,22 @@ module Mongoid #:nodoc:
15
15
  #
16
16
  # A +Hash+ of all atomic updates that need to occur.
17
17
  def _updates
18
- _children.inject({ "$set" => _sets, "$push" => {}}) do |updates, child|
19
- updates["$set"].update(child._sets)
20
- updates["$push"].update(child._pushes)
18
+ processed = {}
19
+
20
+ _children.inject({ "$set" => _sets, "$pushAll" => {}, :other => {} }) do |updates, child|
21
+ changes = child._sets
22
+ updates["$set"].update(changes)
23
+ processed[child.class] = true unless changes.empty?
24
+
25
+ target = processed.has_key?(child.class) ? :other : "$pushAll"
26
+
27
+ child._pushes.each do |attr, val|
28
+ if updates[target].has_key?(attr)
29
+ updates[target][attr] << val
30
+ else
31
+ updates[target].update({attr => [val]})
32
+ end
33
+ end
21
34
  updates
22
35
  end.delete_if do |key, value|
23
36
  value.empty?
@@ -11,10 +11,11 @@ module Mongoid #:nodoc
11
11
  :parameterize_keys,
12
12
  :persist_in_safe_mode,
13
13
  :raise_not_found_error,
14
+ :autocreate_indexes,
14
15
  :use_object_ids,
15
16
  :skip_version_check
16
17
 
17
- # Defaults the configuration options to true.
18
+ # Initializes the configuration with default settings.
18
19
  def initialize
19
20
  reset
20
21
  end
@@ -56,7 +57,7 @@ module Mongoid #:nodoc
56
57
  #
57
58
  # Returns:
58
59
  #
59
- # The Master DB instance.
60
+ # The master +Mongo::DB+ instance.
60
61
  def master=(db)
61
62
  check_database!(db)
62
63
  @master = db
@@ -73,14 +74,21 @@ module Mongoid #:nodoc
73
74
  #
74
75
  # The master +Mongo::DB+
75
76
  def master
76
- @master || (raise Errors::InvalidDatabase.new(nil))
77
+ raise Errors::InvalidDatabase.new(nil) unless @master
78
+
79
+ if @reconnect
80
+ @reconnect = false
81
+ reconnect!
82
+ end
83
+
84
+ @master
77
85
  end
78
86
 
79
87
  alias :database :master
80
88
  alias :database= :master=
81
89
 
82
- # Sets the Mongo::DB slave databases to be used. If the objects trying to me
83
- # set are not valid +Mongo::DBs+, then an error will be raise.
90
+ # Sets the Mongo::DB slave databases to be used. If the objects provided
91
+ # are not valid +Mongo::DBs+ an error will be raised.
84
92
  #
85
93
  # Example:
86
94
  #
@@ -88,7 +96,7 @@ module Mongoid #:nodoc
88
96
  #
89
97
  # Returns:
90
98
  #
91
- # The slaves DB instances.
99
+ # The slave DB instances.
92
100
  def slaves=(dbs)
93
101
  return unless dbs
94
102
  dbs.each do |db|
@@ -97,7 +105,7 @@ module Mongoid #:nodoc
97
105
  @slaves = dbs
98
106
  end
99
107
 
100
- # Returns the slave databases, or if none has been set nil
108
+ # Returns the slave databases or nil if none have been set.
101
109
  #
102
110
  # Example:
103
111
  #
@@ -110,6 +118,26 @@ module Mongoid #:nodoc
110
118
  @slaves
111
119
  end
112
120
 
121
+ # Returns the logger, or defaults to Rails logger or stdout logger.
122
+ #
123
+ # Example:
124
+ #
125
+ # <tt>Config.logger</tt>
126
+ def logger
127
+ return @logger if defined?(@logger)
128
+
129
+ @logger = defined?(Rails) ? Rails.logger : ::Logger.new($stdout)
130
+ end
131
+
132
+ # Sets the logger for Mongoid to use.
133
+ #
134
+ # Example:
135
+ #
136
+ # <tt>Config.logger = Logger.new($stdout, :warn)</tt>
137
+ def logger=(logger)
138
+ @logger = logger
139
+ end
140
+
113
141
  # Return field names that could cause destructive things to happen if
114
142
  # defined in a Mongoid::Document
115
143
  #
@@ -129,7 +157,8 @@ module Mongoid #:nodoc
129
157
  }.call
130
158
  end
131
159
 
132
- # Configure mongoid from a hash that was usually parsed out of yml.
160
+ # Configure mongoid from a hash. This is usually called after parsing a
161
+ # yaml config file such as mongoid.yml.
133
162
  #
134
163
  # Example:
135
164
  #
@@ -137,11 +166,27 @@ module Mongoid #:nodoc
137
166
  def from_hash(settings)
138
167
  _master(settings)
139
168
  _slaves(settings)
140
- settings.except("database").each_pair do |name, value|
169
+ settings.except("database", "slaves").each_pair do |name, value|
141
170
  send("#{name}=", value) if respond_to?(name)
142
171
  end
143
172
  end
144
173
 
174
+ # Convenience method for connecting to the master database after forking a
175
+ # new process.
176
+ #
177
+ # Example:
178
+ #
179
+ # <tt>Mongoid.reconnect!</tt>
180
+ def reconnect!(now = true)
181
+ if now
182
+ master.connection.connect_to_master
183
+ else
184
+ # We set a @reconnect flag so that #master knows to reconnect the next
185
+ # time the connection is accessed.
186
+ @reconnect = true
187
+ end
188
+ end
189
+
145
190
  # Reset the configuration options to the defaults.
146
191
  #
147
192
  # Example:
@@ -153,11 +198,48 @@ module Mongoid #:nodoc
153
198
  @persist_in_safe_mode = true
154
199
  @raise_not_found_error = true
155
200
  @reconnect_time = 3
201
+ @autocreate_indexes = false
156
202
  @use_object_ids = false
157
203
  @skip_version_check = false
158
204
  @time_zone = nil
159
205
  end
160
206
 
207
+ ##
208
+ # If Mongoid.use_object_ids = true
209
+ # Convert args to BSON::ObjectID
210
+ # If this args is an array, convert all args inside
211
+ # Else
212
+ # return args
213
+ #
214
+ # Options:
215
+ #
216
+ # args : A +String+ or an +Array+ convert to +BSON::ObjectID+
217
+ # cast : A +Boolean+ define if we can or not cast to BSON::ObjectID. If false, we use the default type of args
218
+ #
219
+ # Example:
220
+ #
221
+ # <tt>Mongoid.convert_to_object_id("4ab2bc4b8ad548971900005c", true)</tt>
222
+ # <tt>Mongoid.convert_to_object_id(["4ab2bc4b8ad548971900005c", "4ab2bc4b8ad548971900005d"])</tt>
223
+ #
224
+ # Returns:
225
+ #
226
+ # If Mongoid.use_object_ids = true
227
+ # An +Array+ of +BSON::ObjectID+ of each element if params is an +Array+
228
+ # A +BSON::ObjectID+ from params if params is +String+
229
+ # Else
230
+ # <tt>args</tt>
231
+ #
232
+ def convert_to_object_id(args, cast=true)
233
+ return args if !use_object_ids || args.is_a?(BSON::ObjectID) || !cast
234
+ if args.is_a?(String)
235
+ BSON::ObjectID(args)
236
+ else
237
+ args.map{ |a|
238
+ a.is_a?(BSON::ObjectID) ? a : BSON::ObjectID(a)
239
+ }
240
+ end
241
+ end
242
+
161
243
  protected
162
244
 
163
245
  # Check if the database is valid and the correct version.
@@ -173,17 +255,10 @@ module Mongoid #:nodoc
173
255
  end
174
256
  end
175
257
 
176
- # Get a Rails logger or stdout logger.
177
- #
178
- # Example:
179
- #
180
- # <tt>config.logger</tt>
181
- def logger
182
- defined?(Rails) ? Rails.logger : Logger.new($stdout)
183
- end
184
-
185
258
  # Get a master database from settings.
186
259
  #
260
+ # TODO: Durran: This code's a bit hairy, refactor.
261
+ #
187
262
  # Example:
188
263
  #
189
264
  # <tt>config._master({}, "test")</tt>
@@ -193,11 +268,11 @@ module Mongoid #:nodoc
193
268
  name = settings["database"] || mongo_uri.path.to_s.sub("/", "")
194
269
  host = settings["host"] || mongo_uri.host || "localhost"
195
270
  port = settings["port"] || mongo_uri.port || 27017
196
- pool_size = settings["pool_size"] || 1
271
+ pool_size = settings["pool_size"] || 1
197
272
  username = settings["username"] || mongo_uri.user
198
273
  password = settings["password"] || mongo_uri.password
199
274
 
200
- connection = Mongo::Connection.new(host, port, :logger => logger, :pool_size => pool_size)
275
+ connection = Mongo::Connection.new(host, port, :logger => Mongoid::Logger.new, :pool_size => pool_size)
201
276
  if username || password
202
277
  connection.add_auth(name, username, password)
203
278
  connection.apply_saved_authentication
@@ -207,6 +282,8 @@ module Mongoid #:nodoc
207
282
 
208
283
  # Get a bunch-o-slaves from settings and names.
209
284
  #
285
+ # TODO: Durran: This code's a bit hairy, refactor.
286
+ #
210
287
  # Example:
211
288
  #
212
289
  # <tt>config._slaves({}, "test")</tt>