ant-mapper 0.0.2

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 (52) hide show
  1. data/CHANGE +6 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README +25 -0
  4. data/ant.gemspec +48 -0
  5. data/data.tch +0 -0
  6. data/examples/account.rb +71 -0
  7. data/examples/data.tch +0 -0
  8. data/examples/light_cloud.yml +18 -0
  9. data/examples/user.rb +84 -0
  10. data/init.rb +4 -0
  11. data/lib/ant.rb +7 -0
  12. data/lib/ant_mapper.rb +10 -0
  13. data/lib/ant_mapper/adapters/light_cloud.rb +59 -0
  14. data/lib/ant_mapper/adapters/tokyo_cabinet.rb +42 -0
  15. data/lib/ant_mapper/adapters/tokyo_tyrant.rb +14 -0
  16. data/lib/ant_mapper/base.rb +367 -0
  17. data/lib/ant_mapper/callbacks.rb +180 -0
  18. data/lib/ant_mapper/observer.rb +180 -0
  19. data/lib/ant_mapper/validations.rb +687 -0
  20. data/lib/ant_support.rb +4 -0
  21. data/lib/ant_support/callbacks.rb +303 -0
  22. data/lib/ant_support/core_ext.rb +4 -0
  23. data/lib/ant_support/core_ext/array.rb +5 -0
  24. data/lib/ant_support/core_ext/array/extract_options.rb +20 -0
  25. data/lib/ant_support/core_ext/blank.rb +58 -0
  26. data/lib/ant_support/core_ext/class.rb +3 -0
  27. data/lib/ant_support/core_ext/class/attribute_accessors.rb +54 -0
  28. data/lib/ant_support/core_ext/class/inheritable_attributes.rb +140 -0
  29. data/lib/ant_support/core_ext/class/removal.rb +50 -0
  30. data/lib/ant_support/core_ext/duplicable.rb +43 -0
  31. data/lib/ant_support/core_ext/enumerable.rb +72 -0
  32. data/lib/ant_support/core_ext/hash.rb +6 -0
  33. data/lib/ant_support/core_ext/hash/keys.rb +52 -0
  34. data/lib/ant_support/core_ext/module.rb +16 -0
  35. data/lib/ant_support/core_ext/module/aliasing.rb +74 -0
  36. data/lib/ant_support/core_ext/module/attr_accessor_with_default.rb +31 -0
  37. data/lib/ant_support/core_ext/module/attribute_accessors.rb +58 -0
  38. data/lib/ant_support/core_ext/object.rb +1 -0
  39. data/lib/ant_support/core_ext/object/extending.rb +80 -0
  40. data/lib/ant_support/core_ext/string.rb +7 -0
  41. data/lib/ant_support/core_ext/string/inflections.rb +51 -0
  42. data/spec/case/callbacks_observers_test.rb +38 -0
  43. data/spec/case/callbacks_test.rb +417 -0
  44. data/spec/case/create_object_test.rb +56 -0
  45. data/spec/case/set_class_name_test.rb +17 -0
  46. data/spec/case/validations_test.rb +1482 -0
  47. data/spec/helper.rb +15 -0
  48. data/spec/light_cloud.yml +18 -0
  49. data/spec/model/account.rb +3 -0
  50. data/spec/model/topic.rb +28 -0
  51. data/spec/model/user.rb +4 -0
  52. metadata +125 -0
@@ -0,0 +1,31 @@
1
+ class Module
2
+ # Declare an attribute accessor with an initial default return value.
3
+ #
4
+ # To give attribute <tt>:age</tt> the initial value <tt>25</tt>:
5
+ #
6
+ # class Person
7
+ # attr_accessor_with_default :age, 25
8
+ # end
9
+ #
10
+ # some_person.age
11
+ # => 25
12
+ # some_person.age = 26
13
+ # some_person.age
14
+ # => 26
15
+ #
16
+ # To give attribute <tt>:element_name</tt> a dynamic default value, evaluated
17
+ # in scope of self:
18
+ #
19
+ # attr_accessor_with_default(:element_name) { name.underscore }
20
+ #
21
+ def attr_accessor_with_default(sym, default = nil, &block)
22
+ raise 'Default value or block required' unless !default.nil? || block
23
+ define_method(sym, block_given? ? block : Proc.new { default })
24
+ module_eval(<<-EVAL, __FILE__, __LINE__)
25
+ def #{sym}=(value)
26
+ class << self; attr_reader :#{sym} end
27
+ @#{sym} = value
28
+ end
29
+ EVAL
30
+ end
31
+ end
@@ -0,0 +1,58 @@
1
+ # Extends the module object with module and instance accessors for class attributes,
2
+ # just like the native attr* accessors for instance attributes.
3
+ #
4
+ # module AppConfiguration
5
+ # mattr_accessor :google_api_key
6
+ # self.google_api_key = "123456789"
7
+ #
8
+ # mattr_accessor :paypal_url
9
+ # self.paypal_url = "www.sandbox.paypal.com"
10
+ # end
11
+ #
12
+ # AppConfiguration.google_api_key = "overriding the api key!"
13
+ class Module
14
+ def mattr_reader(*syms)
15
+ syms.each do |sym|
16
+ next if sym.is_a?(Hash)
17
+ class_eval(<<-EOS, __FILE__, __LINE__)
18
+ unless defined? @@#{sym}
19
+ @@#{sym} = nil
20
+ end
21
+
22
+ def self.#{sym}
23
+ @@#{sym}
24
+ end
25
+
26
+ def #{sym}
27
+ @@#{sym}
28
+ end
29
+ EOS
30
+ end
31
+ end
32
+
33
+ def mattr_writer(*syms)
34
+ options = syms.extract_options!
35
+ syms.each do |sym|
36
+ class_eval(<<-EOS, __FILE__, __LINE__)
37
+ unless defined? @@#{sym}
38
+ @@#{sym} = nil
39
+ end
40
+
41
+ def self.#{sym}=(obj)
42
+ @@#{sym} = obj
43
+ end
44
+
45
+ #{"
46
+ def #{sym}=(obj)
47
+ @@#{sym} = obj
48
+ end
49
+ " unless options[:instance_writer] == false }
50
+ EOS
51
+ end
52
+ end
53
+
54
+ def mattr_accessor(*syms)
55
+ mattr_reader(*syms)
56
+ mattr_writer(*syms)
57
+ end
58
+ end
@@ -0,0 +1 @@
1
+ require 'ant_support/core_ext/object/extending'
@@ -0,0 +1,80 @@
1
+ class Object
2
+ def remove_subclasses_of(*superclasses) #:nodoc:
3
+ Class.remove_class(*subclasses_of(*superclasses))
4
+ end
5
+
6
+ begin
7
+ ObjectSpace.each_object(Class.new) {}
8
+
9
+ # Exclude this class unless it's a subclass of our supers and is defined.
10
+ # We check defined? in case we find a removed class that has yet to be
11
+ # garbage collected. This also fails for anonymous classes -- please
12
+ # submit a patch if you have a workaround.
13
+ def subclasses_of(*superclasses) #:nodoc:
14
+ subclasses = []
15
+
16
+ superclasses.each do |sup|
17
+ ObjectSpace.each_object(class << sup; self; end) do |k|
18
+ if k != sup && (k.name.blank? || eval("defined?(::#{k}) && ::#{k}.object_id == k.object_id"))
19
+ subclasses << k
20
+ end
21
+ end
22
+ end
23
+
24
+ subclasses
25
+ end
26
+ rescue RuntimeError
27
+ # JRuby and any implementations which cannot handle the objectspace traversal
28
+ # above fall back to this implementation
29
+ def subclasses_of(*superclasses) #:nodoc:
30
+ subclasses = []
31
+
32
+ superclasses.each do |sup|
33
+ ObjectSpace.each_object(Class) do |k|
34
+ if superclasses.any? { |superclass| k < superclass } &&
35
+ (k.name.blank? || eval("defined?(::#{k}) && ::#{k}.object_id == k.object_id"))
36
+ subclasses << k
37
+ end
38
+ end
39
+ subclasses.uniq!
40
+ end
41
+ subclasses
42
+ end
43
+ end
44
+
45
+ def extended_by #:nodoc:
46
+ ancestors = class << self; ancestors end
47
+ ancestors.select { |mod| mod.class == Module } - [ Object, Kernel ]
48
+ end
49
+
50
+ def extend_with_included_modules_from(object) #:nodoc:
51
+ object.extended_by.each { |mod| extend mod }
52
+ end
53
+
54
+ unless defined? instance_exec # 1.9
55
+ module InstanceExecMethods #:nodoc:
56
+ end
57
+ include InstanceExecMethods
58
+
59
+ # Evaluate the block with the given arguments within the context of
60
+ # this object, so self is set to the method receiver.
61
+ #
62
+ # From Mauricio's http://eigenclass.org/hiki/bounded+space+instance_exec
63
+ def instance_exec(*args, &block)
64
+ begin
65
+ old_critical, Thread.critical = Thread.critical, true
66
+ n = 0
67
+ n += 1 while respond_to?(method_name = "__instance_exec#{n}")
68
+ InstanceExecMethods.module_eval { define_method(method_name, &block) }
69
+ ensure
70
+ Thread.critical = old_critical
71
+ end
72
+
73
+ begin
74
+ send(method_name, *args)
75
+ ensure
76
+ InstanceExecMethods.module_eval { remove_method(method_name) } rescue nil
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,7 @@
1
+ # encoding: utf-8
2
+
3
+ require 'ant_support/core_ext/string/inflections'
4
+
5
+ class String #:nodoc:
6
+ include AntSupport::CoreExtensions::String::Inflections
7
+ end
@@ -0,0 +1,51 @@
1
+ module AntSupport #:nodoc:
2
+ module CoreExtensions #:nodoc:
3
+ module String #:nodoc:
4
+ module Inflections
5
+
6
+ # Ruby 1.9 introduces an inherit argument for Module#const_get and
7
+ # #const_defined? and changes their default behavior.
8
+ if Module.method(:const_get).arity == 1
9
+ # Tries to find a constant with the name specified in the argument string:
10
+ #
11
+ # "Module".constantize # => Module
12
+ # "Test::Unit".constantize # => Test::Unit
13
+ #
14
+ # The name is assumed to be the one of a top-level constant, no matter whether
15
+ # it starts with "::" or not. No lexical context is taken into account:
16
+ #
17
+ # C = 'outside'
18
+ # module M
19
+ # C = 'inside'
20
+ # C # => 'inside'
21
+ # "C".constantize # => 'outside', same as ::C
22
+ # end
23
+ #
24
+ # NameError is raised when the name is not in CamelCase or the constant is
25
+ # unknown.
26
+ def constantize
27
+ names = self.split('::')
28
+ names.shift if names.empty? || names.first.empty?
29
+
30
+ constant = Object
31
+ names.each do |name|
32
+ constant = constant.const_defined?(name) ? constant.const_get(name) : constant.const_missing(name)
33
+ end
34
+ constant
35
+ end
36
+ else
37
+ def constantize #:nodoc:
38
+ names = self.split('::')
39
+ names.shift if names.empty? || names.first.empty?
40
+
41
+ constant = Object
42
+ names.each do |name|
43
+ constant = constant.const_get(name, false) || constant.const_missing(name)
44
+ end
45
+ constant
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,38 @@
1
+ require "../helper"
2
+
3
+ class Comment < AntMapper::Base
4
+ attr_accessor :callers
5
+
6
+ before_validation :record_callers
7
+
8
+ def after_validation
9
+ record_callers
10
+ end
11
+
12
+ def record_callers
13
+ callers << self.class if callers
14
+ end
15
+ end
16
+
17
+ class CommentObserver < AntMapper::Observer
18
+ attr_accessor :callers
19
+
20
+ def after_validation(model)
21
+ callers << self.class if callers
22
+ end
23
+ end
24
+
25
+ class CallbacksObserversTest < Test::Unit::TestCase
26
+ def test_model_callbacks_fire_before_observers_are_notified
27
+ callers = []
28
+
29
+ comment = Comment.new
30
+ comment.callers = callers
31
+
32
+ CommentObserver.instance.callers = callers
33
+
34
+ comment.valid?
35
+
36
+ assert_equal [Comment, Comment, CommentObserver], callers, "model callbacks did not fire before observers were notified"
37
+ end
38
+ end
@@ -0,0 +1,417 @@
1
+ require "../helper"
2
+
3
+ class User < AntMapper::Base
4
+ set_primary_key :email
5
+ set_attributes :name,:email,:password,:salary
6
+ end
7
+
8
+ class CallbackDeveloper < AntMapper::Base
9
+ self.class_name = 'User'
10
+
11
+ set_primary_key :email
12
+ set_attributes :name,:email,:password,:salary
13
+
14
+ class << self
15
+ def callback_string(callback_method)
16
+ "history << [#{callback_method.to_sym.inspect}, :string]"
17
+ end
18
+
19
+ def callback_proc(callback_method)
20
+ Proc.new { |model| model.history << [callback_method, :proc] }
21
+ end
22
+
23
+ def define_callback_method(callback_method)
24
+ define_method("#{callback_method}_method") do |model|
25
+ model.history << [callback_method, :method]
26
+ end
27
+ end
28
+
29
+ def callback_object(callback_method)
30
+ klass = Class.new
31
+ klass.send(:define_method, callback_method) do |model|
32
+ model.history << [callback_method, :object]
33
+ end
34
+ klass.new
35
+ end
36
+ end
37
+
38
+ AntMapper::Callbacks::CALLBACKS.each do |callback_method|
39
+ callback_method_sym = callback_method.to_sym
40
+ define_callback_method(callback_method_sym)
41
+ send(callback_method, callback_method_sym)
42
+ send(callback_method, callback_string(callback_method_sym))
43
+ send(callback_method, callback_proc(callback_method_sym))
44
+ send(callback_method, callback_object(callback_method_sym))
45
+ send(callback_method) { |model| model.history << [callback_method_sym, :block] }
46
+ end
47
+
48
+ def history
49
+ @history ||= []
50
+ end
51
+
52
+ # after_initialize and after_find are invoked only if instance methods have been defined.
53
+ def after_initialize
54
+
55
+ end
56
+
57
+ def after_find
58
+ end
59
+ end
60
+
61
+ class ParentDeveloper < AntMapper::Base
62
+ self.class_name = 'User'
63
+ set_primary_key :email
64
+ set_attributes :name,:email,:password,:salary
65
+
66
+ attr_accessor :after_save_called
67
+ before_validation {|record| record.after_save_called = true}
68
+ end
69
+
70
+ class ChildDeveloper < ParentDeveloper
71
+ self.class_name = 'User'
72
+ set_primary_key :email
73
+ set_attributes :name,:email,:password,:salary
74
+ end
75
+
76
+ class RecursiveCallbackDeveloper < AntMapper::Base
77
+ self.class_name = 'User'
78
+ set_primary_key :email
79
+ set_attributes :name,:email,:password,:salary
80
+
81
+ before_save :on_before_save
82
+ after_save :on_after_save
83
+
84
+ attr_reader :on_before_save_called, :on_after_save_called
85
+
86
+ def on_before_save
87
+ @on_before_save_called ||= 0
88
+ @on_before_save_called += 1
89
+ save unless @on_before_save_called > 1
90
+ end
91
+
92
+ def on_after_save
93
+ @on_after_save_called ||= 0
94
+ @on_after_save_called += 1
95
+ save unless @on_after_save_called > 1
96
+ end
97
+ end
98
+
99
+ class ImmutableDeveloper < AntMapper::Base
100
+ self.class_name = 'User'
101
+ set_primary_key :email
102
+ set_attributes :name,:email,:password,:salary
103
+
104
+ validates_inclusion_of :salary, :in => 50000..200000
105
+
106
+ before_save :cancel
107
+ before_destroy :cancel
108
+
109
+ def cancelled?
110
+ @cancelled == true
111
+ end
112
+
113
+ private
114
+ def cancel
115
+ @cancelled = true
116
+ false
117
+ end
118
+ end
119
+
120
+ class ImmutableMethodDeveloper < AntMapper::Base
121
+ self.class_name = 'User'
122
+ set_primary_key :email
123
+ set_attributes :name,:email,:password,:salary
124
+
125
+ validates_inclusion_of :salary, :in => 50000..200000
126
+
127
+ def cancelled?
128
+ @cancelled == true
129
+ end
130
+
131
+ def before_save
132
+ @cancelled = true
133
+ false
134
+ end
135
+
136
+ def before_destroy
137
+ @cancelled = true
138
+ false
139
+ end
140
+ end
141
+
142
+ class CallbackCancellationDeveloper < AntMapper::Base
143
+ self.class_name = 'User'
144
+ set_primary_key :email
145
+ set_attributes :name,:email,:password,:salary
146
+
147
+ def before_create
148
+ false
149
+ end
150
+ end
151
+
152
+ class CallbacksTest < Test::Unit::TestCase
153
+ def setup
154
+ @attributes={:name=>'aaron',:email=>'aaron@nonobo.com',:password=>'123456',:salary=>100000}
155
+ end
156
+
157
+ def test_initialize
158
+ david = CallbackDeveloper.new
159
+ assert_equal [
160
+ [ :after_initialize, :string ],
161
+ [ :after_initialize, :proc ],
162
+ [ :after_initialize, :object ],
163
+ [ :after_initialize, :block ],
164
+ ], david.history
165
+ end
166
+
167
+ def test_find
168
+ User.create(@attributes)
169
+ david = CallbackDeveloper.find(@attributes[:email])
170
+ assert_equal [
171
+ [ :after_initialize, :string ],
172
+ [ :after_initialize, :proc ],
173
+ [ :after_initialize, :object ],
174
+ [ :after_initialize, :block ],
175
+ ], david.history
176
+ david.destroy
177
+ end
178
+
179
+ def test_new_valid?
180
+ david = CallbackDeveloper.new
181
+ david.valid?
182
+ assert_equal [
183
+ [ :after_initialize, :string ],
184
+ [ :after_initialize, :proc ],
185
+ [ :after_initialize, :object ],
186
+ [ :after_initialize, :block ],
187
+ [ :before_validation, :string ],
188
+ [ :before_validation, :proc ],
189
+ [ :before_validation, :object ],
190
+ [ :before_validation, :block ],
191
+ [ :before_validation_on_create, :string ],
192
+ [ :before_validation_on_create, :proc ],
193
+ [ :before_validation_on_create, :object ],
194
+ [ :before_validation_on_create, :block ],
195
+ [ :after_validation, :string ],
196
+ [ :after_validation, :proc ],
197
+ [ :after_validation, :object ],
198
+ [ :after_validation, :block ],
199
+ [ :after_validation_on_create, :string ],
200
+ [ :after_validation_on_create, :proc ],
201
+ [ :after_validation_on_create, :object ],
202
+ [ :after_validation_on_create, :block ]
203
+ ], david.history
204
+ end
205
+
206
+ def test_existing_valid?
207
+ User.create(@attributes)
208
+ david = CallbackDeveloper.find(@attributes[:email])
209
+ david.valid?
210
+ assert_equal [
211
+ [ :after_initialize, :string ],
212
+ [ :after_initialize, :proc ],
213
+ [ :after_initialize, :object ],
214
+ [ :after_initialize, :block ],
215
+ [ :before_validation, :string ],
216
+ [ :before_validation, :proc ],
217
+ [ :before_validation, :object ],
218
+ [ :before_validation, :block ],
219
+ [ :before_validation_on_update, :string ],
220
+ [ :before_validation_on_update, :proc ],
221
+ [ :before_validation_on_update, :object ],
222
+ [ :before_validation_on_update, :block ],
223
+ [ :after_validation, :string ],
224
+ [ :after_validation, :proc ],
225
+ [ :after_validation, :object ],
226
+ [ :after_validation, :block ],
227
+ [ :after_validation_on_update, :string ],
228
+ [ :after_validation_on_update, :proc ],
229
+ [ :after_validation_on_update, :object ],
230
+ [ :after_validation_on_update, :block ]
231
+ ], david.history
232
+ david.destroy
233
+ end
234
+
235
+ def test_create
236
+ david = CallbackDeveloper.create(@attributes)
237
+ assert_equal [
238
+ [ :after_initialize, :string ],
239
+ [ :after_initialize, :proc ],
240
+ [ :after_initialize, :object ],
241
+ [ :after_initialize, :block ],
242
+ [ :before_validation, :string ],
243
+ [ :before_validation, :proc ],
244
+ [ :before_validation, :object ],
245
+ [ :before_validation, :block ],
246
+ [ :before_validation_on_create, :string ],
247
+ [ :before_validation_on_create, :proc ],
248
+ [ :before_validation_on_create, :object ],
249
+ [ :before_validation_on_create, :block ],
250
+ [ :after_validation, :string ],
251
+ [ :after_validation, :proc ],
252
+ [ :after_validation, :object ],
253
+ [ :after_validation, :block ],
254
+ [ :after_validation_on_create, :string ],
255
+ [ :after_validation_on_create, :proc ],
256
+ [ :after_validation_on_create, :object ],
257
+ [ :after_validation_on_create, :block ],
258
+ [ :before_save, :string ],
259
+ [ :before_save, :proc ],
260
+ [ :before_save, :object ],
261
+ [ :before_save, :block ],
262
+ [ :before_create, :string ],
263
+ [ :before_create, :proc ],
264
+ [ :before_create, :object ],
265
+ [ :before_create, :block ],
266
+ [ :after_create, :string ],
267
+ [ :after_create, :proc ],
268
+ [ :after_create, :object ],
269
+ [ :after_create, :block ],
270
+ [ :after_save, :string ],
271
+ [ :after_save, :proc ],
272
+ [ :after_save, :object ],
273
+ [ :after_save, :block ]
274
+ ], david.history
275
+ david.destroy
276
+ end
277
+
278
+ def test_save
279
+ User.create(@attributes)
280
+ david = CallbackDeveloper.find(@attributes[:email])
281
+ david.save
282
+ assert_equal [
283
+ [ :after_initialize, :string ],
284
+ [ :after_initialize, :proc ],
285
+ [ :after_initialize, :object ],
286
+ [ :after_initialize, :block ],
287
+ [ :before_validation, :string ],
288
+ [ :before_validation, :proc ],
289
+ [ :before_validation, :object ],
290
+ [ :before_validation, :block ],
291
+ [ :before_validation_on_update, :string ],
292
+ [ :before_validation_on_update, :proc ],
293
+ [ :before_validation_on_update, :object ],
294
+ [ :before_validation_on_update, :block ],
295
+ [ :after_validation, :string ],
296
+ [ :after_validation, :proc ],
297
+ [ :after_validation, :object ],
298
+ [ :after_validation, :block ],
299
+ [ :after_validation_on_update, :string ],
300
+ [ :after_validation_on_update, :proc ],
301
+ [ :after_validation_on_update, :object ],
302
+ [ :after_validation_on_update, :block ],
303
+ [ :before_save, :string ],
304
+ [ :before_save, :proc ],
305
+ [ :before_save, :object ],
306
+ [ :before_save, :block ],
307
+ [ :before_update, :string ],
308
+ [ :before_update, :proc ],
309
+ [ :before_update, :object ],
310
+ [ :before_update, :block ],
311
+ [ :after_update, :string ],
312
+ [ :after_update, :proc ],
313
+ [ :after_update, :object ],
314
+ [ :after_update, :block ],
315
+ [ :after_save, :string ],
316
+ [ :after_save, :proc ],
317
+ [ :after_save, :object ],
318
+ [ :after_save, :block ]
319
+ ], david.history
320
+ david.destroy
321
+ end
322
+
323
+ def test_destroy
324
+ User.create(@attributes)
325
+ david = CallbackDeveloper.find(@attributes[:email])
326
+ david.destroy
327
+ assert_equal [
328
+ [ :after_initialize, :string ],
329
+ [ :after_initialize, :proc ],
330
+ [ :after_initialize, :object ],
331
+ [ :after_initialize, :block ],
332
+ [ :before_destroy, :string ],
333
+ [ :before_destroy, :proc ],
334
+ [ :before_destroy, :object ],
335
+ [ :before_destroy, :block ],
336
+ [ :after_destroy, :string ],
337
+ [ :after_destroy, :proc ],
338
+ [ :after_destroy, :object ],
339
+ [ :after_destroy, :block ]
340
+ ], david.history
341
+ end
342
+
343
+ def test_delete
344
+ User.create(@attributes)
345
+ david = CallbackDeveloper.find(@attributes[:email])
346
+ CallbackDeveloper.delete(david.object_key)
347
+ assert_equal [
348
+ [ :after_initialize, :string ],
349
+ [ :after_initialize, :proc ],
350
+ [ :after_initialize, :object ],
351
+ [ :after_initialize, :block ],
352
+ ], david.history
353
+ end
354
+
355
+ def test_before_save_returning_false
356
+ User.create(@attributes)
357
+ david = ImmutableDeveloper.find(@attributes[:email])
358
+
359
+ assert david.valid?
360
+ assert !david.save
361
+ assert_raises(AntMapper::ObjectNotSaved) { david.save! }
362
+ david = ImmutableDeveloper.find(@attributes[:email])
363
+ david.salary = 10_000_000
364
+ assert !david.valid?
365
+ assert !david.save
366
+ assert_raises(AntMapper::ObjectInvalid) { david.save! }
367
+ david.destroy
368
+ end
369
+
370
+ def test_before_create_returning_false
371
+ someone = CallbackCancellationDeveloper.new
372
+ assert someone.valid?
373
+ assert !someone.save
374
+ end
375
+
376
+ def test_before_destroy_returning_false
377
+ User.create(@attributes)
378
+ david = ImmutableDeveloper.find(@attributes[:email])
379
+
380
+ assert !david.destroy
381
+ assert_not_nil ImmutableDeveloper.find(@attributes[:email])
382
+ User.find(@attributes[:email]).destroy
383
+ end
384
+
385
+ def test_zzz_callback_returning_false # must be run last since we modify CallbackDeveloper
386
+ User.create(@attributes)
387
+ david = CallbackDeveloper.find(@attributes[:email])
388
+ CallbackDeveloper.before_validation proc { |model| model.history << [:before_validation, :returning_false]; return false }
389
+ CallbackDeveloper.before_validation proc { |model| model.history << [:before_validation, :should_never_get_here] }
390
+ david.save
391
+ assert_equal [
392
+ [ :after_initialize, :string ],
393
+ [ :after_initialize, :proc ],
394
+ [ :after_initialize, :object ],
395
+ [ :after_initialize, :block ],
396
+ [ :before_validation, :string ],
397
+ [ :before_validation, :proc ],
398
+ [ :before_validation, :object ],
399
+ [ :before_validation, :block ],
400
+ [ :before_validation, :returning_false ]
401
+ ], david.history
402
+ david.destroy
403
+ end
404
+
405
+ def test_inheritence_of_callbacks
406
+ parent = ParentDeveloper.new
407
+ assert !parent.after_save_called
408
+ parent.save
409
+ assert parent.after_save_called
410
+
411
+ child = ChildDeveloper.new
412
+ assert !child.after_save_called
413
+ child.save
414
+ assert child.after_save_called
415
+ end
416
+
417
+ end