mongo_mapper-unstable 2009.12.30 → 2010.1.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. data/README.rdoc +2 -17
  2. data/Rakefile +1 -1
  3. data/VERSION +1 -1
  4. data/lib/mongo_mapper/associations/base.rb +19 -10
  5. data/lib/mongo_mapper/associations/in_array_proxy.rb +137 -0
  6. data/lib/mongo_mapper/associations/one_proxy.rb +64 -0
  7. data/lib/mongo_mapper/associations/proxy.rb +7 -4
  8. data/lib/mongo_mapper/associations.rb +11 -3
  9. data/lib/mongo_mapper/callbacks.rb +30 -78
  10. data/lib/mongo_mapper/dirty.rb +5 -24
  11. data/lib/mongo_mapper/document.rb +117 -144
  12. data/lib/mongo_mapper/embedded_document.rb +7 -11
  13. data/lib/mongo_mapper/finder_options.rb +13 -21
  14. data/lib/mongo_mapper/mongo_mapper.rb +125 -0
  15. data/lib/mongo_mapper/pagination.rb +12 -1
  16. data/lib/mongo_mapper/rails_compatibility/embedded_document.rb +1 -0
  17. data/lib/mongo_mapper/serialization.rb +2 -2
  18. data/lib/mongo_mapper/serializers/json_serializer.rb +2 -46
  19. data/lib/mongo_mapper/support.rb +2 -2
  20. data/lib/mongo_mapper.rb +8 -2
  21. data/mongo_mapper.gemspec +14 -8
  22. data/specs.watchr +3 -5
  23. data/test/functional/associations/test_belongs_to_polymorphic_proxy.rb +8 -0
  24. data/test/functional/associations/test_belongs_to_proxy.rb +54 -9
  25. data/test/functional/associations/test_in_array_proxy.rb +309 -0
  26. data/test/functional/associations/test_many_documents_proxy.rb +103 -53
  27. data/test/functional/associations/test_many_embedded_proxy.rb +4 -14
  28. data/test/functional/associations/test_many_polymorphic_proxy.rb +2 -1
  29. data/test/functional/associations/test_one_proxy.rb +149 -0
  30. data/test/functional/test_binary.rb +13 -4
  31. data/test/functional/test_callbacks.rb +1 -5
  32. data/test/functional/test_dirty.rb +1 -4
  33. data/test/functional/test_document.rb +576 -640
  34. data/test/functional/test_embedded_document.rb +7 -20
  35. data/test/functional/test_modifiers.rb +238 -0
  36. data/test/functional/test_pagination.rb +1 -3
  37. data/test/functional/test_string_id_compatibility.rb +3 -8
  38. data/test/functional/test_validations.rb +13 -75
  39. data/test/models.rb +1 -1
  40. data/test/support/timing.rb +1 -1
  41. data/test/test_helper.rb +28 -0
  42. data/test/unit/associations/test_base.rb +54 -13
  43. data/test/unit/associations/test_proxy.rb +12 -0
  44. data/test/unit/test_document.rb +36 -26
  45. data/test/unit/test_embedded_document.rb +14 -51
  46. data/test/unit/test_finder_options.rb +20 -7
  47. data/test/unit/test_key.rb +1 -4
  48. data/test/unit/test_pagination.rb +6 -0
  49. data/test/unit/test_rails_compatibility.rb +4 -1
  50. data/test/unit/test_serializations.rb +1 -2
  51. data/test/unit/test_support.rb +4 -0
  52. data/test/unit/test_time_zones.rb +1 -2
  53. data/test/unit/test_validations.rb +3 -14
  54. metadata +12 -6
  55. data/lib/mongo_mapper/observing.rb +0 -50
  56. data/test/unit/test_observing.rb +0 -101
@@ -6,7 +6,6 @@ module MongoMapper
6
6
  model.class_eval do
7
7
  include EmbeddedDocument
8
8
  include InstanceMethods
9
- include Observing
10
9
  include Callbacks
11
10
  include Dirty
12
11
  include RailsCompatibility::Document
@@ -19,6 +18,9 @@ module MongoMapper
19
18
  end unless respond_to?(:per_page)
20
19
  end
21
20
 
21
+ extra_extensions.each { |extension| model.extend(extension) }
22
+ extra_inclusions.each { |inclusion| model.send(:include, inclusion) }
23
+
22
24
  descendants << model
23
25
  end
24
26
 
@@ -26,6 +28,34 @@ module MongoMapper
26
28
  @descendants ||= Set.new
27
29
  end
28
30
 
31
+ def self.append_extensions(*extensions)
32
+ extra_extensions.concat extensions
33
+
34
+ # Add the extension to existing descendants
35
+ descendants.each do |model|
36
+ extensions.each { |extension| model.extend(extension) }
37
+ end
38
+ end
39
+
40
+ # @api private
41
+ def self.extra_extensions
42
+ @extra_extensions ||= []
43
+ end
44
+
45
+ def self.append_inclusions(*inclusions)
46
+ extra_inclusions.concat inclusions
47
+
48
+ # Add the inclusion to existing descendants
49
+ descendants.each do |model|
50
+ inclusions.each { |inclusion| model.send :include, inclusion }
51
+ end
52
+ end
53
+
54
+ # @api private
55
+ def self.extra_inclusions
56
+ @extra_inclusions ||= []
57
+ end
58
+
29
59
  module ClassMethods
30
60
  def key(*args)
31
61
  key = super
@@ -43,18 +73,6 @@ module MongoMapper
43
73
  MongoMapper.ensure_index(self, keys_to_index, options)
44
74
  end
45
75
 
46
- # @overload find(:first, options)
47
- # @see Document.first
48
- #
49
- # @overload find(:last, options)
50
- # @see Document.last
51
- #
52
- # @overload find(:all, options)
53
- # @see Document.all
54
- #
55
- # @overload find(ids, options)
56
- #
57
- # @raise DocumentNotFound raised when no ID or arguments are provided
58
76
  def find!(*args)
59
77
  options = args.extract_options!
60
78
  case args.first
@@ -91,45 +109,21 @@ module MongoMapper
91
109
  pagination
92
110
  end
93
111
 
94
- # @param [Hash] options any conditions understood by
95
- # FinderOptions.to_mongo_criteria
96
- #
97
- # @return the first document in the ordered collection as described by
98
- # +options+
99
- #
100
- # @see FinderOptions
101
112
  def first(options={})
102
113
  find_one(options)
103
114
  end
104
115
 
105
- # @param [Hash] options any conditions understood by
106
- # FinderOptions.to_mongo_criteria
107
- # @option [String] :order this *mandatory* option describes how to
108
- # identify the ordering of the documents in your collection. Note that
109
- # the *last* document in this collection will be selected.
110
- #
111
- # @return the last document in the ordered collection as described by
112
- # +options+
113
- #
114
- # @raise Exception when no <tt>:order</tt> option has been defined
115
116
  def last(options={})
116
117
  raise ':order option must be provided when using last' if options[:order].blank?
117
118
  find_one(options.merge(:order => invert_order_clause(options[:order])))
118
119
  end
119
120
 
120
- # @param [Hash] options any conditions understood by
121
- # FinderOptions.to_mongo_criteria
122
- #
123
- # @return [Array] all documents in your collection that match the
124
- # provided conditions
125
- #
126
- # @see FinderOptions
127
121
  def all(options={})
128
122
  find_every(options)
129
123
  end
130
124
 
131
125
  def find_by_id(id)
132
- find_one(:_id => id)
126
+ find(id)
133
127
  end
134
128
 
135
129
  def count(options={})
@@ -140,52 +134,14 @@ module MongoMapper
140
134
  !count(options).zero?
141
135
  end
142
136
 
143
- # @overload create(doc_attributes)
144
- # Create a single new document
145
- # @param [Hash] doc_attributes key/value pairs to create a new
146
- # document
147
- #
148
- # @overload create(docs_attributes)
149
- # Create many new documents
150
- # @param [Array<Hash>] provide many Hashes of key/value pairs to create
151
- # multiple documents
152
- #
153
- # @example Creating a single document
154
- # MyModel.create({ :foo => "bar" })
155
- #
156
- # @example Creating multiple documents
157
- # MyModel.create([{ :foo => "bar" }, { :foo => "baz" })
158
- #
159
- # @return [Boolean] when a document is successfully created, +true+ will
160
- # be returned. If a document fails to create, +false+ will be returned.
161
137
  def create(*docs)
162
138
  initialize_each(*docs) { |doc| doc.save }
163
139
  end
164
140
 
165
- # @see Document.create
166
- #
167
- # @raise [DocumentNotValid] raised if a document fails to create
168
141
  def create!(*docs)
169
142
  initialize_each(*docs) { |doc| doc.save! }
170
143
  end
171
144
 
172
- # @overload update(id, attributes)
173
- # Update a single document
174
- # @param id the ID of the document you wish to update
175
- # @param [Hash] attributes the key to update on the document with a new
176
- # value
177
- #
178
- # @overload update(ids_and_attributes)
179
- # Update multiple documents
180
- # @param [Hash] ids_and_attributes each key is the ID of some document
181
- # you wish to update. The value each key points toward are those
182
- # applied to the target document
183
- #
184
- # @example Updating single document
185
- # Person.update(1, {:foo => 'bar'})
186
- #
187
- # @example Updating multiple documents at once:
188
- # Person.update({'1' => {:foo => 'bar'}, '2' => {:baz => 'wick'}})
189
145
  def update(*args)
190
146
  if args.length == 1
191
147
  update_multiple(args[0])
@@ -195,10 +151,6 @@ module MongoMapper
195
151
  end
196
152
  end
197
153
 
198
- # Removes ("deletes") one or many documents from the collection. Note
199
- # that this will bypass any +destroy+ hooks defined by your class.
200
- #
201
- # @param [Array] ids the ID or IDs of the records you wish to delete
202
154
  def delete(*ids)
203
155
  collection.remove(to_criteria(:_id => ids.flatten))
204
156
  end
@@ -207,27 +159,6 @@ module MongoMapper
207
159
  collection.remove(to_criteria(options))
208
160
  end
209
161
 
210
- # Iterates over each document found by the provided IDs and calls their
211
- # +destroy+ method. This has the advantage of processing your document's
212
- # +destroy+ call-backs.
213
- #
214
- # @overload destroy(id)
215
- # Destroy a single document by ID
216
- # @param id the ID of the document to destroy
217
- #
218
- # @overload destroy(ids)
219
- # Destroy many documents by their IDs
220
- # @param [Array] the IDs of each document you wish to destroy
221
- #
222
- # @example Destroying a single document
223
- # Person.destroy("34")
224
- #
225
- # @example Destroying multiple documents
226
- # Person.destroy("34", "45", ..., "54")
227
- #
228
- # # OR...
229
- #
230
- # Person.destroy(["34", "45", ..., "54"])
231
162
  def destroy(*ids)
232
163
  find_some(ids.flatten).each(&:destroy)
233
164
  end
@@ -235,15 +166,58 @@ module MongoMapper
235
166
  def destroy_all(options={})
236
167
  all(options).each(&:destroy)
237
168
  end
169
+
170
+ def increment(*args)
171
+ modifier_update('$inc', args)
172
+ end
173
+
174
+ def decrement(*args)
175
+ criteria, keys = criteria_and_keys_from_args(args)
176
+ values, to_decrement = keys.values, {}
177
+ keys.keys.each_with_index { |k, i| to_decrement[k] = -values[i].abs }
178
+ collection.update(criteria, {'$inc' => to_decrement}, :multi => true)
179
+ end
180
+
181
+ def set(*args)
182
+ modifier_update('$set', args)
183
+ end
184
+
185
+ def push(*args)
186
+ modifier_update('$push', args)
187
+ end
188
+
189
+ def push_all(*args)
190
+ modifier_update('$pushAll', args)
191
+ end
192
+
193
+ def push_uniq(*args)
194
+ criteria, keys = criteria_and_keys_from_args(args)
195
+ keys.each { |key, value | criteria[key] = {'$ne' => value} }
196
+ collection.update(criteria, {'$push' => keys}, :multi => true)
197
+ end
198
+
199
+ def pull(*args)
200
+ modifier_update('$pull', args)
201
+ end
202
+
203
+ def pull_all(*args)
204
+ modifier_update('$pullAll', args)
205
+ end
206
+
207
+ def modifier_update(modifier, args)
208
+ criteria, keys = criteria_and_keys_from_args(args)
209
+ modifiers = {modifier => keys}
210
+ collection.update(criteria, modifiers, :multi => true)
211
+ end
212
+ private :modifier_update
213
+
214
+ def criteria_and_keys_from_args(args)
215
+ keys = args.pop
216
+ criteria = args[0].is_a?(Hash) ? args[0] : {:id => args}
217
+ [to_criteria(criteria), keys]
218
+ end
219
+ private :criteria_and_keys_from_args
238
220
 
239
- # @overload connection()
240
- # @return [Mongo::Connection] the connection used by your document class
241
- #
242
- # @overload connection(mongo_connection)
243
- # @param [Mongo::Connection] mongo_connection a new connection for your
244
- # document class to use
245
- # @return [Mongo::Connection] a new Mongo::Connection for yoru document
246
- # class
247
221
  def connection(mongo_connection=nil)
248
222
  if mongo_connection.nil?
249
223
  @connection ||= MongoMapper.connection
@@ -253,24 +227,14 @@ module MongoMapper
253
227
  @connection
254
228
  end
255
229
 
256
- # Changes the database name from the default to whatever you want
257
- #
258
- # @param [#to_s] name the new database name to use.
259
230
  def set_database_name(name)
260
231
  @database_name = name
261
232
  end
262
233
 
263
- # Returns the database name
264
- #
265
- # @return [String] the database name
266
234
  def database_name
267
235
  @database_name
268
236
  end
269
237
 
270
- # Returns the database the document should use. Defaults to
271
- # MongoMapper.database if other database is not set.
272
- #
273
- # @return [Mongo::DB] the mongo database instance
274
238
  def database
275
239
  if database_name.nil?
276
240
  MongoMapper.database
@@ -279,35 +243,31 @@ module MongoMapper
279
243
  end
280
244
  end
281
245
 
282
- # Changes the collection name from the default to whatever you want
283
- #
284
- # @param [#to_s] name the new collection name to use.
285
246
  def set_collection_name(name)
286
247
  @collection_name = name
287
248
  end
288
249
 
289
- # Returns the collection name, if not set, defaults to class name tableized
290
- #
291
- # @return [String] the collection name, if not set, defaults to class
292
- # name tableized
293
250
  def collection_name
294
251
  @collection_name ||= self.to_s.tableize.gsub(/\//, '.')
295
252
  end
296
253
 
297
- # @return the Mongo Ruby driver +collection+ object
298
254
  def collection
299
255
  database.collection(collection_name)
300
256
  end
301
-
302
- # Defines a +created_at+ and +updated_at+ attribute (with a +Time+
303
- # value) on your document. These attributes are updated by an
304
- # injected +update_timestamps+ +before_save+ hook.
257
+
305
258
  def timestamps!
306
259
  key :created_at, Time
307
260
  key :updated_at, Time
308
261
  class_eval { before_save :update_timestamps }
309
262
  end
310
263
 
264
+ def userstamps!
265
+ key :creator_id, ObjectId
266
+ key :updater_id, ObjectId
267
+ belongs_to :creator, :class_name => 'User'
268
+ belongs_to :updater, :class_name => 'User'
269
+ end
270
+
311
271
  def single_collection_inherited?
312
272
  keys.has_key?('_type') && single_collection_inherited_superclass?
313
273
  end
@@ -406,13 +366,23 @@ module MongoMapper
406
366
  def collection
407
367
  self.class.collection
408
368
  end
369
+
370
+ def database
371
+ self.class.database
372
+ end
409
373
 
410
374
  def new?
411
375
  read_attribute('_id').blank? || using_custom_id?
412
376
  end
413
377
 
414
- def save(perform_validations=true)
415
- !perform_validations || valid? ? create_or_update : false
378
+ def save(options={})
379
+ if options === false
380
+ ActiveSupport::Deprecation.warn "save with true/false is deprecated. You should now use :validate => true/false."
381
+ options = {:validate => false}
382
+ end
383
+ options.reverse_merge!(:validate => true)
384
+ perform_validations = options.delete(:validate)
385
+ !perform_validations || valid? ? create_or_update(options) : false
416
386
  end
417
387
 
418
388
  def save!
@@ -420,9 +390,11 @@ module MongoMapper
420
390
  end
421
391
 
422
392
  def destroy
423
- return false if frozen?
424
- self.class.delete(_id) unless new?
425
- freeze
393
+ self.class.delete(id) unless new?
394
+ end
395
+
396
+ def delete
397
+ self.class.delete(id) unless new?
426
398
  end
427
399
 
428
400
  def reload
@@ -433,14 +405,14 @@ module MongoMapper
433
405
  end
434
406
 
435
407
  private
436
- def create_or_update
437
- result = new? ? create : update
408
+ def create_or_update(options={})
409
+ result = new? ? create(options) : update(options)
438
410
  result != false
439
411
  end
440
412
 
441
- def create
413
+ def create(options={})
442
414
  assign_id
443
- save_to_collection
415
+ save_to_collection(options)
444
416
  end
445
417
 
446
418
  def assign_id
@@ -449,13 +421,14 @@ module MongoMapper
449
421
  end
450
422
  end
451
423
 
452
- def update
453
- save_to_collection
424
+ def update(options={})
425
+ save_to_collection(options)
454
426
  end
455
427
 
456
- def save_to_collection
428
+ def save_to_collection(options={})
457
429
  clear_custom_id_flag
458
- collection.save(to_mongo)
430
+ safe = options.delete(:safe) || false
431
+ collection.save(to_mongo, :safe => safe)
459
432
  end
460
433
 
461
434
  def update_timestamps
@@ -281,7 +281,7 @@ module MongoMapper
281
281
  end
282
282
 
283
283
  def id
284
- read_attribute(:_id)
284
+ self[:_id]
285
285
  end
286
286
 
287
287
  def id=(value)
@@ -291,7 +291,7 @@ module MongoMapper
291
291
  @using_custom_id = true
292
292
  end
293
293
 
294
- write_attribute :_id, value
294
+ self[:_id] = value
295
295
  end
296
296
 
297
297
  def using_custom_id?
@@ -305,16 +305,12 @@ module MongoMapper
305
305
  "#<#{self.class} #{attributes_as_nice_string}>"
306
306
  end
307
307
 
308
- def save
309
- if _root_document
310
- _root_document.save
311
- end
308
+ def save(options={})
309
+ _root_document.try(:save, options)
312
310
  end
313
311
 
314
- def save!
315
- if _root_document
316
- _root_document.save!
317
- end
312
+ def save!(options={})
313
+ _root_document.try(:save!, options)
318
314
  end
319
315
 
320
316
  def update_attributes(attrs={})
@@ -355,7 +351,7 @@ module MongoMapper
355
351
  def read_attribute(name)
356
352
  if key = _keys[name]
357
353
  value = key.get(instance_variable_get("@#{name}"))
358
- instance_variable_set "@#{name}", value if !frozen?
354
+ instance_variable_set "@#{name}", value
359
355
  value
360
356
  else
361
357
  raise KeyNotFound, "Could not find key: #{name.inspect}"
@@ -1,25 +1,20 @@
1
1
  module MongoMapper
2
- # Controls the parsing and handling of options used by finders.
3
- #
4
- # == Important Note
5
- #
2
+ # = Important Note
6
3
  # This class is private to MongoMapper and should not be considered part of
7
- # MongoMapper's public API. Some documentation herein, however, may prove
8
- # useful for understanding how MongoMapper handles the parsing of finder
9
- # conditions and options.
4
+ # MongoMapper's public API.
10
5
  #
11
6
  class FinderOptions
12
7
  OptionKeys = [:fields, :select, :skip, :offset, :limit, :sort, :order]
13
-
8
+
14
9
  def self.normalized_field(field)
15
10
  field.to_s == 'id' ? :_id : field
16
11
  end
17
-
12
+
18
13
  def self.normalized_order_direction(direction)
19
14
  direction ||= 'ASC'
20
15
  direction.upcase == 'ASC' ? 1 : -1
21
16
  end
22
-
17
+
23
18
  def initialize(model, options)
24
19
  raise ArgumentError, "Options must be a hash" unless options.is_a?(Hash)
25
20
  options = options.symbolize_keys
@@ -38,15 +33,11 @@ module MongoMapper
38
33
 
39
34
  add_sci_scope
40
35
  end
41
-
42
- # @return [Hash] Mongo compatible criteria options
43
- #
44
- # @see FinderOptions#to_mongo_criteria
36
+
45
37
  def criteria
46
38
  to_mongo_criteria(@conditions)
47
39
  end
48
-
49
- # @return [Hash] Mongo compatible options
40
+
50
41
  def options
51
42
  fields = @options.delete(:fields) || @options.delete(:select)
52
43
  skip = @options.delete(:skip) || @options.delete(:offset) || 0
@@ -55,8 +46,7 @@ module MongoMapper
55
46
 
56
47
  {:fields => to_mongo_fields(fields), :skip => skip.to_i, :limit => limit.to_i, :sort => sort}
57
48
  end
58
-
59
- # @return [Array<Hash>] Mongo criteria and options enclosed in an Array
49
+
60
50
  def to_a
61
51
  [criteria, options]
62
52
  end
@@ -82,6 +72,8 @@ module MongoMapper
82
72
  criteria[field] = operator?(field) ? value : {'$in' => value}
83
73
  when Hash
84
74
  criteria[field] = to_mongo_criteria(value, field)
75
+ when Time
76
+ criteria[field] = value.utc
85
77
  else
86
78
  criteria[field] = value
87
79
  end
@@ -104,10 +96,10 @@ module MongoMapper
104
96
  def to_mongo_fields(fields)
105
97
  return if fields.blank?
106
98
 
107
- if fields.is_a?(String)
108
- fields.split(',').map { |field| field.strip }
109
- else
99
+ if fields.respond_to?(:flatten, :compact)
110
100
  fields.flatten.compact
101
+ else
102
+ fields.split(',').map { |field| field.strip }
111
103
  end
112
104
  end
113
105
 
@@ -0,0 +1,125 @@
1
+ # if Gem is defined i'll assume you are using rubygems and lock specific versions
2
+ # call me crazy but a plain old require will just get the latest version you have installed
3
+ # so i want to make sure that if you are using gems you do in fact have the correct versions
4
+ # if there is a better way to do this, please enlighten me!
5
+ if self.class.const_defined?(:Gem)
6
+ gem 'activesupport', '>= 2.3'
7
+ gem 'mongo', '0.18.2'
8
+ gem 'jnunemaker-validatable', '1.8.1'
9
+ end
10
+
11
+ require 'active_support'
12
+ require 'mongo'
13
+ require 'validatable'
14
+
15
+ module MongoMapper
16
+ # generic MM error
17
+ class MongoMapperError < StandardError; end
18
+
19
+ # raised when key expected to exist but not found
20
+ class KeyNotFound < MongoMapperError; end
21
+
22
+ # raised when document expected but not found
23
+ class DocumentNotFound < MongoMapperError; end
24
+
25
+ # raised when document not valid and using !
26
+ class DocumentNotValid < MongoMapperError
27
+ def initialize(document)
28
+ super("Validation failed: #{document.errors.full_messages.join(", ")}")
29
+ end
30
+ end
31
+
32
+ # @api public
33
+ def self.connection
34
+ @@connection ||= Mongo::Connection.new
35
+ end
36
+
37
+ # @api public
38
+ def self.connection=(new_connection)
39
+ @@connection = new_connection
40
+ end
41
+
42
+ # @api public
43
+ def self.logger
44
+ connection.logger
45
+ end
46
+
47
+ # @api public
48
+ def self.database=(name)
49
+ @@database = nil
50
+ @@database_name = name
51
+ end
52
+
53
+ # @api public
54
+ def self.database
55
+ if @@database_name.blank?
56
+ raise 'You forgot to set the default database name: MongoMapper.database = "foobar"'
57
+ end
58
+
59
+ @@database ||= MongoMapper.connection.db(@@database_name)
60
+ end
61
+
62
+ # @api private
63
+ def self.ensured_indexes
64
+ @@ensured_indexes ||= []
65
+ end
66
+
67
+ # @api private
68
+ def self.ensured_indexes=(value)
69
+ @@ensured_indexes = value
70
+ end
71
+
72
+ # @api private
73
+ def self.ensure_index(klass, keys, options={})
74
+ ensured_indexes << {:klass => klass, :keys => keys, :options => options}
75
+ end
76
+
77
+ # @api public
78
+ def self.ensure_indexes!
79
+ ensured_indexes.each do |index|
80
+ unique = index[:options].delete(:unique)
81
+ index[:klass].collection.create_index(index[:keys], unique)
82
+ end
83
+ end
84
+
85
+ # @api private
86
+ def self.use_time_zone?
87
+ Time.respond_to?(:zone) && Time.zone ? true : false
88
+ end
89
+
90
+ # @api private
91
+ def self.time_class
92
+ use_time_zone? ? Time.zone : Time
93
+ end
94
+
95
+ # @api private
96
+ def self.normalize_object_id(value)
97
+ value.is_a?(String) ? Mongo::ObjectID.from_string(value) : value
98
+ end
99
+ end
100
+
101
+ require 'mongo_mapper/support'
102
+ require 'mongo_mapper/callbacks'
103
+ require 'mongo_mapper/finder_options'
104
+ require 'mongo_mapper/dirty'
105
+ require 'mongo_mapper/dynamic_finder'
106
+ require 'mongo_mapper/key'
107
+ require 'mongo_mapper/pagination'
108
+ require 'mongo_mapper/serialization'
109
+ require 'mongo_mapper/validations'
110
+ require 'mongo_mapper/rails_compatibility/document'
111
+ require 'mongo_mapper/rails_compatibility/embedded_document'
112
+ require 'mongo_mapper/embedded_document'
113
+ require 'mongo_mapper/document'
114
+ require 'mongo_mapper/associations'
115
+ require 'mongo_mapper/associations/base'
116
+ require 'mongo_mapper/associations/proxy'
117
+ require 'mongo_mapper/associations/collection'
118
+ require 'mongo_mapper/associations/many_documents_proxy'
119
+ require 'mongo_mapper/associations/belongs_to_proxy'
120
+ require 'mongo_mapper/associations/belongs_to_polymorphic_proxy'
121
+ require 'mongo_mapper/associations/many_polymorphic_proxy'
122
+ require 'mongo_mapper/associations/many_embedded_proxy'
123
+ require 'mongo_mapper/associations/many_embedded_polymorphic_proxy'
124
+ require 'mongo_mapper/associations/many_documents_as_proxy'
125
+ require 'mongo_mapper/associations/one_proxy'
@@ -1,7 +1,7 @@
1
1
  module MongoMapper
2
2
  module Pagination
3
3
  class PaginationProxy
4
- instance_methods.each { |m| undef_method m unless m =~ /(^__|^nil\?$|proxy_|^object_id$)/ }
4
+ instance_methods.each { |m| undef_method m unless m =~ /(^__|^nil\?$|^send$|proxy_|^object_id$)/ }
5
5
 
6
6
  attr_accessor :subject
7
7
  attr_reader :total_entries, :per_page, :current_page
@@ -33,7 +33,18 @@ module MongoMapper
33
33
  (current_page - 1) * per_page
34
34
  end
35
35
  alias offset skip # for will paginate support
36
+
37
+ def send(method, *args, &block)
38
+ if respond_to?(method)
39
+ super
40
+ else
41
+ subject.send(method, *args, &block)
42
+ end
43
+ end
36
44
 
45
+ def ===(other)
46
+ other === subject
47
+ end
37
48
 
38
49
  def method_missing(name, *args, &block)
39
50
  @subject.send(name, *args, &block)