mongoid 2.0.0.rc.7 → 2.0.0.rc.8

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 (90) hide show
  1. data/lib/config/locales/en.yml +3 -0
  2. data/lib/config/locales/id.yml +46 -0
  3. data/lib/config/locales/ja.yml +40 -0
  4. data/lib/config/locales/vi.yml +45 -0
  5. data/lib/mongoid.rb +5 -3
  6. data/lib/mongoid/attributes.rb +24 -63
  7. data/lib/mongoid/attributes/processing.rb +5 -2
  8. data/lib/mongoid/callbacks.rb +10 -0
  9. data/lib/mongoid/collection.rb +24 -0
  10. data/lib/mongoid/collections/master.rb +14 -6
  11. data/lib/mongoid/collections/operations.rb +1 -1
  12. data/lib/mongoid/collections/retry.rb +39 -0
  13. data/lib/mongoid/collections/slaves.rb +26 -10
  14. data/lib/mongoid/components.rb +4 -4
  15. data/lib/mongoid/config.rb +6 -3
  16. data/lib/mongoid/contexts.rb +0 -1
  17. data/lib/mongoid/contexts/enumerable.rb +19 -7
  18. data/lib/mongoid/contexts/mongo.rb +9 -5
  19. data/lib/mongoid/copyable.rb +10 -8
  20. data/lib/mongoid/criteria.rb +83 -61
  21. data/lib/mongoid/criterion/builder.rb +34 -0
  22. data/lib/mongoid/criterion/creational.rb +2 -2
  23. data/lib/mongoid/criterion/exclusion.rb +58 -32
  24. data/lib/mongoid/criterion/inclusion.rb +49 -10
  25. data/lib/mongoid/criterion/optional.rb +1 -1
  26. data/lib/mongoid/criterion/selector.rb +80 -11
  27. data/lib/mongoid/cursor.rb +6 -1
  28. data/lib/mongoid/default_scope.rb +27 -19
  29. data/lib/mongoid/document.rb +26 -1
  30. data/lib/mongoid/errors.rb +1 -0
  31. data/lib/mongoid/errors/mixed_relations.rb +37 -0
  32. data/lib/mongoid/extensions/object_id/conversions.rb +7 -4
  33. data/lib/mongoid/factory.rb +1 -1
  34. data/lib/mongoid/field.rb +47 -30
  35. data/lib/mongoid/fields.rb +9 -2
  36. data/lib/mongoid/finders.rb +15 -49
  37. data/lib/mongoid/identity.rb +6 -4
  38. data/lib/mongoid/keys.rb +85 -31
  39. data/lib/mongoid/multi_parameter_attributes.rb +2 -2
  40. data/lib/mongoid/named_scope.rb +129 -28
  41. data/lib/mongoid/observer.rb +36 -0
  42. data/lib/mongoid/paranoia.rb +3 -3
  43. data/lib/mongoid/paths.rb +1 -1
  44. data/lib/mongoid/persistence.rb +2 -0
  45. data/lib/mongoid/persistence/atomic.rb +88 -0
  46. data/lib/mongoid/persistence/atomic/add_to_set.rb +30 -0
  47. data/lib/mongoid/persistence/atomic/inc.rb +28 -0
  48. data/lib/mongoid/persistence/atomic/operation.rb +44 -0
  49. data/lib/mongoid/persistence/atomic/pull_all.rb +33 -0
  50. data/lib/mongoid/persistence/atomic/push.rb +28 -0
  51. data/lib/mongoid/railtie.rb +13 -1
  52. data/lib/mongoid/relations.rb +1 -0
  53. data/lib/mongoid/relations/accessors.rb +20 -2
  54. data/lib/mongoid/relations/builders/embedded/one.rb +1 -0
  55. data/lib/mongoid/relations/builders/nested_attributes/many.rb +17 -6
  56. data/lib/mongoid/relations/builders/referenced/many.rb +2 -1
  57. data/lib/mongoid/relations/builders/referenced/one.rb +1 -0
  58. data/lib/mongoid/relations/embedded/atomic.rb +86 -0
  59. data/lib/mongoid/relations/embedded/atomic/operation.rb +63 -0
  60. data/lib/mongoid/relations/embedded/atomic/pull.rb +65 -0
  61. data/lib/mongoid/relations/embedded/atomic/push_all.rb +59 -0
  62. data/lib/mongoid/relations/embedded/atomic/set.rb +61 -0
  63. data/lib/mongoid/relations/embedded/atomic/unset.rb +41 -0
  64. data/lib/mongoid/relations/embedded/many.rb +57 -25
  65. data/lib/mongoid/relations/macros.rb +6 -4
  66. data/lib/mongoid/relations/many.rb +51 -10
  67. data/lib/mongoid/relations/metadata.rb +4 -2
  68. data/lib/mongoid/relations/proxy.rb +39 -24
  69. data/lib/mongoid/relations/referenced/many.rb +47 -26
  70. data/lib/mongoid/relations/referenced/many_to_many.rb +47 -14
  71. data/lib/mongoid/relations/referenced/one.rb +14 -0
  72. data/lib/mongoid/sharding.rb +51 -0
  73. data/lib/mongoid/state.rb +3 -2
  74. data/lib/mongoid/timestamps.rb +5 -29
  75. data/lib/mongoid/timestamps/created.rb +31 -0
  76. data/lib/mongoid/timestamps/updated.rb +33 -0
  77. data/lib/mongoid/validations.rb +10 -3
  78. data/lib/mongoid/validations/referenced.rb +58 -0
  79. data/lib/mongoid/version.rb +1 -1
  80. data/lib/mongoid/versioning.rb +67 -5
  81. data/lib/rails/generators/mongoid/model/templates/model.rb +2 -0
  82. data/lib/rails/generators/mongoid/observer/observer_generator.rb +17 -0
  83. data/lib/rails/generators/mongoid/observer/templates/observer.rb +4 -0
  84. data/lib/rails/generators/mongoid_generator.rb +10 -1
  85. data/lib/rails/mongoid.rb +1 -0
  86. metadata +29 -8
  87. data/lib/mongoid/contexts/ids.rb +0 -25
  88. data/lib/mongoid/modifiers.rb +0 -24
  89. data/lib/mongoid/modifiers/command.rb +0 -18
  90. data/lib/mongoid/modifiers/inc.rb +0 -24
@@ -0,0 +1,65 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Relations #:nodoc:
4
+ module Embedded #:nodoc:
5
+ module Atomic #:nodoc:
6
+
7
+ class Pull < Operation
8
+
9
+ # Get the merged operations for the single atomic set.
10
+ #
11
+ # @example Get the operations
12
+ # set.operations
13
+ #
14
+ # @return [ Hash ] The pull operations.
15
+ #
16
+ # @since 2.0.0
17
+ def operations
18
+ { "$pull" =>
19
+ { path =>
20
+ { "_id" =>
21
+ { "$in" => documents.map { |doc| doc["_id"] } }
22
+ }
23
+ }
24
+ }
25
+ end
26
+
27
+ private
28
+
29
+ # Parses the incoming operations to get the documents to set.
30
+ #
31
+ # @example Parse the operations.
32
+ # set.parse(
33
+ # { "$pull" => { "addresses" => { "_id" => "street" } } }
34
+ # )
35
+ #
36
+ # @param [ Hash ] operations The ops to parse.
37
+ #
38
+ # @since 2.0.0
39
+ def parse(operations)
40
+ modifier = operations.keys.first
41
+ extract(modifier, operations[modifier])
42
+ end
43
+
44
+ # Extract a document from the operation.
45
+ #
46
+ # @example Extract the document.
47
+ # set.extract({ "$pull" => [{ "_id" => "street" }] })
48
+ #
49
+ # @param [ Hash ] operation The op to extract from.
50
+ #
51
+ # @since 2.0.0
52
+ def extract(modifier, operations)
53
+ @path = operations.keys.first
54
+ case modifier
55
+ when "$pull"
56
+ documents.push(operations[path])
57
+ when "$pullAll"
58
+ documents.concat(operations[path])
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,59 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Relations #:nodoc:
4
+ module Embedded #:nodoc:
5
+ module Atomic #:nodoc:
6
+
7
+ class PushAll < Operation
8
+
9
+ # Get the merged operations for the single atomic set.
10
+ #
11
+ # @example Get the operations
12
+ # set.operations
13
+ #
14
+ # @return [ Hash ] The set operations.
15
+ #
16
+ # @since 2.0.0
17
+ def operations
18
+ { "$pushAll" => { path => documents } }
19
+ end
20
+
21
+ private
22
+
23
+ # Parses the incoming operations to get the documents to set.
24
+ #
25
+ # @example Parse the operations.
26
+ # set.parse(
27
+ # { "$push" => { "addresses" => { "_id" => "street" } } }
28
+ # )
29
+ #
30
+ # @param [ Hash ] operations The ops to parse.
31
+ #
32
+ # @since 2.0.0
33
+ def parse(operations)
34
+ modifier = operations.keys.first
35
+ extract(modifier, operations[modifier])
36
+ end
37
+
38
+ # Extract a document from the operation.
39
+ #
40
+ # @example Extract the document.
41
+ # set.extract({ "$pushAll" => [{ "_id" => "street" }] })
42
+ #
43
+ # @param [ Hash ] operation The op to extract from.
44
+ #
45
+ # @since 2.0.0
46
+ def extract(modifier, operations)
47
+ @path = operations.keys.first
48
+ case modifier
49
+ when "$push"
50
+ documents.push(operations[path])
51
+ when "$pushAll"
52
+ documents.concat(operations[path])
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,61 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Relations #:nodoc:
4
+ module Embedded #:nodoc:
5
+ module Atomic #:nodoc:
6
+
7
+ class Set < Operation
8
+
9
+ # Get the merged operations for the single atomic set.
10
+ #
11
+ # @example Get the operations
12
+ # set.operations
13
+ #
14
+ # @return [ Hash ] The set operations.
15
+ #
16
+ # @since 2.0.0
17
+ def operations
18
+ { "$set" => { path => documents } }
19
+ end
20
+
21
+ private
22
+
23
+ # Parses the incoming operations to get the documents to set.
24
+ #
25
+ # @example Parse the operations.
26
+ # set.parse(
27
+ # { "addresses" => { "$pushAll" => [{ "_id" => "street" }] } }
28
+ # )
29
+ #
30
+ # @param [ Hash ] operations The ops to parse.
31
+ #
32
+ # @since 2.0.0
33
+ def parse(operations)
34
+ modifier = operations.keys.first
35
+ extract(modifier, operations[modifier])
36
+ end
37
+
38
+ # Extract a document from the operation.
39
+ #
40
+ # @example Extract the document.
41
+ # set.extract({ "$pushAll" => [{ "_id" => "street" }] })
42
+ #
43
+ # @param [ Hash ] operation The op to extract from.
44
+ #
45
+ # @since 2.0.0
46
+ def extract(modifier, operations)
47
+ @path = operations.keys.first
48
+ case modifier
49
+ when "$push"
50
+ documents.push(operations[path])
51
+ when "$pushAll"
52
+ documents.push(operations[path].first)
53
+ when "$set"
54
+ @documents = operations[path]
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,41 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Relations #:nodoc:
4
+ module Embedded #:nodoc:
5
+ module Atomic #:nodoc:
6
+
7
+ class Unset < Operation
8
+
9
+ # Get the merged operations for the single atomic set.
10
+ #
11
+ # @example Get the operations
12
+ # set.operations
13
+ #
14
+ # @return [ Hash ] The set operations.
15
+ #
16
+ # @since 2.0.0
17
+ def operations
18
+ { "$unset" => { path => true } }
19
+ end
20
+
21
+ private
22
+
23
+ # Parses the incoming operations to get the documents to set.
24
+ #
25
+ # @example Parse the operations.
26
+ # set.parse(
27
+ # { "addresses" => { "$pushAll" => [{ "_id" => "street" }] } }
28
+ # )
29
+ #
30
+ # @param [ Hash ] operations The ops to parse.
31
+ #
32
+ # @since 2.0.0
33
+ def parse(operations)
34
+ modifier = operations.keys.first
35
+ @path ||= operations[modifier].keys.first
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -6,6 +6,31 @@ module Mongoid # :nodoc:
6
6
  # This class handles the behaviour for a document that embeds many other
7
7
  # documents within in it as an array.
8
8
  class Many < Relations::Many
9
+ include Atomic
10
+
11
+ # Appends a document or array of documents to the relation. Will set
12
+ # the parent and update the index in the process.
13
+ #
14
+ # @example Append a document.
15
+ # person.addresses << address
16
+ #
17
+ # @example Push a document.
18
+ # person.addresses.push(address)
19
+ #
20
+ # @example Concat with other documents.
21
+ # person.addresses.concat([ address_one, address_two ])
22
+ #
23
+ # @param [ Document, Array<Document> ] *args Any number of documents.
24
+ def <<(*args)
25
+ options = default_options(args)
26
+ atomically(:$pushAll) do
27
+ args.flatten.each do |doc|
28
+ return doc unless doc
29
+ append(doc, options)
30
+ doc.save if base.persisted? && !options[:binding]
31
+ end
32
+ end
33
+ end
9
34
 
10
35
  # Binds the base object to the inverse of the relation. This is so we
11
36
  # are referenced to the actual objects themselves and dont hit the
@@ -26,7 +51,9 @@ module Mongoid # :nodoc:
26
51
  # @since 2.0.0.rc.1
27
52
  def bind(options = {})
28
53
  binding.bind(options)
29
- target.each(&:save) if base.persisted? && !options[:binding]
54
+ if base.persisted? && !options[:binding]
55
+ atomically(:$set) { target.each(&:save) }
56
+ end
30
57
  end
31
58
 
32
59
  # Bind the inverse relation between a single document in this proxy
@@ -80,8 +107,8 @@ module Mongoid # :nodoc:
80
107
  # @param [ Class ] type Optional class to create the document with.
81
108
  #
82
109
  # @return [ Document ] The newly created document.
83
- def create(attributes = {}, type = nil)
84
- build(attributes, type).tap(&:save)
110
+ def create(attributes = {}, type = nil, &block)
111
+ build(attributes, type, &block).tap(&:save)
85
112
  end
86
113
 
87
114
  # Create a new document in the relation. This is essentially the same
@@ -97,15 +124,15 @@ module Mongoid # :nodoc:
97
124
  # @raise [ Errors::Validations ] If a validation error occured.
98
125
  #
99
126
  # @return [ Document ] The newly created document.
100
- def create!(attributes = {}, type = nil)
101
- build(attributes, type).tap(&:save!)
127
+ def create!(attributes = {}, type = nil, &block)
128
+ build(attributes, type, &block).tap(&:save!)
102
129
  end
103
130
 
104
131
  # Delete the supplied document from the target. This method is proxied
105
132
  # in order to reindex the array after the operation occurs.
106
133
  #
107
134
  # @example Delete the document from the relation.
108
- # perosn.addresses.delete(address)
135
+ # person.addresses.delete(address)
109
136
  #
110
137
  # @param [ Document ] document The document to be deleted.
111
138
  #
@@ -128,7 +155,7 @@ module Mongoid # :nodoc:
128
155
  #
129
156
  # @return [ Integer ] The number of documents deleted.
130
157
  def delete_all(conditions = {})
131
- remove_all(conditions, false)
158
+ atomically(:$pull) { remove_all(conditions, :delete) }
132
159
  end
133
160
 
134
161
  # Destroy all the documents in the association whilst running callbacks.
@@ -143,7 +170,7 @@ module Mongoid # :nodoc:
143
170
  #
144
171
  # @return [ Integer ] The number of documents destroyed.
145
172
  def destroy_all(conditions = {})
146
- remove_all(conditions, true)
173
+ atomically(:$pull) { remove_all(conditions, :destroy) }
147
174
  end
148
175
 
149
176
  # Finds a document in this association through several different
@@ -164,15 +191,7 @@ module Mongoid # :nodoc:
164
191
  #
165
192
  # @return [ Array<Document>, Document ] A single or multiple documents.
166
193
  def find(*args)
167
- type, criteria = Criteria.parse!(self, true, *args)
168
- case type
169
- when :first then return criteria.one
170
- when :last then return criteria.last
171
- else
172
- criteria.tap do |crit|
173
- crit.documents = target if crit.is_a?(Criteria)
174
- end
175
- end
194
+ criteria.find(*args)
176
195
  end
177
196
 
178
197
  # Instantiate a new embeds_many relation.
@@ -226,8 +245,6 @@ module Mongoid # :nodoc:
226
245
  #
227
246
  # @return [ WillPaginate::Collection ] The paginated documents.
228
247
  def paginate(options)
229
- criteria = Mongoid::Criteria.translate(metadata.klass, true, options)
230
- criteria.documents = target
231
248
  criteria.paginate(options)
232
249
  end
233
250
 
@@ -248,10 +265,9 @@ module Mongoid # :nodoc:
248
265
  tap do |relation|
249
266
  relation.target = new_target || []
250
267
  if !new_target.blank?
251
- unbind(old_target, options)
252
- bind(options)
268
+ atomically(:$set) { rebind(old_target, options) }
253
269
  else
254
- unbind(old_target, options)
270
+ atomically(:$unset) { unbind(old_target, options) }
255
271
  end
256
272
  end
257
273
  end
@@ -382,17 +398,33 @@ module Mongoid # :nodoc:
382
398
  # @param [ true, false ] destroy If true then destroy, else delete.
383
399
  #
384
400
  # @return [ Integer ] The number of documents removed.
385
- def remove_all(conditions = {}, destroy = false)
386
- criteria = find(conditions || {})
401
+ def remove_all(conditions = {}, method = :delete)
402
+ criteria = find(:all, conditions || {})
387
403
  criteria.size.tap do
388
404
  criteria.each do |doc|
389
405
  target.delete(doc)
390
- destroy ? doc.destroy : doc.delete
406
+ doc.send(method)
391
407
  end
392
408
  reindex
393
409
  end
394
410
  end
395
411
 
412
+ # Convenience method to clean up the substitute code. Unbinds the old
413
+ # target and reindexes.
414
+ #
415
+ # @example Rebind the relation.
416
+ # relation.rebind([])
417
+ #
418
+ # @param [ Array<Document> ] old_target The old target.
419
+ # @param [ Hash ] options The options passed to substitute.
420
+ #
421
+ # @since 2.0.0
422
+ def rebind(old_target, options)
423
+ reindex
424
+ unbind(old_target, options)
425
+ bind(options)
426
+ end
427
+
396
428
  class << self
397
429
 
398
430
  # Return the builder that is responsible for generating the documents
@@ -78,7 +78,7 @@ module Mongoid # :nodoc:
78
78
  def embeds_many(name, options = {}, &block)
79
79
  characterize(name, Embedded::Many, options, &block).tap do |meta|
80
80
  relate(name, meta)
81
- validate_relation(meta)
81
+ validates_relation(meta)
82
82
  end
83
83
  end
84
84
 
@@ -105,7 +105,7 @@ module Mongoid # :nodoc:
105
105
  characterize(name, Embedded::One, options, &block).tap do |meta|
106
106
  relate(name, meta)
107
107
  builder(name).creator(name)
108
- validate_relation(meta)
108
+ validates_relation(meta)
109
109
  end
110
110
  end
111
111
 
@@ -131,6 +131,7 @@ module Mongoid # :nodoc:
131
131
  characterize(name, Referenced::In, options, &block).tap do |meta|
132
132
  relate(name, meta)
133
133
  reference(meta)
134
+ validates_relation(meta)
134
135
  end
135
136
  end
136
137
  alias :belongs_to_related :referenced_in
@@ -160,7 +161,7 @@ module Mongoid # :nodoc:
160
161
  relate(name, meta)
161
162
  reference(meta)
162
163
  autosave(meta)
163
- validate_relation(meta)
164
+ validates_relation(meta)
164
165
  end
165
166
  end
166
167
  alias :has_many_related :references_many
@@ -190,6 +191,7 @@ module Mongoid # :nodoc:
190
191
  characterize(name, Referenced::ManyToMany, options, &block).tap do |meta|
191
192
  relate(name, meta)
192
193
  reference(meta)
194
+ validates_relation(meta)
193
195
  end
194
196
  end
195
197
  alias :has_and_belongs_to_many :references_and_referenced_in_many
@@ -217,7 +219,7 @@ module Mongoid # :nodoc:
217
219
  relate(name, meta)
218
220
  reference(meta)
219
221
  builder(name).creator(name).autosave(meta)
220
- validate_relation(meta)
222
+ validates_relation(meta)
221
223
  end
222
224
  end
223
225
  alias :has_one_related :references_one
@@ -16,7 +16,7 @@ module Mongoid #:nodoc:
16
16
  # person.addresses.push(address)
17
17
  #
18
18
  # @example Concat with other documents.
19
- # perosn.addresses.concat([ address_one, address_two ])
19
+ # person.addresses.concat([ address_one, address_two ])
20
20
  #
21
21
  # @param [ Document, Array<Document> ] *args Any number of documents.
22
22
  def <<(*args)
@@ -40,11 +40,12 @@ module Mongoid #:nodoc:
40
40
  # @param [ Class ] type Optional class to build the document with.
41
41
  #
42
42
  # @return [ Document ] The new document.
43
- def build(attributes = {}, type = nil)
43
+ def build(attributes = {}, type = nil, &block)
44
44
  instantiated(type).tap do |doc|
45
- append(doc, default_options(:binding => true))
46
45
  doc.write_attributes(attributes)
47
46
  doc.identify
47
+ append(doc, default_options(:binding => true))
48
+ block.call(doc) if block
48
49
  end
49
50
  end
50
51
  alias :new :build
@@ -59,8 +60,9 @@ module Mongoid #:nodoc:
59
60
  # @param [ Class ] type The optional type of document to create.
60
61
  #
61
62
  # @return [ Document ] The newly created document.
62
- def create(attributes = nil, type = nil)
63
+ def create(attributes = nil, type = nil, &block)
63
64
  build(attributes, type).tap do |doc|
65
+ block.call(doc) if block
64
66
  doc.save if base.persisted?
65
67
  end
66
68
  end
@@ -103,8 +105,8 @@ module Mongoid #:nodoc:
103
105
  # @param [ Hash ] attrs The attributes to search or create with.
104
106
  #
105
107
  # @return [ Document ] An existing document or newly created one.
106
- def find_or_create_by(attrs = {})
107
- find_or(:create, attrs)
108
+ def find_or_create_by(attrs = {}, &block)
109
+ find_or(:create, attrs, &block)
108
110
  end
109
111
 
110
112
  # Find the first +Document+ given the conditions, or instantiates a new document
@@ -116,8 +118,34 @@ module Mongoid #:nodoc:
116
118
  # @param [ Hash ] attrs The attributes to search or initialize with.
117
119
  #
118
120
  # @return [ Document ] An existing document or newly instantiated one.
119
- def find_or_initialize_by(attrs = {})
120
- find_or(:build, attrs)
121
+ def find_or_initialize_by(attrs = {}, &block)
122
+ find_or(:build, attrs, &block)
123
+ end
124
+
125
+ # This proxy can never be nil.
126
+ #
127
+ # @example Is the proxy nil?
128
+ # relation.nil?
129
+ #
130
+ # @return [ false ] Always false.
131
+ #
132
+ # @since 2.0.0
133
+ def nil?
134
+ false
135
+ end
136
+
137
+ # Since method_missing is overridden we should override this as well.
138
+ #
139
+ # @example Does the proxy respond to the method?
140
+ # relation.respond_to?(:name)
141
+ #
142
+ # @param [ Symbol ] name The method name.
143
+ #
144
+ # @return [ true, false ] If the proxy responds to the method.
145
+ #
146
+ # @since 2.0.0
147
+ def respond_to?(name)
148
+ [].respond_to?(name) || methods.include?(name)
121
149
  end
122
150
 
123
151
  # Gets the document as a serializable hash, used by ActiveModel's JSON and
@@ -140,6 +168,19 @@ module Mongoid #:nodoc:
140
168
  target.map { |document| document.serializable_hash(options) }
141
169
  end
142
170
 
171
+ # Always returns the number of documents that are in memory.
172
+ #
173
+ # @example Get the number of loaded documents.
174
+ # relation.size
175
+ #
176
+ # @return [ Integer ] The number of documents in memory.
177
+ #
178
+ # @since 2.0.0
179
+ def size
180
+ target.size
181
+ end
182
+ alias :length :size
183
+
143
184
  private
144
185
 
145
186
  # Get the default options used in binding functions.
@@ -164,8 +205,8 @@ module Mongoid #:nodoc:
164
205
  # @param [ Hash ] attrs The attributes to build with.
165
206
  #
166
207
  # @return [ Document ] A matching document or a new/created one.
167
- def find_or(method, attrs = {})
168
- find(:first, :conditions => attrs) || send(method, attrs)
208
+ def find_or(method, attrs = {}, &block)
209
+ find(:first, :conditions => attrs) || send(method, attrs, &block)
169
210
  end
170
211
  end
171
212
  end