mongo_mapper 0.12.0 → 0.13.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.rdoc +35 -13
- data/bin/mmconsole +1 -1
- data/lib/mongo_mapper.rb +4 -0
- data/lib/mongo_mapper/connection.rb +17 -6
- data/lib/mongo_mapper/document.rb +1 -0
- data/lib/mongo_mapper/exceptions.rb +4 -1
- data/lib/mongo_mapper/extensions/binary.rb +1 -1
- data/lib/mongo_mapper/extensions/boolean.rb +20 -23
- data/lib/mongo_mapper/extensions/date.rb +3 -3
- data/lib/mongo_mapper/extensions/integer.rb +5 -1
- data/lib/mongo_mapper/extensions/kernel.rb +2 -0
- data/lib/mongo_mapper/extensions/ordered_hash.rb +23 -0
- data/lib/mongo_mapper/extensions/string.rb +2 -2
- data/lib/mongo_mapper/extensions/time.rb +7 -5
- data/lib/mongo_mapper/middleware/identity_map.rb +3 -4
- data/lib/mongo_mapper/plugins.rb +1 -1
- data/lib/mongo_mapper/plugins/associations.rb +11 -5
- data/lib/mongo_mapper/plugins/associations/base.rb +5 -3
- data/lib/mongo_mapper/plugins/associations/belongs_to_polymorphic_proxy.rb +0 -0
- data/lib/mongo_mapper/plugins/associations/belongs_to_proxy.rb +8 -8
- data/lib/mongo_mapper/plugins/associations/collection.rb +2 -0
- data/lib/mongo_mapper/plugins/associations/many_documents_proxy.rb +32 -7
- data/lib/mongo_mapper/plugins/associations/many_embedded_proxy.rb +2 -2
- data/lib/mongo_mapper/plugins/associations/one_proxy.rb +12 -12
- data/lib/mongo_mapper/plugins/associations/proxy.rb +5 -1
- data/lib/mongo_mapper/plugins/associations/single_association.rb +6 -6
- data/lib/mongo_mapper/plugins/clone.rb +4 -2
- data/lib/mongo_mapper/plugins/dirty.rb +22 -21
- data/lib/mongo_mapper/plugins/document.rb +4 -4
- data/lib/mongo_mapper/plugins/dumpable.rb +22 -0
- data/lib/mongo_mapper/plugins/embedded_callbacks.rb +58 -9
- data/lib/mongo_mapper/plugins/identity_map.rb +42 -32
- data/lib/mongo_mapper/plugins/keys.rb +133 -54
- data/lib/mongo_mapper/plugins/keys/key.rb +68 -22
- data/lib/mongo_mapper/plugins/modifiers.rb +26 -19
- data/lib/mongo_mapper/plugins/persistence.rb +15 -5
- data/lib/mongo_mapper/plugins/querying.rb +15 -40
- data/lib/mongo_mapper/plugins/querying/{decorator.rb → decorated_plucky_query.rb} +24 -4
- data/lib/mongo_mapper/plugins/rails.rb +22 -2
- data/lib/mongo_mapper/plugins/safe.rb +8 -5
- data/lib/mongo_mapper/plugins/sci.rb +26 -4
- data/lib/mongo_mapper/plugins/scopes.rb +5 -4
- data/lib/mongo_mapper/plugins/timestamps.rb +11 -4
- data/lib/mongo_mapper/plugins/validations.rb +1 -1
- data/lib/mongo_mapper/utils.rb +12 -0
- data/lib/mongo_mapper/version.rb +1 -1
- data/lib/rails/generators/mongo_mapper/config/config_generator.rb +20 -7
- data/lib/rails/generators/mongo_mapper/config/templates/mongo.yml +6 -0
- data/lib/rails/generators/mongo_mapper/model/model_generator.rb +18 -1
- data/lib/rails/generators/mongo_mapper/model/templates/model.rb +9 -5
- data/{test/functional/test_accessible.rb → spec/functional/accessible_spec.rb} +29 -29
- data/{test/functional/associations/test_belongs_to_polymorphic_proxy.rb → spec/functional/associations/belongs_to_polymorphic_proxy_spec.rb} +10 -10
- data/{test/functional/associations/test_belongs_to_proxy.rb → spec/functional/associations/belongs_to_proxy_spec.rb} +82 -64
- data/{test/functional/associations/test_in_array_proxy.rb → spec/functional/associations/in_array_proxy_spec.rb} +68 -68
- data/{test/functional/associations/test_many_documents_as_proxy.rb → spec/functional/associations/many_documents_as_proxy_spec.rb} +37 -38
- data/{test/functional/associations/test_many_documents_proxy.rb → spec/functional/associations/many_documents_proxy_spec.rb} +233 -146
- data/{test/functional/associations/test_many_embedded_polymorphic_proxy.rb → spec/functional/associations/many_embedded_polymorphic_proxy_spec.rb} +19 -20
- data/{test/functional/associations/test_many_embedded_proxy.rb → spec/functional/associations/many_embedded_proxy_spec.rb} +23 -24
- data/{test/functional/associations/test_many_polymorphic_proxy.rb → spec/functional/associations/many_polymorphic_proxy_spec.rb} +45 -46
- data/{test/functional/associations/test_one_as_proxy.rb → spec/functional/associations/one_as_proxy_spec.rb} +75 -77
- data/{test/functional/associations/test_one_embedded_polymorphic_proxy.rb → spec/functional/associations/one_embedded_polymorphic_proxy_spec.rb} +31 -32
- data/{test/functional/associations/test_one_embedded_proxy.rb → spec/functional/associations/one_embedded_proxy_spec.rb} +10 -10
- data/{test/functional/associations/test_one_proxy.rb → spec/functional/associations/one_proxy_spec.rb} +125 -102
- data/spec/functional/associations_spec.rb +48 -0
- data/{test/functional/test_binary.rb → spec/functional/binary_spec.rb} +6 -6
- data/spec/functional/caching_spec.rb +75 -0
- data/{test/functional/test_callbacks.rb → spec/functional/callbacks_spec.rb} +84 -26
- data/{test/functional/test_dirty.rb → spec/functional/dirty_spec.rb} +57 -42
- data/{test/functional/test_document.rb → spec/functional/document_spec.rb} +52 -52
- data/spec/functional/dumpable_spec.rb +24 -0
- data/{test/functional/test_dynamic_querying.rb → spec/functional/dynamic_querying_spec.rb} +14 -14
- data/{test/functional/test_embedded_document.rb → spec/functional/embedded_document_spec.rb} +51 -42
- data/{test/functional/test_equality.rb → spec/functional/equality_spec.rb} +4 -4
- data/spec/functional/extensions_spec.rb +16 -0
- data/{test/functional/test_identity_map.rb → spec/functional/identity_map_spec.rb} +73 -61
- data/spec/functional/indexes_spec.rb +48 -0
- data/spec/functional/keys_spec.rb +224 -0
- data/{test/functional/test_logger.rb → spec/functional/logger_spec.rb} +6 -6
- data/spec/functional/modifiers_spec.rb +550 -0
- data/spec/functional/pagination_spec.rb +89 -0
- data/spec/functional/protected_spec.rb +199 -0
- data/spec/functional/querying_spec.rb +1003 -0
- data/spec/functional/rails_spec.rb +55 -0
- data/spec/functional/safe_spec.rb +163 -0
- data/{test/functional/test_sci.rb → spec/functional/sci_spec.rb} +123 -34
- data/{test/functional/test_scopes.rb → spec/functional/scopes_spec.rb} +59 -26
- data/spec/functional/timestamps_spec.rb +97 -0
- data/{test/functional/test_touch.rb → spec/functional/touch_spec.rb} +13 -13
- data/spec/functional/userstamps_spec.rb +46 -0
- data/{test/functional/test_validations.rb → spec/functional/validations_spec.rb} +64 -64
- data/spec/spec_helper.rb +81 -0
- data/spec/support/matchers.rb +24 -0
- data/{test → spec/support}/models.rb +1 -6
- data/spec/unit/associations/base_spec.rb +146 -0
- data/spec/unit/associations/belongs_to_association_spec.rb +30 -0
- data/spec/unit/associations/many_association_spec.rb +64 -0
- data/spec/unit/associations/one_association_spec.rb +48 -0
- data/{test/unit/associations/test_proxy.rb → spec/unit/associations/proxy_spec.rb} +21 -21
- data/{test/unit/test_clone.rb → spec/unit/clone_spec.rb} +21 -11
- data/spec/unit/config_generator_spec.rb +24 -0
- data/{test/unit/test_document.rb → spec/unit/document_spec.rb} +42 -42
- data/{test/unit/test_dynamic_finder.rb → spec/unit/dynamic_finder_spec.rb} +28 -28
- data/{test/unit/test_embedded_document.rb → spec/unit/embedded_document_spec.rb} +102 -108
- data/{test/unit/test_equality.rb → spec/unit/equality_spec.rb} +7 -7
- data/{test/unit/test_exceptions.rb → spec/unit/exceptions_spec.rb} +3 -3
- data/{test/unit/test_extensions.rb → spec/unit/extensions_spec.rb} +85 -71
- data/spec/unit/identity_map_middleware_spec.rb +134 -0
- data/{test/unit/test_inspect.rb → spec/unit/inspect_spec.rb} +8 -8
- data/{test/unit/test_key.rb → spec/unit/key_spec.rb} +82 -52
- data/spec/unit/keys_spec.rb +155 -0
- data/spec/unit/model_generator_spec.rb +47 -0
- data/spec/unit/mongo_mapper_spec.rb +184 -0
- data/spec/unit/pagination_spec.rb +11 -0
- data/{test/unit/test_plugins.rb → spec/unit/plugins_spec.rb} +14 -14
- data/spec/unit/rails_compatibility_spec.rb +40 -0
- data/{test/unit/test_rails_reflect_on_association.rb → spec/unit/rails_reflect_on_association_spec.rb} +9 -9
- data/{test/unit/test_rails.rb → spec/unit/rails_spec.rb} +31 -31
- data/spec/unit/serialization_spec.rb +169 -0
- data/spec/unit/serializers/json_serializer_spec.rb +218 -0
- data/spec/unit/serializers/xml_serializer_spec.rb +198 -0
- data/{test/unit/test_time_zones.rb → spec/unit/time_zones_spec.rb} +8 -8
- data/{test/unit/test_translation.rb → spec/unit/translation_spec.rb} +6 -6
- data/{test/unit/test_validations.rb → spec/unit/validations_spec.rb} +72 -59
- metadata +199 -179
- data/test/_NOTE_ON_TESTING +0 -1
- data/test/functional/test_associations.rb +0 -46
- data/test/functional/test_caching.rb +0 -77
- data/test/functional/test_indexes.rb +0 -50
- data/test/functional/test_modifiers.rb +0 -537
- data/test/functional/test_pagination.rb +0 -91
- data/test/functional/test_protected.rb +0 -201
- data/test/functional/test_querying.rb +0 -935
- data/test/functional/test_safe.rb +0 -76
- data/test/functional/test_timestamps.rb +0 -62
- data/test/functional/test_userstamps.rb +0 -44
- data/test/support/railtie.rb +0 -4
- data/test/support/railtie/autoloaded.rb +0 -2
- data/test/support/railtie/not_autoloaded.rb +0 -3
- data/test/support/railtie/parent.rb +0 -3
- data/test/test_active_model_lint.rb +0 -18
- data/test/test_helper.rb +0 -93
- data/test/unit/associations/test_base.rb +0 -146
- data/test/unit/associations/test_belongs_to_association.rb +0 -29
- data/test/unit/associations/test_many_association.rb +0 -63
- data/test/unit/associations/test_one_association.rb +0 -47
- data/test/unit/serializers/test_json_serializer.rb +0 -216
- data/test/unit/serializers/test_xml_serializer.rb +0 -196
- data/test/unit/test_identity_map_middleware.rb +0 -132
- data/test/unit/test_keys.rb +0 -65
- data/test/unit/test_mongo_mapper.rb +0 -157
- data/test/unit/test_pagination.rb +0 -11
- data/test/unit/test_rails_compatibility.rb +0 -38
- data/test/unit/test_serialization.rb +0 -166
@@ -57,37 +57,10 @@ module MongoMapper
|
|
57
57
|
IdentityMap.repository[IdentityMap.key(self, id)]
|
58
58
|
end
|
59
59
|
|
60
|
-
module IdentityMapQueryMethods
|
61
|
-
def all(opts={})
|
62
|
-
query = clone.amend(opts)
|
63
|
-
super.tap do |docs|
|
64
|
-
model.remove_documents_from_map(docs) if query.fields?
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
def find_one(opts={})
|
69
|
-
query = clone.amend(opts)
|
70
|
-
|
71
|
-
if IdentityMap.enabled? && query.simple? && (document = model.get_from_identity_map(query[:_id]))
|
72
|
-
document
|
73
|
-
else
|
74
|
-
super.tap do |doc|
|
75
|
-
model.remove_documents_from_map(doc) if query.fields?
|
76
|
-
end
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
def find_each(opts={}, &block)
|
81
|
-
query = clone.amend(opts)
|
82
|
-
super(opts) do |doc|
|
83
|
-
model.remove_documents_from_map(doc) if query.fields?
|
84
|
-
block.call(doc) unless block.nil?
|
85
|
-
end
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
60
|
def query(opts={})
|
90
|
-
super.
|
61
|
+
super.tap do |query|
|
62
|
+
query.identity_map = self if Thread.current[:mongo_mapper_identity_map_enabled]
|
63
|
+
end
|
91
64
|
end
|
92
65
|
|
93
66
|
def remove_documents_from_map(*documents)
|
@@ -97,10 +70,11 @@ module MongoMapper
|
|
97
70
|
end
|
98
71
|
|
99
72
|
def load(attrs)
|
100
|
-
return
|
73
|
+
return super unless Thread.current[:mongo_mapper_identity_map_enabled]
|
74
|
+
return nil unless attrs
|
101
75
|
document = get_from_identity_map(attrs['_id'])
|
102
76
|
|
103
|
-
if document
|
77
|
+
if !document
|
104
78
|
document = super
|
105
79
|
document.add_to_identity_map
|
106
80
|
end
|
@@ -133,3 +107,39 @@ module MongoMapper
|
|
133
107
|
end
|
134
108
|
end
|
135
109
|
end
|
110
|
+
|
111
|
+
module PluckyMethods
|
112
|
+
module ClassMethods
|
113
|
+
extend ActiveSupport::Concern
|
114
|
+
|
115
|
+
included do
|
116
|
+
attr_accessor :identity_map
|
117
|
+
|
118
|
+
# Ensure that these aliased methods in plucky also get overridden.
|
119
|
+
alias_method :first, :find_one
|
120
|
+
alias_method :each, :find_each
|
121
|
+
end
|
122
|
+
|
123
|
+
def find_one(opts={})
|
124
|
+
query = clone.amend(opts)
|
125
|
+
|
126
|
+
if identity_map && query.simple? && (document = identity_map.get_from_identity_map(query[:_id]))
|
127
|
+
document
|
128
|
+
else
|
129
|
+
super.tap do |doc|
|
130
|
+
doc.remove_from_identity_map if doc && query.fields?
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def find_each(opts={})
|
136
|
+
query = clone.amend(opts)
|
137
|
+
super(opts) do |doc|
|
138
|
+
doc.remove_from_identity_map if doc && query.fields?
|
139
|
+
yield doc if block_given?
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
::MongoMapper::Plugins::Querying::DecoratedPluckyQuery.send :include, ::PluckyMethods::ClassMethods
|
@@ -6,6 +6,8 @@ module MongoMapper
|
|
6
6
|
module Keys
|
7
7
|
extend ActiveSupport::Concern
|
8
8
|
|
9
|
+
IS_RUBY_1_9 = method(:const_defined?).arity == 1
|
10
|
+
|
9
11
|
included do
|
10
12
|
extend ActiveSupport::DescendantsTracker
|
11
13
|
key :_id, ObjectId, :default => lambda { BSON::ObjectId.new }
|
@@ -21,18 +23,37 @@ module MongoMapper
|
|
21
23
|
@keys ||= {}
|
22
24
|
end
|
23
25
|
|
26
|
+
def dynamic_keys
|
27
|
+
@dynamic_keys ||= Hash[*unaliased_keys.select {|k, v| v.dynamic? }.flatten(1)]
|
28
|
+
end
|
29
|
+
|
30
|
+
def defined_keys
|
31
|
+
@defined_keys ||= Hash[*unaliased_keys.select {|k, v| !v.dynamic? }.flatten(1)]
|
32
|
+
end
|
33
|
+
|
34
|
+
def unaliased_keys
|
35
|
+
@unaliased_keys ||= Hash[*keys.select {|k, v| k == v.name }.flatten(1)]
|
36
|
+
end
|
37
|
+
|
24
38
|
def key(*args)
|
25
39
|
Key.new(*args).tap do |key|
|
26
40
|
keys[key.name] = key
|
27
|
-
|
41
|
+
keys[key.abbr] = key if key.abbr
|
42
|
+
create_accessors_for(key) if key.valid_ruby_name?
|
28
43
|
create_key_in_descendants(*args)
|
29
44
|
create_indexes_for(key)
|
30
45
|
create_validations_for(key)
|
46
|
+
@dynamic_keys = @defined_keys = @unaliased_keys = @object_id_keys = nil
|
31
47
|
end
|
32
48
|
end
|
33
49
|
|
50
|
+
def persisted_name(name)
|
51
|
+
keys[name.to_s].persisted_name
|
52
|
+
end
|
53
|
+
alias_method :abbr, :persisted_name
|
54
|
+
|
34
55
|
def key?(key)
|
35
|
-
keys.
|
56
|
+
keys.key? key.to_s
|
36
57
|
end
|
37
58
|
|
38
59
|
def using_object_id?
|
@@ -40,7 +61,7 @@ module MongoMapper
|
|
40
61
|
end
|
41
62
|
|
42
63
|
def object_id_keys
|
43
|
-
|
64
|
+
@object_id_keys ||= unaliased_keys.keys.select { |key| keys[key].type == ObjectId }.map(&:to_sym)
|
44
65
|
end
|
45
66
|
|
46
67
|
def object_id_key?(name)
|
@@ -48,20 +69,18 @@ module MongoMapper
|
|
48
69
|
end
|
49
70
|
|
50
71
|
def to_mongo(instance)
|
51
|
-
|
52
|
-
instance.to_mongo
|
72
|
+
instance && instance.to_mongo
|
53
73
|
end
|
54
74
|
|
55
75
|
def from_mongo(value)
|
56
|
-
|
57
|
-
value.is_a?(self) ? value : load(value)
|
76
|
+
value && (value.instance_of?(self) ? value : load(value))
|
58
77
|
end
|
59
78
|
|
60
79
|
# load is overridden in identity map to ensure same objects are loaded
|
61
80
|
def load(attrs)
|
62
81
|
return nil if attrs.nil?
|
63
82
|
begin
|
64
|
-
attrs['_type']
|
83
|
+
attrs['_type'] ? attrs['_type'].constantize : self
|
65
84
|
rescue NameError
|
66
85
|
self
|
67
86
|
end.allocate.initialize_from_database(attrs)
|
@@ -69,11 +88,13 @@ module MongoMapper
|
|
69
88
|
|
70
89
|
private
|
71
90
|
def key_accessors_module_defined?
|
72
|
-
|
91
|
+
# :nocov:
|
92
|
+
if IS_RUBY_1_9
|
73
93
|
const_defined?('MongoMapperKeys')
|
74
94
|
else
|
75
95
|
const_defined?('MongoMapperKeys', false)
|
76
96
|
end
|
97
|
+
# :nocov:
|
77
98
|
end
|
78
99
|
|
79
100
|
def accessors_module
|
@@ -90,10 +111,6 @@ module MongoMapper
|
|
90
111
|
read_key(:#{key.name})
|
91
112
|
end
|
92
113
|
|
93
|
-
def #{key.name}_before_type_cast
|
94
|
-
read_key_before_type_cast(:#{key.name})
|
95
|
-
end
|
96
|
-
|
97
114
|
def #{key.name}=(value)
|
98
115
|
write_key(:#{key.name}, value)
|
99
116
|
end
|
@@ -103,6 +120,12 @@ module MongoMapper
|
|
103
120
|
end
|
104
121
|
end_eval
|
105
122
|
|
123
|
+
if block_given?
|
124
|
+
accessors_module.module_eval do
|
125
|
+
yield
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
106
129
|
include accessors_module
|
107
130
|
end
|
108
131
|
|
@@ -165,11 +188,16 @@ module MongoMapper
|
|
165
188
|
|
166
189
|
def initialize(attrs={})
|
167
190
|
@_new = true
|
191
|
+
init_ivars
|
192
|
+
initialize_default_values(attrs)
|
168
193
|
self.attributes = attrs
|
194
|
+
yield self if block_given?
|
169
195
|
end
|
170
196
|
|
171
197
|
def initialize_from_database(attrs={})
|
172
198
|
@_new = false
|
199
|
+
init_ivars
|
200
|
+
initialize_default_values(attrs)
|
173
201
|
load_from_database(attrs)
|
174
202
|
self
|
175
203
|
end
|
@@ -179,7 +207,7 @@ module MongoMapper
|
|
179
207
|
end
|
180
208
|
|
181
209
|
def attributes=(attrs)
|
182
|
-
return if attrs.blank?
|
210
|
+
return if attrs == nil || attrs.blank?
|
183
211
|
|
184
212
|
attrs.each_pair do |key, value|
|
185
213
|
if respond_to?(:"#{key}=")
|
@@ -190,25 +218,30 @@ module MongoMapper
|
|
190
218
|
end
|
191
219
|
end
|
192
220
|
|
193
|
-
def
|
194
|
-
|
195
|
-
|
196
|
-
value =
|
197
|
-
|
221
|
+
def to_mongo(include_abbreviatons = true)
|
222
|
+
BSON::OrderedHash.new.tap do |attrs|
|
223
|
+
self.class.unaliased_keys.each do |name, key|
|
224
|
+
value = self.read_key(key.name)
|
225
|
+
if key.type == ObjectId || !value.nil?
|
226
|
+
attrs[include_abbreviatons && key.persisted_name || name] = key.set(value)
|
227
|
+
end
|
198
228
|
end
|
199
229
|
|
200
230
|
embedded_associations.each do |association|
|
201
231
|
if documents = instance_variable_get(association.ivar)
|
202
|
-
if association.
|
232
|
+
if association.instance_of?(Associations::OneAssociation)
|
203
233
|
attrs[association.name] = documents.to_mongo
|
204
234
|
else
|
205
|
-
attrs[association.name] = documents.map
|
235
|
+
attrs[association.name] = documents.map(&:to_mongo)
|
206
236
|
end
|
207
237
|
end
|
208
238
|
end
|
209
239
|
end
|
210
240
|
end
|
211
|
-
|
241
|
+
|
242
|
+
def attributes
|
243
|
+
to_mongo(false).with_indifferent_access
|
244
|
+
end
|
212
245
|
|
213
246
|
def assign(attrs={})
|
214
247
|
warn "[DEPRECATION] #assign is deprecated, use #attributes="
|
@@ -231,7 +264,7 @@ module MongoMapper
|
|
231
264
|
end
|
232
265
|
|
233
266
|
def id
|
234
|
-
_id
|
267
|
+
self[:_id]
|
235
268
|
end
|
236
269
|
|
237
270
|
def id=(value)
|
@@ -242,72 +275,118 @@ module MongoMapper
|
|
242
275
|
self[:_id] = value
|
243
276
|
end
|
244
277
|
|
245
|
-
def
|
246
|
-
|
278
|
+
def keys
|
279
|
+
self.class.keys
|
247
280
|
end
|
248
281
|
|
249
|
-
def
|
250
|
-
|
251
|
-
|
282
|
+
def read_key(key_name)
|
283
|
+
key_name_sym = key_name.to_sym
|
284
|
+
if @_dynamic_attributes && @_dynamic_attributes.key?(key_name_sym)
|
285
|
+
@_dynamic_attributes[key_name_sym]
|
286
|
+
elsif key = keys[key_name.to_s]
|
287
|
+
if key.ivar && instance_variable_defined?(key.ivar)
|
288
|
+
value = instance_variable_get(key.ivar)
|
289
|
+
else
|
290
|
+
if key.ivar
|
291
|
+
instance_variable_set key.ivar, key.get(nil)
|
292
|
+
else
|
293
|
+
@_dynamic_attributes[key_name_sym] = key.get(nil)
|
294
|
+
end
|
295
|
+
end
|
296
|
+
end
|
252
297
|
end
|
253
298
|
|
254
|
-
|
255
|
-
|
299
|
+
alias_method :[], :read_key
|
300
|
+
alias_method :attribute, :read_key
|
301
|
+
|
302
|
+
def []=(name, value)
|
303
|
+
write_key(name, value)
|
256
304
|
end
|
257
305
|
|
258
306
|
def key_names
|
259
|
-
keys.keys
|
307
|
+
@key_names ||= keys.keys
|
260
308
|
end
|
261
309
|
|
262
310
|
def non_embedded_keys
|
263
|
-
keys.values.select { |key| !key.embeddable? }
|
311
|
+
@non_embedded_keys ||= keys.values.select { |key| !key.embeddable? }
|
264
312
|
end
|
265
313
|
|
266
314
|
def embedded_keys
|
267
|
-
keys.values.select
|
315
|
+
@embedded_keys ||= keys.values.select(&:embeddable?)
|
316
|
+
end
|
317
|
+
|
318
|
+
protected
|
319
|
+
|
320
|
+
def unalias_key(name)
|
321
|
+
name = name.to_s
|
322
|
+
if key = keys[name]
|
323
|
+
key.name
|
324
|
+
else
|
325
|
+
name
|
326
|
+
end
|
268
327
|
end
|
269
328
|
|
270
329
|
private
|
330
|
+
|
331
|
+
def init_ivars
|
332
|
+
@__mm_keys = self.class.keys # Not dumpable
|
333
|
+
@__mm_default_keys = @__mm_keys.values.select(&:default?) # Not dumpable
|
334
|
+
@_dynamic_attributes = {} # Dumpable
|
335
|
+
end
|
336
|
+
|
271
337
|
def load_from_database(attrs)
|
272
|
-
return if attrs.blank?
|
338
|
+
return if attrs == nil || attrs.blank?
|
339
|
+
|
273
340
|
attrs.each do |key, value|
|
274
|
-
if respond_to?(:"#{key}=")
|
341
|
+
if !@__mm_keys.key?(key) && respond_to?(:"#{key}=")
|
275
342
|
self.send(:"#{key}=", value)
|
276
343
|
else
|
277
|
-
|
344
|
+
internal_write_key key, value, false
|
278
345
|
end
|
279
346
|
end
|
280
347
|
end
|
281
348
|
|
282
|
-
def ensure_key_exists(name)
|
283
|
-
self.class.key(name) unless respond_to?("#{name}=")
|
284
|
-
end
|
285
|
-
|
286
349
|
def set_parent_document(key, value)
|
287
|
-
if key.
|
350
|
+
if key.type and value.instance_of?(key.type) && key.embeddable? && value.respond_to?(:_parent_document)
|
288
351
|
value._parent_document = self
|
289
352
|
end
|
290
353
|
end
|
291
354
|
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
355
|
+
# This exists to be patched over by plugins, while letting us still get to the undecorated
|
356
|
+
# version of the method.
|
357
|
+
def write_key(name, value)
|
358
|
+
init_ivars unless @__mm_keys
|
359
|
+
internal_write_key(name.to_s, value)
|
360
|
+
end
|
361
|
+
|
362
|
+
def internal_write_key(name, value, cast = true)
|
363
|
+
key = @__mm_keys[name] || dynamic_key(name)
|
364
|
+
as_mongo = cast ? key.set(value) : value
|
365
|
+
as_typecast = key.get(as_mongo)
|
366
|
+
if key.ivar
|
367
|
+
if key.embeddable?
|
368
|
+
set_parent_document(key, value)
|
369
|
+
set_parent_document(key, as_typecast)
|
370
|
+
end
|
371
|
+
instance_variable_set key.ivar, as_typecast
|
372
|
+
else
|
373
|
+
@_dynamic_attributes[key.name.to_sym] = as_typecast
|
297
374
|
end
|
375
|
+
@attributes = nil
|
298
376
|
end
|
299
377
|
|
300
|
-
def
|
301
|
-
|
378
|
+
def dynamic_key(name)
|
379
|
+
self.class.key(name, :__dynamic => true)
|
302
380
|
end
|
303
381
|
|
304
|
-
def
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
382
|
+
def initialize_default_values(except = {})
|
383
|
+
@__mm_default_keys.each do |key|
|
384
|
+
if !(except && except.key?(key.name))
|
385
|
+
internal_write_key key.name, key.default_value, false
|
386
|
+
end
|
387
|
+
end
|
309
388
|
end
|
389
|
+
#end private
|
310
390
|
end
|
311
391
|
end
|
312
392
|
end
|
313
|
-
|
@@ -3,58 +3,104 @@ module MongoMapper
|
|
3
3
|
module Plugins
|
4
4
|
module Keys
|
5
5
|
class Key
|
6
|
-
attr_accessor :name, :type, :options, :
|
6
|
+
attr_accessor :name, :type, :options, :default, :ivar, :abbr
|
7
|
+
|
8
|
+
ID_STR = '_id'
|
7
9
|
|
8
10
|
def initialize(*args)
|
9
|
-
|
11
|
+
options_from_args = args.extract_options!
|
10
12
|
@name, @type = args.shift.to_s, args.shift
|
11
|
-
self.options = (
|
12
|
-
|
13
|
+
self.options = (options_from_args || {}).symbolize_keys
|
14
|
+
@dynamic = !!options[:__dynamic]
|
15
|
+
@embeddable = type.respond_to?(:embeddable?) ? type.embeddable? : false
|
16
|
+
@is_id = @name == ID_STR
|
17
|
+
@typecast = @options[:typecast]
|
18
|
+
@has_default = !!options.key?(:default)
|
19
|
+
self.default = self.options[:default] if default?
|
20
|
+
|
21
|
+
if abbr = @options[:abbr] || @options[:alias] || @options[:field_name]
|
22
|
+
@abbr = abbr.to_s
|
23
|
+
elsif @name.match(/^[A-Z]/) and !dynamic?
|
24
|
+
@abbr = @name
|
25
|
+
@name = @name.gsub(/^([A-Z])/) {|m| m.downcase }
|
26
|
+
Kernel.warn "Key names may not start with uppercase letters. If your field starts " +
|
27
|
+
"with an uppercase letter, use :field_name to specify the real field name. " +
|
28
|
+
"Accessors called `#{@name}` have been created instead."
|
29
|
+
end
|
30
|
+
@ivar = :"@#{name}" if valid_ruby_name?
|
31
|
+
validate_key_name! unless dynamic?
|
32
|
+
end
|
33
|
+
|
34
|
+
def persisted_name
|
35
|
+
@abbr || @name
|
13
36
|
end
|
14
37
|
|
15
38
|
def ==(other)
|
16
|
-
@name == other.name && @type == other.type
|
39
|
+
@name == other.name && @type == other.type && @abbr == other.abbr
|
17
40
|
end
|
18
41
|
|
19
42
|
def embeddable?
|
20
|
-
|
21
|
-
type.embeddable?
|
43
|
+
@embeddable
|
22
44
|
end
|
23
45
|
|
24
46
|
def number?
|
25
47
|
type == Integer || type == Float
|
26
48
|
end
|
27
49
|
|
50
|
+
def default?
|
51
|
+
@has_default
|
52
|
+
end
|
53
|
+
|
54
|
+
def dynamic?
|
55
|
+
@dynamic
|
56
|
+
end
|
57
|
+
|
28
58
|
def get(value)
|
29
|
-
|
30
|
-
|
31
|
-
return default_value.call
|
32
|
-
else
|
33
|
-
# Using Marshal is easiest way to get a copy of mutable objects
|
34
|
-
# without getting an error on immutable objects
|
35
|
-
return Marshal.load(Marshal.dump(default_value))
|
36
|
-
end
|
37
|
-
end
|
59
|
+
# Special Case: Generate default _id on access
|
60
|
+
value = default_value if @is_id and !value
|
38
61
|
|
39
|
-
if
|
40
|
-
|
62
|
+
if @typecast
|
63
|
+
klass = typecast_class # Don't make this lookup on every call
|
64
|
+
type.from_mongo(value).map! { |v| klass.from_mongo(v) }
|
41
65
|
else
|
42
66
|
type.from_mongo(value)
|
43
67
|
end
|
44
68
|
end
|
45
69
|
|
46
70
|
def set(value)
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
71
|
+
# Avoid tap here so we don't have to create a block binding.
|
72
|
+
values = type.to_mongo(value)
|
73
|
+
values.map! { |v| typecast_class.to_mongo(v) } if @typecast
|
74
|
+
values
|
75
|
+
end
|
76
|
+
|
77
|
+
def default_value
|
78
|
+
return unless default?
|
79
|
+
if default.instance_of? Proc
|
80
|
+
type.to_mongo default.call
|
81
|
+
else
|
82
|
+
# Using Marshal is easiest way to get a copy of mutable objects
|
83
|
+
# without getting an error on immutable objects
|
84
|
+
type.to_mongo Marshal.load(Marshal.dump(default))
|
51
85
|
end
|
52
86
|
end
|
53
87
|
|
88
|
+
def valid_ruby_name?
|
89
|
+
!!@name.match(/\A[a-z_][a-z0-9_]*\z/i)
|
90
|
+
end
|
91
|
+
|
54
92
|
private
|
55
93
|
def typecast_class
|
56
94
|
@typecast_class ||= options[:typecast].constantize
|
57
95
|
end
|
96
|
+
|
97
|
+
def validate_key_name!
|
98
|
+
if %w( id ).include? @name
|
99
|
+
raise MongoMapper::InvalidKey.new("`#{@name}` is a reserved key name (did you mean to use _id?)")
|
100
|
+
elsif !valid_ruby_name?
|
101
|
+
raise MongoMapper::InvalidKey.new("`#{@name}` is not a valid key name. Keys must match [a-z][a-z0-9_]*")
|
102
|
+
end
|
103
|
+
end
|
58
104
|
end
|
59
105
|
end
|
60
106
|
end
|