state_machine 1.0.1 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (120) hide show
  1. data/.gitignore +11 -0
  2. data/.travis.yml +16 -0
  3. data/.yardopts +5 -0
  4. data/Appraisals +260 -0
  5. data/CHANGELOG.rdoc +15 -0
  6. data/Gemfile +3 -0
  7. data/README.rdoc +156 -29
  8. data/Rakefile +31 -57
  9. data/gemfiles/active_model-3.0.0.gemfile +7 -0
  10. data/gemfiles/active_model-3.0.0.gemfile.lock +32 -0
  11. data/gemfiles/active_model-3.0.5.gemfile +7 -0
  12. data/gemfiles/active_model-3.0.5.gemfile.lock +32 -0
  13. data/gemfiles/active_record-2.0.0.gemfile +8 -0
  14. data/gemfiles/active_record-2.0.0.gemfile.lock +30 -0
  15. data/gemfiles/active_record-2.0.5.gemfile +8 -0
  16. data/gemfiles/active_record-2.0.5.gemfile.lock +30 -0
  17. data/gemfiles/active_record-2.1.0.gemfile +8 -0
  18. data/gemfiles/active_record-2.1.0.gemfile.lock +30 -0
  19. data/gemfiles/active_record-2.1.2.gemfile +8 -0
  20. data/gemfiles/active_record-2.1.2.gemfile.lock +30 -0
  21. data/gemfiles/active_record-2.2.3.gemfile +8 -0
  22. data/gemfiles/active_record-2.2.3.gemfile.lock +30 -0
  23. data/gemfiles/active_record-2.3.12.gemfile +8 -0
  24. data/gemfiles/active_record-2.3.12.gemfile.lock +30 -0
  25. data/gemfiles/active_record-3.0.0.gemfile +8 -0
  26. data/gemfiles/active_record-3.0.0.gemfile.lock +44 -0
  27. data/gemfiles/active_record-3.0.5.gemfile +8 -0
  28. data/gemfiles/active_record-3.0.5.gemfile.lock +43 -0
  29. data/gemfiles/data_mapper-0.10.2.gemfile +12 -0
  30. data/gemfiles/data_mapper-0.10.2.gemfile.lock +45 -0
  31. data/gemfiles/data_mapper-0.9.11.gemfile +12 -0
  32. data/gemfiles/data_mapper-0.9.11.gemfile.lock +47 -0
  33. data/gemfiles/data_mapper-0.9.4.gemfile +12 -0
  34. data/gemfiles/data_mapper-0.9.4.gemfile.lock +61 -0
  35. data/gemfiles/data_mapper-0.9.7.gemfile +12 -0
  36. data/gemfiles/data_mapper-0.9.7.gemfile.lock +57 -0
  37. data/gemfiles/data_mapper-1.0.0.gemfile +12 -0
  38. data/gemfiles/data_mapper-1.0.0.gemfile.lock +53 -0
  39. data/gemfiles/data_mapper-1.0.1.gemfile +12 -0
  40. data/gemfiles/data_mapper-1.0.1.gemfile.lock +53 -0
  41. data/gemfiles/data_mapper-1.0.2.gemfile +12 -0
  42. data/gemfiles/data_mapper-1.0.2.gemfile.lock +53 -0
  43. data/gemfiles/data_mapper-1.1.0.gemfile +12 -0
  44. data/gemfiles/data_mapper-1.1.0.gemfile.lock +51 -0
  45. data/gemfiles/default.gemfile +7 -0
  46. data/gemfiles/default.gemfile.lock +24 -0
  47. data/gemfiles/mongo_mapper-0.5.5.gemfile +8 -0
  48. data/gemfiles/mongo_mapper-0.5.5.gemfile.lock +33 -0
  49. data/gemfiles/mongo_mapper-0.5.8.gemfile +8 -0
  50. data/gemfiles/mongo_mapper-0.5.8.gemfile.lock +33 -0
  51. data/gemfiles/mongo_mapper-0.6.0.gemfile +8 -0
  52. data/gemfiles/mongo_mapper-0.6.0.gemfile.lock +33 -0
  53. data/gemfiles/mongo_mapper-0.6.10.gemfile +8 -0
  54. data/gemfiles/mongo_mapper-0.6.10.gemfile.lock +33 -0
  55. data/gemfiles/mongo_mapper-0.7.0.gemfile +8 -0
  56. data/gemfiles/mongo_mapper-0.7.0.gemfile.lock +33 -0
  57. data/gemfiles/mongo_mapper-0.7.5.gemfile +8 -0
  58. data/gemfiles/mongo_mapper-0.7.5.gemfile.lock +36 -0
  59. data/gemfiles/mongo_mapper-0.8.0.gemfile +10 -0
  60. data/gemfiles/mongo_mapper-0.8.0.gemfile.lock +40 -0
  61. data/gemfiles/mongo_mapper-0.8.3.gemfile +10 -0
  62. data/gemfiles/mongo_mapper-0.8.3.gemfile.lock +40 -0
  63. data/gemfiles/mongo_mapper-0.8.4.gemfile +8 -0
  64. data/gemfiles/mongo_mapper-0.8.4.gemfile.lock +38 -0
  65. data/gemfiles/mongo_mapper-0.8.6.gemfile +8 -0
  66. data/gemfiles/mongo_mapper-0.8.6.gemfile.lock +38 -0
  67. data/gemfiles/mongo_mapper-0.9.0.gemfile +7 -0
  68. data/gemfiles/mongo_mapper-0.9.0.gemfile.lock +41 -0
  69. data/gemfiles/mongoid-2.0.0.gemfile +7 -0
  70. data/gemfiles/mongoid-2.0.0.gemfile.lock +42 -0
  71. data/gemfiles/mongoid-2.1.4.gemfile +7 -0
  72. data/gemfiles/mongoid-2.1.4.gemfile.lock +40 -0
  73. data/gemfiles/sequel-2.11.0.gemfile +8 -0
  74. data/gemfiles/sequel-2.11.0.gemfile.lock +28 -0
  75. data/gemfiles/sequel-2.12.0.gemfile +8 -0
  76. data/gemfiles/sequel-2.12.0.gemfile.lock +28 -0
  77. data/gemfiles/sequel-2.8.0.gemfile +8 -0
  78. data/gemfiles/sequel-2.8.0.gemfile.lock +28 -0
  79. data/gemfiles/sequel-3.0.0.gemfile +8 -0
  80. data/gemfiles/sequel-3.0.0.gemfile.lock +28 -0
  81. data/gemfiles/sequel-3.13.0.gemfile +8 -0
  82. data/gemfiles/sequel-3.13.0.gemfile.lock +28 -0
  83. data/gemfiles/sequel-3.14.0.gemfile +8 -0
  84. data/gemfiles/sequel-3.14.0.gemfile.lock +28 -0
  85. data/gemfiles/sequel-3.23.0.gemfile +8 -0
  86. data/gemfiles/sequel-3.23.0.gemfile.lock +28 -0
  87. data/gemfiles/sequel-3.24.0.gemfile +8 -0
  88. data/gemfiles/sequel-3.24.0.gemfile.lock +28 -0
  89. data/lib/state_machine/event.rb +13 -90
  90. data/lib/state_machine/helper_module.rb +17 -0
  91. data/lib/state_machine/integrations/active_model.rb +35 -0
  92. data/lib/state_machine/integrations/active_record.rb +41 -2
  93. data/lib/state_machine/integrations/data_mapper.rb +17 -2
  94. data/lib/state_machine/integrations/mongo_mapper.rb +34 -7
  95. data/lib/state_machine/integrations/mongoid.rb +34 -26
  96. data/lib/state_machine/integrations/mongoid/versions.rb +29 -3
  97. data/lib/state_machine/integrations/sequel.rb +22 -72
  98. data/lib/state_machine/integrations/sequel/versions.rb +87 -6
  99. data/lib/state_machine/machine.rb +279 -19
  100. data/lib/state_machine/state.rb +2 -2
  101. data/lib/state_machine/state_context.rb +133 -0
  102. data/lib/state_machine/version.rb +3 -0
  103. data/state_machine.gemspec +22 -0
  104. data/test/test_helper.rb +1 -3
  105. data/test/unit/branch_test.rb +1 -3
  106. data/test/unit/event_collection_test.rb +3 -3
  107. data/test/unit/event_test.rb +1 -3
  108. data/test/unit/helper_module_test.rb +17 -0
  109. data/test/unit/integrations/active_model_test.rb +0 -4
  110. data/test/unit/integrations/active_record_test.rb +50 -9
  111. data/test/unit/integrations/data_mapper_test.rb +267 -253
  112. data/test/unit/integrations/mongo_mapper_test.rb +47 -15
  113. data/test/unit/integrations/mongoid_test.rb +50 -8
  114. data/test/unit/integrations/sequel_test.rb +10 -6
  115. data/test/unit/machine_test.rb +206 -25
  116. data/test/unit/state_context_test.rb +421 -0
  117. data/test/unit/state_test.rb +20 -3
  118. metadata +303 -128
  119. data/lib/state_machine/condition_proxy.rb +0 -94
  120. data/test/unit/condition_proxy_test.rb +0 -328
@@ -137,7 +137,46 @@ module StateMachine
137
137
  # end
138
138
  #
139
139
  # If using the +save+ action for the machine, this option will be ignored as
140
- # the transaction will be created by ActiveRecord within +save+.
140
+ # the transaction will be created by ActiveRecord within +save+. To avoid
141
+ # this, use a different action like so:
142
+ #
143
+ # class Vehicle < ActiveRecord::Base
144
+ # state_machine :initial => :parked, :use_transactions => false, :action => :save_state do
145
+ # ...
146
+ # end
147
+ #
148
+ # alias_method :save_state, :save
149
+ # end
150
+ #
151
+ # == Validations
152
+ #
153
+ # As mentioned in StateMachine::Machine#state, you can define behaviors,
154
+ # like validations, that only execute for certain states. One *important*
155
+ # caveat here is that, due to a constraint in ActiveRecord's validation
156
+ # framework, custom validators will not work as expected when defined to run
157
+ # in multiple states. For example:
158
+ #
159
+ # class Vehicle < ActiveRecord::Base
160
+ # state_machine do
161
+ # ...
162
+ # state :first_gear, :second_gear do
163
+ # validate :speed_is_legal
164
+ # end
165
+ # end
166
+ # end
167
+ #
168
+ # In this case, the <tt>:speed_is_legal</tt> validation will only get run
169
+ # for the <tt>:second_gear</tt> state. To avoid this, you can define your
170
+ # custom validation like so:
171
+ #
172
+ # class Vehicle < ActiveRecord::Base
173
+ # state_machine do
174
+ # ...
175
+ # state :first_gear, :second_gear do
176
+ # validate {|vehicle| vehicle.speed_is_legal}
177
+ # end
178
+ # end
179
+ # end
141
180
  #
142
181
  # == Validation errors
143
182
  #
@@ -417,7 +456,7 @@ module StateMachine
417
456
  # breaks both ancestor lookups and defined?(super). Need to special-case
418
457
  # the existence of query attribute methods.
419
458
  def owner_class_ancestor_has_method?(scope, method)
420
- scope == :instance && method == "#{name}?" || super
459
+ scope == :instance && method == "#{attribute}?" ? owner_class : super
421
460
  end
422
461
  end
423
462
  end
@@ -137,7 +137,10 @@ module StateMachine
137
137
  #
138
138
  # To turn on transactions:
139
139
  #
140
- # class Vehicle < ActiveRecord::Base
140
+ # class Vehicle
141
+ # include DataMapper::Resource
142
+ # ...
143
+ #
141
144
  # state_machine :initial => :parked, :use_transactions => true do
142
145
  # ...
143
146
  # end
@@ -145,7 +148,18 @@ module StateMachine
145
148
  #
146
149
  # If using the +save+ action for the machine, this option will be ignored as
147
150
  # the transaction behavior will depend on the +save+ implementation within
148
- # DataMapper.
151
+ # DataMapper. To avoid this, use a different action like so:
152
+ #
153
+ # class Vehicle
154
+ # include DataMapper::Resource
155
+ # ...
156
+ #
157
+ # state_machine :initial => :parked, :use_transactions => false, :action => :save_state do
158
+ # ...
159
+ # end
160
+ #
161
+ # alias_method :save_state, :save
162
+ # end
149
163
  #
150
164
  # == Validation errors
151
165
  #
@@ -294,6 +308,7 @@ module StateMachine
294
308
  protected
295
309
  # Initializes class-level extensions and defaults for this machine
296
310
  def after_initialize
311
+ super
297
312
  load_observer_extensions
298
313
  end
299
314
 
@@ -107,6 +107,40 @@ module StateMachine
107
107
  # end
108
108
  # end
109
109
  #
110
+ # == Validations
111
+ #
112
+ # As mentioned in StateMachine::Machine#state, you can define behaviors,
113
+ # like validations, that only execute for certain states. One *important*
114
+ # caveat here is that, due to a constraint in MongoMapper's validation
115
+ # framework, custom validators will not work as expected when defined to run
116
+ # in multiple states. For example:
117
+ #
118
+ # class Vehicle
119
+ # include MongoMapper::Document
120
+ #
121
+ # state_machine do
122
+ # ...
123
+ # state :first_gear, :second_gear do
124
+ # validate :speed_is_legal
125
+ # end
126
+ # end
127
+ # end
128
+ #
129
+ # In this case, the <tt>:speed_is_legal</tt> validation will only get run
130
+ # for the <tt>:second_gear</tt> state. To avoid this, you can define your
131
+ # custom validation like so:
132
+ #
133
+ # class Vehicle
134
+ # include MongoMapper::Document
135
+ #
136
+ # state_machine do
137
+ # ...
138
+ # state :first_gear, :second_gear do
139
+ # validate {|vehicle| vehicle.speed_is_legal}
140
+ # end
141
+ # end
142
+ # end
143
+ #
110
144
  # == Validation errors
111
145
  #
112
146
  # If an event fails to successfully fire because there are no matching
@@ -260,13 +294,6 @@ module StateMachine
260
294
  def define_scope(name, scope)
261
295
  lambda {|model, values| model.query.merge(model.query(scope.call(values)))}
262
296
  end
263
-
264
- # ActiveModel's use of method_missing / respond_to for attribute methods
265
- # breaks both ancestor lookups and defined?(super). Need to special-case
266
- # the existence of query attribute methods.
267
- def owner_class_ancestor_has_method?(scope, method)
268
- scope == :instance && method == "#{name}?" || super
269
- end
270
297
  end
271
298
  end
272
299
  end
@@ -105,6 +105,40 @@ module StateMachine
105
105
  # end
106
106
  # end
107
107
  #
108
+ # == Validations
109
+ #
110
+ # As mentioned in StateMachine::Machine#state, you can define behaviors,
111
+ # like validations, that only execute for certain states. One *important*
112
+ # caveat here is that, due to a constraint in Mongoid's validation
113
+ # framework, custom validators will not work as expected when defined to run
114
+ # in multiple states. For example:
115
+ #
116
+ # class Vehicle
117
+ # include Mongoid::Document
118
+ #
119
+ # state_machine do
120
+ # ...
121
+ # state :first_gear, :second_gear do
122
+ # validate :speed_is_legal
123
+ # end
124
+ # end
125
+ # end
126
+ #
127
+ # In this case, the <tt>:speed_is_legal</tt> validation will only get run
128
+ # for the <tt>:second_gear</tt> state. To avoid this, you can define your
129
+ # custom validation like so:
130
+ #
131
+ # class Vehicle
132
+ # include Mongoid::Document
133
+ #
134
+ # state_machine do
135
+ # ...
136
+ # state :first_gear, :second_gear do
137
+ # validate {|vehicle| vehicle.speed_is_legal}
138
+ # end
139
+ # end
140
+ # end
141
+ #
108
142
  # == Validation errors
109
143
  #
110
144
  # If an event fails to successfully fire because there are no matching
@@ -262,26 +296,7 @@ module StateMachine
262
296
  super
263
297
  end
264
298
 
265
- # Forces the change in state to be recognized regardless of whether the
266
- # state value actually changed
267
- def write(object, attribute, value, *args)
268
- result = super
269
-
270
- if (attribute == :state || attribute == :event && value) && !object.send("#{self.attribute}_changed?")
271
- current = read(object, :state)
272
- object.changes[self.attribute.to_s] = [attribute == :event ? current : value, current]
273
- end
274
-
275
- result
276
- end
277
-
278
299
  protected
279
- # Mongoid uses its own implementation of dirty tracking instead of
280
- # ActiveModel's and doesn't support the #{attribute}_will_change! APIs
281
- def supports_dirty_tracking?(object)
282
- false
283
- end
284
-
285
300
  # Only runs validations on the action if using <tt>:save</tt>
286
301
  def runs_validations_on_action?
287
302
  action == :save
@@ -345,13 +360,6 @@ module StateMachine
345
360
  def define_scope(name, scope)
346
361
  lambda {|model, values| model.criteria.where(scope.call(values))}
347
362
  end
348
-
349
- # ActiveModel's use of method_missing / respond_to for attribute methods
350
- # breaks both ancestor lookups and defined?(super). Need to special-case
351
- # the existence of query attribute methods.
352
- def owner_class_ancestor_has_method?(scope, method)
353
- scope == :instance && method == "#{name}?" || super
354
- end
355
363
  end
356
364
  end
357
365
  end
@@ -1,10 +1,10 @@
1
1
  module StateMachine
2
2
  module Integrations #:nodoc:
3
3
  module Mongoid
4
- # Assumes Mongoid 2.1+ uses ActiveModel 3.1+
5
- version '2.0.x' do
4
+ # Assumes Mongoid 2.2+ uses ActiveModel 3.1+
5
+ version '2.0.x - 2.1.x' do
6
6
  def self.active?
7
- ::Mongoid::VERSION >= '2.0.0' && ::Mongoid::VERSION < '2.1.0'
7
+ ::Mongoid::VERSION >= '2.0.0' && ::Mongoid::VERSION < '2.2.0'
8
8
  end
9
9
 
10
10
  def define_action_hook
@@ -13,6 +13,32 @@ module StateMachine
13
13
  super
14
14
  end
15
15
  end
16
+
17
+ version '2.0.x' do
18
+ def self.active?
19
+ ::Mongoid::VERSION >= '2.0.0' && ::Mongoid::VERSION < '2.1.0'
20
+ end
21
+
22
+ # Forces the change in state to be recognized regardless of whether the
23
+ # state value actually changed
24
+ def write(object, attribute, value, *args)
25
+ result = super
26
+
27
+ if (attribute == :state || attribute == :event && value) && !object.send("#{self.attribute}_changed?")
28
+ current = read(object, :state)
29
+ object.changes[self.attribute.to_s] = [attribute == :event ? current : value, current]
30
+ end
31
+
32
+ result
33
+ end
34
+
35
+ protected
36
+ # Mongoid uses its own implementation of dirty tracking instead of
37
+ # ActiveModel's and doesn't support the #{attribute}_will_change! APIs
38
+ def supports_dirty_tracking?(object)
39
+ false
40
+ end
41
+ end
16
42
  end
17
43
  end
18
44
  end
@@ -135,7 +135,16 @@ module StateMachine
135
135
  # end
136
136
  #
137
137
  # If using the +save+ action for the machine, this option will be ignored as
138
- # the transaction will be created by Sequel within +save+.
138
+ # the transaction will be created by Sequel within +save+. To avoid
139
+ # this, use a different action like so:
140
+ #
141
+ # class Vehicle < Sequel::Model
142
+ # state_machine :initial => :parked, :use_transactions => false, :action => :save_state do
143
+ # ...
144
+ # end
145
+ #
146
+ # alias_method :save_state, :save
147
+ # end
139
148
  #
140
149
  # == Validation errors
141
150
  #
@@ -276,22 +285,9 @@ module StateMachine
276
285
  # initial state of the machine *before* any attributes are set on the
277
286
  # object
278
287
  def define_state_initializer
279
- # Hooks in to attribute initialization to set the states *prior* to
280
- # the attributes being set
281
288
  define_helper :instance, <<-end_eval, __FILE__, __LINE__ + 1
282
- # Initializes dynamic states
283
- def initialize(*)
284
- super do |*args|
285
- self.class.state_machines.initialize_states(self, :static => false)
286
- changed_columns.clear
287
- yield(*args) if block_given?
288
- end
289
- end
290
-
291
- # Initializes static states
292
- def set(*)
293
- self.class.state_machines.initialize_states(self, :dynamic => false) if values.empty?
294
- super
289
+ def initialize_set(*)
290
+ self.class.state_machines.initialize_states(self) { super }
295
291
  end
296
292
  end_eval
297
293
  end
@@ -305,70 +301,24 @@ module StateMachine
305
301
  end
306
302
  end
307
303
 
308
- # Adds hooks into validation for automatically firing events. This is
309
- # a bit more complicated than other integrations since Sequel doesn't
310
- # provide an easy way to hook around validation calls
304
+ # Defines validation hooks if the machine's action is to save the model
311
305
  def define_action_helpers
312
306
  super
313
-
314
- if action == :save
315
- define_helper :instance, <<-end_eval, __FILE__, __LINE__ + 1
316
- def valid?(*args)
317
- yielded = false
318
- result = self.class.state_machines.transitions(self, :save, :after => false).perform do
319
- yielded = true
320
- super
321
- end
322
-
323
- if yielded || result
324
- result
325
- else
326
- #{handle_validation_failure}
327
- end
328
- end
329
- end_eval
330
- end
307
+ define_validation_hook if action == :save
331
308
  end
332
309
 
333
- # Uses custom hooks for :save actions in order to preserve failure
334
- # behavior within Sequel. This is a bit more complicated than other
335
- # integrations since Sequel doesn't provide an easy way to hook around
336
- # save calls.
337
- def define_action_hook
338
- if action == :save
339
- define_helper :instance, <<-end_eval, __FILE__, __LINE__ + 1
340
- def #{action_hook}(*)
341
- yielded = false
342
- result = self.class.state_machines.transitions(self, :save).perform do
343
- yielded = true
344
- super
345
- end
346
-
347
- if yielded || result
348
- result
349
- else
350
- #{handle_save_failure}
351
- end
352
- end
353
- end_eval
354
- else
355
- super
356
- end
310
+ # Adds hooks into validation for automatically firing events
311
+ def define_validation_hook
312
+ define_helper :instance, <<-end_eval, __FILE__, __LINE__ + 1
313
+ def around_validation(*)
314
+ self.class.state_machines.transitions(self, :save, :after => false).perform { super }
315
+ end
316
+ end_eval
357
317
  end
358
318
 
359
319
  # Uses internal save hooks if using the :save action
360
320
  def action_hook
361
- action == :save ? :_save : super
362
- end
363
-
364
- # Handles whether validation errors should be raised
365
- def handle_validation_failure
366
- 'raise_on_failure?(args.first || {}) ? raise_hook_failure(:validation) : result'
367
- end
368
-
369
- # Handles how save failures are raised
370
- def handle_save_failure
371
- 'raise_hook_failure(:save)'
321
+ action == :save ? :around_save : super
372
322
  end
373
323
 
374
324
  # Creates a scope for finding records *with* a particular state or
@@ -1,17 +1,70 @@
1
1
  module StateMachine
2
2
  module Integrations #:nodoc:
3
3
  module Sequel
4
- version '2.8.x - 3.13.x' do
4
+ version '2.8.x - 3.23.x' do
5
5
  def self.active?
6
- !defined?(::Sequel::MAJOR) || ::Sequel::MAJOR == 2 || ::Sequel::MAJOR == 3 && ::Sequel::MINOR <= 13
6
+ !defined?(::Sequel::MAJOR) || ::Sequel::MAJOR == 2 || ::Sequel::MAJOR == 3 && ::Sequel::MINOR <= 23
7
7
  end
8
8
 
9
- def handle_validation_failure
10
- 'raise_on_save_failure ? save_failure(:validation) : result'
9
+ def define_state_initializer
10
+ define_helper :instance, <<-end_eval, __FILE__, __LINE__ + 1
11
+ def initialize(*)
12
+ super do |*args|
13
+ self.class.state_machines.initialize_states(self, :static => false)
14
+ changed_columns.clear
15
+ yield(*args) if block_given?
16
+ end
17
+ end
18
+
19
+ def set(*)
20
+ self.class.state_machines.initialize_states(self, :dynamic => false) if values.empty?
21
+ super
22
+ end
23
+ end_eval
11
24
  end
12
25
 
13
- def handle_save_failure
14
- 'save_failure(:save)'
26
+ def define_validation_hook
27
+ define_helper :instance, <<-end_eval, __FILE__, __LINE__ + 1
28
+ def valid?(*args)
29
+ yielded = false
30
+ result = self.class.state_machines.transitions(self, :save, :after => false).perform do
31
+ yielded = true
32
+ super
33
+ end
34
+
35
+ if yielded || result
36
+ result
37
+ else
38
+ #{handle_validation_failure}
39
+ end
40
+ end
41
+ end_eval
42
+ end
43
+
44
+ def define_action_hook
45
+ if action == :save
46
+ define_helper :instance, <<-end_eval, __FILE__, __LINE__ + 1
47
+ def #{action_hook}(*)
48
+ yielded = false
49
+ result = self.class.state_machines.transitions(self, :save).perform do
50
+ yielded = true
51
+ super
52
+ end
53
+
54
+ if yielded || result
55
+ result
56
+ else
57
+ #{handle_save_failure}
58
+ end
59
+ end
60
+ end_eval
61
+ else
62
+ super
63
+ end
64
+ end
65
+
66
+ def action_hook
67
+ action == :save ? :_save : super
15
68
  end
16
69
  end
17
70
 
@@ -31,6 +84,34 @@ module StateMachine
31
84
  dataset.model_classes[nil]
32
85
  end
33
86
  end
87
+
88
+ version '2.8.x - 3.13.x' do
89
+ def self.active?
90
+ !defined?(::Sequel::MAJOR) || ::Sequel::MAJOR == 2 || ::Sequel::MAJOR == 3 && ::Sequel::MINOR <= 13
91
+ end
92
+
93
+ def handle_validation_failure
94
+ 'raise_on_save_failure ? save_failure(:validation) : result'
95
+ end
96
+
97
+ def handle_save_failure
98
+ 'save_failure(:save)'
99
+ end
100
+ end
101
+
102
+ version '3.14.x - 3.23.x' do
103
+ def self.active?
104
+ defined?(::Sequel::MAJOR) && ::Sequel::MAJOR == 3 && ::Sequel::MINOR >= 14 && ::Sequel::MINOR <= 23
105
+ end
106
+
107
+ def handle_validation_failure
108
+ 'raise_on_failure?(args.first || {}) ? raise_hook_failure(:validation) : result'
109
+ end
110
+
111
+ def handle_save_failure
112
+ 'raise_hook_failure(:save)'
113
+ end
114
+ end
34
115
  end
35
116
  end
36
117
  end