jonbell-mongo_mapper 0.8.6

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 (145) hide show
  1. data/LICENSE +20 -0
  2. data/README.rdoc +33 -0
  3. data/UPGRADES +7 -0
  4. data/bin/mmconsole +60 -0
  5. data/examples/attr_accessible.rb +22 -0
  6. data/examples/attr_protected.rb +22 -0
  7. data/examples/cache_key.rb +24 -0
  8. data/examples/custom_types.rb +24 -0
  9. data/examples/identity_map.rb +33 -0
  10. data/examples/identity_map/automatic.rb +8 -0
  11. data/examples/identity_map/middleware.rb +14 -0
  12. data/examples/keys.rb +40 -0
  13. data/examples/modifiers/set.rb +25 -0
  14. data/examples/plugins.rb +41 -0
  15. data/examples/querying.rb +35 -0
  16. data/examples/safe.rb +43 -0
  17. data/examples/scopes.rb +52 -0
  18. data/examples/validating/embedded_docs.rb +29 -0
  19. data/lib/mongo_mapper.rb +79 -0
  20. data/lib/mongo_mapper/connection.rb +83 -0
  21. data/lib/mongo_mapper/document.rb +41 -0
  22. data/lib/mongo_mapper/embedded_document.rb +31 -0
  23. data/lib/mongo_mapper/exceptions.rb +27 -0
  24. data/lib/mongo_mapper/extensions/array.rb +19 -0
  25. data/lib/mongo_mapper/extensions/binary.rb +22 -0
  26. data/lib/mongo_mapper/extensions/boolean.rb +44 -0
  27. data/lib/mongo_mapper/extensions/date.rb +25 -0
  28. data/lib/mongo_mapper/extensions/float.rb +14 -0
  29. data/lib/mongo_mapper/extensions/hash.rb +14 -0
  30. data/lib/mongo_mapper/extensions/integer.rb +19 -0
  31. data/lib/mongo_mapper/extensions/kernel.rb +9 -0
  32. data/lib/mongo_mapper/extensions/nil_class.rb +18 -0
  33. data/lib/mongo_mapper/extensions/object.rb +27 -0
  34. data/lib/mongo_mapper/extensions/object_id.rb +30 -0
  35. data/lib/mongo_mapper/extensions/set.rb +20 -0
  36. data/lib/mongo_mapper/extensions/string.rb +18 -0
  37. data/lib/mongo_mapper/extensions/time.rb +29 -0
  38. data/lib/mongo_mapper/plugins.rb +15 -0
  39. data/lib/mongo_mapper/plugins/accessible.rb +44 -0
  40. data/lib/mongo_mapper/plugins/associations.rb +97 -0
  41. data/lib/mongo_mapper/plugins/associations/base.rb +124 -0
  42. data/lib/mongo_mapper/plugins/associations/belongs_to_polymorphic_proxy.rb +29 -0
  43. data/lib/mongo_mapper/plugins/associations/belongs_to_proxy.rb +24 -0
  44. data/lib/mongo_mapper/plugins/associations/collection.rb +27 -0
  45. data/lib/mongo_mapper/plugins/associations/embedded_collection.rb +40 -0
  46. data/lib/mongo_mapper/plugins/associations/in_array_proxy.rb +127 -0
  47. data/lib/mongo_mapper/plugins/associations/many_documents_as_proxy.rb +28 -0
  48. data/lib/mongo_mapper/plugins/associations/many_documents_proxy.rb +109 -0
  49. data/lib/mongo_mapper/plugins/associations/many_embedded_polymorphic_proxy.rb +32 -0
  50. data/lib/mongo_mapper/plugins/associations/many_embedded_proxy.rb +24 -0
  51. data/lib/mongo_mapper/plugins/associations/many_polymorphic_proxy.rb +14 -0
  52. data/lib/mongo_mapper/plugins/associations/one_embedded_proxy.rb +40 -0
  53. data/lib/mongo_mapper/plugins/associations/one_proxy.rb +68 -0
  54. data/lib/mongo_mapper/plugins/associations/proxy.rb +139 -0
  55. data/lib/mongo_mapper/plugins/caching.rb +21 -0
  56. data/lib/mongo_mapper/plugins/callbacks.rb +241 -0
  57. data/lib/mongo_mapper/plugins/clone.rb +22 -0
  58. data/lib/mongo_mapper/plugins/descendants.rb +17 -0
  59. data/lib/mongo_mapper/plugins/dirty.rb +124 -0
  60. data/lib/mongo_mapper/plugins/document.rb +41 -0
  61. data/lib/mongo_mapper/plugins/dynamic_querying.rb +43 -0
  62. data/lib/mongo_mapper/plugins/dynamic_querying/dynamic_finder.rb +44 -0
  63. data/lib/mongo_mapper/plugins/embedded_document.rb +48 -0
  64. data/lib/mongo_mapper/plugins/equality.rb +17 -0
  65. data/lib/mongo_mapper/plugins/identity_map.rb +128 -0
  66. data/lib/mongo_mapper/plugins/indexes.rb +12 -0
  67. data/lib/mongo_mapper/plugins/inspect.rb +15 -0
  68. data/lib/mongo_mapper/plugins/keys.rb +313 -0
  69. data/lib/mongo_mapper/plugins/keys/key.rb +59 -0
  70. data/lib/mongo_mapper/plugins/logger.rb +18 -0
  71. data/lib/mongo_mapper/plugins/modifiers.rb +112 -0
  72. data/lib/mongo_mapper/plugins/pagination.rb +14 -0
  73. data/lib/mongo_mapper/plugins/persistence.rb +69 -0
  74. data/lib/mongo_mapper/plugins/protected.rb +53 -0
  75. data/lib/mongo_mapper/plugins/querying.rb +176 -0
  76. data/lib/mongo_mapper/plugins/querying/decorator.rb +46 -0
  77. data/lib/mongo_mapper/plugins/querying/plucky_methods.rb +15 -0
  78. data/lib/mongo_mapper/plugins/rails.rb +58 -0
  79. data/lib/mongo_mapper/plugins/safe.rb +28 -0
  80. data/lib/mongo_mapper/plugins/sci.rb +32 -0
  81. data/lib/mongo_mapper/plugins/scopes.rb +21 -0
  82. data/lib/mongo_mapper/plugins/serialization.rb +76 -0
  83. data/lib/mongo_mapper/plugins/timestamps.rb +22 -0
  84. data/lib/mongo_mapper/plugins/userstamps.rb +15 -0
  85. data/lib/mongo_mapper/plugins/validations.rb +50 -0
  86. data/lib/mongo_mapper/support/descendant_appends.rb +45 -0
  87. data/lib/mongo_mapper/version.rb +4 -0
  88. data/rails/init.rb +19 -0
  89. data/test/_NOTE_ON_TESTING +1 -0
  90. data/test/functional/associations/test_belongs_to_polymorphic_proxy.rb +63 -0
  91. data/test/functional/associations/test_belongs_to_proxy.rb +93 -0
  92. data/test/functional/associations/test_in_array_proxy.rb +319 -0
  93. data/test/functional/associations/test_many_documents_as_proxy.rb +229 -0
  94. data/test/functional/associations/test_many_documents_proxy.rb +615 -0
  95. data/test/functional/associations/test_many_embedded_polymorphic_proxy.rb +176 -0
  96. data/test/functional/associations/test_many_embedded_proxy.rb +256 -0
  97. data/test/functional/associations/test_many_polymorphic_proxy.rb +302 -0
  98. data/test/functional/associations/test_one_embedded_proxy.rb +81 -0
  99. data/test/functional/associations/test_one_proxy.rb +181 -0
  100. data/test/functional/test_accessible.rb +168 -0
  101. data/test/functional/test_associations.rb +44 -0
  102. data/test/functional/test_binary.rb +27 -0
  103. data/test/functional/test_caching.rb +76 -0
  104. data/test/functional/test_callbacks.rb +151 -0
  105. data/test/functional/test_dirty.rb +163 -0
  106. data/test/functional/test_document.rb +253 -0
  107. data/test/functional/test_dynamic_querying.rb +75 -0
  108. data/test/functional/test_embedded_document.rb +210 -0
  109. data/test/functional/test_identity_map.rb +514 -0
  110. data/test/functional/test_indexes.rb +42 -0
  111. data/test/functional/test_logger.rb +20 -0
  112. data/test/functional/test_modifiers.rb +416 -0
  113. data/test/functional/test_pagination.rb +91 -0
  114. data/test/functional/test_protected.rb +175 -0
  115. data/test/functional/test_querying.rb +873 -0
  116. data/test/functional/test_safe.rb +76 -0
  117. data/test/functional/test_sci.rb +230 -0
  118. data/test/functional/test_scopes.rb +171 -0
  119. data/test/functional/test_string_id_compatibility.rb +67 -0
  120. data/test/functional/test_timestamps.rb +62 -0
  121. data/test/functional/test_userstamps.rb +27 -0
  122. data/test/functional/test_validations.rb +342 -0
  123. data/test/models.rb +233 -0
  124. data/test/test_active_model_lint.rb +13 -0
  125. data/test/test_helper.rb +100 -0
  126. data/test/unit/associations/test_base.rb +212 -0
  127. data/test/unit/associations/test_proxy.rb +105 -0
  128. data/test/unit/serializers/test_json_serializer.rb +217 -0
  129. data/test/unit/test_clone.rb +69 -0
  130. data/test/unit/test_descendant_appends.rb +71 -0
  131. data/test/unit/test_document.rb +213 -0
  132. data/test/unit/test_dynamic_finder.rb +125 -0
  133. data/test/unit/test_embedded_document.rb +644 -0
  134. data/test/unit/test_extensions.rb +376 -0
  135. data/test/unit/test_key.rb +185 -0
  136. data/test/unit/test_keys.rb +89 -0
  137. data/test/unit/test_mongo_mapper.rb +110 -0
  138. data/test/unit/test_pagination.rb +11 -0
  139. data/test/unit/test_plugins.rb +50 -0
  140. data/test/unit/test_rails.rb +181 -0
  141. data/test/unit/test_rails_compatibility.rb +52 -0
  142. data/test/unit/test_serialization.rb +51 -0
  143. data/test/unit/test_time_zones.rb +39 -0
  144. data/test/unit/test_validations.rb +564 -0
  145. metadata +337 -0
@@ -0,0 +1,14 @@
1
+ # encoding: UTF-8
2
+ module MongoMapper
3
+ module Plugins
4
+ module Associations
5
+ class ManyPolymorphicProxy < ManyDocumentsProxy
6
+ private
7
+ def apply_scope(doc)
8
+ doc[association.type_key_name] = doc.class.name
9
+ super
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,40 @@
1
+ # encoding: UTF-8
2
+ module MongoMapper
3
+ module Plugins
4
+ module Associations
5
+ class OneEmbeddedProxy < Proxy
6
+ def build(attributes={})
7
+ @target = klass.new(attributes)
8
+ assign_references(@target)
9
+ loaded
10
+ @target
11
+ end
12
+
13
+ def replace(doc)
14
+ if doc.respond_to?(:attributes)
15
+ @target = klass.load(doc.attributes)
16
+ else
17
+ @target = klass.load(doc)
18
+ end
19
+ assign_references(@target)
20
+ loaded
21
+ @target
22
+ end
23
+
24
+ protected
25
+
26
+ def find_target
27
+ if @value
28
+ klass.load(@value).tap do |child|
29
+ assign_references(child)
30
+ end
31
+ end
32
+ end
33
+
34
+ def assign_references(doc)
35
+ doc._parent_document = proxy_owner if doc
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,68 @@
1
+ # encoding: UTF-8
2
+ module MongoMapper
3
+ module Plugins
4
+ module Associations
5
+ class OneProxy < Proxy
6
+ def build(attrs={})
7
+ instantiate_target(:new, attrs)
8
+ end
9
+
10
+ def create(attrs={})
11
+ instantiate_target(:create, attrs)
12
+ end
13
+
14
+ def create!(attrs={})
15
+ instantiate_target(:create!, attrs)
16
+ end
17
+
18
+ def replace(doc)
19
+ load_target
20
+
21
+ if !target.nil? && target != doc
22
+ if options[:dependent] && !target.new?
23
+ case options[:dependent]
24
+ when :delete
25
+ target.delete
26
+ when :destroy
27
+ target.destroy
28
+ when :nullify
29
+ target[foreign_key] = nil
30
+ target.save
31
+ end
32
+ end
33
+ end
34
+
35
+ if doc.nil?
36
+ target.update_attributes(foreign_key => nil) unless target.nil?
37
+ else
38
+ proxy_owner.save if proxy_owner.new?
39
+ doc = klass.new(doc) unless doc.is_a?(klass)
40
+ doc[foreign_key] = proxy_owner.id
41
+ doc.save if doc.new?
42
+ loaded
43
+ @target = doc
44
+ end
45
+ end
46
+
47
+ protected
48
+ def find_target
49
+ target_class.first(association.query_options.merge(foreign_key => proxy_owner.id))
50
+ end
51
+
52
+ def instantiate_target(instantiator, attrs={})
53
+ @target = target_class.send(instantiator, attrs.update(foreign_key => proxy_owner.id))
54
+ loaded
55
+ @target
56
+ end
57
+
58
+ def target_class
59
+ @target_class ||= options[:class] || (options[:class_name] || association.name.to_s.camelize).constantize
60
+ end
61
+
62
+ def foreign_key
63
+ options[:foreign_key] || proxy_owner.class.name.foreign_key
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,139 @@
1
+ # encoding: UTF-8
2
+ require 'forwardable'
3
+ module MongoMapper
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_|^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 mongomapper 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
+ def replace(v)
80
+ raise NotImplementedError
81
+ end
82
+
83
+ def reset
84
+ @loaded = false
85
+ @target = nil
86
+ end
87
+
88
+ def respond_to?(*args)
89
+ proxy_respond_to?(*args) || (load_target && target.respond_to?(*args))
90
+ end
91
+
92
+ def send(method, *args)
93
+ if proxy_respond_to?(method)
94
+ super
95
+ else
96
+ load_target
97
+ target.send(method, *args)
98
+ end
99
+ end
100
+
101
+ def ===(other)
102
+ load_target
103
+ other === target
104
+ end
105
+
106
+ protected
107
+ def method_missing(method, *args, &block)
108
+ if load_target
109
+ target.send(method, *args, &block)
110
+ end
111
+ end
112
+
113
+ def load_target
114
+ unless loaded?
115
+ if @target.is_a?(Array) && @target.any?
116
+ @target = find_target + @target.find_all { |record| record.new? }
117
+ else
118
+ @target = find_target
119
+ end
120
+ loaded
121
+ end
122
+ @target
123
+ rescue MongoMapper::DocumentNotFound
124
+ reset
125
+ end
126
+
127
+ def find_target
128
+ raise NotImplementedError
129
+ end
130
+
131
+ def flatten_deeper(array)
132
+ array.collect do |element|
133
+ (element.respond_to?(:flatten) && !element.is_a?(Hash)) ? element.flatten : element
134
+ end.flatten
135
+ end
136
+ end
137
+ end
138
+ end
139
+ end
@@ -0,0 +1,21 @@
1
+ # encoding: UTF-8
2
+ module MongoMapper
3
+ module Plugins
4
+ module Caching
5
+ module InstanceMethods
6
+ def cache_key(*suffixes)
7
+ cache_key = case
8
+ when new?
9
+ "#{self.class.name}/new"
10
+ when timestamp = self[:updated_at]
11
+ "#{self.class.name}/#{id}-#{timestamp.to_s(:number)}"
12
+ else
13
+ "#{self.class.name}/#{id}"
14
+ end
15
+ cache_key += "/#{suffixes.join('/')}" unless suffixes.empty?
16
+ cache_key
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,241 @@
1
+ # encoding: UTF-8
2
+ # Almost all of this callback stuff is pulled directly from ActiveSupport
3
+ # in the interest of support rails 2 and 3 at the same time and is the
4
+ # same copyright as rails.
5
+ module MongoMapper
6
+ module Plugins
7
+ module Callbacks
8
+ def self.configure(model)
9
+ model.class_eval do
10
+ define_callbacks(
11
+ :before_save, :after_save,
12
+ :before_create, :after_create,
13
+ :before_update, :after_update,
14
+ :before_validation, :after_validation,
15
+ :before_validation_on_create, :after_validation_on_create,
16
+ :before_validation_on_update, :after_validation_on_update,
17
+ :before_destroy, :after_destroy,
18
+ :validate_on_create, :validate_on_update,
19
+ :validate
20
+ )
21
+ end
22
+ end
23
+
24
+ module ClassMethods
25
+ def define_callbacks(*callbacks)
26
+ callbacks.each do |callback|
27
+ class_eval <<-"end_eval"
28
+ def self.#{callback}(*methods, &block)
29
+ callbacks = CallbackChain.build(:#{callback}, *methods, &block)
30
+ @#{callback}_callbacks ||= CallbackChain.new
31
+ @#{callback}_callbacks.concat callbacks
32
+ end
33
+
34
+ def self.#{callback}_callback_chain
35
+ @#{callback}_callbacks ||= CallbackChain.new
36
+
37
+ if superclass.respond_to?(:#{callback}_callback_chain)
38
+ CallbackChain.new(
39
+ superclass.#{callback}_callback_chain +
40
+ @#{callback}_callbacks
41
+ )
42
+ else
43
+ @#{callback}_callbacks
44
+ end
45
+ end
46
+ end_eval
47
+ end
48
+ end
49
+ end
50
+
51
+ module InstanceMethods
52
+ def valid?
53
+ action = new? ? 'create' : 'update'
54
+ run_callbacks(:before_validation)
55
+ run_callbacks("before_validation_on_#{action}".to_sym)
56
+ result = super
57
+ run_callbacks("after_validation_on_#{action}".to_sym)
58
+ run_callbacks(:after_validation)
59
+ result
60
+ end
61
+
62
+ # Overriding validatable's valid_for_group? to integrate validation callbacks
63
+ def valid_for_group?(group) #:nodoc:
64
+ errors.clear
65
+ run_before_validations
66
+ run_callbacks(:validate)
67
+ new? ? run_callbacks(:validate_on_create) : run_callbacks(:validate_on_update)
68
+ self.class.validate_children(self, group)
69
+ self.validate_group(group)
70
+ errors.empty?
71
+ end
72
+
73
+ def destroy
74
+ run_callbacks(:before_destroy)
75
+ result = super
76
+ run_callbacks(:after_destroy)
77
+ result
78
+ end
79
+
80
+ def run_callbacks(kind, options={}, &block)
81
+ callback_chain_method = "#{kind}_callback_chain"
82
+ return unless self.class.respond_to?(callback_chain_method)
83
+ self.class.send(callback_chain_method).run(self, options, &block)
84
+ self.embedded_associations.each do |association|
85
+ if association.one?
86
+ self.send(association.name).run_callbacks(kind, options, &block)
87
+ else
88
+ self.send(association.name).each do |document|
89
+ document.run_callbacks(kind, options, &block)
90
+ end
91
+ end
92
+ end
93
+ end
94
+
95
+ private
96
+ def create_or_update(*args)
97
+ run_callbacks(:before_save)
98
+ if result = super
99
+ run_callbacks(:after_save)
100
+ end
101
+ result
102
+ end
103
+
104
+ def create(*args)
105
+ run_callbacks(:before_create)
106
+ result = super
107
+ run_callbacks(:after_create)
108
+ result
109
+ end
110
+
111
+ def update(*args)
112
+ run_callbacks(:before_update)
113
+ result = super
114
+ run_callbacks(:after_update)
115
+ result
116
+ end
117
+ end
118
+
119
+ class CallbackChain < Array
120
+ def self.build(kind, *methods, &block)
121
+ methods, options = extract_options(*methods, &block)
122
+ methods.map! { |method| Callback.new(kind, method, options) }
123
+ new(methods)
124
+ end
125
+
126
+ def run(object, options={}, &terminator)
127
+ enumerator = options[:enumerator] || :each
128
+
129
+ unless block_given?
130
+ send(enumerator) { |callback| callback.call(object) }
131
+ else
132
+ send(enumerator) do |callback|
133
+ result = callback.call(object)
134
+ break result if terminator.call(result, object)
135
+ end
136
+ end
137
+ end
138
+
139
+ # TODO: Decompose into more Array like behavior
140
+ def replace_or_append!(chain)
141
+ if index = index(chain)
142
+ self[index] = chain
143
+ else
144
+ self << chain
145
+ end
146
+ self
147
+ end
148
+
149
+ def find(callback, &block)
150
+ select { |c| c == callback && (!block_given? || yield(c)) }.first
151
+ end
152
+
153
+ def delete(callback)
154
+ super(callback.is_a?(Callback) ? callback : find(callback))
155
+ end
156
+
157
+ private
158
+ def self.extract_options(*methods, &block)
159
+ methods.flatten!
160
+ options = methods.extract_options!
161
+ methods << block if block_given?
162
+ return methods, options
163
+ end
164
+
165
+ def extract_options(*methods, &block)
166
+ self.class.extract_options(*methods, &block)
167
+ end
168
+ end
169
+
170
+ class Callback
171
+ attr_reader :kind, :method, :identifier, :options
172
+
173
+ def initialize(kind, method, options={})
174
+ @kind = kind
175
+ @method = method
176
+ @identifier = options[:identifier]
177
+ @options = options
178
+ end
179
+
180
+ def ==(other)
181
+ case other
182
+ when Callback
183
+ (self.identifier && self.identifier == other.identifier) || self.method == other.method
184
+ else
185
+ (self.identifier && self.identifier == other) || self.method == other
186
+ end
187
+ end
188
+
189
+ def eql?(other)
190
+ self == other
191
+ end
192
+
193
+ def dup
194
+ self.class.new(@kind, @method, @options.dup)
195
+ end
196
+
197
+ def hash
198
+ if @identifier
199
+ @identifier.hash
200
+ else
201
+ @method.hash
202
+ end
203
+ end
204
+
205
+ def call(*args, &block)
206
+ evaluate_method(method, *args, &block) if should_run_callback?(*args)
207
+ rescue LocalJumpError
208
+ raise ArgumentError,
209
+ "Cannot yield from a Proc type filter. The Proc must take two " +
210
+ "arguments and execute #call on the second argument."
211
+ end
212
+
213
+ private
214
+ def evaluate_method(method, *args, &block)
215
+ case method
216
+ when Symbol
217
+ object = args.shift
218
+ object.send(method, *args, &block)
219
+ when String
220
+ eval(method, args.first.instance_eval { binding })
221
+ when Proc, Method
222
+ method.call(*args, &block)
223
+ else
224
+ if method.respond_to?(kind)
225
+ method.send(kind, *args, &block)
226
+ else
227
+ raise ArgumentError,
228
+ "Callbacks must be a symbol denoting the method to call, a string to be evaluated, " +
229
+ "a block to be invoked, or an object responding to the callback method."
230
+ end
231
+ end
232
+ end
233
+
234
+ def should_run_callback?(*args)
235
+ [options[:if]].flatten.compact.all? { |a| evaluate_method(a, *args) } &&
236
+ ![options[:unless]].flatten.compact.any? { |a| evaluate_method(a, *args) }
237
+ end
238
+ end
239
+ end
240
+ end
241
+ end