mark_mapper 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (211) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.rdoc +39 -0
  4. data/examples/attr_accessible.rb +24 -0
  5. data/examples/attr_protected.rb +24 -0
  6. data/examples/cache_key.rb +26 -0
  7. data/examples/custom_types.rb +26 -0
  8. data/examples/identity_map.rb +30 -0
  9. data/examples/identity_map/automatic.rb +2 -0
  10. data/examples/keys.rb +42 -0
  11. data/examples/modifiers/set.rb +27 -0
  12. data/examples/plugins.rb +40 -0
  13. data/examples/querying.rb +39 -0
  14. data/examples/sample_app.rb +43 -0
  15. data/examples/scopes.rb +56 -0
  16. data/examples/validating/embedded_docs.rb +31 -0
  17. data/lib/mark_mapper.rb +125 -0
  18. data/lib/mark_mapper/config.rb +90 -0
  19. data/lib/mark_mapper/connection.rb +60 -0
  20. data/lib/mark_mapper/criteria_hash.rb +194 -0
  21. data/lib/mark_mapper/document.rb +46 -0
  22. data/lib/mark_mapper/embedded_document.rb +32 -0
  23. data/lib/mark_mapper/exceptions.rb +33 -0
  24. data/lib/mark_mapper/extensions/array.rb +27 -0
  25. data/lib/mark_mapper/extensions/boolean.rb +45 -0
  26. data/lib/mark_mapper/extensions/date.rb +29 -0
  27. data/lib/mark_mapper/extensions/duplicable.rb +86 -0
  28. data/lib/mark_mapper/extensions/float.rb +18 -0
  29. data/lib/mark_mapper/extensions/hash.rb +26 -0
  30. data/lib/mark_mapper/extensions/integer.rb +27 -0
  31. data/lib/mark_mapper/extensions/kernel.rb +11 -0
  32. data/lib/mark_mapper/extensions/nil_class.rb +18 -0
  33. data/lib/mark_mapper/extensions/object.rb +30 -0
  34. data/lib/mark_mapper/extensions/object_id.rb +18 -0
  35. data/lib/mark_mapper/extensions/set.rb +20 -0
  36. data/lib/mark_mapper/extensions/string.rb +31 -0
  37. data/lib/mark_mapper/extensions/symbol.rb +87 -0
  38. data/lib/mark_mapper/extensions/time.rb +29 -0
  39. data/lib/mark_mapper/locale/en.yml +5 -0
  40. data/lib/mark_mapper/middleware/identity_map.rb +41 -0
  41. data/lib/mark_mapper/normalizers/criteria_hash_key.rb +17 -0
  42. data/lib/mark_mapper/normalizers/criteria_hash_value.rb +66 -0
  43. data/lib/mark_mapper/normalizers/fields_value.rb +26 -0
  44. data/lib/mark_mapper/normalizers/hash_key.rb +19 -0
  45. data/lib/mark_mapper/normalizers/integer.rb +19 -0
  46. data/lib/mark_mapper/normalizers/options_hash_value.rb +83 -0
  47. data/lib/mark_mapper/normalizers/sort_value.rb +55 -0
  48. data/lib/mark_mapper/options_hash.rb +103 -0
  49. data/lib/mark_mapper/pagination.rb +6 -0
  50. data/lib/mark_mapper/pagination/collection.rb +32 -0
  51. data/lib/mark_mapper/pagination/paginator.rb +46 -0
  52. data/lib/mark_mapper/plugins.rb +22 -0
  53. data/lib/mark_mapper/plugins/accessible.rb +61 -0
  54. data/lib/mark_mapper/plugins/active_model.rb +18 -0
  55. data/lib/mark_mapper/plugins/associations.rb +96 -0
  56. data/lib/mark_mapper/plugins/associations/base.rb +98 -0
  57. data/lib/mark_mapper/plugins/associations/belongs_to_association.rb +63 -0
  58. data/lib/mark_mapper/plugins/associations/belongs_to_polymorphic_proxy.rb +35 -0
  59. data/lib/mark_mapper/plugins/associations/belongs_to_proxy.rb +52 -0
  60. data/lib/mark_mapper/plugins/associations/collection.rb +29 -0
  61. data/lib/mark_mapper/plugins/associations/embedded_collection.rb +44 -0
  62. data/lib/mark_mapper/plugins/associations/in_array_proxy.rb +133 -0
  63. data/lib/mark_mapper/plugins/associations/many_association.rb +63 -0
  64. data/lib/mark_mapper/plugins/associations/many_documents_as_proxy.rb +28 -0
  65. data/lib/mark_mapper/plugins/associations/many_documents_proxy.rb +142 -0
  66. data/lib/mark_mapper/plugins/associations/many_embedded_polymorphic_proxy.rb +32 -0
  67. data/lib/mark_mapper/plugins/associations/many_embedded_proxy.rb +24 -0
  68. data/lib/mark_mapper/plugins/associations/many_polymorphic_proxy.rb +14 -0
  69. data/lib/mark_mapper/plugins/associations/one_as_proxy.rb +22 -0
  70. data/lib/mark_mapper/plugins/associations/one_association.rb +48 -0
  71. data/lib/mark_mapper/plugins/associations/one_embedded_polymorphic_proxy.rb +30 -0
  72. data/lib/mark_mapper/plugins/associations/one_embedded_proxy.rb +44 -0
  73. data/lib/mark_mapper/plugins/associations/one_proxy.rb +95 -0
  74. data/lib/mark_mapper/plugins/associations/proxy.rb +138 -0
  75. data/lib/mark_mapper/plugins/associations/single_association.rb +46 -0
  76. data/lib/mark_mapper/plugins/caching.rb +21 -0
  77. data/lib/mark_mapper/plugins/callbacks.rb +42 -0
  78. data/lib/mark_mapper/plugins/clone.rb +24 -0
  79. data/lib/mark_mapper/plugins/counter_cache.rb +97 -0
  80. data/lib/mark_mapper/plugins/dirty.rb +61 -0
  81. data/lib/mark_mapper/plugins/document.rb +41 -0
  82. data/lib/mark_mapper/plugins/dumpable.rb +22 -0
  83. data/lib/mark_mapper/plugins/dynamic_querying.rb +45 -0
  84. data/lib/mark_mapper/plugins/dynamic_querying/dynamic_finder.rb +44 -0
  85. data/lib/mark_mapper/plugins/embedded_callbacks.rb +81 -0
  86. data/lib/mark_mapper/plugins/embedded_document.rb +53 -0
  87. data/lib/mark_mapper/plugins/equality.rb +23 -0
  88. data/lib/mark_mapper/plugins/identity_map.rb +144 -0
  89. data/lib/mark_mapper/plugins/indexable.rb +86 -0
  90. data/lib/mark_mapper/plugins/inspect.rb +16 -0
  91. data/lib/mark_mapper/plugins/keys.rb +470 -0
  92. data/lib/mark_mapper/plugins/keys/key.rb +134 -0
  93. data/lib/mark_mapper/plugins/keys/static.rb +45 -0
  94. data/lib/mark_mapper/plugins/logger.rb +18 -0
  95. data/lib/mark_mapper/plugins/modifiers.rb +140 -0
  96. data/lib/mark_mapper/plugins/pagination.rb +16 -0
  97. data/lib/mark_mapper/plugins/partial_updates.rb +77 -0
  98. data/lib/mark_mapper/plugins/persistence.rb +79 -0
  99. data/lib/mark_mapper/plugins/protected.rb +45 -0
  100. data/lib/mark_mapper/plugins/querying.rb +173 -0
  101. data/lib/mark_mapper/plugins/querying/decorated_markmapper_query.rb +75 -0
  102. data/lib/mark_mapper/plugins/rails.rb +79 -0
  103. data/lib/mark_mapper/plugins/rails/active_record_association_adapter.rb +33 -0
  104. data/lib/mark_mapper/plugins/sci.rb +82 -0
  105. data/lib/mark_mapper/plugins/scopes.rb +28 -0
  106. data/lib/mark_mapper/plugins/serialization.rb +109 -0
  107. data/lib/mark_mapper/plugins/timestamps.rb +29 -0
  108. data/lib/mark_mapper/plugins/touch.rb +18 -0
  109. data/lib/mark_mapper/plugins/userstamps.rb +18 -0
  110. data/lib/mark_mapper/plugins/validations.rb +96 -0
  111. data/lib/mark_mapper/query.rb +278 -0
  112. data/lib/mark_mapper/railtie.rb +52 -0
  113. data/lib/mark_mapper/railtie/database.rake +65 -0
  114. data/lib/mark_mapper/translation.rb +10 -0
  115. data/lib/mark_mapper/version.rb +4 -0
  116. data/lib/rails/generators/mark_mapper/config/config_generator.rb +37 -0
  117. data/lib/rails/generators/mark_mapper/config/templates/marklogic.yml +19 -0
  118. data/lib/rails/generators/mark_mapper/model/model_generator.rb +40 -0
  119. data/lib/rails/generators/mark_mapper/model/templates/model.rb +17 -0
  120. data/spec/config/mark_mapper.yml +6 -0
  121. data/spec/examples_spec.rb +25 -0
  122. data/spec/functional/accessible_spec.rb +198 -0
  123. data/spec/functional/associations/belongs_to_polymorphic_proxy_spec.rb +64 -0
  124. data/spec/functional/associations/belongs_to_proxy_spec.rb +255 -0
  125. data/spec/functional/associations/in_array_proxy_spec.rb +349 -0
  126. data/spec/functional/associations/many_documents_as_proxy_spec.rb +230 -0
  127. data/spec/functional/associations/many_documents_proxy_spec.rb +968 -0
  128. data/spec/functional/associations/many_embedded_polymorphic_proxy_spec.rb +238 -0
  129. data/spec/functional/associations/many_embedded_proxy_spec.rb +288 -0
  130. data/spec/functional/associations/many_polymorphic_proxy_spec.rb +302 -0
  131. data/spec/functional/associations/one_as_proxy_spec.rb +489 -0
  132. data/spec/functional/associations/one_embedded_polymorphic_proxy_spec.rb +207 -0
  133. data/spec/functional/associations/one_embedded_proxy_spec.rb +100 -0
  134. data/spec/functional/associations/one_proxy_spec.rb +406 -0
  135. data/spec/functional/associations_spec.rb +48 -0
  136. data/spec/functional/caching_spec.rb +75 -0
  137. data/spec/functional/callbacks_spec.rb +330 -0
  138. data/spec/functional/counter_cache_spec.rb +235 -0
  139. data/spec/functional/dirty_spec.rb +316 -0
  140. data/spec/functional/document_spec.rb +310 -0
  141. data/spec/functional/dumpable_spec.rb +24 -0
  142. data/spec/functional/dynamic_querying_spec.rb +75 -0
  143. data/spec/functional/embedded_document_spec.rb +316 -0
  144. data/spec/functional/equality_spec.rb +20 -0
  145. data/spec/functional/extensions_spec.rb +16 -0
  146. data/spec/functional/identity_map_spec.rb +483 -0
  147. data/spec/functional/keys_spec.rb +339 -0
  148. data/spec/functional/logger_spec.rb +20 -0
  149. data/spec/functional/modifiers_spec.rb +446 -0
  150. data/spec/functional/options_hash_spec.rb +41 -0
  151. data/spec/functional/pagination_spec.rb +89 -0
  152. data/spec/functional/partial_updates_spec.rb +530 -0
  153. data/spec/functional/protected_spec.rb +199 -0
  154. data/spec/functional/querying_spec.rb +984 -0
  155. data/spec/functional/rails_spec.rb +55 -0
  156. data/spec/functional/sci_spec.rb +374 -0
  157. data/spec/functional/scopes_spec.rb +204 -0
  158. data/spec/functional/static_keys_spec.rb +153 -0
  159. data/spec/functional/timestamps_spec.rb +97 -0
  160. data/spec/functional/touch_spec.rb +125 -0
  161. data/spec/functional/userstamps_spec.rb +46 -0
  162. data/spec/functional/validations_spec.rb +416 -0
  163. data/spec/quality_spec.rb +51 -0
  164. data/spec/spec_helper.rb +150 -0
  165. data/spec/support/matchers.rb +15 -0
  166. data/spec/support/models.rb +256 -0
  167. data/spec/symbol_operator_spec.rb +70 -0
  168. data/spec/symbol_spec.rb +9 -0
  169. data/spec/unit/associations/base_spec.rb +146 -0
  170. data/spec/unit/associations/belongs_to_association_spec.rb +30 -0
  171. data/spec/unit/associations/many_association_spec.rb +64 -0
  172. data/spec/unit/associations/one_association_spec.rb +48 -0
  173. data/spec/unit/associations/proxy_spec.rb +103 -0
  174. data/spec/unit/clone_spec.rb +79 -0
  175. data/spec/unit/config_generator_spec.rb +24 -0
  176. data/spec/unit/criteria_hash_spec.rb +218 -0
  177. data/spec/unit/document_spec.rb +251 -0
  178. data/spec/unit/dynamic_finder_spec.rb +125 -0
  179. data/spec/unit/embedded_document_spec.rb +676 -0
  180. data/spec/unit/equality_spec.rb +38 -0
  181. data/spec/unit/exceptions_spec.rb +12 -0
  182. data/spec/unit/extensions_spec.rb +368 -0
  183. data/spec/unit/identity_map_middleware_spec.rb +134 -0
  184. data/spec/unit/inspect_spec.rb +47 -0
  185. data/spec/unit/key_spec.rb +276 -0
  186. data/spec/unit/keys_spec.rb +155 -0
  187. data/spec/unit/mark_mapper_spec.rb +37 -0
  188. data/spec/unit/model_generator_spec.rb +45 -0
  189. data/spec/unit/normalizers/criteria_hash_key_spec.rb +37 -0
  190. data/spec/unit/normalizers/criteria_hash_value_spec.rb +200 -0
  191. data/spec/unit/normalizers/fields_value_spec.rb +45 -0
  192. data/spec/unit/normalizers/hash_key_spec.rb +15 -0
  193. data/spec/unit/normalizers/integer_spec.rb +24 -0
  194. data/spec/unit/normalizers/options_hash_value_spec.rb +99 -0
  195. data/spec/unit/normalizers/sort_value_spec.rb +98 -0
  196. data/spec/unit/options_hash_spec.rb +64 -0
  197. data/spec/unit/pagination/collection_spec.rb +30 -0
  198. data/spec/unit/pagination/paginator_spec.rb +118 -0
  199. data/spec/unit/pagination_spec.rb +11 -0
  200. data/spec/unit/plugins_spec.rb +89 -0
  201. data/spec/unit/query_spec.rb +837 -0
  202. data/spec/unit/rails_compatibility_spec.rb +40 -0
  203. data/spec/unit/rails_reflect_on_association_spec.rb +118 -0
  204. data/spec/unit/rails_spec.rb +188 -0
  205. data/spec/unit/serialization_spec.rb +169 -0
  206. data/spec/unit/serializers/json_serializer_spec.rb +218 -0
  207. data/spec/unit/serializers/xml_serializer_spec.rb +198 -0
  208. data/spec/unit/time_zones_spec.rb +44 -0
  209. data/spec/unit/translation_spec.rb +27 -0
  210. data/spec/unit/validations_spec.rb +588 -0
  211. metadata +307 -0
@@ -0,0 +1,86 @@
1
+ module MarkMapper
2
+ module Plugins
3
+ module Indexable
4
+ extend ActiveSupport::Concern
5
+
6
+ module ClassMethods
7
+ # Get all the defined indexes for this document
8
+ #
9
+ # @example Get the indexes hash
10
+ # person.index_defs
11
+ #
12
+ # @return[ Hash ] A hash containing all the index definitions
13
+ #
14
+ # @since 0.0.1
15
+ def index_defs
16
+ @index_defs ||= {}
17
+ end
18
+
19
+ # Defines all the MarkLogic indexes for this document
20
+ #
21
+ # @example Define an index.
22
+ # Model.index :first_name, :type => String, :facet => true
23
+ #
24
+ # @param [ Symbol ] name The localname of the element to index.
25
+ # @param [ Hash ] options The options to pass to the index.
26
+ #
27
+ # @option options [ String ] :type The atomic type of the element to index.
28
+ # @option options [ Boolean ] :facet Whether to facet on this element index or not
29
+ #
30
+ # @since 0.0.1
31
+ def index(name, options = {})
32
+ options[:type] ||= String
33
+ options[:type] = options[:type].xs_type if options[:type].respond_to?(:xs_type)
34
+
35
+ raise InvalidIndexType, "Invalid index type: #{options[:type]}" unless [Boolean.xs_type, Integer.xs_type, String.xs_type, Float.xs_type].include? options[:type]
36
+
37
+ new_index =
38
+ begin
39
+ case options[:kind]
40
+ when "field" then MarkLogic::DatabaseSettings::RangeFieldIndex.new(name, options)
41
+ when "path" then MarkLogic::DatabaseSettings::RangePathIndex.new(name, options)
42
+ else MarkLogic::DatabaseSettings::RangeElementIndex.new(name, options)
43
+ end
44
+ end
45
+
46
+ if !index_defs.has_key?(name.to_s)
47
+ index_defs[name.to_s] = new_index
48
+ MarkMapper.application.add_index(new_index)
49
+ end
50
+ end
51
+
52
+ # Removes the definition for an index
53
+ # Used for testing
54
+ #
55
+ # @example Remove an index
56
+ # Model.remove_index :name
57
+ #
58
+ # @param [ String, Symbol ] name The name of the index to remove
59
+ #
60
+ # @since 0.0.1
61
+ def remove_index(name)
62
+ index_defs.delete name.to_s
63
+ end
64
+
65
+ # Generates an xml string with bootstrap configuration for the indexes
66
+ # This is used when generating indexes automagically within MarkLogic
67
+ #
68
+ # @return [ String ] XML configuration
69
+ #
70
+ # @since 0.0.1
71
+ # def index_setup_xml
72
+ # xml = ""
73
+ # index_defs.values do |index|
74
+ # xml << index.setup_xml
75
+ # end
76
+ # xml
77
+ # end
78
+ end
79
+
80
+ included do
81
+ index :_id, :type => String
82
+ end
83
+
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,16 @@
1
+ # encoding: UTF-8
2
+ module MarkMapper
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,470 @@
1
+ # encoding: UTF-8
2
+ require 'mark_mapper/plugins/keys/key'
3
+ require 'mark_mapper/plugins/keys/static'
4
+
5
+ module MarkMapper
6
+ module Plugins
7
+ module Keys
8
+ extend ActiveSupport::Concern
9
+
10
+ IS_RUBY_1_9 = method(:const_defined?).arity == 1
11
+
12
+ included do
13
+ extend ActiveSupport::DescendantsTracker
14
+ key :_id, ObjectId, :default => lambda { MarkLogic::ObjectId.new }
15
+ end
16
+
17
+ module ClassMethods
18
+ def inherited(descendant)
19
+ descendant.instance_variable_set(:@keys, keys.dup)
20
+ super
21
+ end
22
+
23
+ def keys
24
+ @keys ||= {}
25
+ end
26
+
27
+ def dynamic_keys
28
+ @dynamic_keys ||= Hash[*unaliased_keys.select {|k, v| v.dynamic? }.flatten(1)]
29
+ end
30
+
31
+ def defined_keys
32
+ @defined_keys ||= Hash[*unaliased_keys.select {|k, v| !v.dynamic? }.flatten(1)]
33
+ end
34
+
35
+ def unaliased_keys
36
+ @unaliased_keys ||= Hash[*keys.select {|k, v| k == v.name }.flatten(1)]
37
+ end
38
+
39
+ def dealias_keys(hash)
40
+ out = {}
41
+ hash.each do |k, v|
42
+ key = keys[k.to_s]
43
+ name = key && key.abbr || k
44
+ out[name] = k.to_s.match(/^\$/) && v.is_a?(Hash) ? dealias_keys(v) : v
45
+ end
46
+ out
47
+ end
48
+
49
+ def dealias_key(name)
50
+ key = keys[name.to_s]
51
+ key && key.abbr || k
52
+ end
53
+
54
+ alias_method :dealias, :dealias_keys
55
+ alias_method :unalias, :dealias_keys
56
+
57
+ def key(*args)
58
+ Key.new(*args).tap do |key|
59
+ keys[key.name] = key
60
+ keys[key.abbr] = key if key.abbr
61
+ create_accessors_for(key) if key.valid_ruby_name? && !key.reserved_name?
62
+ create_key_in_descendants(*args)
63
+ create_indexes_for(key)
64
+ create_validations_for(key)
65
+ @dynamic_keys = @defined_keys = @unaliased_keys = @object_id_keys = nil
66
+ end
67
+ end
68
+
69
+ def remove_key(name)
70
+ if key = keys[name.to_s]
71
+ keys.delete key.name
72
+ keys.delete key.abbr
73
+ remove_method key.name if respond_to? "#{key.name}"
74
+ remove_method "#{key.name}=" if respond_to? "#{key.name}="
75
+ remove_method "#{key.name}?" if respond_to? "#{key.name}?"
76
+ remove_method "#{key.name}_before_type_cast" if respond_to? "#{key.name}_before_type_cast"
77
+ remove_key_in_descendants key.name
78
+ remove_validations_for key.name
79
+ @dynamic_keys = @defined_keys = @unaliased_keys = @object_id_keys = nil
80
+ end
81
+ end
82
+
83
+ def persisted_name(name)
84
+ if key = keys[name.to_s]
85
+ key.persisted_name
86
+ else
87
+ name
88
+ end
89
+ end
90
+ alias_method :abbr, :persisted_name
91
+
92
+ def key?(key)
93
+ keys.key? key.to_s
94
+ end
95
+
96
+ def using_object_id?
97
+ object_id_key?(:_id)
98
+ end
99
+
100
+ def object_id_keys
101
+ @object_id_keys ||= unaliased_keys.keys.select { |key| keys[key].type == ObjectId }.map(&:to_sym)
102
+ end
103
+
104
+ def object_id_key?(name)
105
+ object_id_keys.include?(name.to_sym)
106
+ end
107
+
108
+ def to_marklogic(instance)
109
+ instance && instance.to_marklogic
110
+ end
111
+
112
+ def from_marklogic(value)
113
+ value && (value.instance_of?(self) ? value : load(value))
114
+ end
115
+
116
+ # load is overridden in identity map to ensure same objects are loaded
117
+ def load(attrs, with_cast = false)
118
+ return nil if attrs.nil?
119
+ begin
120
+ attrs['_type'] ? attrs['_type'].constantize : self
121
+ rescue NameError
122
+ self
123
+ end.allocate.initialize_from_database(attrs, with_cast)
124
+ end
125
+
126
+ private
127
+ def key_accessors_module_defined?
128
+ # :nocov:
129
+ if IS_RUBY_1_9
130
+ const_defined?('MarkMapperKeys')
131
+ else
132
+ const_defined?('MarkMapperKeys', false)
133
+ end
134
+ # :nocov:
135
+ end
136
+
137
+ def accessors_module
138
+ if key_accessors_module_defined?
139
+ const_get 'MarkMapperKeys'
140
+ else
141
+ const_set 'MarkMapperKeys', Module.new
142
+ end
143
+ end
144
+
145
+ def create_accessors_for(key)
146
+ accessors = ""
147
+ if key.read_accessor?
148
+ accessors << <<-end_eval
149
+ def #{key.name}
150
+ read_key(:#{key.name})
151
+ end
152
+
153
+ def #{key.name}_before_type_cast
154
+ read_key_before_type_cast(:#{key.name})
155
+ end
156
+ end_eval
157
+ end
158
+
159
+ if key.write_accessor?
160
+ accessors << <<-end_eval
161
+ def #{key.name}=(value)
162
+ write_key(:#{key.name}, value)
163
+ end
164
+ end_eval
165
+ end
166
+
167
+ if key.predicate_accessor?
168
+ accessors << <<-end_eval
169
+ def #{key.name}?
170
+ read_key(:#{key.name}).present?
171
+ end
172
+ end_eval
173
+ end
174
+
175
+ if block_given?
176
+ accessors_module.module_eval do
177
+ yield
178
+ end
179
+ end
180
+
181
+ accessors_module.module_eval accessors
182
+ include accessors_module
183
+ end
184
+
185
+ def create_key_in_descendants(*args)
186
+ descendants.each { |descendant| descendant.key(*args) }
187
+ end
188
+
189
+ def remove_key_in_descendants(name)
190
+ descendants.each { |descendant| descendant.remove_key(name) }
191
+ end
192
+
193
+ def create_indexes_for(key)
194
+ if key.options[:index] && !key.embeddable?
195
+ warn "[DEPRECATION] :index option when defining key #{key.name.inspect} is deprecated. Put indexes in `db/indexes.rb`"
196
+ ensure_index key.name
197
+ end
198
+ end
199
+
200
+ def create_validations_for(key)
201
+ attribute = key.name.to_sym
202
+
203
+ if key.options[:required]
204
+ if key.type == Boolean
205
+ validates_inclusion_of attribute, :in => [true, false]
206
+ else
207
+ validates_presence_of(attribute)
208
+ end
209
+ end
210
+
211
+ if key.options[:unique]
212
+ validates_uniqueness_of(attribute)
213
+ end
214
+
215
+ if key.options[:numeric]
216
+ number_options = key.type == Integer ? {:only_integer => true} : {}
217
+ validates_numericality_of(attribute, number_options)
218
+ end
219
+
220
+ if key.options[:format]
221
+ validates_format_of(attribute, :with => key.options[:format])
222
+ end
223
+
224
+ if key.options[:in]
225
+ validates_inclusion_of(attribute, :in => key.options[:in])
226
+ end
227
+
228
+ if key.options[:not_in]
229
+ validates_exclusion_of(attribute, :in => key.options[:not_in])
230
+ end
231
+
232
+ if key.options[:length]
233
+ length_options = case key.options[:length]
234
+ when Integer
235
+ {:minimum => 0, :maximum => key.options[:length]}
236
+ when Range
237
+ {:within => key.options[:length]}
238
+ when Hash
239
+ key.options[:length]
240
+ end
241
+ validates_length_of(attribute, length_options)
242
+ end
243
+ end
244
+
245
+ def remove_validations_for(name)
246
+ name = name.to_sym
247
+ a_name = [name]
248
+
249
+ _validators.reject!{ |key, _| key == name }
250
+ remove_validate_callbacks a_name
251
+ end
252
+
253
+ def remove_validate_callbacks(a_name)
254
+ chain = _validate_callbacks.dup.reject do |callback|
255
+ f = callback.raw_filter
256
+ f.respond_to?(:attributes) && f.attributes == a_name
257
+ end
258
+ reset_callbacks(:validate)
259
+ chain.each do |callback|
260
+ set_callback 'validate', callback.raw_filter
261
+ end
262
+ end
263
+
264
+ end
265
+
266
+ def initialize(attrs={})
267
+ @_new = true
268
+ init_ivars
269
+ initialize_default_values(attrs)
270
+ self.attributes = attrs
271
+ yield self if block_given?
272
+ end
273
+
274
+ def initialize_from_database(attrs={}, with_cast = false)
275
+ @_new = false
276
+ init_ivars
277
+ initialize_default_values(attrs)
278
+ load_from_database(attrs, with_cast)
279
+ self
280
+ end
281
+
282
+ def persisted?
283
+ !new? && !destroyed?
284
+ end
285
+
286
+ def attributes=(attrs)
287
+ return if attrs == nil || attrs.blank?
288
+
289
+ attrs.each_pair do |key, value|
290
+ if respond_to?(:"#{key}=")
291
+ self.send(:"#{key}=", value)
292
+ else
293
+ self[key] = value
294
+ end
295
+ end
296
+ end
297
+
298
+ def to_marklogic(include_abbreviatons = true)
299
+ Hash.new.tap do |attrs|
300
+ self.class.unaliased_keys.each do |name, key|
301
+ value = self.read_key(key.name)
302
+ if key.type == ObjectId || !value.nil?
303
+ attrs[include_abbreviatons && key.persisted_name || name] = key.set(value)
304
+ end
305
+ end
306
+
307
+ embedded_associations.each do |association|
308
+ if documents = instance_variable_get(association.ivar)
309
+ if association.is_a?(Associations::OneAssociation)
310
+ attrs[association.name] = documents.to_marklogic
311
+ else
312
+ attrs[association.name] = documents.map(&:to_marklogic)
313
+ end
314
+ end
315
+ end
316
+ end
317
+ end
318
+
319
+ def attributes
320
+ to_marklogic(false).with_indifferent_access
321
+ end
322
+
323
+ def assign(attrs={})
324
+ warn "[DEPRECATION] #assign is deprecated, use #attributes="
325
+ self.attributes = attrs
326
+ end
327
+
328
+ def update_attributes(attrs={})
329
+ self.attributes = attrs
330
+ save
331
+ end
332
+
333
+ def update_attributes!(attrs={})
334
+ self.attributes = attrs
335
+ save!
336
+ end
337
+
338
+ def update_attribute(name, value)
339
+ self.send(:"#{name}=", value)
340
+ save(:validate => false)
341
+ end
342
+
343
+ def id
344
+ self[:_id]
345
+ end
346
+
347
+ def id=(value)
348
+ if self.class.using_object_id?
349
+ value = ObjectId.to_marklogic(value)
350
+ end
351
+
352
+ self[:_id] = value
353
+ end
354
+
355
+ def keys
356
+ self.class.keys
357
+ end
358
+
359
+ def read_key(key_name)
360
+ key_name_sym = key_name.to_sym
361
+ if @_dynamic_attributes && @_dynamic_attributes.key?(key_name_sym)
362
+ @_dynamic_attributes[key_name_sym]
363
+ elsif key = keys[key_name.to_s]
364
+ if key.ivar && instance_variable_defined?(key.ivar)
365
+ value = instance_variable_get(key.ivar)
366
+ else
367
+ if key.ivar
368
+ instance_variable_set key.ivar, key.get(nil)
369
+ else
370
+ @_dynamic_attributes[key_name_sym] = key.get(nil)
371
+ end
372
+ end
373
+ end
374
+ end
375
+
376
+ def [](key_name); read_key(key_name); end
377
+ def attribute(key_name); read_key(key_name); end
378
+
379
+ def []=(name, value)
380
+ write_key(name, value)
381
+ end
382
+
383
+ def key_names
384
+ @key_names ||= keys.keys
385
+ end
386
+
387
+ def non_embedded_keys
388
+ @non_embedded_keys ||= keys.values.select { |key| !key.embeddable? }
389
+ end
390
+
391
+ def embedded_keys
392
+ @embedded_keys ||= keys.values.select(&:embeddable?)
393
+ end
394
+
395
+ protected
396
+
397
+ def unalias_key(name)
398
+ name = name.to_s
399
+ if key = keys[name]
400
+ key.name
401
+ else
402
+ name
403
+ end
404
+ end
405
+
406
+ private
407
+
408
+ def init_ivars
409
+ @__mm_keys = self.class.keys # Not dumpable
410
+ @__mm_default_keys = @__mm_keys.values.select(&:default?) # Not dumpable
411
+ @_dynamic_attributes = {} # Dumpable
412
+ end
413
+
414
+ def load_from_database(attrs, with_cast = false)
415
+ return if attrs == nil || attrs.blank?
416
+
417
+ attrs.each do |key, value|
418
+ if !@__mm_keys.key?(key) && respond_to?(:"#{key}=")
419
+ self.send(:"#{key}=", value)
420
+ else
421
+ internal_write_key key, value, with_cast
422
+ end
423
+ end
424
+ end
425
+
426
+ def set_parent_document(key, value)
427
+ if key.type and value.instance_of?(key.type) && key.embeddable? && value.respond_to?(:_parent_document)
428
+ value._parent_document = self
429
+ end
430
+ end
431
+
432
+ # This exists to be patched over by plugins, while letting us still get to the undecorated
433
+ # version of the method.
434
+ def write_key(name, value)
435
+ init_ivars unless @__mm_keys
436
+ internal_write_key(name.to_s, value)
437
+ end
438
+
439
+ def internal_write_key(name, value, cast = true)
440
+ key = @__mm_keys[name] || dynamic_key(name)
441
+ as_marklogic = cast ? key.set(value) : value
442
+ as_typecast = key.get(as_marklogic)
443
+ if key.ivar
444
+ if key.embeddable?
445
+ set_parent_document(key, value)
446
+ set_parent_document(key, as_typecast)
447
+ end
448
+ instance_variable_set key.ivar, as_typecast
449
+ else
450
+ @_dynamic_attributes[key.name.to_sym] = as_typecast
451
+ end
452
+ @attributes = nil
453
+ value
454
+ end
455
+
456
+ def dynamic_key(name)
457
+ self.class.key(name, :__dynamic => true)
458
+ end
459
+
460
+ def initialize_default_values(except = {})
461
+ @__mm_default_keys.each do |key|
462
+ if !(except && except.key?(key.name))
463
+ internal_write_key key.name, key.default_value, false
464
+ end
465
+ end
466
+ end
467
+ #end private
468
+ end
469
+ end
470
+ end