mongo_mapper 0.5.8 → 0.6.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.
- 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
|