mark_mapper 0.0.1

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 (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