mongo_mapper 0.5.8 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +4 -4
- data/VERSION +1 -1
- data/bin/mmconsole +10 -5
- data/lib/mongo_mapper.rb +28 -5
- data/lib/mongo_mapper/associations.rb +113 -12
- data/lib/mongo_mapper/associations/base.rb +24 -9
- data/lib/mongo_mapper/associations/belongs_to_polymorphic_proxy.rb +1 -1
- data/lib/mongo_mapper/associations/belongs_to_proxy.rb +1 -1
- data/lib/mongo_mapper/associations/many_documents_as_proxy.rb +2 -2
- data/lib/mongo_mapper/associations/many_documents_proxy.rb +7 -2
- data/lib/mongo_mapper/associations/many_embedded_proxy.rb +22 -36
- data/lib/mongo_mapper/associations/proxy.rb +11 -6
- data/lib/mongo_mapper/document.rb +37 -21
- data/lib/mongo_mapper/embedded_document.rb +32 -18
- data/lib/mongo_mapper/finder_options.rb +19 -12
- data/lib/mongo_mapper/rails_compatibility/document.rb +4 -0
- data/lib/mongo_mapper/rails_compatibility/embedded_document.rb +4 -0
- data/lib/mongo_mapper/support.rb +18 -46
- data/lib/mongo_mapper/types.rb +64 -0
- data/lib/mongo_mapper/validations.rb +13 -43
- data/mongo_mapper.gemspec +13 -10
- data/test/functional/associations/test_belongs_to_polymorphic_proxy.rb +10 -10
- data/test/functional/associations/test_belongs_to_proxy.rb +29 -30
- data/test/functional/associations/test_many_documents_as_proxy.rb +13 -12
- data/test/functional/associations/test_many_embedded_polymorphic_proxy.rb +34 -34
- data/test/functional/associations/test_many_embedded_proxy.rb +69 -74
- data/test/functional/associations/test_many_polymorphic_proxy.rb +10 -10
- data/test/functional/associations/test_many_proxy.rb +14 -15
- data/test/functional/test_associations.rb +4 -4
- data/test/functional/test_binary.rb +1 -1
- data/test/functional/test_dirty.rb +6 -6
- data/test/functional/test_document.rb +76 -69
- data/test/functional/test_embedded_document.rb +15 -14
- data/test/functional/test_pagination.rb +9 -1
- data/test/functional/test_string_id_compatibility.rb +72 -0
- data/test/functional/test_validations.rb +56 -7
- data/test/models.rb +7 -7
- data/test/test_helper.rb +2 -5
- data/test/unit/test_association_base.rb +6 -1
- data/test/unit/test_document.rb +22 -13
- data/test/unit/test_embedded_document.rb +47 -5
- data/test/unit/test_finder_options.rb +22 -3
- data/test/unit/test_mongo_mapper.rb +65 -0
- data/test/unit/test_rails_compatibility.rb +14 -0
- data/test/unit/test_support.rb +45 -0
- metadata +9 -6
- data/test/unit/test_mongomapper.rb +0 -28
@@ -6,7 +6,7 @@ module MongoMapper
|
|
6
6
|
def initialize(owner, association)
|
7
7
|
@owner = owner
|
8
8
|
@association = association
|
9
|
-
@association.options[:extend].each { |ext|
|
9
|
+
@association.options[:extend].each { |ext| class << self; self; end.instance_eval { include ext } }
|
10
10
|
reset
|
11
11
|
end
|
12
12
|
|
@@ -25,7 +25,12 @@ module MongoMapper
|
|
25
25
|
end
|
26
26
|
|
27
27
|
def send(method, *args)
|
28
|
-
|
28
|
+
metaclass_instance_methods = class << self; self; end.instance_methods
|
29
|
+
|
30
|
+
if metaclass_instance_methods.any? { |m| m.to_s == method.to_s }
|
31
|
+
return __send__(method, *args)
|
32
|
+
end
|
33
|
+
|
29
34
|
load_target
|
30
35
|
@target.send(method, *args)
|
31
36
|
end
|
@@ -45,12 +50,12 @@ module MongoMapper
|
|
45
50
|
end
|
46
51
|
|
47
52
|
protected
|
48
|
-
def method_missing(method, *args)
|
53
|
+
def method_missing(method, *args, &block)
|
49
54
|
if load_target
|
50
|
-
if
|
51
|
-
@target.send(method, *args) { |*block_args| yield(*block_args) }
|
52
|
-
else
|
55
|
+
if block.nil?
|
53
56
|
@target.send(method, *args)
|
57
|
+
else
|
58
|
+
@target.send(method, *args) { |*block_args| block.call(*block_args) }
|
54
59
|
end
|
55
60
|
end
|
56
61
|
end
|
@@ -55,7 +55,7 @@ module MongoMapper
|
|
55
55
|
# @overload find(ids, options)
|
56
56
|
#
|
57
57
|
# @raise DocumentNotFound raised when no ID or arguments are provided
|
58
|
-
def find(*args)
|
58
|
+
def find!(*args)
|
59
59
|
options = args.extract_options!
|
60
60
|
case args.first
|
61
61
|
when :first then first(options)
|
@@ -73,6 +73,12 @@ module MongoMapper
|
|
73
73
|
end
|
74
74
|
end
|
75
75
|
end
|
76
|
+
|
77
|
+
def find(*args)
|
78
|
+
find!(*args)
|
79
|
+
rescue DocumentNotFound
|
80
|
+
nil
|
81
|
+
end
|
76
82
|
|
77
83
|
def paginate(options)
|
78
84
|
per_page = options.delete(:per_page) || self.per_page
|
@@ -247,26 +253,36 @@ module MongoMapper
|
|
247
253
|
@connection
|
248
254
|
end
|
249
255
|
|
250
|
-
#
|
251
|
-
#
|
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
|
+
def set_database_name(name)
|
260
|
+
@database_name = name
|
261
|
+
end
|
262
|
+
|
263
|
+
# Returns the database name
|
264
|
+
#
|
265
|
+
# @return [String] the database name
|
266
|
+
def database_name
|
267
|
+
@database_name
|
268
|
+
end
|
269
|
+
|
270
|
+
# Returns the database the document should use. Defaults to
|
271
|
+
# MongoMapper.database if other database is not set.
|
252
272
|
#
|
253
|
-
# @
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
if name.nil?
|
258
|
-
@database ||= MongoMapper.database
|
273
|
+
# @return [Mongo::DB] the mongo database instance
|
274
|
+
def database
|
275
|
+
if database_name.nil?
|
276
|
+
MongoMapper.database
|
259
277
|
else
|
260
|
-
|
278
|
+
connection.db(database_name)
|
261
279
|
end
|
262
|
-
@database
|
263
280
|
end
|
264
281
|
|
265
282
|
# Changes the collection name from the default to whatever you want
|
266
283
|
#
|
267
|
-
# @param [#to_s] name the new collection name to use.
|
268
|
-
def set_collection_name(name
|
269
|
-
@collection = nil
|
284
|
+
# @param [#to_s] name the new collection name to use.
|
285
|
+
def set_collection_name(name)
|
270
286
|
@collection_name = name
|
271
287
|
end
|
272
288
|
|
@@ -280,7 +296,7 @@ module MongoMapper
|
|
280
296
|
|
281
297
|
# @return the Mongo Ruby driver +collection+ object
|
282
298
|
def collection
|
283
|
-
|
299
|
+
database.collection(collection_name)
|
284
300
|
end
|
285
301
|
|
286
302
|
# Defines a +created_at+ and +updated_at+ attribute (with a +Time+
|
@@ -416,22 +432,22 @@ module MongoMapper
|
|
416
432
|
read_attribute('_id').blank? || using_custom_id?
|
417
433
|
end
|
418
434
|
|
419
|
-
def save
|
420
|
-
valid? ? create_or_update : false
|
435
|
+
def save(perform_validations=true)
|
436
|
+
!perform_validations || valid? ? create_or_update : false
|
421
437
|
end
|
422
438
|
|
423
439
|
def save!
|
424
|
-
|
440
|
+
save || raise(DocumentNotValid.new(self))
|
425
441
|
end
|
426
442
|
|
427
443
|
def destroy
|
428
444
|
return false if frozen?
|
429
|
-
self.class.delete(
|
445
|
+
self.class.delete(_id) unless new?
|
430
446
|
freeze
|
431
447
|
end
|
432
448
|
|
433
449
|
def reload
|
434
|
-
self.class.find(
|
450
|
+
self.class.find(_id)
|
435
451
|
end
|
436
452
|
|
437
453
|
private
|
@@ -447,7 +463,7 @@ module MongoMapper
|
|
447
463
|
|
448
464
|
def assign_id
|
449
465
|
if read_attribute(:_id).blank?
|
450
|
-
write_attribute
|
466
|
+
write_attribute :_id, Mongo::ObjectID.new
|
451
467
|
end
|
452
468
|
end
|
453
469
|
|
@@ -16,7 +16,7 @@ module MongoMapper
|
|
16
16
|
|
17
17
|
extend Validations::Macros
|
18
18
|
|
19
|
-
key :_id,
|
19
|
+
key :_id, ObjectId
|
20
20
|
attr_accessor :_root_document
|
21
21
|
end
|
22
22
|
end
|
@@ -25,7 +25,7 @@ module MongoMapper
|
|
25
25
|
def logger
|
26
26
|
MongoMapper.logger
|
27
27
|
end
|
28
|
-
|
28
|
+
|
29
29
|
def inherited(subclass)
|
30
30
|
unless subclass.embeddable?
|
31
31
|
subclass.set_collection_name(collection_name)
|
@@ -48,19 +48,23 @@ module MongoMapper
|
|
48
48
|
|
49
49
|
def key(*args)
|
50
50
|
key = Key.new(*args)
|
51
|
+
keys[key.name] = key
|
51
52
|
|
52
|
-
|
53
|
-
|
53
|
+
create_accessors_for(key)
|
54
|
+
create_key_in_subclasses(*args)
|
55
|
+
create_validations_for(key)
|
54
56
|
|
55
|
-
create_accessors_for(key)
|
56
|
-
create_key_in_subclasses(*args)
|
57
|
-
create_validations_for(key)
|
58
|
-
|
59
|
-
key
|
60
|
-
end
|
61
|
-
|
62
57
|
key
|
63
58
|
end
|
59
|
+
|
60
|
+
def using_object_id?
|
61
|
+
object_id_key?(:_id)
|
62
|
+
end
|
63
|
+
|
64
|
+
def object_id_key?(name)
|
65
|
+
key = keys[name.to_s]
|
66
|
+
key && key.type == ObjectId
|
67
|
+
end
|
64
68
|
|
65
69
|
def embeddable?
|
66
70
|
!self.ancestors.include?(Document)
|
@@ -89,7 +93,13 @@ module MongoMapper
|
|
89
93
|
|
90
94
|
private
|
91
95
|
def accessors_module
|
92
|
-
if const_defined?
|
96
|
+
module_defined = if method(:const_defined?).arity == 1 # Ruby 1.9 compat check
|
97
|
+
const_defined?('MongoMapperKeys')
|
98
|
+
else
|
99
|
+
const_defined?('MongoMapperKeys', false)
|
100
|
+
end
|
101
|
+
|
102
|
+
if module_defined
|
93
103
|
const_get 'MongoMapperKeys'
|
94
104
|
else
|
95
105
|
const_set 'MongoMapperKeys', Module.new
|
@@ -187,14 +197,14 @@ module MongoMapper
|
|
187
197
|
|
188
198
|
if self.class.embeddable?
|
189
199
|
if read_attribute(:_id).blank?
|
190
|
-
write_attribute :_id, Mongo::ObjectID.new
|
200
|
+
write_attribute :_id, Mongo::ObjectID.new
|
191
201
|
@new_document = true
|
192
202
|
else
|
193
203
|
@new_document = false
|
194
204
|
end
|
195
205
|
end
|
196
206
|
end
|
197
|
-
|
207
|
+
|
198
208
|
def new?
|
199
209
|
!!@new_document
|
200
210
|
end
|
@@ -220,7 +230,6 @@ module MongoMapper
|
|
220
230
|
attrs = HashWithIndifferentAccess.new
|
221
231
|
|
222
232
|
embedded_keys.each do |key|
|
223
|
-
puts key.inspect
|
224
233
|
attrs[key.name] = read_attribute(key.name).try(:attributes)
|
225
234
|
end
|
226
235
|
|
@@ -270,15 +279,20 @@ module MongoMapper
|
|
270
279
|
end
|
271
280
|
|
272
281
|
def ==(other)
|
273
|
-
other.is_a?(self.class) &&
|
282
|
+
other.is_a?(self.class) && _id == other._id
|
274
283
|
end
|
275
284
|
|
276
285
|
def id
|
277
|
-
read_attribute(:_id)
|
286
|
+
read_attribute(:_id).to_s
|
278
287
|
end
|
279
288
|
|
280
289
|
def id=(value)
|
281
|
-
|
290
|
+
if self.class.using_object_id?
|
291
|
+
value = MongoMapper.normalize_object_id(value)
|
292
|
+
else
|
293
|
+
@using_custom_id = true
|
294
|
+
end
|
295
|
+
|
282
296
|
write_attribute :_id, value
|
283
297
|
end
|
284
298
|
|
@@ -8,24 +8,15 @@ module MongoMapper
|
|
8
8
|
# useful for understanding how MongoMapper handles the parsing of finder
|
9
9
|
# conditions and options.
|
10
10
|
#
|
11
|
-
# @private
|
12
|
-
class FinderOperator
|
13
|
-
def initialize(field, operator)
|
14
|
-
@field, @operator = field, operator
|
15
|
-
end
|
16
|
-
|
17
|
-
def to_criteria(value)
|
18
|
-
{@field => {@operator => value}}
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
11
|
+
# @private
|
22
12
|
class FinderOptions
|
23
13
|
OptionKeys = [:fields, :select, :skip, :offset, :limit, :sort, :order]
|
24
14
|
|
25
15
|
def initialize(model, options)
|
26
16
|
raise ArgumentError, "Options must be a hash" unless options.is_a?(Hash)
|
17
|
+
options = options.clone
|
27
18
|
options.symbolize_keys!
|
28
|
-
|
19
|
+
|
29
20
|
@model = model
|
30
21
|
@options = {}
|
31
22
|
@conditions = options.delete(:conditions) || {}
|
@@ -71,10 +62,16 @@ module MongoMapper
|
|
71
62
|
|
72
63
|
conditions.each_pair do |field, value|
|
73
64
|
field = normalized_field(field)
|
65
|
+
|
66
|
+
if @model.object_id_key?(field) && value.is_a?(String)
|
67
|
+
value = Mongo::ObjectID.from_string(value)
|
68
|
+
end
|
69
|
+
|
74
70
|
if field.is_a?(FinderOperator)
|
75
71
|
criteria.merge!(field.to_criteria(value))
|
76
72
|
next
|
77
73
|
end
|
74
|
+
|
78
75
|
case value
|
79
76
|
when Array
|
80
77
|
operator_present = field.to_s =~ /^\$/
|
@@ -127,4 +124,14 @@ module MongoMapper
|
|
127
124
|
[field, direction]
|
128
125
|
end
|
129
126
|
end
|
127
|
+
|
128
|
+
class FinderOperator
|
129
|
+
def initialize(field, operator)
|
130
|
+
@field, @operator = field, operator
|
131
|
+
end
|
132
|
+
|
133
|
+
def to_criteria(value)
|
134
|
+
{@field => {@operator => value}}
|
135
|
+
end
|
136
|
+
end
|
130
137
|
end
|
data/lib/mongo_mapper/support.rb
CHANGED
@@ -1,10 +1,12 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
1
3
|
class BasicObject #:nodoc:
|
2
|
-
|
3
|
-
instance_methods.each { |m| undef_method m unless m =~ /(^__|^nil\?$|^send$|^methods$|instance_eval|proxy_|^object_id$)/ }
|
4
|
+
instance_methods.each { |m| undef_method m unless m =~ /(^__|instance_eval)/ }
|
4
5
|
end unless defined?(BasicObject)
|
5
6
|
|
6
7
|
class Array
|
7
8
|
def self.to_mongo(value)
|
9
|
+
value = value.respond_to?(:lines) ? value.lines : value
|
8
10
|
value.to_a
|
9
11
|
end
|
10
12
|
|
@@ -13,34 +15,6 @@ class Array
|
|
13
15
|
end
|
14
16
|
end
|
15
17
|
|
16
|
-
class Binary
|
17
|
-
def self.to_mongo(value)
|
18
|
-
if value.is_a?(ByteBuffer)
|
19
|
-
value
|
20
|
-
else
|
21
|
-
value.nil? ? nil : ByteBuffer.new(value)
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
def self.from_mongo(value)
|
26
|
-
value
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
class Boolean
|
31
|
-
def self.to_mongo(value)
|
32
|
-
if value.is_a?(Boolean)
|
33
|
-
value
|
34
|
-
else
|
35
|
-
['true', 't', '1'].include?(value.to_s.downcase)
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
def self.from_mongo(value)
|
40
|
-
!!value
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
18
|
class Date
|
45
19
|
def self.to_mongo(value)
|
46
20
|
date = Date.parse(value.to_s)
|
@@ -120,6 +94,16 @@ class Object
|
|
120
94
|
end
|
121
95
|
end
|
122
96
|
|
97
|
+
class Set
|
98
|
+
def self.to_mongo(value)
|
99
|
+
value.to_a
|
100
|
+
end
|
101
|
+
|
102
|
+
def self.from_mongo(value)
|
103
|
+
Set.new(value || [])
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
123
107
|
class String
|
124
108
|
def self.to_mongo(value)
|
125
109
|
value.nil? ? nil : value.to_s
|
@@ -138,33 +122,21 @@ class Symbol
|
|
138
122
|
end
|
139
123
|
end
|
140
124
|
|
141
|
-
class Time
|
125
|
+
class Time
|
142
126
|
def self.to_mongo(value)
|
143
127
|
if value.nil? || value == ''
|
144
128
|
nil
|
145
129
|
else
|
146
|
-
|
130
|
+
time = MongoMapper.time_class.parse(value.to_s)
|
131
|
+
time && time.utc
|
147
132
|
end
|
148
133
|
end
|
149
134
|
|
150
135
|
def self.from_mongo(value)
|
151
|
-
if
|
136
|
+
if MongoMapper.use_time_zone? && value.present?
|
152
137
|
value.in_time_zone(Time.zone)
|
153
138
|
else
|
154
139
|
value
|
155
140
|
end
|
156
141
|
end
|
157
|
-
|
158
|
-
def self.to_utc_time(value)
|
159
|
-
to_local_time(value).try(:utc)
|
160
|
-
end
|
161
|
-
|
162
|
-
# make sure we have a time and that it is local
|
163
|
-
def self.to_local_time(value)
|
164
|
-
if Time.respond_to?(:zone) && Time.zone
|
165
|
-
Time.zone.parse(value.to_s)
|
166
|
-
else
|
167
|
-
Time.parse(value.to_s)
|
168
|
-
end
|
169
|
-
end
|
170
142
|
end
|