mongo_mapper-unstable 2010.1.6 → 2010.1.12

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 (86) hide show
  1. data/VERSION +1 -1
  2. data/lib/mongo_mapper/descendant_appends.rb +44 -0
  3. data/lib/mongo_mapper/document.rb +54 -98
  4. data/lib/mongo_mapper/embedded_document.rb +28 -348
  5. data/lib/mongo_mapper/finder_options.rb +15 -33
  6. data/lib/mongo_mapper/plugins/associations/base.rb +121 -0
  7. data/lib/mongo_mapper/plugins/associations/belongs_to_polymorphic_proxy.rb +28 -0
  8. data/lib/mongo_mapper/plugins/associations/belongs_to_proxy.rb +23 -0
  9. data/lib/mongo_mapper/plugins/associations/collection.rb +21 -0
  10. data/lib/mongo_mapper/plugins/associations/embedded_collection.rb +49 -0
  11. data/lib/mongo_mapper/plugins/associations/in_array_proxy.rb +139 -0
  12. data/lib/mongo_mapper/plugins/associations/many_documents_as_proxy.rb +28 -0
  13. data/lib/mongo_mapper/plugins/associations/many_documents_proxy.rb +117 -0
  14. data/lib/mongo_mapper/plugins/associations/many_embedded_polymorphic_proxy.rb +31 -0
  15. data/lib/mongo_mapper/plugins/associations/many_embedded_proxy.rb +23 -0
  16. data/lib/mongo_mapper/plugins/associations/many_polymorphic_proxy.rb +13 -0
  17. data/lib/mongo_mapper/plugins/associations/one_proxy.rb +66 -0
  18. data/lib/mongo_mapper/plugins/associations/proxy.rb +118 -0
  19. data/lib/mongo_mapper/plugins/associations.rb +104 -0
  20. data/lib/mongo_mapper/plugins/callbacks.rb +65 -0
  21. data/lib/mongo_mapper/plugins/clone.rb +13 -0
  22. data/lib/mongo_mapper/plugins/descendants.rb +16 -0
  23. data/lib/mongo_mapper/plugins/dirty.rb +119 -0
  24. data/lib/mongo_mapper/plugins/equality.rb +11 -0
  25. data/lib/mongo_mapper/plugins/identity_map.rb +66 -0
  26. data/lib/mongo_mapper/plugins/inspect.rb +14 -0
  27. data/lib/mongo_mapper/plugins/keys.rb +295 -0
  28. data/lib/mongo_mapper/plugins/logger.rb +17 -0
  29. data/lib/mongo_mapper/plugins/pagination.rb +85 -0
  30. data/lib/mongo_mapper/plugins/rails.rb +45 -0
  31. data/lib/mongo_mapper/plugins/serialization.rb +109 -0
  32. data/lib/mongo_mapper/plugins/validations.rb +48 -0
  33. data/lib/mongo_mapper/plugins.rb +19 -0
  34. data/lib/mongo_mapper/support.rb +36 -15
  35. data/lib/mongo_mapper.rb +23 -22
  36. data/performance/read_write.rb +52 -0
  37. data/specs.watchr +23 -2
  38. data/test/functional/associations/test_belongs_to_proxy.rb +1 -1
  39. data/test/functional/associations/test_many_embedded_polymorphic_proxy.rb +58 -39
  40. data/test/functional/associations/test_many_embedded_proxy.rb +103 -69
  41. data/test/functional/test_dirty.rb +1 -1
  42. data/test/functional/test_document.rb +25 -25
  43. data/test/functional/test_embedded_document.rb +66 -63
  44. data/test/functional/test_identity_map.rb +233 -0
  45. data/test/functional/test_modifiers.rb +14 -0
  46. data/test/functional/test_string_id_compatibility.rb +4 -4
  47. data/test/functional/test_validations.rb +13 -0
  48. data/test/models.rb +0 -39
  49. data/test/test_helper.rb +8 -2
  50. data/test/unit/associations/test_base.rb +1 -1
  51. data/test/unit/associations/test_proxy.rb +3 -3
  52. data/test/unit/test_descendant_appends.rb +71 -0
  53. data/test/unit/test_document.rb +35 -46
  54. data/test/unit/test_embedded_document.rb +218 -271
  55. data/test/unit/{test_key.rb → test_keys.rb} +0 -0
  56. data/test/unit/test_pagination.rb +10 -2
  57. data/test/unit/test_plugins.rb +42 -0
  58. data/test/unit/test_rails.rb +123 -0
  59. data/test/unit/{test_serializations.rb → test_serialization.rb} +0 -0
  60. data/test/unit/test_support.rb +10 -6
  61. data/test/unit/test_time_zones.rb +2 -2
  62. metadata +44 -31
  63. data/lib/mongo_mapper/associations/base.rb +0 -119
  64. data/lib/mongo_mapper/associations/belongs_to_polymorphic_proxy.rb +0 -26
  65. data/lib/mongo_mapper/associations/belongs_to_proxy.rb +0 -21
  66. data/lib/mongo_mapper/associations/collection.rb +0 -19
  67. data/lib/mongo_mapper/associations/in_array_proxy.rb +0 -137
  68. data/lib/mongo_mapper/associations/many_documents_as_proxy.rb +0 -26
  69. data/lib/mongo_mapper/associations/many_documents_proxy.rb +0 -115
  70. data/lib/mongo_mapper/associations/many_embedded_polymorphic_proxy.rb +0 -31
  71. data/lib/mongo_mapper/associations/many_embedded_proxy.rb +0 -54
  72. data/lib/mongo_mapper/associations/many_polymorphic_proxy.rb +0 -11
  73. data/lib/mongo_mapper/associations/one_proxy.rb +0 -64
  74. data/lib/mongo_mapper/associations/proxy.rb +0 -116
  75. data/lib/mongo_mapper/associations.rb +0 -78
  76. data/lib/mongo_mapper/callbacks.rb +0 -61
  77. data/lib/mongo_mapper/dirty.rb +0 -117
  78. data/lib/mongo_mapper/key.rb +0 -36
  79. data/lib/mongo_mapper/mongo_mapper.rb +0 -125
  80. data/lib/mongo_mapper/pagination.rb +0 -66
  81. data/lib/mongo_mapper/rails_compatibility/document.rb +0 -15
  82. data/lib/mongo_mapper/rails_compatibility/embedded_document.rb +0 -28
  83. data/lib/mongo_mapper/serialization.rb +0 -54
  84. data/lib/mongo_mapper/serializers/json_serializer.rb +0 -48
  85. data/lib/mongo_mapper/validations.rb +0 -39
  86. data/test/functional/test_rails_compatibility.rb +0 -25
@@ -0,0 +1,295 @@
1
+ module MongoMapper
2
+ module Plugins
3
+ module Keys
4
+ module ClassMethods
5
+ def inherited(descendant)
6
+ descendant.instance_variable_set(:@keys, keys.dup)
7
+ super
8
+ end
9
+
10
+ def keys
11
+ @keys ||= HashWithIndifferentAccess.new
12
+ end
13
+
14
+ def key(*args)
15
+ key = Key.new(*args)
16
+ keys[key.name] = key
17
+
18
+ create_accessors_for(key)
19
+ create_key_in_descendants(*args)
20
+ create_indexes_for(key)
21
+ create_validations_for(key)
22
+
23
+ key
24
+ end
25
+
26
+ def using_object_id?
27
+ object_id_key?(:_id)
28
+ end
29
+
30
+ def object_id_key?(name)
31
+ key = keys[name.to_s]
32
+ key && key.type == ObjectId
33
+ end
34
+
35
+ def to_mongo(instance)
36
+ return nil if instance.nil?
37
+ instance.to_mongo
38
+ end
39
+
40
+ def from_mongo(value)
41
+ return nil if value.nil?
42
+ value.is_a?(self) ? value : load(value)
43
+ end
44
+
45
+ def load(attrs)
46
+ begin
47
+ klass = attrs['_type'].present? ? attrs['_type'].constantize : self
48
+ doc = klass.new(attrs)
49
+ doc.instance_variable_set("@new", false)
50
+ doc
51
+ rescue NameError
52
+ doc = new(attrs)
53
+ doc.instance_variable_set("@new", false)
54
+ doc
55
+ end
56
+ end
57
+
58
+ private
59
+ def accessors_module
60
+ module_defined = if method(:const_defined?).arity == 1 # Ruby 1.9 compat check
61
+ const_defined?('MongoMapperKeys')
62
+ else
63
+ const_defined?('MongoMapperKeys', false)
64
+ end
65
+
66
+ if module_defined
67
+ const_get 'MongoMapperKeys'
68
+ else
69
+ const_set 'MongoMapperKeys', Module.new
70
+ end
71
+ end
72
+
73
+ def create_accessors_for(key)
74
+ accessors_module.module_eval <<-end_eval
75
+ def #{key.name}
76
+ read_key(:#{key.name})
77
+ end
78
+
79
+ def #{key.name}_before_typecast
80
+ read_key_before_typecast(:#{key.name})
81
+ end
82
+
83
+ def #{key.name}=(value)
84
+ write_key(:#{key.name}, value)
85
+ end
86
+
87
+ def #{key.name}?
88
+ read_key(:#{key.name}).present?
89
+ end
90
+ end_eval
91
+
92
+ include accessors_module
93
+ end
94
+
95
+ def create_key_in_descendants(*args)
96
+ return if descendants.blank?
97
+ descendants.each { |descendant| descendant.key(*args) }
98
+ end
99
+
100
+ def create_indexes_for(key)
101
+ ensure_index key.name if key.options[:index] && !key.embeddable?
102
+ end
103
+
104
+ def create_validations_for(key)
105
+ attribute = key.name.to_sym
106
+
107
+ if key.options[:required]
108
+ validates_presence_of(attribute)
109
+ end
110
+
111
+ if key.options[:unique]
112
+ validates_uniqueness_of(attribute)
113
+ end
114
+
115
+ if key.options[:numeric]
116
+ number_options = key.type == Integer ? {:only_integer => true} : {}
117
+ validates_numericality_of(attribute, number_options)
118
+ end
119
+
120
+ if key.options[:format]
121
+ validates_format_of(attribute, :with => key.options[:format])
122
+ end
123
+
124
+ if key.options[:length]
125
+ length_options = case key.options[:length]
126
+ when Integer
127
+ {:minimum => 0, :maximum => key.options[:length]}
128
+ when Range
129
+ {:within => key.options[:length]}
130
+ when Hash
131
+ key.options[:length]
132
+ end
133
+ validates_length_of(attribute, length_options)
134
+ end
135
+ end
136
+ end
137
+
138
+ module InstanceMethods
139
+ def self.included(model)
140
+ model.key :_id, ObjectId
141
+ end
142
+
143
+ def initialize(attrs={})
144
+ unless attrs.nil?
145
+ provided_keys = attrs.keys.map { |k| k.to_s }
146
+ unless provided_keys.include?('_id') || provided_keys.include?('id')
147
+ write_key :_id, Mongo::ObjectID.new
148
+ end
149
+ end
150
+
151
+ @new = true
152
+ self._type = self.class.name if respond_to?(:_type=)
153
+ self.attributes = attrs
154
+ end
155
+
156
+ def new?
157
+ @new
158
+ end
159
+
160
+ def attributes=(attrs)
161
+ return if attrs.blank?
162
+
163
+ attrs.each_pair do |name, value|
164
+ writer_method = "#{name}="
165
+
166
+ if respond_to?(writer_method)
167
+ self.send(writer_method, value)
168
+ else
169
+ self[name.to_s] = value
170
+ end
171
+ end
172
+ end
173
+
174
+ def attributes
175
+ attrs = HashWithIndifferentAccess.new
176
+
177
+ keys.each_pair do |name, key|
178
+ value = key.set(self[key.name])
179
+ attrs[name] = value
180
+ end
181
+
182
+ embedded_associations.each do |association|
183
+ if documents = instance_variable_get(association.ivar)
184
+ attrs[association.name] = documents.map { |document| document.to_mongo }
185
+ end
186
+ end
187
+
188
+ attrs
189
+ end
190
+ alias :to_mongo :attributes
191
+
192
+ def id
193
+ _id
194
+ end
195
+
196
+ def id=(value)
197
+ if self.class.using_object_id?
198
+ value = MongoMapper.normalize_object_id(value)
199
+ end
200
+
201
+ self[:_id] = value
202
+ end
203
+
204
+ def [](name)
205
+ read_key(name)
206
+ end
207
+
208
+ def []=(name, value)
209
+ ensure_key_exists(name)
210
+ write_key(name, value)
211
+ end
212
+
213
+ # @api public
214
+ def keys
215
+ self.class.keys
216
+ end
217
+
218
+ # @api private?
219
+ def key_names
220
+ keys.keys
221
+ end
222
+
223
+ # @api private?
224
+ def non_embedded_keys
225
+ keys.values.select { |key| !key.embeddable? }
226
+ end
227
+
228
+ # @api private?
229
+ def embedded_keys
230
+ keys.values.select { |key| key.embeddable? }
231
+ end
232
+
233
+ private
234
+ def ensure_key_exists(name)
235
+ self.class.key(name) unless respond_to?("#{name}=")
236
+ end
237
+
238
+ def read_key(name)
239
+ if key = keys[name]
240
+ var_name = "@#{name}"
241
+ value = key.get(instance_variable_get(var_name))
242
+ instance_variable_set(var_name, value)
243
+ else
244
+ raise KeyNotFound, "Could not find key: #{name.inspect}"
245
+ end
246
+ end
247
+
248
+ def read_key_before_typecast(name)
249
+ instance_variable_get("@#{name}_before_typecast")
250
+ end
251
+
252
+ def write_key(name, value)
253
+ key = keys[name]
254
+ instance_variable_set "@#{name}_before_typecast", value
255
+ instance_variable_set "@#{name}", key.set(value)
256
+ end
257
+ end
258
+
259
+ class Key
260
+ attr_accessor :name, :type, :options, :default_value
261
+
262
+ def initialize(*args)
263
+ options = args.extract_options!
264
+ @name, @type = args.shift.to_s, args.shift
265
+ self.options = (options || {}).symbolize_keys
266
+ self.default_value = self.options.delete(:default)
267
+ end
268
+
269
+ def ==(other)
270
+ @name == other.name && @type == other.type
271
+ end
272
+
273
+ def embeddable?
274
+ type.respond_to?(:embeddable?) && type.embeddable? ? true : false
275
+ end
276
+
277
+ def number?
278
+ [Integer, Float].include?(type)
279
+ end
280
+
281
+ def get(value)
282
+ if value.nil? && !default_value.nil?
283
+ return default_value
284
+ end
285
+
286
+ type.from_mongo(value)
287
+ end
288
+
289
+ def set(value)
290
+ type.to_mongo(value)
291
+ end
292
+ end
293
+ end
294
+ end
295
+ end
@@ -0,0 +1,17 @@
1
+ module MongoMapper
2
+ module Plugins
3
+ module Logger
4
+ module ClassMethods
5
+ def logger
6
+ MongoMapper.logger
7
+ end
8
+ end
9
+
10
+ module InstanceMethods
11
+ def logger
12
+ self.class.logger
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,85 @@
1
+ module MongoMapper
2
+ module Plugins
3
+ module Pagination
4
+ module ClassMethods
5
+ def per_page
6
+ 25
7
+ end
8
+
9
+ def paginate(options)
10
+ per_page = options.delete(:per_page) || self.per_page
11
+ page = options.delete(:page)
12
+ total_entries = count(options)
13
+ pagination = Pagination::PaginationProxy.new(total_entries, page, per_page)
14
+
15
+ options.merge!(:limit => pagination.limit, :skip => pagination.skip)
16
+ pagination.subject = find_every(options)
17
+ pagination
18
+ end
19
+ end
20
+
21
+ class PaginationProxy
22
+ instance_methods.each { |m| undef_method m unless m =~ /(^__|^nil\?$|^send$|proxy_|^object_id$)/ }
23
+
24
+ attr_accessor :subject
25
+ attr_reader :total_entries, :per_page, :current_page
26
+ alias limit per_page
27
+
28
+ def initialize(total_entries, current_page, per_page=nil)
29
+ @total_entries = total_entries.to_i
30
+ self.per_page = per_page
31
+ self.current_page = current_page
32
+ end
33
+
34
+ def total_pages
35
+ (total_entries / per_page.to_f).ceil
36
+ end
37
+
38
+ def out_of_bounds?
39
+ current_page > total_pages
40
+ end
41
+
42
+ def previous_page
43
+ current_page > 1 ? (current_page - 1) : nil
44
+ end
45
+
46
+ def next_page
47
+ current_page < total_pages ? (current_page + 1) : nil
48
+ end
49
+
50
+ def skip
51
+ (current_page - 1) * per_page
52
+ end
53
+ alias offset skip # for will paginate support
54
+
55
+ def send(method, *args, &block)
56
+ if respond_to?(method)
57
+ super
58
+ else
59
+ subject.send(method, *args, &block)
60
+ end
61
+ end
62
+
63
+ def ===(other)
64
+ other === subject
65
+ end
66
+
67
+ def method_missing(name, *args, &block)
68
+ @subject.send(name, *args, &block)
69
+ end
70
+
71
+ private
72
+ def per_page=(value)
73
+ value = 25 if value.blank?
74
+ @per_page = value.to_i
75
+ end
76
+
77
+ def current_page=(value)
78
+ value = value.to_i
79
+ value = 1 if value < 1
80
+ @current_page = value
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,45 @@
1
+ module MongoMapper
2
+ module Plugins
3
+ module Rails
4
+ module InstanceMethods
5
+ def to_param
6
+ id.to_s
7
+ end
8
+
9
+ def new_record?
10
+ new?
11
+ end
12
+
13
+ def read_attribute(name)
14
+ self[name]
15
+ end
16
+
17
+ def read_attribute_before_typecast(name)
18
+ read_key_before_typecast(name)
19
+ end
20
+
21
+ def write_attribute(name, value)
22
+ self[name] = value
23
+ end
24
+ end
25
+
26
+ module ClassMethods
27
+ def has_one(*args)
28
+ one(*args)
29
+ end
30
+
31
+ def has_many(*args)
32
+ many(*args)
33
+ end
34
+
35
+ def column_names
36
+ keys.keys
37
+ end
38
+
39
+ def human_name
40
+ self.name.demodulize.titleize
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,109 @@
1
+ require 'active_support/json'
2
+
3
+ module MongoMapper
4
+ module Plugins
5
+ module Serialization
6
+ class Serializer
7
+ attr_reader :options
8
+
9
+ def initialize(record, options={})
10
+ @record, @options = record, options.dup
11
+ end
12
+
13
+ def serializable_key_names
14
+ key_names = @record.attributes.keys
15
+
16
+ if options[:only]
17
+ options.delete(:except)
18
+ key_names = key_names & Array(options[:only]).collect { |n| n.to_s }
19
+ else
20
+ options[:except] = Array(options[:except])
21
+ key_names = key_names - options[:except].collect { |n| n.to_s }
22
+ end
23
+
24
+ key_names
25
+ end
26
+
27
+ def serializable_method_names
28
+ Array(options[:methods]).inject([]) do |method_attributes, name|
29
+ method_attributes << name if @record.respond_to?(name.to_s)
30
+ method_attributes
31
+ end
32
+ end
33
+
34
+ def serializable_names
35
+ serializable_key_names + serializable_method_names
36
+ end
37
+
38
+ def serializable_record
39
+ returning(serializable_record = {}) do
40
+ serializable_names.each { |name| serializable_record[name] = @record.send(name) }
41
+ end
42
+ end
43
+
44
+ def serialize
45
+ # overwrite to implement
46
+ end
47
+
48
+ def to_s(&block)
49
+ serialize(&block)
50
+ end
51
+ end
52
+
53
+ module Json
54
+ def self.included(base)
55
+ base.cattr_accessor :include_root_in_json, :instance_writer => false
56
+ base.extend ClassMethods
57
+ end
58
+
59
+ module ClassMethods
60
+ def json_class_name
61
+ @json_class_name ||= name.demodulize.underscore.inspect
62
+ end
63
+ end
64
+
65
+ def to_json(options={})
66
+ apply_to_json_defaults(options)
67
+
68
+ if include_root_in_json
69
+ "{#{self.class.json_class_name}: #{JsonSerializer.new(self, options).to_s}}"
70
+ else
71
+ JsonSerializer.new(self, options).to_s
72
+ end
73
+ end
74
+
75
+ def from_json(json)
76
+ self.attributes = ActiveSupport::JSON.decode(json)
77
+ self
78
+ end
79
+
80
+ class JsonSerializer < Serializer
81
+ def serialize
82
+ serializable_record.to_json
83
+ end
84
+ end
85
+
86
+ private
87
+ def apply_to_json_defaults(options)
88
+ unless options[:only]
89
+ methods = [options.delete(:methods)].flatten.compact
90
+ methods << :id
91
+ options[:methods] = methods.uniq
92
+ end
93
+
94
+ except = [options.delete(:except)].flatten.compact
95
+ except << :_id
96
+ options[:except] = except
97
+ end
98
+ end
99
+
100
+ module InstanceMethods
101
+ def self.included(model)
102
+ model.class_eval do
103
+ include Json
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,48 @@
1
+ module MongoMapper
2
+ module Plugins
3
+ module Validations
4
+ module InstanceMethods
5
+ def self.included(model)
6
+ model.class_eval { include Validatable }
7
+ end
8
+ end
9
+
10
+ module DocumentMacros
11
+ def validates_uniqueness_of(*args)
12
+ add_validations(args, MongoMapper::Plugins::Validations::ValidatesUniquenessOf)
13
+ end
14
+ end
15
+
16
+ class ValidatesUniquenessOf < Validatable::ValidationBase
17
+ option :scope, :case_sensitive
18
+ default :case_sensitive => true
19
+
20
+ def valid?(instance)
21
+ value = instance[attribute]
22
+ return true if allow_blank && value.blank?
23
+ return true if allow_nil && value.nil?
24
+ base_conditions = case_sensitive ? {self.attribute => value} : {}
25
+ doc = instance.class.first(base_conditions.merge(scope_conditions(instance)).merge(where_conditions(instance)))
26
+ doc.nil? || instance._id == doc._id
27
+ end
28
+
29
+ def message(instance)
30
+ super || "has already been taken"
31
+ end
32
+
33
+ def scope_conditions(instance)
34
+ return {} unless scope
35
+ Array(scope).inject({}) do |conditions, key|
36
+ conditions.merge(key => instance[key])
37
+ end
38
+ end
39
+
40
+ def where_conditions(instance)
41
+ conditions = {}
42
+ conditions[attribute] = /#{instance[attribute].to_s}/i unless case_sensitive
43
+ conditions
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,19 @@
1
+ module MongoMapper
2
+ module Plugins
3
+ def plugins
4
+ @plugins ||= []
5
+ end
6
+
7
+ def plugin(mod)
8
+ if mod.const_defined?(:ClassMethods)
9
+ extend mod::ClassMethods
10
+ end
11
+
12
+ if mod.const_defined?(:InstanceMethods)
13
+ include mod::InstanceMethods
14
+ end
15
+
16
+ plugins << mod
17
+ end
18
+ end
19
+ end
@@ -1,5 +1,3 @@
1
- require 'set'
2
-
3
1
  class Array
4
2
  def self.to_mongo(value)
5
3
  value = value.respond_to?(:lines) ? value.lines : value
@@ -26,11 +24,18 @@ class Binary
26
24
  end
27
25
 
28
26
  class Boolean
27
+ BOOLEAN_MAPPING = {
28
+ true => true, 'true' => true, 'TRUE' => true, 'True' => true, 't' => true, 'T' => true, '1' => true, 1 => true, 1.0 => true,
29
+ false => false, 'false' => false, 'FALSE' => false, 'False' => false, 'f' => false, 'F' => false, '0' => false, 0 => false, 0.0 => false, nil => false
30
+ }
31
+
29
32
  def self.to_mongo(value)
30
33
  if value.is_a?(Boolean)
31
34
  value
32
35
  else
33
- ['true', 't', '1'].include?(value.to_s.downcase)
36
+ v = BOOLEAN_MAPPING[value]
37
+ v = value.to_s.downcase == 'true' if v.nil? # Check all mixed case spellings for true
38
+ v
34
39
  end
35
40
  end
36
41
 
@@ -41,8 +46,12 @@ end
41
46
 
42
47
  class Date
43
48
  def self.to_mongo(value)
44
- date = Date.parse(value.to_s)
45
- Time.utc(date.year, date.month, date.day)
49
+ if value.nil? || value == ''
50
+ nil
51
+ else
52
+ date = value.is_a?(Date) || value.is_a?(Time) ? value : Date.parse(value.to_s)
53
+ Time.utc(date.year, date.month, date.day)
54
+ end
46
55
  rescue
47
56
  nil
48
57
  end
@@ -71,7 +80,7 @@ end
71
80
  class Integer
72
81
  def self.to_mongo(value)
73
82
  value_to_i = value.to_i
74
- if value_to_i == 0
83
+ if value_to_i == 0 && value != value_to_i
75
84
  value.to_s =~ /^(0x|0b)?0+/ ? 0 : nil
76
85
  else
77
86
  value_to_i
@@ -154,15 +163,26 @@ class String
154
163
  end
155
164
  end
156
165
 
157
- class Symbol
158
- %w{gt lt gte lte ne in nin mod size where exists}.each do |operator|
159
- define_method operator do
160
- MongoMapper::FinderOperator.new(self, "$#{operator}")
161
- end
166
+ class SymbolOperator
167
+ def initialize(field, operator, options={})
168
+ @field, @operator = field, operator
169
+ end unless method_defined?(:initialize)
170
+
171
+ def to_mm_criteria(value)
172
+ {MongoMapper::FinderOptions.normalized_field(@field) => {"$#{@operator}" => value}}
162
173
  end
163
174
 
164
- def asc; MongoMapper::OrderOperator.new(self, 'asc') end
165
- def desc; MongoMapper::OrderOperator.new(self, 'desc') end
175
+ def to_mm_order
176
+ [@field.to_s, MongoMapper::FinderOptions.normalized_order_direction(@operator)]
177
+ end
178
+ end
179
+
180
+ class Symbol
181
+ %w(gt lt gte lte ne in nin mod size where exists asc desc).each do |operator|
182
+ define_method(operator) do
183
+ SymbolOperator.new(self, operator)
184
+ end unless method_defined?(operator)
185
+ end
166
186
  end
167
187
 
168
188
  class Time
@@ -170,8 +190,9 @@ class Time
170
190
  if value.nil? || value == ''
171
191
  nil
172
192
  else
173
- time = MongoMapper.time_class.parse(value.to_s)
174
- time && time.utc
193
+ time = value.is_a?(Time) ? value : MongoMapper.time_class.parse(value.to_s)
194
+ # Convert time to milliseconds since BSON stores dates with that accurracy, but Ruby uses microseconds
195
+ Time.at((time.to_f * 1000).round / 1000.0).utc if time
175
196
  end
176
197
  end
177
198