thorsson-mongo_mapper 0.8.2

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 (134) hide show
  1. data/LICENSE +20 -0
  2. data/README.rdoc +27 -0
  3. data/bin/mmconsole +60 -0
  4. data/examples/keys.rb +40 -0
  5. data/examples/modifiers/set.rb +25 -0
  6. data/examples/plugins.rb +41 -0
  7. data/examples/querying.rb +35 -0
  8. data/examples/scopes.rb +52 -0
  9. data/lib/mongo_mapper.rb +79 -0
  10. data/lib/mongo_mapper/connection.rb +83 -0
  11. data/lib/mongo_mapper/document.rb +41 -0
  12. data/lib/mongo_mapper/embedded_document.rb +31 -0
  13. data/lib/mongo_mapper/exceptions.rb +27 -0
  14. data/lib/mongo_mapper/extensions/array.rb +19 -0
  15. data/lib/mongo_mapper/extensions/binary.rb +22 -0
  16. data/lib/mongo_mapper/extensions/boolean.rb +44 -0
  17. data/lib/mongo_mapper/extensions/date.rb +25 -0
  18. data/lib/mongo_mapper/extensions/float.rb +14 -0
  19. data/lib/mongo_mapper/extensions/hash.rb +14 -0
  20. data/lib/mongo_mapper/extensions/integer.rb +19 -0
  21. data/lib/mongo_mapper/extensions/kernel.rb +9 -0
  22. data/lib/mongo_mapper/extensions/nil_class.rb +18 -0
  23. data/lib/mongo_mapper/extensions/object.rb +27 -0
  24. data/lib/mongo_mapper/extensions/object_id.rb +30 -0
  25. data/lib/mongo_mapper/extensions/set.rb +20 -0
  26. data/lib/mongo_mapper/extensions/string.rb +18 -0
  27. data/lib/mongo_mapper/extensions/time.rb +29 -0
  28. data/lib/mongo_mapper/plugins.rb +16 -0
  29. data/lib/mongo_mapper/plugins/accessible.rb +44 -0
  30. data/lib/mongo_mapper/plugins/associations.rb +97 -0
  31. data/lib/mongo_mapper/plugins/associations/base.rb +124 -0
  32. data/lib/mongo_mapper/plugins/associations/belongs_to_polymorphic_proxy.rb +29 -0
  33. data/lib/mongo_mapper/plugins/associations/belongs_to_proxy.rb +24 -0
  34. data/lib/mongo_mapper/plugins/associations/collection.rb +22 -0
  35. data/lib/mongo_mapper/plugins/associations/embedded_collection.rb +40 -0
  36. data/lib/mongo_mapper/plugins/associations/in_array_proxy.rb +127 -0
  37. data/lib/mongo_mapper/plugins/associations/many_documents_as_proxy.rb +28 -0
  38. data/lib/mongo_mapper/plugins/associations/many_documents_proxy.rb +110 -0
  39. data/lib/mongo_mapper/plugins/associations/many_embedded_polymorphic_proxy.rb +32 -0
  40. data/lib/mongo_mapper/plugins/associations/many_embedded_proxy.rb +24 -0
  41. data/lib/mongo_mapper/plugins/associations/many_polymorphic_proxy.rb +14 -0
  42. data/lib/mongo_mapper/plugins/associations/one_embedded_proxy.rb +40 -0
  43. data/lib/mongo_mapper/plugins/associations/one_proxy.rb +68 -0
  44. data/lib/mongo_mapper/plugins/associations/proxy.rb +126 -0
  45. data/lib/mongo_mapper/plugins/caching.rb +21 -0
  46. data/lib/mongo_mapper/plugins/callbacks.rb +241 -0
  47. data/lib/mongo_mapper/plugins/clone.rb +19 -0
  48. data/lib/mongo_mapper/plugins/descendants.rb +17 -0
  49. data/lib/mongo_mapper/plugins/dirty.rb +120 -0
  50. data/lib/mongo_mapper/plugins/document.rb +41 -0
  51. data/lib/mongo_mapper/plugins/dynamic_querying.rb +43 -0
  52. data/lib/mongo_mapper/plugins/dynamic_querying/dynamic_finder.rb +44 -0
  53. data/lib/mongo_mapper/plugins/embedded_document.rb +49 -0
  54. data/lib/mongo_mapper/plugins/equality.rb +17 -0
  55. data/lib/mongo_mapper/plugins/identity_map.rb +128 -0
  56. data/lib/mongo_mapper/plugins/indexes.rb +12 -0
  57. data/lib/mongo_mapper/plugins/inspect.rb +15 -0
  58. data/lib/mongo_mapper/plugins/keys.rb +309 -0
  59. data/lib/mongo_mapper/plugins/keys/key.rb +55 -0
  60. data/lib/mongo_mapper/plugins/logger.rb +18 -0
  61. data/lib/mongo_mapper/plugins/modifiers.rb +112 -0
  62. data/lib/mongo_mapper/plugins/pagination.rb +14 -0
  63. data/lib/mongo_mapper/plugins/persistence.rb +69 -0
  64. data/lib/mongo_mapper/plugins/protected.rb +53 -0
  65. data/lib/mongo_mapper/plugins/querying.rb +176 -0
  66. data/lib/mongo_mapper/plugins/querying/decorator.rb +46 -0
  67. data/lib/mongo_mapper/plugins/querying/plucky_methods.rb +15 -0
  68. data/lib/mongo_mapper/plugins/rails.rb +58 -0
  69. data/lib/mongo_mapper/plugins/safe.rb +28 -0
  70. data/lib/mongo_mapper/plugins/sci.rb +32 -0
  71. data/lib/mongo_mapper/plugins/scopes.rb +21 -0
  72. data/lib/mongo_mapper/plugins/serialization.rb +76 -0
  73. data/lib/mongo_mapper/plugins/timestamps.rb +22 -0
  74. data/lib/mongo_mapper/plugins/userstamps.rb +15 -0
  75. data/lib/mongo_mapper/plugins/validations.rb +50 -0
  76. data/lib/mongo_mapper/support/descendant_appends.rb +45 -0
  77. data/lib/mongo_mapper/version.rb +4 -0
  78. data/test/_NOTE_ON_TESTING +1 -0
  79. data/test/functional/associations/test_belongs_to_polymorphic_proxy.rb +63 -0
  80. data/test/functional/associations/test_belongs_to_proxy.rb +93 -0
  81. data/test/functional/associations/test_in_array_proxy.rb +319 -0
  82. data/test/functional/associations/test_many_documents_as_proxy.rb +229 -0
  83. data/test/functional/associations/test_many_documents_proxy.rb +575 -0
  84. data/test/functional/associations/test_many_embedded_polymorphic_proxy.rb +176 -0
  85. data/test/functional/associations/test_many_embedded_proxy.rb +256 -0
  86. data/test/functional/associations/test_many_polymorphic_proxy.rb +302 -0
  87. data/test/functional/associations/test_one_embedded_proxy.rb +67 -0
  88. data/test/functional/associations/test_one_proxy.rb +182 -0
  89. data/test/functional/test_accessible.rb +168 -0
  90. data/test/functional/test_associations.rb +44 -0
  91. data/test/functional/test_binary.rb +27 -0
  92. data/test/functional/test_caching.rb +76 -0
  93. data/test/functional/test_callbacks.rb +151 -0
  94. data/test/functional/test_dirty.rb +163 -0
  95. data/test/functional/test_document.rb +253 -0
  96. data/test/functional/test_dynamic_querying.rb +75 -0
  97. data/test/functional/test_embedded_document.rb +210 -0
  98. data/test/functional/test_identity_map.rb +506 -0
  99. data/test/functional/test_indexes.rb +42 -0
  100. data/test/functional/test_logger.rb +20 -0
  101. data/test/functional/test_modifiers.rb +416 -0
  102. data/test/functional/test_pagination.rb +91 -0
  103. data/test/functional/test_protected.rb +175 -0
  104. data/test/functional/test_querying.rb +873 -0
  105. data/test/functional/test_safe.rb +76 -0
  106. data/test/functional/test_sci.rb +230 -0
  107. data/test/functional/test_scopes.rb +171 -0
  108. data/test/functional/test_string_id_compatibility.rb +67 -0
  109. data/test/functional/test_timestamps.rb +62 -0
  110. data/test/functional/test_userstamps.rb +27 -0
  111. data/test/functional/test_validations.rb +342 -0
  112. data/test/models.rb +233 -0
  113. data/test/test_active_model_lint.rb +13 -0
  114. data/test/test_helper.rb +104 -0
  115. data/test/unit/associations/test_base.rb +212 -0
  116. data/test/unit/associations/test_proxy.rb +105 -0
  117. data/test/unit/serializers/test_json_serializer.rb +202 -0
  118. data/test/unit/test_clone.rb +69 -0
  119. data/test/unit/test_descendant_appends.rb +71 -0
  120. data/test/unit/test_document.rb +213 -0
  121. data/test/unit/test_dynamic_finder.rb +125 -0
  122. data/test/unit/test_embedded_document.rb +644 -0
  123. data/test/unit/test_extensions.rb +380 -0
  124. data/test/unit/test_key.rb +185 -0
  125. data/test/unit/test_keys.rb +55 -0
  126. data/test/unit/test_mongo_mapper.rb +110 -0
  127. data/test/unit/test_pagination.rb +11 -0
  128. data/test/unit/test_plugins.rb +50 -0
  129. data/test/unit/test_rails.rb +181 -0
  130. data/test/unit/test_rails_compatibility.rb +52 -0
  131. data/test/unit/test_serialization.rb +51 -0
  132. data/test/unit/test_time_zones.rb +39 -0
  133. data/test/unit/test_validations.rb +544 -0
  134. metadata +316 -0
@@ -0,0 +1,43 @@
1
+ # encoding: UTF-8
2
+ require 'mongo_mapper/plugins/dynamic_querying/dynamic_finder'
3
+
4
+ module MongoMapper
5
+ module Plugins
6
+ module DynamicQuerying
7
+ module ClassMethods
8
+ def dynamic_find(finder, args)
9
+ attributes = {}
10
+
11
+ finder.attributes.each_with_index do |attr, index|
12
+ attributes[attr] = args[index]
13
+ end
14
+
15
+ options = args.extract_options!.merge(attributes)
16
+
17
+ if result = send(finder.finder, options)
18
+ result
19
+ else
20
+ if finder.raise?
21
+ raise DocumentNotFound, "Couldn't find Document with #{attributes.inspect} in collection named #{collection.name}"
22
+ end
23
+
24
+ if finder.instantiator
25
+ self.send(finder.instantiator, attributes)
26
+ end
27
+ end
28
+ end
29
+
30
+ protected
31
+ def method_missing(method, *args, &block)
32
+ finder = DynamicFinder.new(method)
33
+
34
+ if finder.found?
35
+ dynamic_find(finder, args)
36
+ else
37
+ super
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,44 @@
1
+ # encoding: UTF-8
2
+ module MongoMapper
3
+ module Plugins
4
+ module DynamicQuerying
5
+ class DynamicFinder
6
+ attr_reader :method, :attributes, :finder, :bang, :instantiator
7
+
8
+ def initialize(method)
9
+ @method = method
10
+ @finder = :first
11
+ @bang = false
12
+ match
13
+ end
14
+
15
+ def found?
16
+ @finder.present?
17
+ end
18
+
19
+ def raise?
20
+ bang == true
21
+ end
22
+
23
+ protected
24
+ def match
25
+ case method.to_s
26
+ when /^find_(all_by|by)_([_a-zA-Z]\w*)$/
27
+ @finder = :all if $1 == 'all_by'
28
+ names = $2
29
+ when /^find_by_([_a-zA-Z]\w*)\!$/
30
+ @bang = true
31
+ names = $1
32
+ when /^find_or_(initialize|create)_by_([_a-zA-Z]\w*)$/
33
+ @instantiator = $1 == 'initialize' ? :new : :create
34
+ names = $2
35
+ else
36
+ @finder = nil
37
+ end
38
+
39
+ @attributes = names && names.split('_and_')
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,49 @@
1
+ # encoding: UTF-8
2
+ module MongoMapper
3
+ module Plugins
4
+ module EmbeddedDocument
5
+ def self.configure(model)
6
+ model.class_eval do
7
+ attr_reader :_root_document, :_parent_document
8
+ end
9
+ end
10
+
11
+ module ClassMethods
12
+ def embeddable?
13
+ true
14
+ end
15
+
16
+ def embedded_in(owner_name)
17
+ define_method(owner_name) { _parent_document }
18
+ end
19
+ end
20
+
21
+ module InstanceMethods
22
+ def new?
23
+ _root_document.try(:new?) || @_new
24
+ end
25
+
26
+ def destroyed?
27
+ !!_root_document.try(:destroyed?)
28
+ end
29
+
30
+ def save(options={})
31
+ _root_document.try(:save, options).tap do |result|
32
+ @_new = false if result
33
+ end
34
+ end
35
+
36
+ def save!(options={})
37
+ _root_document.try(:save, options).tap do |result|
38
+ @_new = false if result
39
+ end
40
+ end
41
+
42
+ def _parent_document=(value)
43
+ @_root_document = value._root_document
44
+ @_parent_document = value
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,17 @@
1
+ # encoding: UTF-8
2
+ module MongoMapper
3
+ module Plugins
4
+ module Equality
5
+ module InstanceMethods
6
+ def eql?(other)
7
+ other.is_a?(self.class) && _id == other._id
8
+ end
9
+ alias :== :eql?
10
+
11
+ def hash
12
+ _id.hash
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,128 @@
1
+ # encoding: UTF-8
2
+ require 'set'
3
+
4
+ module MongoMapper
5
+ module Plugins
6
+ module IdentityMap
7
+ def self.models
8
+ @models ||= Set.new
9
+ end
10
+
11
+ def self.clear
12
+ models.each { |m| m.identity_map.clear }
13
+ end
14
+
15
+ def self.configure(model)
16
+ IdentityMap.models << model
17
+ end
18
+
19
+ module ClassMethods
20
+ def inherited(descendant)
21
+ descendant.identity_map = identity_map
22
+ super
23
+ end
24
+
25
+ def identity_map
26
+ @identity_map ||= {}
27
+ end
28
+
29
+ def identity_map=(v)
30
+ @identity_map = v
31
+ end
32
+
33
+ module IdentityMapQueryMethods
34
+ def all(opts={})
35
+ query = clone.update(opts)
36
+ super.tap do |docs|
37
+ model.remove_documents_from_map(docs) if query.fields?
38
+ end
39
+ end
40
+
41
+ def find_one(opts={})
42
+ query = clone.update(opts)
43
+
44
+ if query.simple? && model.identity_map[query[:_id]]
45
+ model.identity_map[query[:_id]]
46
+ else
47
+ super.tap do |doc|
48
+ model.remove_documents_from_map(doc) if query.fields?
49
+ end
50
+ end
51
+ end
52
+ end
53
+
54
+ def query(opts={})
55
+ super.extend(IdentityMapQueryMethods)
56
+ end
57
+
58
+ def remove_documents_from_map(*documents)
59
+ documents.flatten.compact.each do |document|
60
+ identity_map.delete(document['_id'])
61
+ end
62
+ end
63
+
64
+ def load(attrs)
65
+ return nil if attrs.nil?
66
+ document = identity_map[attrs['_id']]
67
+
68
+ if document.nil? || identity_map_off?
69
+ document = super
70
+ identity_map[document._id] = document if identity_map_on?
71
+ end
72
+
73
+ document
74
+ end
75
+
76
+ def identity_map_status
77
+ defined?(@identity_map_status) ? @identity_map_status : true
78
+ end
79
+
80
+ def identity_map_on
81
+ @identity_map_status = true
82
+ end
83
+
84
+ def identity_map_off
85
+ @identity_map_status = false
86
+ end
87
+
88
+ def identity_map_on?
89
+ identity_map_status
90
+ end
91
+
92
+ def identity_map_off?
93
+ !identity_map_on?
94
+ end
95
+
96
+ def without_identity_map(&block)
97
+ identity_map_off
98
+ yield
99
+ ensure
100
+ identity_map_on
101
+ end
102
+
103
+ private
104
+ def selecting_fields?(options)
105
+ !options[:fields].nil?
106
+ end
107
+ end
108
+
109
+ module InstanceMethods
110
+ def identity_map
111
+ self.class.identity_map
112
+ end
113
+
114
+ def save(*args)
115
+ if result = super
116
+ identity_map[_id] = self if self.class.identity_map_on?
117
+ end
118
+ result
119
+ end
120
+
121
+ def delete
122
+ identity_map.delete(_id) if self.class.identity_map_on?
123
+ super
124
+ end
125
+ end
126
+ end
127
+ end
128
+ end
@@ -0,0 +1,12 @@
1
+ # encoding: UTF-8
2
+ module MongoMapper
3
+ module Plugins
4
+ module Indexes
5
+ module ClassMethods
6
+ def ensure_index(spec, options={})
7
+ collection.create_index(spec, options)
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,15 @@
1
+ # encoding: UTF-8
2
+ module MongoMapper
3
+ module Plugins
4
+ module Inspect
5
+ module InstanceMethods
6
+ def inspect
7
+ attributes_as_nice_string = key_names.collect do |name|
8
+ "#{name}: #{self[name].inspect}"
9
+ end.join(", ")
10
+ "#<#{self.class} #{attributes_as_nice_string}>"
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,309 @@
1
+ # encoding: UTF-8
2
+ require 'mongo_mapper/plugins/keys/key'
3
+
4
+ module MongoMapper
5
+ module Plugins
6
+ module Keys
7
+ def self.configure(model)
8
+ model.key :_id, ObjectId
9
+ end
10
+
11
+ module ClassMethods
12
+ def inherited(descendant)
13
+ descendant.instance_variable_set(:@keys, keys.dup)
14
+ super
15
+ end
16
+
17
+ def keys
18
+ @keys ||= HashWithIndifferentAccess.new
19
+ end
20
+
21
+ def key(*args)
22
+ Key.new(*args).tap do |key|
23
+ keys[key.name] = key
24
+ create_accessors_for(key)
25
+ create_key_in_descendants(*args)
26
+ create_indexes_for(key)
27
+ create_validations_for(key)
28
+ end
29
+ end
30
+
31
+ def key?(key)
32
+ keys.keys.include?(key.to_s)
33
+ end
34
+
35
+ def using_object_id?
36
+ object_id_key?(:_id)
37
+ end
38
+
39
+ def object_id_keys
40
+ keys.keys.select { |key| keys[key].type == ObjectId }.map(&:to_sym)
41
+ end
42
+
43
+ def object_id_key?(name)
44
+ object_id_keys.include?(name.to_sym)
45
+ end
46
+
47
+ def to_mongo(instance)
48
+ return nil if instance.nil?
49
+ instance.to_mongo
50
+ end
51
+
52
+ def from_mongo(value)
53
+ return nil if value.nil?
54
+ value.is_a?(self) ? value : load(value)
55
+ end
56
+
57
+ # load is overridden in identity map to ensure same objects are loaded
58
+ def load(attrs)
59
+ return nil if attrs.nil?
60
+ begin
61
+ klass = attrs['_type'].present? ? attrs['_type'].constantize : self
62
+ klass.new(attrs, true)
63
+ rescue NameError
64
+ new(attrs, true)
65
+ end
66
+ end
67
+
68
+ private
69
+ def key_accessors_module_defined?
70
+ if method(:const_defined?).arity == 1 # Ruby 1.9 compat check
71
+ const_defined?('MongoMapperKeys')
72
+ else
73
+ const_defined?('MongoMapperKeys', false)
74
+ end
75
+ end
76
+
77
+ def accessors_module
78
+ if key_accessors_module_defined?
79
+ const_get 'MongoMapperKeys'
80
+ else
81
+ const_set 'MongoMapperKeys', Module.new
82
+ end
83
+ end
84
+
85
+ def create_accessors_for(key)
86
+ accessors_module.module_eval <<-end_eval
87
+ def #{key.name}
88
+ read_key(:#{key.name})
89
+ end
90
+
91
+ def #{key.name}_before_typecast
92
+ read_key_before_typecast(:#{key.name})
93
+ end
94
+
95
+ def #{key.name}=(value)
96
+ write_key(:#{key.name}, value)
97
+ end
98
+
99
+ def #{key.name}?
100
+ read_key(:#{key.name}).present?
101
+ end
102
+ end_eval
103
+
104
+ include accessors_module
105
+ end
106
+
107
+ def create_key_in_descendants(*args)
108
+ descendants.each { |descendant| descendant.key(*args) }
109
+ end
110
+
111
+ def create_indexes_for(key)
112
+ ensure_index key.name if key.options[:index] && !key.embeddable?
113
+ end
114
+
115
+ def create_validations_for(key)
116
+ attribute = key.name.to_sym
117
+
118
+ if key.options[:required]
119
+ validates_presence_of(attribute)
120
+ end
121
+
122
+ if key.options[:unique]
123
+ validates_uniqueness_of(attribute)
124
+ end
125
+
126
+ if key.options[:numeric]
127
+ number_options = key.type == Integer ? {:only_integer => true} : {}
128
+ validates_numericality_of(attribute, number_options)
129
+ end
130
+
131
+ if key.options[:format]
132
+ validates_format_of(attribute, :with => key.options[:format])
133
+ end
134
+
135
+ if key.options[:in]
136
+ validates_inclusion_of(attribute, :within => key.options[:in])
137
+ end
138
+
139
+ if key.options[:not_in]
140
+ validates_exclusion_of(attribute, :within => key.options[:not_in])
141
+ end
142
+
143
+ if key.options[:length]
144
+ length_options = case key.options[:length]
145
+ when Integer
146
+ {:minimum => 0, :maximum => key.options[:length]}
147
+ when Range
148
+ {:within => key.options[:length]}
149
+ when Hash
150
+ key.options[:length]
151
+ end
152
+ validates_length_of(attribute, length_options)
153
+ end
154
+ end
155
+ end
156
+
157
+ module InstanceMethods
158
+ def initialize(attrs={}, from_database=false)
159
+ default_id_value(attrs)
160
+
161
+ if from_database
162
+ @_new = false
163
+ load_from_database(attrs)
164
+ else
165
+ @_new = true
166
+ assign(attrs)
167
+ end
168
+ end
169
+
170
+ def persisted?
171
+ !new? && !destroyed?
172
+ end
173
+
174
+ def attributes=(attrs)
175
+ return if attrs.blank?
176
+
177
+ attrs.each_pair do |key, value|
178
+ if respond_to?(:"#{key}=")
179
+ self.send(:"#{key}=", value)
180
+ else
181
+ self[key] = value
182
+ end
183
+ end
184
+ end
185
+
186
+ def attributes
187
+ HashWithIndifferentAccess.new.tap do |attrs|
188
+ keys.each_pair do |name, key|
189
+ value = key.set(self[key.name])
190
+ attrs[name] = value
191
+ end
192
+
193
+ embedded_associations.each do |association|
194
+ if documents = instance_variable_get(association.ivar)
195
+ if association.one?
196
+ attrs[association.name] = documents.to_mongo
197
+ else
198
+ attrs[association.name] = documents.map { |document| document.to_mongo }
199
+ end
200
+ end
201
+ end
202
+ end
203
+ end
204
+ alias :to_mongo :attributes
205
+
206
+ def assign(attrs={})
207
+ self.attributes = attrs
208
+ end
209
+
210
+ def update_attributes(attrs={})
211
+ assign(attrs)
212
+ save
213
+ end
214
+
215
+ def update_attributes!(attrs={})
216
+ assign(attrs)
217
+ save!
218
+ end
219
+
220
+ def id
221
+ _id
222
+ end
223
+
224
+ def id=(value)
225
+ if self.class.using_object_id?
226
+ value = ObjectId.to_mongo(value)
227
+ end
228
+
229
+ self[:_id] = value
230
+ end
231
+
232
+ def [](name)
233
+ read_key(name)
234
+ end
235
+
236
+ def []=(name, value)
237
+ ensure_key_exists(name)
238
+ write_key(name, value)
239
+ end
240
+
241
+ def keys
242
+ self.class.keys
243
+ end
244
+
245
+ def key_names
246
+ keys.keys
247
+ end
248
+
249
+ def non_embedded_keys
250
+ keys.values.select { |key| !key.embeddable? }
251
+ end
252
+
253
+ def embedded_keys
254
+ keys.values.select { |key| key.embeddable? }
255
+ end
256
+
257
+ private
258
+ def load_from_database(attrs)
259
+ return if attrs.blank?
260
+ attrs.each do |key, value|
261
+ if respond_to?(:"#{key}=") && !self.class.key?(key)
262
+ self.send(:"#{key}=", value)
263
+ else
264
+ self[key] = value
265
+ end
266
+ end
267
+ end
268
+
269
+ def default_id_value(attrs)
270
+ unless attrs.nil?
271
+ provided_keys = attrs.keys.map { |k| k.to_s }
272
+ unless provided_keys.include?('_id') || provided_keys.include?('id')
273
+ write_key :_id, BSON::ObjectID.new
274
+ end
275
+ end
276
+ end
277
+
278
+ def ensure_key_exists(name)
279
+ self.class.key(name) unless respond_to?("#{name}=")
280
+ end
281
+
282
+ def set_parent_document(key, value)
283
+ if key.embeddable? && value.is_a?(key.type)
284
+ value._parent_document = self
285
+ end
286
+ end
287
+
288
+ def read_key(key_name)
289
+ if key = keys[key_name]
290
+ value = key.get(instance_variable_get(:"@#{key_name}"))
291
+ set_parent_document(key, value)
292
+ instance_variable_set(:"@#{key_name}", value)
293
+ end
294
+ end
295
+
296
+ def read_key_before_typecast(name)
297
+ instance_variable_get(:"@#{name}_before_typecast")
298
+ end
299
+
300
+ def write_key(name, value)
301
+ key = keys[name.to_s]
302
+ set_parent_document(key, value)
303
+ instance_variable_set :"@#{name}_before_typecast", value
304
+ instance_variable_set :"@#{name}", key.set(value)
305
+ end
306
+ end
307
+ end
308
+ end
309
+ end