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,61 @@
1
+ # encoding: UTF-8
2
+ module MarkMapper
3
+ module Plugins
4
+ module Dirty
5
+ extend ActiveSupport::Concern
6
+
7
+ include ::ActiveModel::Dirty
8
+
9
+ def initialize(*)
10
+ # never register initial id assignment as a change
11
+ # Chaining super into tap breaks implicit block passing in Ruby 1.8
12
+ doc = super
13
+ doc.tap { changed_attributes.delete('_id') }
14
+ end
15
+
16
+ def save(*)
17
+ clear_changes { super }
18
+ end
19
+
20
+ def reload(*)
21
+ doc = super
22
+ doc.tap { clear_changes }
23
+ end
24
+
25
+ def clear_changes
26
+ previous = changes
27
+ (block_given? ? yield : true).tap do |result|
28
+ unless result == false #failed validation; nil is OK.
29
+ @previously_changed = previous
30
+ changed_attributes.clear
31
+ end
32
+ end
33
+ end
34
+
35
+ protected
36
+
37
+ # We don't call super here to avoid invoking #attributes, which builds a whole new hash per call.
38
+ def attribute_method?(attr_name)
39
+ keys.key?(attr_name) || !embedded_associations.detect {|a| a.name == attr_name }.nil?
40
+ end
41
+
42
+ private
43
+
44
+ def write_key(key, value)
45
+ key = unalias_key(key)
46
+ if !keys.key?(key)
47
+ super
48
+ else
49
+ attribute_will_change!(key) unless attribute_changed?(key)
50
+ super.tap do
51
+ changed_attributes.delete(key) unless attribute_value_changed?(key)
52
+ end
53
+ end
54
+ end
55
+
56
+ def attribute_value_changed?(key_name)
57
+ changed_attributes[key_name] != read_key(key_name)
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,41 @@
1
+ # encoding: UTF-8
2
+ module MarkMapper
3
+ module Plugins
4
+ module Document
5
+ extend ActiveSupport::Concern
6
+
7
+ module ClassMethods
8
+ def embeddable?
9
+ false
10
+ end
11
+ end
12
+
13
+ def new?
14
+ !!(@_new ||= false)
15
+ end
16
+
17
+ def destroyed?
18
+ !!(@_destroyed ||= false)
19
+ end
20
+
21
+ def reload
22
+ if doc = collection.find_one(:_id => id)
23
+ self.class.associations.each_value do |association|
24
+ get_proxy(association).reset
25
+ end
26
+ instance_variables.each { |ivar| remove_instance_variable(ivar) }
27
+ initialize_from_database(doc)
28
+ self
29
+ else
30
+ raise DocumentNotFound, "Document match #{_id.inspect} does not exist in #{collection.name} collection"
31
+ end
32
+ end
33
+
34
+ # Used by embedded docs to find root easily without if/respond_to? stuff.
35
+ # Documents are always root documents.
36
+ def _root_document
37
+ self
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,22 @@
1
+ require 'set'
2
+
3
+ module MarkMapper
4
+ module Plugins
5
+ module Dumpable
6
+ DUMP_BLACKLIST = Set.new([:@errors])
7
+
8
+ def marshal_dump
9
+ instance_variables.map(&:to_sym).inject({}) do |h, var|
10
+ h[var] = instance_variable_get(var) unless DUMP_BLACKLIST.include?(var) or var.to_s.start_with?("@__")
11
+ h
12
+ end
13
+ end
14
+
15
+ def marshal_load(data)
16
+ data.each do |k, v|
17
+ instance_variable_set(k, v)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,45 @@
1
+ # encoding: UTF-8
2
+ require 'mark_mapper/plugins/dynamic_querying/dynamic_finder'
3
+
4
+ module MarkMapper
5
+ module Plugins
6
+ module DynamicQuerying
7
+ extend ActiveSupport::Concern
8
+
9
+ module ClassMethods
10
+ def dynamic_find(finder, args)
11
+ attributes = {}
12
+
13
+ finder.attributes.each_with_index do |attr, index|
14
+ attributes[attr] = args[index]
15
+ end
16
+
17
+ options = args.extract_options!.merge(attributes)
18
+
19
+ if result = send(finder.finder, options)
20
+ result
21
+ else
22
+ if finder.raise?
23
+ raise DocumentNotFound, "Couldn't find Document with #{attributes.inspect} in collection named #{collection.name}"
24
+ end
25
+
26
+ if finder.instantiator
27
+ self.send(finder.instantiator, attributes)
28
+ end
29
+ end
30
+ end
31
+
32
+ protected
33
+ def method_missing(method, *args, &block)
34
+ finder = DynamicFinder.new(method)
35
+
36
+ if finder.found?
37
+ dynamic_find(finder, args)
38
+ else
39
+ super
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,44 @@
1
+ # encoding: UTF-8
2
+ module MarkMapper
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,81 @@
1
+ # encoding: UTF-8
2
+ module MarkMapper
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 :initialize, :find, :touch, :only => [:after]
12
+
13
+ proxy_callbacks(
14
+ :before => [:save, :create, :update, :destroy],
15
+ :after => [:save, :create, :update, :destroy, :touch]
16
+ )
17
+
18
+ @embedded_callbacks_status = nil
19
+ end
20
+
21
+ module ClassMethods
22
+ def define_callbacks(*args)
23
+ embedded_callbacks_on if @embedded_callbacks_status.nil?
24
+ super
25
+ end
26
+
27
+ def embedded_callbacks_on
28
+ @embedded_callbacks_status = true
29
+ end
30
+
31
+ def embedded_callbacks_off
32
+ @embedded_callbacks_status = false
33
+ end
34
+
35
+ def embedded_callbacks_on?
36
+ !!@embedded_callbacks_status
37
+ end
38
+
39
+ def embedded_callbacks_off?
40
+ !@embedded_callbacks_status
41
+ end
42
+
43
+ def proxy_callbacks(definition)
44
+ definition.each do |prefix, suffixes|
45
+ suffixes.each do |suffix|
46
+ callback = "%s_%s" % [prefix, suffix]
47
+ class_eval <<-CALLBACK, __FILE__, __LINE__ + 1
48
+ class << self
49
+ alias_method :__original_#{callback}, :#{callback}
50
+
51
+ def #{callback}(*args, &block)
52
+ embedded_callbacks_on if @embedded_callbacks_status.nil?
53
+ __original_#{callback}(*args, &block)
54
+ end
55
+ end
56
+ CALLBACK
57
+ end
58
+ end
59
+ end
60
+ end
61
+
62
+ def run_callbacks(callback, *args, &block)
63
+ if self.class.embedded_callbacks_on? and embedded_associations.length > 0
64
+ embedded_docs = embedded_associations.map do |association|
65
+ Array(get_proxy(association).send(:load_target))
66
+ end.flatten(1)
67
+
68
+ block = embedded_docs.inject(block) do |chain, doc|
69
+ if doc.class.respond_to?("_#{callback}_callbacks")
70
+ lambda { doc.run_callbacks(callback, *args, &chain) }
71
+ else
72
+ chain
73
+ end
74
+ end
75
+ end
76
+
77
+ super callback, *args, &block
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,53 @@
1
+ # encoding: UTF-8
2
+ module MarkMapper
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 MarkMapper
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,144 @@
1
+ # encoding: UTF-8
2
+ require 'set'
3
+
4
+ module MarkMapper
5
+ module Plugins
6
+ module IdentityMap
7
+ extend ActiveSupport::Concern
8
+
9
+ def self.enabled=(flag)
10
+ Thread.current[:mark_mapper_identity_map_enabled] = flag
11
+ end
12
+
13
+ def self.enabled
14
+ Thread.current[:mark_mapper_identity_map_enabled]
15
+ end
16
+
17
+ def self.enabled?
18
+ enabled == true
19
+ end
20
+
21
+ def self.repository
22
+ Thread.current[:mark_mapper_identity_map] ||= {}
23
+ end
24
+
25
+ def self.clear
26
+ repository.clear
27
+ end
28
+
29
+ def self.include?(document)
30
+ repository.key?(IdentityMap.key(document.class, document._id))
31
+ end
32
+
33
+ def self.key(model, id)
34
+ "#{model.single_collection_root.name}:#{id}"
35
+ end
36
+
37
+ def self.use
38
+ old, self.enabled = enabled, true
39
+
40
+ yield if block_given?
41
+ ensure
42
+ self.enabled = old
43
+ clear
44
+ end
45
+
46
+ def self.without
47
+ old, self.enabled = enabled, false
48
+
49
+ yield if block_given?
50
+ ensure
51
+ self.enabled = old
52
+ end
53
+
54
+ module ClassMethods
55
+ # Private - Looks for a document in the identity map
56
+ def get_from_identity_map(id)
57
+ IdentityMap.repository[IdentityMap.key(self, id)]
58
+ end
59
+
60
+ def query(opts={})
61
+ super.tap do |query|
62
+ query.identity_map = self if Thread.current[:mark_mapper_identity_map_enabled]
63
+ end
64
+ end
65
+
66
+ def remove_documents_from_map(*documents)
67
+ documents.flatten.compact.each do |document|
68
+ document.remove_from_identity_map
69
+ end
70
+ end
71
+
72
+ def load(attrs, with_cast = false)
73
+ return super unless Thread.current[:mark_mapper_identity_map_enabled]
74
+ return nil unless attrs
75
+ document = get_from_identity_map(attrs['_id'])
76
+
77
+ if !document
78
+ document = super
79
+ document.add_to_identity_map
80
+ end
81
+
82
+ document
83
+ end
84
+ end
85
+
86
+ def save(*args)
87
+ super.tap { |result| add_to_identity_map if result }
88
+ end
89
+
90
+ def delete
91
+ super.tap { remove_from_identity_map }
92
+ end
93
+
94
+ def add_to_identity_map
95
+ if IdentityMap.enabled?
96
+ key = IdentityMap.key(self.class, _id)
97
+ IdentityMap.repository[key] = self
98
+ end
99
+ end
100
+
101
+ def remove_from_identity_map
102
+ if IdentityMap.enabled?
103
+ key = IdentityMap.key(self.class, _id)
104
+ IdentityMap.repository.delete(key)
105
+ end
106
+ end
107
+ end
108
+ end
109
+ end
110
+
111
+ module MarkMapperMethods
112
+ module ClassMethods
113
+ extend ActiveSupport::Concern
114
+
115
+ included do
116
+ attr_accessor :identity_map
117
+
118
+ alias_method :first, :find_one
119
+ alias_method :each, :find_each
120
+ end
121
+
122
+ def find_one(opts={})
123
+ query = clone.amend(opts)
124
+
125
+ if identity_map && query.simple? && (document = identity_map.get_from_identity_map(query[:_id]))
126
+ document
127
+ else
128
+ super.tap do |doc|
129
+ doc.remove_from_identity_map if doc && query.fields?
130
+ end
131
+ end
132
+ end
133
+
134
+ def find_each(opts={})
135
+ query = clone.amend(opts)
136
+ super(opts) do |doc|
137
+ doc.remove_from_identity_map if doc && query.fields?
138
+ yield doc if block_given?
139
+ end
140
+ end
141
+ end
142
+ end
143
+
144
+ ::MarkMapper::Plugins::Querying::DecoratedMarkMapperQuery.send :include, ::MarkMapperMethods::ClassMethods