mongoid 2.1.9 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. data/CHANGELOG.md +281 -0
  2. data/{README.rdoc → README.md} +24 -14
  3. data/Rakefile +1 -1
  4. data/lib/config/locales/bg.yml +5 -0
  5. data/lib/config/locales/de.yml +5 -0
  6. data/lib/config/locales/en-GB.yml +5 -0
  7. data/lib/config/locales/en.yml +5 -0
  8. data/lib/config/locales/es.yml +3 -0
  9. data/lib/config/locales/fr.yml +5 -0
  10. data/lib/config/locales/hi.yml +5 -0
  11. data/lib/config/locales/hu.yml +5 -0
  12. data/lib/config/locales/id.yml +5 -0
  13. data/lib/config/locales/it.yml +5 -0
  14. data/lib/config/locales/ja.yml +5 -0
  15. data/lib/config/locales/kr.yml +5 -0
  16. data/lib/config/locales/nl.yml +5 -0
  17. data/lib/config/locales/pl.yml +5 -0
  18. data/lib/config/locales/pt-BR.yml +5 -0
  19. data/lib/config/locales/pt.yml +5 -0
  20. data/lib/config/locales/ro.yml +5 -0
  21. data/lib/config/locales/ru.yml +5 -0
  22. data/lib/config/locales/sv.yml +5 -0
  23. data/lib/config/locales/vi.yml +5 -0
  24. data/lib/config/locales/zh-CN.yml +5 -0
  25. data/lib/mongoid/atomic.rb +61 -10
  26. data/lib/mongoid/atomic/modifiers.rb +156 -24
  27. data/lib/mongoid/attributes.rb +38 -10
  28. data/lib/mongoid/collection.rb +21 -3
  29. data/lib/mongoid/collections.rb +52 -6
  30. data/lib/mongoid/collections/master.rb +8 -2
  31. data/lib/mongoid/collections/operations.rb +1 -0
  32. data/lib/mongoid/config.rb +5 -2
  33. data/lib/mongoid/config/database.rb +15 -2
  34. data/lib/mongoid/contexts/mongo.rb +3 -0
  35. data/lib/mongoid/criteria.rb +27 -3
  36. data/lib/mongoid/criterion/inclusion.rb +58 -0
  37. data/lib/mongoid/dirty.rb +2 -0
  38. data/lib/mongoid/document.rb +23 -0
  39. data/lib/mongoid/errors.rb +3 -0
  40. data/lib/mongoid/errors/callback.rb +26 -0
  41. data/lib/mongoid/errors/eager_load.rb +25 -0
  42. data/lib/mongoid/errors/invalid_find.rb +19 -0
  43. data/lib/mongoid/extensions.rb +3 -1
  44. data/lib/mongoid/extensions/object_id/conversions.rb +1 -1
  45. data/lib/mongoid/extras.rb +12 -2
  46. data/lib/mongoid/fields.rb +41 -2
  47. data/lib/mongoid/fields/serializable.rb +36 -0
  48. data/lib/mongoid/fields/serializable/foreign_keys/array.rb +13 -14
  49. data/lib/mongoid/fields/serializable/foreign_keys/object.rb +13 -14
  50. data/lib/mongoid/finders.rb +4 -4
  51. data/lib/mongoid/identity_map.rb +33 -20
  52. data/lib/mongoid/keys.rb +24 -1
  53. data/lib/mongoid/nested_attributes.rb +16 -4
  54. data/lib/mongoid/persistence.rb +50 -6
  55. data/lib/mongoid/persistence/operations.rb +1 -4
  56. data/lib/mongoid/persistence/operations/update.rb +3 -1
  57. data/lib/mongoid/relations/builders/nested_attributes/many.rb +27 -44
  58. data/lib/mongoid/relations/builders/referenced/many.rb +2 -1
  59. data/lib/mongoid/relations/builders/referenced/one.rb +1 -1
  60. data/lib/mongoid/relations/cascading.rb +12 -1
  61. data/lib/mongoid/relations/embedded/many.rb +11 -4
  62. data/lib/mongoid/relations/embedded/one.rb +6 -2
  63. data/lib/mongoid/relations/macros.rb +19 -12
  64. data/lib/mongoid/relations/metadata.rb +17 -0
  65. data/lib/mongoid/relations/polymorphic.rb +12 -1
  66. data/lib/mongoid/relations/proxy.rb +24 -0
  67. data/lib/mongoid/relations/referenced/in.rb +20 -0
  68. data/lib/mongoid/relations/referenced/many.rb +30 -6
  69. data/lib/mongoid/relations/referenced/many_to_many.rb +18 -2
  70. data/lib/mongoid/relations/referenced/one.rb +19 -0
  71. data/lib/mongoid/relations/reflections.rb +23 -3
  72. data/lib/mongoid/relations/targets/enumerable.rb +29 -1
  73. data/lib/mongoid/serialization.rb +1 -1
  74. data/lib/mongoid/sharding.rb +12 -2
  75. data/lib/mongoid/threaded.rb +98 -0
  76. data/lib/mongoid/version.rb +1 -1
  77. data/lib/mongoid/versioning.rb +12 -2
  78. metadata +25 -21
@@ -25,6 +25,7 @@ module Mongoid #:nodoc:
25
25
  attribute = read_attribute(name)
26
26
  ! attribute.blank? || attribute == false
27
27
  end
28
+ alias :has_attribute? :attribute_present?
28
29
 
29
30
  # Read a value from the document attributes. If the value does not exist
30
31
  # it will return nil.
@@ -55,9 +56,11 @@ module Mongoid #:nodoc:
55
56
  #
56
57
  # @since 1.0.0
57
58
  def remove_attribute(name)
58
- access = name.to_s
59
- attribute_will_change!(access)
60
- attributes.delete(access)
59
+ assigning do
60
+ access = name.to_s
61
+ attribute_will_change!(access)
62
+ attributes.delete(access)
63
+ end
61
64
  end
62
65
 
63
66
  # Override respond_to? so it responds properly for dynamic attributes.
@@ -92,12 +95,14 @@ module Mongoid #:nodoc:
92
95
  #
93
96
  # @since 1.0.0
94
97
  def write_attribute(name, value)
95
- access = name.to_s
96
- typed_value_for(access, value).tap do |value|
97
- unless attributes[access] == value || attribute_changed?(access)
98
- attribute_will_change!(access)
98
+ assigning do
99
+ access = name.to_s
100
+ typed_value_for(access, value).tap do |value|
101
+ unless attributes[access] == value || attribute_changed?(access)
102
+ attribute_will_change!(access)
103
+ end
104
+ attributes[access] = value
99
105
  end
100
- attributes[access] = value
101
106
  end
102
107
  end
103
108
  alias :[]= :write_attribute
@@ -117,8 +122,10 @@ module Mongoid #:nodoc:
117
122
  #
118
123
  # @since 1.0.0
119
124
  def write_attributes(attrs = nil, guard_protected_attributes = true)
120
- process(attrs, guard_protected_attributes) do |document|
121
- document.identify if new? && id.blank?
125
+ assigning do
126
+ process(attrs, guard_protected_attributes) do |document|
127
+ document.identify if new? && id.blank?
128
+ end
122
129
  end
123
130
  end
124
131
  alias :attributes= :write_attributes
@@ -145,6 +152,27 @@ module Mongoid #:nodoc:
145
152
  end
146
153
  end
147
154
 
155
+ # Begin the assignment of attributes. While in this block embedded
156
+ # documents will not autosave themselves in order to allow the document to
157
+ # be in a valid state.
158
+ #
159
+ # @example Execute the assignment.
160
+ # assigning do
161
+ # person.attributes = { :addresses => [ address ] }
162
+ # end
163
+ #
164
+ # @return [ Object ] The yielded value.
165
+ #
166
+ # @since 2.2.0
167
+ def assigning
168
+ begin
169
+ Threaded.begin_assign
170
+ yield
171
+ ensure
172
+ Threaded.exit_assign
173
+ end
174
+ end
175
+
148
176
  # Used for allowing accessor methods for dynamic attributes.
149
177
  #
150
178
  # @param [ String, Symbol ] name The name of the method.
@@ -17,6 +17,18 @@ module Mongoid #:nodoc
17
17
  # collection.save({ :name => "Al" })
18
18
  delegate *(Collections::Operations::PROXIED.dup << {:to => :master})
19
19
 
20
+ # Get the unwrapped driver collection for this mongoid collection.
21
+ #
22
+ # @example Get the driver collection.
23
+ # collection.driver
24
+ #
25
+ # @return [ Mongo::Collection ] The driver collection.
26
+ #
27
+ # @since 2.2.0
28
+ def driver
29
+ master.collection
30
+ end
31
+
20
32
  # Find documents from the database given a selector and options.
21
33
  #
22
34
  # @example Find documents in the collection.
@@ -56,8 +68,14 @@ module Mongoid #:nodoc
56
68
  #
57
69
  # @param [ Class ] klass The class the collection is for.
58
70
  # @param [ String ] name The name of the collection.
59
- def initialize(klass, name)
60
- @klass, @name = klass, name
71
+ # @param [ Hash ] options The collection options.
72
+ #
73
+ # @option options [ true, false ] :capped If the collection is capped.
74
+ # @option options [ Integer ] :size The capped collection size.
75
+ # @option options [ Integer ] :max The maximum number of docs in the
76
+ # capped collection.
77
+ def initialize(klass, name, options = {})
78
+ @klass, @name, @options = klass, name, options || {}
61
79
  end
62
80
 
63
81
  # Inserts one or more documents in the collection.
@@ -106,7 +124,7 @@ module Mongoid #:nodoc
106
124
  def master(options = {})
107
125
  options.delete(:cache)
108
126
  db = Mongoid.databases[klass.database] || Mongoid.master
109
- @master ||= Collections::Master.new(db, @name)
127
+ @master ||= Collections::Master.new(db, @name, @options)
110
128
  end
111
129
 
112
130
  # Updates one or more documents in the collection.
@@ -6,13 +6,41 @@ module Mongoid #:nodoc
6
6
  module Collections
7
7
  extend ActiveSupport::Concern
8
8
 
9
- delegate :collection, :db, :to => "self.class"
10
-
11
9
  included do
12
10
  cattr_accessor :_collection, :collection_name
13
11
  self.collection_name = self.name.collectionize
14
12
  end
15
13
 
14
+ # Get the collection for the class.
15
+ #
16
+ # @note Defining methods instead of delegate to avoid calls to
17
+ # Kernel.caller for class load performance reasons.
18
+ #
19
+ # @example Get the collection.
20
+ # person.collection
21
+ #
22
+ # @return [ Collection ] The class collection.
23
+ #
24
+ # @since 1.0.0
25
+ def collection
26
+ self.class.collection
27
+ end
28
+
29
+ # Get the database for the class.
30
+ #
31
+ # @note Defining methods instead of delegate to avoid calls to
32
+ # Kernel.caller for class load performance reasons.
33
+ #
34
+ # @example Get the database.
35
+ # person.db
36
+ #
37
+ # @return [ DB ] The class db.
38
+ #
39
+ # @since 1.0.0
40
+ def db
41
+ self.class.db
42
+ end
43
+
16
44
  module ClassMethods #:nodoc:
17
45
 
18
46
  # Returns the collection associated with this +Document+. If the
@@ -53,9 +81,20 @@ module Mongoid #:nodoc
53
81
  #
54
82
  # @example Store in a separate collection than the default.
55
83
  # Model.store_in :population
56
- def store_in(name)
84
+ #
85
+ # @example Store in a capped collection.
86
+ # Model.store_in :population, :capped => true, :max => 10000
87
+ #
88
+ # @param [ Symbol ] name The name of the collection.
89
+ # @param [ Hash ] options The collection options.
90
+ #
91
+ # @option options [ true, false ] :capped If the collection is capped.
92
+ # @option options [ Integer ] :size The capped collection size.
93
+ # @option options [ Integer ] :max The maximum number of docs in the
94
+ # capped collection.
95
+ def store_in(name, options = {})
57
96
  self.collection_name = name.to_s
58
- set_collection
97
+ set_collection(options)
59
98
  end
60
99
 
61
100
  protected
@@ -65,9 +104,16 @@ module Mongoid #:nodoc
65
104
  # @example Set the collection.
66
105
  # Model.set_collection
67
106
  #
107
+ # @param [ Hash ] options The collection options.
108
+ #
109
+ # @option options [ true, false ] :capped If the collection is capped.
110
+ # @option options [ Integer ] :size The capped collection size.
111
+ # @option options [ Integer ] :max The maximum number of docs in the
112
+ # capped collection.
113
+
68
114
  # @return [ Collection ] The Mongoid collection wrapper.
69
- def set_collection
70
- self._collection = Mongoid::Collection.new(self, self.collection_name)
115
+ def set_collection(options = {})
116
+ self._collection = Collection.new(self, self.collection_name, options)
71
117
  end
72
118
  end
73
119
  end
@@ -31,8 +31,14 @@ module Mongoid #:nodoc:
31
31
  #
32
32
  # @param [ Mongo::DB ] master The master database.
33
33
  # @param [ String ] name The name of the database.
34
- def initialize(master, name)
35
- @collection = master.collection(name)
34
+ # @param [ Hash ] options The collection options.
35
+ #
36
+ # @option options [ true, false ] :capped If the collection is capped.
37
+ # @option options [ Integer ] :size The capped collection size.
38
+ # @option options [ Integer ] :max The maximum number of docs in the
39
+ # capped collection.
40
+ def initialize(master, name, options = {})
41
+ @collection = master.create_collection(name, options)
36
42
  end
37
43
  end
38
44
  end
@@ -28,6 +28,7 @@ module Mongoid #:nodoc:
28
28
  :drop,
29
29
  :drop_index,
30
30
  :drop_indexes,
31
+ :find_and_modify,
31
32
  :insert,
32
33
  :remove,
33
34
  :rename,
@@ -166,7 +166,10 @@ module Mongoid #:nodoc
166
166
  #
167
167
  # @return [ Logger ] The newly set logger.
168
168
  def logger=(logger)
169
- @logger = logger
169
+ case logger
170
+ when Logger then @logger = logger
171
+ when false, nil then @logger = nil
172
+ end
170
173
  end
171
174
 
172
175
  # Purge all data in all collections, including indexes.
@@ -202,7 +205,7 @@ module Mongoid #:nodoc
202
205
  # set is not a valid +Mongo::DB+, then an error will be raised.
203
206
  #
204
207
  # @example Set the master database.
205
- # config.master = Mongo::Connection.db("test")
208
+ # config.master = Mongo::Connection.new.db("test")
206
209
  #
207
210
  # @param [ Mongo::DB ] db The master database.
208
211
  #
@@ -7,7 +7,7 @@ module Mongoid #:nodoc:
7
7
  class Database < Hash
8
8
 
9
9
  # keys to remove from self to not pass through to Mongo::Connection
10
- PRIVATE_OPTIONS = %w(uri database username password)
10
+ PRIVATE_OPTIONS = %w(uri database username password logger)
11
11
 
12
12
  # Configure the database connections. This will return an array
13
13
  # containing the master and an array of slaves.
@@ -108,6 +108,19 @@ module Mongoid #:nodoc:
108
108
  end
109
109
  end
110
110
 
111
+ # Should we use a logger?
112
+ #
113
+ # @example Should we use a logger?
114
+ # database.logger?
115
+ #
116
+ # @return [ true, false ] Defaults to true, false if specifically
117
+ # defined.
118
+ #
119
+ # @since 2.2.0
120
+ def logger?
121
+ self[:logger].nil? || self[:logger] ? true : false
122
+ end
123
+
111
124
  # Convenience for accessing the hash via dot notation.
112
125
  #
113
126
  # @example Access a value in alternate syntax.
@@ -147,7 +160,7 @@ module Mongoid #:nodoc:
147
160
  def optional(slave = false)
148
161
  ({
149
162
  :pool_size => pool_size,
150
- :logger => Mongoid::Logger.new,
163
+ :logger => logger? ? Mongoid::Logger.new : nil,
151
164
  :slave_ok => slave
152
165
  }).merge(self).reject { |k,v| PRIVATE_OPTIONS.include? k }.
153
166
  inject({}) { |memo, (k, v)| memo[k.to_sym] = v; memo} # mongo likes symbols
@@ -140,6 +140,9 @@ module Mongoid #:nodoc:
140
140
  #
141
141
  # @return [ Cursor ] An enumerable +Cursor+ of results.
142
142
  def execute
143
+ criteria.inclusions.reject! do |metadata|
144
+ metadata.eager_load(criteria)
145
+ end
143
146
  klass.collection.find(selector, process_options) || []
144
147
  end
145
148
 
@@ -32,7 +32,6 @@ module Mongoid #:nodoc:
32
32
  include Criterion::Optional
33
33
 
34
34
  attr_accessor \
35
- :collection,
36
35
  :documents,
37
36
  :embedded,
38
37
  :ids,
@@ -109,6 +108,18 @@ module Mongoid #:nodoc:
109
108
  end
110
109
  end
111
110
 
111
+ # Get the collection associated with the criteria.
112
+ #
113
+ # @example Get the collection.
114
+ # criteria.collection
115
+ #
116
+ # @return [ Collection ] The collection.
117
+ #
118
+ # @since 2.2.0
119
+ def collection
120
+ klass.collection
121
+ end
122
+
112
123
  # Return or create the context in which this criteria should be executed.
113
124
  #
114
125
  # This will return an Enumerable context if the class is embedded,
@@ -154,7 +165,7 @@ module Mongoid #:nodoc:
154
165
  #
155
166
  # @since 2.0.0
156
167
  def freeze
157
- context and super
168
+ context and inclusions and super
158
169
  end
159
170
 
160
171
  # Merges the supplied argument hash into a single criteria
@@ -298,7 +309,7 @@ module Mongoid #:nodoc:
298
309
  #
299
310
  # @since 2.0.0
300
311
  def raise_invalid
301
- raise Errors::InvalidOptions.new(:calling_document_find_with_nil_is_invalid, {})
312
+ raise Errors::InvalidFind.new
302
313
  end
303
314
 
304
315
  protected
@@ -316,6 +327,18 @@ module Mongoid #:nodoc:
316
327
  other.is_a?(Criteria) ? other.entries : other
317
328
  end
318
329
 
330
+ # Get the raw driver collection from the criteria.
331
+ #
332
+ # @example Get the raw driver collection.
333
+ # criteria.driver
334
+ #
335
+ # @return [ Mongo::Collection ] The driver collection.
336
+ #
337
+ # @since 2.2.0
338
+ def driver
339
+ collection.driver
340
+ end
341
+
319
342
  # Clone or dup the current +Criteria+. This will return a new criteria with
320
343
  # the selector, options, klass, embedded options, etc intact.
321
344
  #
@@ -331,6 +354,7 @@ module Mongoid #:nodoc:
331
354
  def initialize_copy(other)
332
355
  @selector = other.selector.dup
333
356
  @options = other.options.dup
357
+ @includes = other.inclusions.dup
334
358
  @context = nil
335
359
  end
336
360
 
@@ -125,6 +125,64 @@ module Mongoid #:nodoc:
125
125
  end
126
126
  alias :any_in :in
127
127
 
128
+ # Eager loads all the provided relations. Will load all the documents
129
+ # into the identity map who's ids match based on the extra query for the
130
+ # ids.
131
+ #
132
+ # @note This will only work if Mongoid's identity map is enabled. To do
133
+ # so set identity_map_enabled: true in your mongoid.yml
134
+ #
135
+ # @note This will work for embedded relations that reference another
136
+ # collection via belongs_to as well.
137
+ #
138
+ # @note Eager loading brings all the documents into memory, so there is a
139
+ # sweet spot on the performance gains. Internal benchmarks show that
140
+ # eager loading becomes slower around 100k documents, but this will
141
+ # naturally depend on the specific application.
142
+ #
143
+ # @example Eager load the provided relations.
144
+ # Person.includes(:posts, :game)
145
+ #
146
+ # @param [ Array<Symbol> ] relations The names of the relations to eager
147
+ # load.
148
+ #
149
+ # @return [ Criteria ] The cloned criteria.
150
+ #
151
+ # @since 2.2.0
152
+ def includes(*relations)
153
+ relations.each do |name|
154
+ inclusions.push(klass.reflect_on_association(name))
155
+ end
156
+ clone
157
+ end
158
+
159
+ # Get a list of criteria that are to be executed for eager loading.
160
+ #
161
+ # @example Get the eager loading inclusions.
162
+ # Person.includes(:game).inclusions
163
+ #
164
+ # @return [ Array<Metadata> ] The inclusions.
165
+ #
166
+ # @since 2.2.0
167
+ def inclusions
168
+ @inclusions ||= []
169
+ end
170
+
171
+ # Loads an array of ids only for the current criteria. Used by eager
172
+ # loading to determine the documents to load.
173
+ #
174
+ # @example Load the related ids.
175
+ # criteria.load_ids("person_id")
176
+ #
177
+ # @param [ String ] key The id or foriegn key string.
178
+ #
179
+ # @return [ Array<String, BSON::ObjectId> ] The ids to load.
180
+ #
181
+ # @since 2.2.0
182
+ def load_ids(key)
183
+ driver.find(selector, { :fields => { key => 1 }}).map { |doc| doc[key] }
184
+ end
185
+
128
186
  # Adds a criterion to the +Criteria+ that specifies values to do
129
187
  # geospacial searches by. The field must be indexed with the "2d" option.
130
188
  #
@@ -41,6 +41,8 @@ module Mongoid #:nodoc:
41
41
  @_children = nil
42
42
  @previously_changed = changes
43
43
  @validated = false
44
+ atomic_pulls.clear
45
+ atomic_unsets.clear
44
46
  changed_attributes.clear
45
47
  end
46
48