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,95 @@
1
+ # encoding: UTF-8
2
+ module MarkMapper
3
+ module Plugins
4
+ module Associations
5
+ class OneProxy < Proxy
6
+ def build(attrs={}, &block)
7
+ instantiate_target(:new, attrs, &block)
8
+ end
9
+
10
+ def create(attrs={}, &block)
11
+ instantiate_target(:create, attrs, &block)
12
+ end
13
+
14
+ def create!(attrs={}, &block)
15
+ instantiate_target(:create!, attrs, &block)
16
+ end
17
+
18
+ def replace(doc)
19
+ load_target
20
+
21
+ if !target.nil? && target != doc
22
+ if target.persisted?
23
+ case options[:dependent]
24
+ when :delete then target.delete
25
+ when :destroy then target.destroy
26
+ else
27
+ nullify_scope(target)
28
+ target.save
29
+ end
30
+ end
31
+ end
32
+
33
+ unless doc.nil?
34
+ proxy_owner.save unless proxy_owner.persisted?
35
+ doc = klass.new(doc) unless doc.is_a?(klass)
36
+ apply_scope(doc)
37
+ doc.save unless doc.persisted?
38
+ end
39
+
40
+ loaded
41
+ @target = doc
42
+ end
43
+
44
+ def destroy
45
+ target.destroy
46
+ reset
47
+ end
48
+
49
+ def delete
50
+ target.delete
51
+ reset
52
+ end
53
+
54
+ def nullify
55
+ nullify_scope(target)
56
+ target.save
57
+ reset
58
+ end
59
+
60
+ protected
61
+ def find_target
62
+ target_class.first(association.query_options.merge(criteria))
63
+ end
64
+
65
+ def instantiate_target(instantiator, attrs={}, &block)
66
+ @target = target_class.send(instantiator, attrs.update(criteria), &block)
67
+ loaded
68
+ @target
69
+ end
70
+
71
+ def target_class
72
+ @target_class ||= options[:class] || (options[:class_name] || association.name.to_s.camelize).constantize
73
+ end
74
+
75
+ def foreign_key
76
+ options[:foreign_key] || proxy_owner.class.name.foreign_key
77
+ end
78
+
79
+ def criteria
80
+ {self.foreign_key => proxy_owner.id}
81
+ end
82
+
83
+ def nullify_scope(doc)
84
+ criteria.each { |key, value| doc[key] = nil }
85
+ doc
86
+ end
87
+
88
+ def apply_scope(doc)
89
+ criteria.each { |key, value| doc[key] = value }
90
+ doc
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,138 @@
1
+ # encoding: UTF-8
2
+ require 'forwardable'
3
+ module MarkMapper
4
+ module Plugins
5
+ module Associations
6
+ class Proxy
7
+ extend Forwardable
8
+
9
+ alias :proxy_respond_to? :respond_to?
10
+ alias :proxy_extend :extend
11
+
12
+ instance_methods.each { |m| undef_method m unless m =~ /(^__|^nil\?$|^send$|proxy_|^respond_to_missing\?$|^object_id$)/ }
13
+
14
+ attr_reader :proxy_owner, :association, :target
15
+
16
+ alias :proxy_target :target
17
+ alias :proxy_association :association
18
+
19
+ def_delegators :proxy_association, :klass, :options
20
+ def_delegator :klass, :collection
21
+
22
+ def initialize(owner, association)
23
+ @proxy_owner, @association, @loaded = owner, association, false
24
+ Array(association.options[:extend]).each { |ext| proxy_extend(ext) }
25
+ reset
26
+ end
27
+
28
+ # Active support in rails 3 beta 4 can override to_json after this is loaded,
29
+ # at least when run in markmapper tests. The implementation was changed in master
30
+ # some time after this, so not sure whether this is still a problem.
31
+ #
32
+ # In rails 2, this isn't a problem however it also solves an issue where
33
+ # to_json isn't forwarded because it supports to_json itself
34
+ def to_json(*options)
35
+ load_target
36
+ target.to_json(*options)
37
+ end
38
+
39
+ # see comments to to_json
40
+ def as_json(*options)
41
+ load_target
42
+ target.as_json(*options)
43
+ end
44
+
45
+ def inspect
46
+ load_target
47
+ target.inspect
48
+ end
49
+
50
+ def loaded?
51
+ @loaded
52
+ end
53
+
54
+ def loaded
55
+ @loaded = true
56
+ end
57
+
58
+ def nil?
59
+ load_target
60
+ target.nil?
61
+ end
62
+
63
+ def blank?
64
+ load_target
65
+ target.blank?
66
+ end
67
+
68
+ def present?
69
+ load_target
70
+ target.present?
71
+ end
72
+
73
+ def reload
74
+ reset
75
+ load_target
76
+ self unless target.nil?
77
+ end
78
+
79
+ # :nocov:
80
+ def replace(v)
81
+ raise NotImplementedError
82
+ end
83
+ # :nocov:
84
+
85
+ def reset
86
+ @loaded = false
87
+ @target = nil
88
+ end
89
+
90
+ def respond_to?(*args)
91
+ proxy_respond_to?(*args) || (load_target && target.respond_to?(*args))
92
+ end
93
+
94
+ def send(method, *args, &block)
95
+ if proxy_respond_to?(method, true)
96
+ super
97
+ else
98
+ load_target
99
+ target.send(method, *args, &block)
100
+ end
101
+ end
102
+
103
+ protected
104
+ def method_missing(method, *args, &block)
105
+ if load_target
106
+ target.send(method, *args, &block)
107
+ end
108
+ end
109
+
110
+ def load_target
111
+ unless loaded?
112
+ if @target.is_a?(Array) && @target.any?
113
+ @target = find_target + @target.find_all { |record| !record.persisted? }
114
+ else
115
+ @target = find_target
116
+ end
117
+ loaded
118
+ end
119
+ @target
120
+ rescue MarkMapper::DocumentNotFound
121
+ reset
122
+ end
123
+
124
+ # :nocov:
125
+ def find_target
126
+ raise NotImplementedError
127
+ end
128
+ # :nocov:
129
+
130
+ def flatten_deeper(array)
131
+ array.collect do |element|
132
+ (element.respond_to?(:flatten) && !element.is_a?(Hash)) ? element.flatten : element
133
+ end.flatten
134
+ end
135
+ end
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,46 @@
1
+ # encoding: UTF-8
2
+ module MarkMapper
3
+ module Plugins
4
+ module Associations
5
+ class SingleAssociation < Base
6
+ def setup(model)
7
+ @model = model
8
+ model.associations_module.module_eval <<-end_eval
9
+ def #{name}
10
+ proxy = get_proxy(associations[#{name.inspect}])
11
+ proxy.nil? ? nil : proxy
12
+ end
13
+
14
+ def #{name}=(value)
15
+ association = associations[#{name.inspect}]
16
+ proxy = get_proxy(association)
17
+
18
+ if proxy.nil? || proxy.target != value
19
+ proxy = build_proxy(association)
20
+ end
21
+
22
+ proxy.replace(value)
23
+ value
24
+ end
25
+
26
+ def #{name}?
27
+ get_proxy(associations[#{name.inspect}]).present?
28
+ end
29
+
30
+ def build_#{name}(attrs={}, &block)
31
+ get_proxy(associations[#{name.inspect}]).build(attrs, &block)
32
+ end
33
+
34
+ def create_#{name}(attrs={}, &block)
35
+ get_proxy(associations[#{name.inspect}]).create(attrs, &block)
36
+ end
37
+
38
+ def create_#{name}!(attrs={}, &block)
39
+ get_proxy(associations[#{name.inspect}]).create!(attrs, &block)
40
+ end
41
+ end_eval
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,21 @@
1
+ # encoding: UTF-8
2
+ module MarkMapper
3
+ module Plugins
4
+ module Caching
5
+ extend ActiveSupport::Concern
6
+
7
+ def cache_key(*suffixes)
8
+ cache_key = case
9
+ when !persisted?
10
+ "#{self.class.name}/new"
11
+ when timestamp = self[:updated_at]
12
+ "#{self.class.name}/#{id}-#{timestamp.to_s(:number)}"
13
+ else
14
+ "#{self.class.name}/#{id}"
15
+ end
16
+ cache_key += "/#{suffixes.join('/')}" unless suffixes.empty?
17
+ cache_key
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,42 @@
1
+ # encoding: UTF-8
2
+ module MarkMapper
3
+ module Plugins
4
+ module Callbacks
5
+ extend ActiveSupport::Concern
6
+
7
+ def initialize(*)
8
+ run_callbacks(:initialize) { super }
9
+ end
10
+
11
+ def initialize_from_database(*)
12
+ run_callbacks(:initialize) do
13
+ run_callbacks(:find) do
14
+ super
15
+ end
16
+ end
17
+ end
18
+
19
+ def destroy
20
+ run_callbacks(:destroy) { super }
21
+ end
22
+
23
+ def touch(*)
24
+ run_callbacks(:touch) { super }
25
+ end
26
+
27
+ private
28
+
29
+ def create_or_update(*)
30
+ run_callbacks(:save) { super }
31
+ end
32
+
33
+ def create(*)
34
+ run_callbacks(:create) { super }
35
+ end
36
+
37
+ def update(*)
38
+ run_callbacks(:update) { super }
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,24 @@
1
+ # encoding: UTF-8
2
+ module MarkMapper
3
+ module Plugins
4
+ module Clone
5
+ extend ActiveSupport::Concern
6
+
7
+ def initialize_copy(other)
8
+ @_new = true
9
+ @_destroyed = false
10
+ remove_instance_variable :@_id if instance_variable_defined?(:@_id)
11
+ init_ivars
12
+
13
+ associations.each do |name, association|
14
+ instance_variable_set(association.ivar, nil)
15
+ end
16
+ self.attributes = other.attributes.clone.except(:_id).inject({}) do |hash, entry|
17
+ key, value = entry
18
+ hash[key] = value.duplicable? ? value.clone : value
19
+ hash
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,97 @@
1
+ module MarkMapper
2
+ module Plugins
3
+ # Counter Caching for MarkMapper::Document
4
+ #
5
+ # Examples:
6
+ #
7
+ # class Post
8
+ # belongs_to :user
9
+ # counter_cache :user
10
+ # end
11
+ #
12
+ # or:
13
+ #
14
+ # class Post
15
+ # belongs_to :user
16
+ # counter_cache :user, :custom_posts_count
17
+ # end
18
+ #
19
+ # Field names follow rails conventions, so counter_cache :user will increment the Integer field `posts_count' on User
20
+ #
21
+ # Alternatively, you can also use the more common ActiveRecord syntax:
22
+ #
23
+ # class Post
24
+ # belongs_to :user, :counter_cache => true
25
+ # end
26
+ #
27
+ # Or with an alternative field name:
28
+ #
29
+ # class Post
30
+ # belongs_to :user, :counter_cache => :custom_posts_count
31
+ # end
32
+ #
33
+ module CounterCache
34
+ class InvalidCounterCacheError < StandardError; end
35
+
36
+ extend ActiveSupport::Concern
37
+
38
+ module ClassMethods
39
+ def counter_cache(association_name, options = {})
40
+ options.symbolize_keys!
41
+
42
+ field = options[:field] ?
43
+ options[:field] :
44
+ "#{self.collection_name.gsub(/.*\./, '')}_count"
45
+
46
+ association = associations[association_name]
47
+
48
+ if !association
49
+ raise InvalidCounterCacheError, "You must define an association with name `#{association_name}' on model #{self}"
50
+ end
51
+
52
+ # make a define-time check to make sure the counter cache field is defined.
53
+ # note: this can only be done in non-polymorphic classes
54
+ # (since we may not know the class on the other side of the association)
55
+ if !association.polymorphic?
56
+ association_class = association.klass
57
+ key_names = association_class.keys.keys
58
+
59
+ if !key_names.include?(field.to_s)
60
+ _raise_when_missing_counter_cache_key(association_class, field)
61
+ end
62
+ end
63
+
64
+ after_create do
65
+ if obj = self.send(association_name)
66
+ if !obj.respond_to?(field)
67
+ self.class._raise_when_missing_counter_cache_key(obj.class, field)
68
+ end
69
+
70
+ obj.increment(field => 1)
71
+ obj.write_attribute(field, obj.read_attribute(field) + 1)
72
+ end
73
+
74
+ true
75
+ end
76
+
77
+ after_destroy do
78
+ if obj = self.send(association_name)
79
+ if !obj.respond_to?(field)
80
+ self.class._raise_when_missing_counter_cache_key(obj.class, field)
81
+ end
82
+
83
+ obj.decrement(field => 1)
84
+ obj.write_attribute(field, obj.read_attribute(field) - 1)
85
+ end
86
+
87
+ true
88
+ end
89
+ end
90
+
91
+ def _raise_when_missing_counter_cache_key(klass, field)
92
+ raise InvalidCounterCacheError, "Missing `key #{field.to_sym.inspect}, Integer, :default => 0' on model #{klass}"
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end