mongoid 2.1.9 → 2.2.0

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 (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