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

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