lookout-mongo_mapper 0.11.3

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 (176) hide show
  1. data/LICENSE +20 -0
  2. data/README.rdoc +33 -0
  3. data/UPGRADES +26 -0
  4. data/bin/mmconsole +59 -0
  5. data/examples/attr_accessible.rb +22 -0
  6. data/examples/attr_protected.rb +22 -0
  7. data/examples/cache_key.rb +24 -0
  8. data/examples/custom_types.rb +24 -0
  9. data/examples/identity_map.rb +33 -0
  10. data/examples/identity_map/automatic.rb +2 -0
  11. data/examples/keys.rb +40 -0
  12. data/examples/modifiers/set.rb +25 -0
  13. data/examples/plugins.rb +38 -0
  14. data/examples/querying.rb +35 -0
  15. data/examples/safe.rb +43 -0
  16. data/examples/scopes.rb +52 -0
  17. data/examples/validating/embedded_docs.rb +29 -0
  18. data/lib/mongo_mapper.rb +94 -0
  19. data/lib/mongo_mapper/connection.rb +96 -0
  20. data/lib/mongo_mapper/document.rb +42 -0
  21. data/lib/mongo_mapper/embedded_document.rb +32 -0
  22. data/lib/mongo_mapper/exceptions.rb +30 -0
  23. data/lib/mongo_mapper/extensions/array.rb +19 -0
  24. data/lib/mongo_mapper/extensions/binary.rb +22 -0
  25. data/lib/mongo_mapper/extensions/boolean.rb +44 -0
  26. data/lib/mongo_mapper/extensions/date.rb +25 -0
  27. data/lib/mongo_mapper/extensions/float.rb +14 -0
  28. data/lib/mongo_mapper/extensions/hash.rb +14 -0
  29. data/lib/mongo_mapper/extensions/integer.rb +19 -0
  30. data/lib/mongo_mapper/extensions/kernel.rb +9 -0
  31. data/lib/mongo_mapper/extensions/nil_class.rb +18 -0
  32. data/lib/mongo_mapper/extensions/object.rb +26 -0
  33. data/lib/mongo_mapper/extensions/object_id.rb +32 -0
  34. data/lib/mongo_mapper/extensions/set.rb +20 -0
  35. data/lib/mongo_mapper/extensions/string.rb +18 -0
  36. data/lib/mongo_mapper/extensions/time.rb +28 -0
  37. data/lib/mongo_mapper/locale/en.yml +5 -0
  38. data/lib/mongo_mapper/middleware/identity_map.rb +16 -0
  39. data/lib/mongo_mapper/plugins.rb +22 -0
  40. data/lib/mongo_mapper/plugins/accessible.rb +52 -0
  41. data/lib/mongo_mapper/plugins/active_model.rb +18 -0
  42. data/lib/mongo_mapper/plugins/associations.rb +90 -0
  43. data/lib/mongo_mapper/plugins/associations/base.rb +92 -0
  44. data/lib/mongo_mapper/plugins/associations/belongs_to_association.rb +54 -0
  45. data/lib/mongo_mapper/plugins/associations/belongs_to_polymorphic_proxy.rb +34 -0
  46. data/lib/mongo_mapper/plugins/associations/belongs_to_proxy.rb +52 -0
  47. data/lib/mongo_mapper/plugins/associations/collection.rb +27 -0
  48. data/lib/mongo_mapper/plugins/associations/embedded_collection.rb +44 -0
  49. data/lib/mongo_mapper/plugins/associations/in_array_proxy.rb +133 -0
  50. data/lib/mongo_mapper/plugins/associations/many_association.rb +63 -0
  51. data/lib/mongo_mapper/plugins/associations/many_documents_as_proxy.rb +28 -0
  52. data/lib/mongo_mapper/plugins/associations/many_documents_proxy.rb +118 -0
  53. data/lib/mongo_mapper/plugins/associations/many_embedded_polymorphic_proxy.rb +32 -0
  54. data/lib/mongo_mapper/plugins/associations/many_embedded_proxy.rb +24 -0
  55. data/lib/mongo_mapper/plugins/associations/many_polymorphic_proxy.rb +14 -0
  56. data/lib/mongo_mapper/plugins/associations/one_as_proxy.rb +22 -0
  57. data/lib/mongo_mapper/plugins/associations/one_association.rb +48 -0
  58. data/lib/mongo_mapper/plugins/associations/one_embedded_polymorphic_proxy.rb +30 -0
  59. data/lib/mongo_mapper/plugins/associations/one_embedded_proxy.rb +44 -0
  60. data/lib/mongo_mapper/plugins/associations/one_proxy.rb +95 -0
  61. data/lib/mongo_mapper/plugins/associations/proxy.rb +134 -0
  62. data/lib/mongo_mapper/plugins/associations/single_association.rb +46 -0
  63. data/lib/mongo_mapper/plugins/caching.rb +21 -0
  64. data/lib/mongo_mapper/plugins/callbacks.rb +29 -0
  65. data/lib/mongo_mapper/plugins/clone.rb +22 -0
  66. data/lib/mongo_mapper/plugins/dirty.rb +60 -0
  67. data/lib/mongo_mapper/plugins/document.rb +41 -0
  68. data/lib/mongo_mapper/plugins/dynamic_querying.rb +45 -0
  69. data/lib/mongo_mapper/plugins/dynamic_querying/dynamic_finder.rb +44 -0
  70. data/lib/mongo_mapper/plugins/embedded_callbacks.rb +56 -0
  71. data/lib/mongo_mapper/plugins/embedded_document.rb +53 -0
  72. data/lib/mongo_mapper/plugins/equality.rb +23 -0
  73. data/lib/mongo_mapper/plugins/identity_map.rb +128 -0
  74. data/lib/mongo_mapper/plugins/indexes.rb +13 -0
  75. data/lib/mongo_mapper/plugins/inspect.rb +16 -0
  76. data/lib/mongo_mapper/plugins/keys.rb +313 -0
  77. data/lib/mongo_mapper/plugins/keys/key.rb +61 -0
  78. data/lib/mongo_mapper/plugins/logger.rb +18 -0
  79. data/lib/mongo_mapper/plugins/modifiers.rb +134 -0
  80. data/lib/mongo_mapper/plugins/pagination.rb +16 -0
  81. data/lib/mongo_mapper/plugins/persistence.rb +69 -0
  82. data/lib/mongo_mapper/plugins/protected.rb +45 -0
  83. data/lib/mongo_mapper/plugins/querying.rb +165 -0
  84. data/lib/mongo_mapper/plugins/querying/decorator.rb +36 -0
  85. data/lib/mongo_mapper/plugins/rails.rb +58 -0
  86. data/lib/mongo_mapper/plugins/rails/active_record_association_adapter.rb +33 -0
  87. data/lib/mongo_mapper/plugins/safe.rb +28 -0
  88. data/lib/mongo_mapper/plugins/sci.rb +36 -0
  89. data/lib/mongo_mapper/plugins/scopes.rb +27 -0
  90. data/lib/mongo_mapper/plugins/serialization.rb +109 -0
  91. data/lib/mongo_mapper/plugins/timestamps.rb +22 -0
  92. data/lib/mongo_mapper/plugins/touch.rb +18 -0
  93. data/lib/mongo_mapper/plugins/userstamps.rb +18 -0
  94. data/lib/mongo_mapper/plugins/validations.rb +86 -0
  95. data/lib/mongo_mapper/railtie.rb +48 -0
  96. data/lib/mongo_mapper/railtie/database.rake +65 -0
  97. data/lib/mongo_mapper/translation.rb +10 -0
  98. data/lib/mongo_mapper/version.rb +4 -0
  99. data/lib/rails/generators/mongo_mapper/config/config_generator.rb +24 -0
  100. data/lib/rails/generators/mongo_mapper/config/templates/mongo.yml +18 -0
  101. data/lib/rails/generators/mongo_mapper/model/model_generator.rb +23 -0
  102. data/lib/rails/generators/mongo_mapper/model/templates/model.rb +13 -0
  103. data/test/_NOTE_ON_TESTING +1 -0
  104. data/test/functional/associations/test_belongs_to_polymorphic_proxy.rb +64 -0
  105. data/test/functional/associations/test_belongs_to_proxy.rb +238 -0
  106. data/test/functional/associations/test_in_array_proxy.rb +349 -0
  107. data/test/functional/associations/test_many_documents_as_proxy.rb +231 -0
  108. data/test/functional/associations/test_many_documents_proxy.rb +866 -0
  109. data/test/functional/associations/test_many_embedded_polymorphic_proxy.rb +239 -0
  110. data/test/functional/associations/test_many_embedded_proxy.rb +289 -0
  111. data/test/functional/associations/test_many_polymorphic_proxy.rb +303 -0
  112. data/test/functional/associations/test_one_as_proxy.rb +491 -0
  113. data/test/functional/associations/test_one_embedded_polymorphic_proxy.rb +208 -0
  114. data/test/functional/associations/test_one_embedded_proxy.rb +100 -0
  115. data/test/functional/associations/test_one_proxy.rb +383 -0
  116. data/test/functional/test_accessible.rb +198 -0
  117. data/test/functional/test_associations.rb +46 -0
  118. data/test/functional/test_binary.rb +27 -0
  119. data/test/functional/test_caching.rb +77 -0
  120. data/test/functional/test_callbacks.rb +232 -0
  121. data/test/functional/test_dirty.rb +301 -0
  122. data/test/functional/test_document.rb +282 -0
  123. data/test/functional/test_dynamic_querying.rb +75 -0
  124. data/test/functional/test_embedded_document.rb +288 -0
  125. data/test/functional/test_equality.rb +20 -0
  126. data/test/functional/test_identity_map.rb +513 -0
  127. data/test/functional/test_indexes.rb +50 -0
  128. data/test/functional/test_logger.rb +20 -0
  129. data/test/functional/test_modifiers.rb +537 -0
  130. data/test/functional/test_pagination.rb +91 -0
  131. data/test/functional/test_protected.rb +201 -0
  132. data/test/functional/test_querying.rb +935 -0
  133. data/test/functional/test_safe.rb +76 -0
  134. data/test/functional/test_sci.rb +240 -0
  135. data/test/functional/test_scopes.rb +171 -0
  136. data/test/functional/test_timestamps.rb +62 -0
  137. data/test/functional/test_touch.rb +125 -0
  138. data/test/functional/test_userstamps.rb +44 -0
  139. data/test/functional/test_validations.rb +414 -0
  140. data/test/models.rb +261 -0
  141. data/test/support/railtie.rb +4 -0
  142. data/test/support/railtie/autoloaded.rb +2 -0
  143. data/test/support/railtie/not_autoloaded.rb +3 -0
  144. data/test/support/railtie/parent.rb +3 -0
  145. data/test/test_active_model_lint.rb +18 -0
  146. data/test/test_helper.rb +93 -0
  147. data/test/unit/associations/test_base.rb +146 -0
  148. data/test/unit/associations/test_belongs_to_association.rb +29 -0
  149. data/test/unit/associations/test_many_association.rb +63 -0
  150. data/test/unit/associations/test_one_association.rb +47 -0
  151. data/test/unit/associations/test_proxy.rb +100 -0
  152. data/test/unit/serializers/test_json_serializer.rb +216 -0
  153. data/test/unit/serializers/test_xml_serializer.rb +196 -0
  154. data/test/unit/test_clone.rb +69 -0
  155. data/test/unit/test_document.rb +249 -0
  156. data/test/unit/test_dynamic_finder.rb +125 -0
  157. data/test/unit/test_embedded_document.rb +682 -0
  158. data/test/unit/test_equality.rb +38 -0
  159. data/test/unit/test_exceptions.rb +12 -0
  160. data/test/unit/test_extensions.rb +380 -0
  161. data/test/unit/test_identity_map_middleware.rb +34 -0
  162. data/test/unit/test_inspect.rb +47 -0
  163. data/test/unit/test_key.rb +205 -0
  164. data/test/unit/test_keys.rb +65 -0
  165. data/test/unit/test_mongo_mapper.rb +143 -0
  166. data/test/unit/test_pagination.rb +11 -0
  167. data/test/unit/test_plugins.rb +89 -0
  168. data/test/unit/test_rails.rb +183 -0
  169. data/test/unit/test_rails_compatibility.rb +38 -0
  170. data/test/unit/test_rails_reflect_on_association.rb +118 -0
  171. data/test/unit/test_railtie.rb +66 -0
  172. data/test/unit/test_serialization.rb +166 -0
  173. data/test/unit/test_time_zones.rb +44 -0
  174. data/test/unit/test_translation.rb +27 -0
  175. data/test/unit/test_validations.rb +562 -0
  176. metadata +285 -0
@@ -0,0 +1,56 @@
1
+ # encoding: UTF-8
2
+ module MongoMapper
3
+ module Plugins
4
+ module EmbeddedCallbacks
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ extend ::ActiveModel::Callbacks
9
+
10
+ define_model_callbacks :save, :create, :update, :destroy, :only => [:before, :after]
11
+ define_model_callbacks :touch, :only => [:after]
12
+
13
+ embedded_callbacks_on
14
+ end
15
+
16
+ module ClassMethods
17
+ def embedded_callbacks_on
18
+ @embedded_callbacks_status = true
19
+ end
20
+
21
+ def embedded_callbacks_off
22
+ @embedded_callbacks_status = false
23
+ end
24
+
25
+ def embedded_callbacks_on?
26
+ @embedded_callbacks_status == true
27
+ end
28
+
29
+ def embedded_callbacks_off?
30
+ !embedded_callbacks_on?
31
+ end
32
+ end
33
+
34
+ def run_callbacks(callback, *args, &block)
35
+
36
+ if self.class.embedded_callbacks_on?
37
+ embedded_docs = []
38
+
39
+ embedded_associations.each do |association|
40
+ embedded_docs += Array(get_proxy(association).send(:load_target))
41
+ end
42
+
43
+ block = embedded_docs.inject(block) do |chain, doc|
44
+ if doc.class.respond_to?("_#{callback}_callbacks")
45
+ lambda { doc.run_callbacks(callback, *args, &chain) }
46
+ else
47
+ chain
48
+ end
49
+ end
50
+ end
51
+
52
+ super callback, *args, &block
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,53 @@
1
+ # encoding: UTF-8
2
+ module MongoMapper
3
+ module Plugins
4
+ module EmbeddedDocument
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ attr_accessor :_parent_document
9
+ end
10
+
11
+ module ClassMethods
12
+ def embeddable?
13
+ true
14
+ end
15
+
16
+ def embedded_in(owner_name)
17
+ alias_method owner_name, :_parent_document
18
+ end
19
+ end
20
+
21
+ def new?
22
+ _root_document.try(:new?) || @_new
23
+ end
24
+
25
+ def destroyed?
26
+ !!_root_document.try(:destroyed?)
27
+ end
28
+
29
+ def save(options={})
30
+ _root_document.try(:save, options).tap do |result|
31
+ persist(options) if result
32
+ end
33
+ end
34
+
35
+ def save!(options={})
36
+ valid? || raise(DocumentNotValid.new(self))
37
+ _root_document.try(:save!, options).tap do |result|
38
+ persist(options) if result
39
+ end
40
+ end
41
+
42
+ def persist(options={})
43
+ @_new = false
44
+ clear_changes if respond_to?(:clear_changes)
45
+ save_to_collection(options)
46
+ end
47
+
48
+ def _root_document
49
+ @_root_document ||= _parent_document.try(:_root_document)
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,23 @@
1
+ # encoding: UTF-8
2
+ module MongoMapper
3
+ module Plugins
4
+ module Equality
5
+ extend ActiveSupport::Concern
6
+
7
+ module ClassMethods
8
+ def ===(other)
9
+ other.is_a?(self)
10
+ end
11
+ end
12
+
13
+ def eql?(other)
14
+ other.is_a?(self.class) && _id == other._id
15
+ end
16
+ alias :== :eql?
17
+
18
+ def hash
19
+ _id.hash
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,128 @@
1
+ # encoding: UTF-8
2
+ require 'set'
3
+
4
+ module MongoMapper
5
+ module Plugins
6
+ module IdentityMap
7
+ extend ActiveSupport::Concern
8
+
9
+ def self.models
10
+ @models ||= Set.new
11
+ end
12
+
13
+ def self.clear
14
+ models.each { |m| m.identity_map.clear }
15
+ end
16
+
17
+ included do
18
+ IdentityMap.models << self
19
+ end
20
+
21
+ module ClassMethods
22
+ def inherited(descendant)
23
+ descendant.identity_map = identity_map
24
+ super
25
+ end
26
+
27
+ def identity_map
28
+ @identity_map ||= {}
29
+ end
30
+
31
+ def identity_map=(v)
32
+ @identity_map = v
33
+ end
34
+
35
+ module IdentityMapQueryMethods
36
+ def all(opts={})
37
+ query = clone.amend(opts)
38
+ super.tap do |docs|
39
+ model.remove_documents_from_map(docs) if query.fields?
40
+ end
41
+ end
42
+
43
+ def find_one(opts={})
44
+ query = clone.amend(opts)
45
+
46
+ if model.identity_map_on? && query.simple? && model.identity_map[query[:_id]]
47
+ model.identity_map[query[:_id]]
48
+ else
49
+ super.tap do |doc|
50
+ model.remove_documents_from_map(doc) if query.fields?
51
+ end
52
+ end
53
+ end
54
+ end
55
+
56
+ def query(opts={})
57
+ super.extend(IdentityMapQueryMethods)
58
+ end
59
+
60
+ def remove_documents_from_map(*documents)
61
+ documents.flatten.compact.each do |document|
62
+ identity_map.delete(document['_id'])
63
+ end
64
+ end
65
+
66
+ def load(attrs)
67
+ return nil if attrs.nil?
68
+ document = identity_map[attrs['_id']]
69
+
70
+ if document.nil? || identity_map_off?
71
+ document = super
72
+ identity_map[document._id] = document if identity_map_on?
73
+ end
74
+
75
+ document
76
+ end
77
+
78
+ def identity_map_status
79
+ defined?(@identity_map_status) ? @identity_map_status : true
80
+ end
81
+
82
+ def identity_map_on
83
+ @identity_map_status = true
84
+ end
85
+
86
+ def identity_map_off
87
+ @identity_map_status = false
88
+ end
89
+
90
+ def identity_map_on?
91
+ identity_map_status
92
+ end
93
+
94
+ def identity_map_off?
95
+ !identity_map_on?
96
+ end
97
+
98
+ def without_identity_map(&block)
99
+ identity_map_off
100
+ yield
101
+ ensure
102
+ identity_map_on
103
+ end
104
+
105
+ private
106
+ def selecting_fields?(options)
107
+ !options[:fields].nil?
108
+ end
109
+ end
110
+
111
+ def identity_map
112
+ self.class.identity_map
113
+ end
114
+
115
+ def save(*args)
116
+ if result = super
117
+ identity_map[_id] = self if self.class.identity_map_on?
118
+ end
119
+ result
120
+ end
121
+
122
+ def delete
123
+ identity_map.delete(_id) if self.class.identity_map_on?
124
+ super
125
+ end
126
+ end
127
+ end
128
+ end
@@ -0,0 +1,13 @@
1
+ # encoding: UTF-8
2
+ module MongoMapper
3
+ module Plugins
4
+ module Indexes
5
+ extend ActiveSupport::Concern
6
+
7
+ module ClassMethods
8
+ extend Forwardable
9
+ def_delegators :collection, :ensure_index, :create_index, :drop_index, :drop_indexes
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,16 @@
1
+ # encoding: UTF-8
2
+ module MongoMapper
3
+ module Plugins
4
+ module Inspect
5
+ extend ActiveSupport::Concern
6
+
7
+ def inspect(include_nil = false)
8
+ keys = include_nil ? key_names : attributes.keys
9
+ attributes_as_nice_string = keys.sort.collect do |name|
10
+ "#{name}: #{self.send(:"#{name}").inspect}"
11
+ end.join(", ")
12
+ "#<#{self.class} #{attributes_as_nice_string}>"
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,313 @@
1
+ # encoding: UTF-8
2
+ require 'mongo_mapper/plugins/keys/key'
3
+
4
+ module MongoMapper
5
+ module Plugins
6
+ module Keys
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ extend ActiveSupport::DescendantsTracker
11
+ key :_id, ObjectId, :default => lambda { BSON::ObjectId.new }
12
+ end
13
+
14
+ module ClassMethods
15
+ def inherited(descendant)
16
+ descendant.instance_variable_set(:@keys, keys.dup)
17
+ super
18
+ end
19
+
20
+ def keys
21
+ @keys ||= {}
22
+ end
23
+
24
+ def key(*args)
25
+ Key.new(*args).tap do |key|
26
+ keys[key.name] = key
27
+ create_accessors_for(key)
28
+ create_key_in_descendants(*args)
29
+ create_indexes_for(key)
30
+ create_validations_for(key)
31
+ end
32
+ end
33
+
34
+ def key?(key)
35
+ keys.keys.include?(key.to_s)
36
+ end
37
+
38
+ def using_object_id?
39
+ object_id_key?(:_id)
40
+ end
41
+
42
+ def object_id_keys
43
+ keys.keys.select { |key| keys[key].type == ObjectId }.map { |k| k.to_sym }
44
+ end
45
+
46
+ def object_id_key?(name)
47
+ object_id_keys.include?(name.to_sym)
48
+ end
49
+
50
+ def to_mongo(instance)
51
+ return nil if instance.nil?
52
+ instance.to_mongo
53
+ end
54
+
55
+ def from_mongo(value)
56
+ return nil if value.nil?
57
+ value.is_a?(self) ? value : load(value)
58
+ end
59
+
60
+ # load is overridden in identity map to ensure same objects are loaded
61
+ def load(attrs)
62
+ return nil if attrs.nil?
63
+ begin
64
+ attrs['_type'].present? ? attrs['_type'].constantize : self
65
+ rescue NameError
66
+ self
67
+ end.allocate.initialize_from_database(attrs)
68
+ end
69
+
70
+ private
71
+ def key_accessors_module_defined?
72
+ if method(:const_defined?).arity == 1 # Ruby 1.9 compat check
73
+ const_defined?('MongoMapperKeys')
74
+ else
75
+ const_defined?('MongoMapperKeys', false)
76
+ end
77
+ end
78
+
79
+ def accessors_module
80
+ if key_accessors_module_defined?
81
+ const_get 'MongoMapperKeys'
82
+ else
83
+ const_set 'MongoMapperKeys', Module.new
84
+ end
85
+ end
86
+
87
+ def create_accessors_for(key)
88
+ accessors_module.module_eval <<-end_eval
89
+ def #{key.name}
90
+ read_key(:#{key.name})
91
+ end
92
+
93
+ def #{key.name}_before_type_cast
94
+ read_key_before_type_cast(:#{key.name})
95
+ end
96
+
97
+ def #{key.name}=(value)
98
+ write_key(:#{key.name}, value)
99
+ end
100
+
101
+ def #{key.name}?
102
+ read_key(:#{key.name}).present?
103
+ end
104
+ end_eval
105
+
106
+ include accessors_module
107
+ end
108
+
109
+ def create_key_in_descendants(*args)
110
+ descendants.each { |descendant| descendant.key(*args) }
111
+ end
112
+
113
+ def create_indexes_for(key)
114
+ if key.options[:index] && !key.embeddable?
115
+ warn "[DEPRECATION] :index option when defining key #{key.name.inspect} is deprecated. Put indexes in `db/indexes.rb`"
116
+ ensure_index key.name
117
+ end
118
+ end
119
+
120
+ def create_validations_for(key)
121
+ attribute = key.name.to_sym
122
+
123
+ if key.options[:required]
124
+ if key.type == Boolean
125
+ validates_inclusion_of attribute, :in => [true, false]
126
+ else
127
+ validates_presence_of(attribute)
128
+ end
129
+ end
130
+
131
+ if key.options[:unique]
132
+ validates_uniqueness_of(attribute)
133
+ end
134
+
135
+ if key.options[:numeric]
136
+ number_options = key.type == Integer ? {:only_integer => true} : {}
137
+ validates_numericality_of(attribute, number_options)
138
+ end
139
+
140
+ if key.options[:format]
141
+ validates_format_of(attribute, :with => key.options[:format])
142
+ end
143
+
144
+ if key.options[:in]
145
+ validates_inclusion_of(attribute, :in => key.options[:in])
146
+ end
147
+
148
+ if key.options[:not_in]
149
+ validates_exclusion_of(attribute, :in => key.options[:not_in])
150
+ end
151
+
152
+ if key.options[:length]
153
+ length_options = case key.options[:length]
154
+ when Integer
155
+ {:minimum => 0, :maximum => key.options[:length]}
156
+ when Range
157
+ {:within => key.options[:length]}
158
+ when Hash
159
+ key.options[:length]
160
+ end
161
+ validates_length_of(attribute, length_options)
162
+ end
163
+ end
164
+ end
165
+
166
+ def initialize(attrs={})
167
+ @_new = true
168
+ self.attributes = attrs
169
+ end
170
+
171
+ def initialize_from_database(attrs={})
172
+ @_new = false
173
+ load_from_database(attrs)
174
+ self
175
+ end
176
+
177
+ def persisted?
178
+ !new? && !destroyed?
179
+ end
180
+
181
+ def attributes=(attrs)
182
+ return if attrs.blank?
183
+
184
+ attrs.each_pair do |key, value|
185
+ if respond_to?(:"#{key}=")
186
+ self.send(:"#{key}=", value)
187
+ else
188
+ self[key] = value
189
+ end
190
+ end
191
+ end
192
+
193
+ def attributes
194
+ HashWithIndifferentAccess.new.tap do |attrs|
195
+ keys.select { |name,key| !self[key.name].nil? || key.type == ObjectId }.each do |name, key|
196
+ value = key.set(self[key.name])
197
+ attrs[name] = value
198
+ end
199
+
200
+ embedded_associations.each do |association|
201
+ if documents = instance_variable_get(association.ivar)
202
+ if association.is_a?(Associations::OneAssociation)
203
+ attrs[association.name] = documents.to_mongo
204
+ else
205
+ attrs[association.name] = documents.map { |document| document.to_mongo }
206
+ end
207
+ end
208
+ end
209
+ end
210
+ end
211
+ alias :to_mongo :attributes
212
+
213
+ def assign(attrs={})
214
+ warn "[DEPRECATION] #assign is deprecated, use #attributes="
215
+ self.attributes = attrs
216
+ end
217
+
218
+ def update_attributes(attrs={})
219
+ self.attributes = attrs
220
+ save
221
+ end
222
+
223
+ def update_attributes!(attrs={})
224
+ self.attributes = attrs
225
+ save!
226
+ end
227
+
228
+ def update_attribute(name, value)
229
+ self.send(:"#{name}=", value)
230
+ save(:validate => false)
231
+ end
232
+
233
+ def id
234
+ _id
235
+ end
236
+
237
+ def id=(value)
238
+ if self.class.using_object_id?
239
+ value = ObjectId.to_mongo(value)
240
+ end
241
+
242
+ self[:_id] = value
243
+ end
244
+
245
+ def [](name)
246
+ read_key(name)
247
+ end
248
+
249
+ def []=(name, value)
250
+ ensure_key_exists(name)
251
+ write_key(name, value)
252
+ end
253
+
254
+ def keys
255
+ self.class.keys
256
+ end
257
+
258
+ def key_names
259
+ keys.keys
260
+ end
261
+
262
+ def non_embedded_keys
263
+ keys.values.select { |key| !key.embeddable? }
264
+ end
265
+
266
+ def embedded_keys
267
+ keys.values.select { |key| key.embeddable? }
268
+ end
269
+
270
+ private
271
+ def load_from_database(attrs)
272
+ return if attrs.blank?
273
+ attrs.each do |key, value|
274
+ if respond_to?(:"#{key}=") && !self.class.key?(key)
275
+ self.send(:"#{key}=", value)
276
+ else
277
+ self[key] = value
278
+ end
279
+ end
280
+ end
281
+
282
+ def ensure_key_exists(name)
283
+ self.class.key(name) unless respond_to?("#{name}=")
284
+ end
285
+
286
+ def set_parent_document(key, value)
287
+ if key.embeddable? && value.is_a?(key.type)
288
+ value._parent_document = self
289
+ end
290
+ end
291
+
292
+ def read_key(key_name)
293
+ if key = keys[key_name.to_s]
294
+ value = key.get(instance_variable_get(:"@#{key_name}"))
295
+ set_parent_document(key, value)
296
+ instance_variable_set(:"@#{key_name}", value)
297
+ end
298
+ end
299
+
300
+ def read_key_before_type_cast(name)
301
+ instance_variable_get(:"@#{name}_before_type_cast")
302
+ end
303
+
304
+ def write_key(name, value)
305
+ key = keys[name.to_s]
306
+ set_parent_document(key, value)
307
+ instance_variable_set :"@#{name}_before_type_cast", value
308
+ instance_variable_set :"@#{name}", key.set(value)
309
+ end
310
+ end
311
+ end
312
+ end
313
+