mongo_mapper 0.12.0 → 0.13.0.beta1
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.
- 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
|