state_machine 1.1.2 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (240) hide show
  1. data/.gitignore +7 -11
  2. data/.travis.yml +49 -7
  3. data/Appraisals +255 -87
  4. data/CHANGELOG.md +30 -0
  5. data/README.md +142 -21
  6. data/Rakefile +1 -11
  7. data/examples/Gemfile +5 -0
  8. data/examples/Gemfile.lock +14 -0
  9. data/examples/auto_shop.rb +2 -0
  10. data/examples/car.rb +2 -0
  11. data/examples/doc/AutoShop.html +2856 -0
  12. data/examples/doc/AutoShop_state.png +0 -0
  13. data/examples/doc/Car.html +919 -0
  14. data/examples/doc/Car_state.png +0 -0
  15. data/examples/doc/TrafficLight.html +2230 -0
  16. data/examples/doc/TrafficLight_state.png +0 -0
  17. data/examples/doc/Vehicle.html +7921 -0
  18. data/examples/doc/Vehicle_state.png +0 -0
  19. data/examples/doc/_index.html +136 -0
  20. data/examples/doc/class_list.html +47 -0
  21. data/examples/doc/css/common.css +1 -0
  22. data/examples/doc/css/full_list.css +55 -0
  23. data/examples/doc/css/style.css +322 -0
  24. data/examples/doc/file_list.html +46 -0
  25. data/examples/doc/frames.html +13 -0
  26. data/examples/doc/index.html +136 -0
  27. data/examples/doc/js/app.js +205 -0
  28. data/examples/doc/js/full_list.js +173 -0
  29. data/examples/doc/js/jquery.js +16 -0
  30. data/examples/doc/method_list.html +734 -0
  31. data/examples/doc/top-level-namespace.html +105 -0
  32. data/examples/rails-rest/migration.rb +1 -5
  33. data/examples/rails-rest/view__form.html.erb +34 -0
  34. data/examples/rails-rest/view_edit.html.erb +2 -21
  35. data/examples/rails-rest/view_index.html.erb +6 -4
  36. data/examples/rails-rest/view_new.html.erb +2 -11
  37. data/examples/rails-rest/view_show.html.erb +5 -3
  38. data/examples/traffic_light.rb +2 -0
  39. data/examples/vehicle.rb +2 -0
  40. data/gemfiles/active_model-3.0.0.gemfile.lock +9 -6
  41. data/gemfiles/active_model-3.0.5.gemfile.lock +10 -7
  42. data/gemfiles/active_model-3.1.1.gemfile.lock +12 -10
  43. data/gemfiles/{active_model-3.2.0.gemfile → active_model-3.2.1.gemfile} +1 -1
  44. data/gemfiles/{graphviz-0.9.0.gemfile → active_model-3.2.12.gemfile} +1 -1
  45. data/gemfiles/active_model-3.2.12.gemfile.lock +36 -0
  46. data/gemfiles/{active_record-3.2.0.gemfile → active_model-3.2.13.rc1.gemfile} +1 -2
  47. data/gemfiles/active_model-3.2.13.rc1.gemfile.lock +36 -0
  48. data/gemfiles/active_model-4.0.0.gemfile +9 -0
  49. data/gemfiles/active_model-4.0.0.gemfile.lock +78 -0
  50. data/gemfiles/active_record-2.0.0.gemfile +2 -1
  51. data/gemfiles/active_record-2.0.0.gemfile.lock +15 -6
  52. data/gemfiles/active_record-2.0.5.gemfile +2 -1
  53. data/gemfiles/active_record-2.0.5.gemfile.lock +15 -6
  54. data/gemfiles/active_record-2.1.0.gemfile +2 -1
  55. data/gemfiles/active_record-2.1.0.gemfile.lock +15 -6
  56. data/gemfiles/active_record-2.1.2.gemfile +2 -1
  57. data/gemfiles/active_record-2.1.2.gemfile.lock +15 -6
  58. data/gemfiles/active_record-2.2.3.gemfile +2 -1
  59. data/gemfiles/active_record-2.2.3.gemfile.lock +15 -6
  60. data/gemfiles/active_record-2.3.12.gemfile +2 -1
  61. data/gemfiles/active_record-2.3.12.gemfile.lock +15 -6
  62. data/gemfiles/active_record-2.3.5.gemfile +9 -0
  63. data/gemfiles/active_record-2.3.5.gemfile.lock +39 -0
  64. data/gemfiles/active_record-3.0.0.gemfile +2 -1
  65. data/gemfiles/active_record-3.0.0.gemfile.lock +18 -11
  66. data/gemfiles/active_record-3.0.5.gemfile +2 -1
  67. data/gemfiles/active_record-3.0.5.gemfile.lock +19 -12
  68. data/gemfiles/active_record-3.1.1.gemfile +2 -1
  69. data/gemfiles/active_record-3.1.1.gemfile.lock +22 -16
  70. data/gemfiles/active_record-3.2.12.gemfile +9 -0
  71. data/gemfiles/active_record-3.2.12.gemfile.lock +51 -0
  72. data/gemfiles/active_record-3.2.13.rc1.gemfile +9 -0
  73. data/gemfiles/active_record-3.2.13.rc1.gemfile.lock +51 -0
  74. data/gemfiles/active_record-4.0.0.gemfile +11 -0
  75. data/gemfiles/active_record-4.0.0.gemfile.lock +83 -0
  76. data/gemfiles/data_mapper-0.10.2.gemfile +1 -0
  77. data/gemfiles/data_mapper-0.10.2.gemfile.lock +13 -9
  78. data/gemfiles/data_mapper-0.9.11.gemfile +1 -0
  79. data/gemfiles/data_mapper-0.9.11.gemfile.lock +31 -7
  80. data/gemfiles/data_mapper-0.9.4.gemfile.lock +25 -14
  81. data/gemfiles/data_mapper-0.9.7.gemfile +1 -0
  82. data/gemfiles/data_mapper-0.9.7.gemfile.lock +27 -15
  83. data/gemfiles/data_mapper-1.0.0.gemfile.lock +20 -17
  84. data/gemfiles/data_mapper-1.0.1.gemfile.lock +20 -17
  85. data/gemfiles/data_mapper-1.0.2.gemfile.lock +20 -17
  86. data/gemfiles/data_mapper-1.1.0.gemfile.lock +19 -16
  87. data/gemfiles/data_mapper-1.2.0.gemfile.lock +19 -16
  88. data/gemfiles/default.gemfile.lock +8 -5
  89. data/gemfiles/graphviz-0.9.17.gemfile +7 -0
  90. data/gemfiles/graphviz-0.9.17.gemfile.lock +29 -0
  91. data/gemfiles/graphviz-0.9.21.gemfile.lock +7 -4
  92. data/gemfiles/graphviz-1.0.0.gemfile.lock +7 -4
  93. data/gemfiles/graphviz-1.0.3.gemfile +7 -0
  94. data/gemfiles/graphviz-1.0.3.gemfile.lock +29 -0
  95. data/gemfiles/graphviz-1.0.8.gemfile +7 -0
  96. data/gemfiles/graphviz-1.0.8.gemfile.lock +29 -0
  97. data/gemfiles/mongo_mapper-0.10.0.gemfile +1 -0
  98. data/gemfiles/mongo_mapper-0.10.0.gemfile.lock +14 -11
  99. data/gemfiles/mongo_mapper-0.11.1.gemfile +7 -0
  100. data/gemfiles/mongo_mapper-0.11.1.gemfile.lock +44 -0
  101. data/gemfiles/mongo_mapper-0.11.2.gemfile +9 -0
  102. data/gemfiles/mongo_mapper-0.11.2.gemfile.lock +48 -0
  103. data/gemfiles/mongo_mapper-0.12.0.gemfile +9 -0
  104. data/gemfiles/mongo_mapper-0.12.0.gemfile.lock +48 -0
  105. data/gemfiles/mongo_mapper-0.5.5.gemfile.lock +7 -4
  106. data/gemfiles/mongo_mapper-0.5.8.gemfile.lock +7 -4
  107. data/gemfiles/mongo_mapper-0.6.0.gemfile.lock +7 -4
  108. data/gemfiles/mongo_mapper-0.6.10.gemfile.lock +7 -4
  109. data/gemfiles/mongo_mapper-0.7.0.gemfile.lock +7 -4
  110. data/gemfiles/mongo_mapper-0.7.5.gemfile.lock +7 -4
  111. data/gemfiles/mongo_mapper-0.8.0.gemfile.lock +7 -4
  112. data/gemfiles/mongo_mapper-0.8.3.gemfile.lock +7 -4
  113. data/gemfiles/mongo_mapper-0.8.4.gemfile.lock +7 -4
  114. data/gemfiles/mongo_mapper-0.8.6.gemfile.lock +7 -4
  115. data/gemfiles/mongo_mapper-0.9.0.gemfile.lock +7 -4
  116. data/gemfiles/mongoid-2.0.0.gemfile +2 -0
  117. data/gemfiles/mongoid-2.0.0.gemfile.lock +22 -18
  118. data/gemfiles/mongoid-2.1.4.gemfile +2 -0
  119. data/gemfiles/mongoid-2.1.4.gemfile.lock +21 -17
  120. data/gemfiles/mongoid-2.2.4.gemfile +2 -0
  121. data/gemfiles/mongoid-2.2.4.gemfile.lock +21 -17
  122. data/gemfiles/mongoid-2.3.3.gemfile +2 -0
  123. data/gemfiles/mongoid-2.3.3.gemfile.lock +21 -17
  124. data/gemfiles/mongoid-2.4.0.gemfile +9 -0
  125. data/gemfiles/mongoid-2.4.0.gemfile.lock +47 -0
  126. data/gemfiles/mongoid-2.4.10.gemfile +9 -0
  127. data/gemfiles/mongoid-2.4.10.gemfile.lock +47 -0
  128. data/gemfiles/mongoid-2.5.2.gemfile +9 -0
  129. data/gemfiles/mongoid-2.5.2.gemfile.lock +47 -0
  130. data/gemfiles/mongoid-2.6.0.gemfile +9 -0
  131. data/gemfiles/mongoid-2.6.0.gemfile.lock +47 -0
  132. data/gemfiles/mongoid-3.0.0.gemfile +8 -0
  133. data/gemfiles/mongoid-3.0.0.gemfile.lock +45 -0
  134. data/gemfiles/mongoid-3.0.22.gemfile +8 -0
  135. data/gemfiles/mongoid-3.0.22.gemfile.lock +45 -0
  136. data/gemfiles/mongoid-3.1.0.gemfile +8 -0
  137. data/gemfiles/mongoid-3.1.0.gemfile.lock +45 -0
  138. data/gemfiles/sequel-2.11.0.gemfile +2 -1
  139. data/gemfiles/sequel-2.11.0.gemfile.lock +11 -6
  140. data/gemfiles/sequel-2.12.0.gemfile +2 -1
  141. data/gemfiles/sequel-2.12.0.gemfile.lock +11 -6
  142. data/gemfiles/sequel-2.8.0.gemfile +2 -1
  143. data/gemfiles/sequel-2.8.0.gemfile.lock +11 -6
  144. data/gemfiles/sequel-3.0.0.gemfile +2 -1
  145. data/gemfiles/sequel-3.0.0.gemfile.lock +11 -6
  146. data/gemfiles/sequel-3.10.0.gemfile +9 -0
  147. data/gemfiles/sequel-3.10.0.gemfile.lock +33 -0
  148. data/gemfiles/sequel-3.13.0.gemfile +2 -1
  149. data/gemfiles/sequel-3.13.0.gemfile.lock +11 -6
  150. data/gemfiles/sequel-3.14.0.gemfile +2 -1
  151. data/gemfiles/sequel-3.14.0.gemfile.lock +11 -6
  152. data/gemfiles/sequel-3.23.0.gemfile +2 -1
  153. data/gemfiles/sequel-3.23.0.gemfile.lock +11 -6
  154. data/gemfiles/sequel-3.24.0.gemfile +2 -1
  155. data/gemfiles/sequel-3.24.0.gemfile.lock +11 -6
  156. data/gemfiles/sequel-3.29.0.gemfile +2 -1
  157. data/gemfiles/sequel-3.29.0.gemfile.lock +11 -6
  158. data/gemfiles/sequel-3.34.0.gemfile +9 -0
  159. data/gemfiles/sequel-3.34.0.gemfile.lock +33 -0
  160. data/gemfiles/sequel-3.35.0.gemfile +9 -0
  161. data/gemfiles/sequel-3.35.0.gemfile.lock +33 -0
  162. data/gemfiles/sequel-3.4.0.gemfile +9 -0
  163. data/gemfiles/sequel-3.4.0.gemfile.lock +33 -0
  164. data/gemfiles/sequel-3.44.0.gemfile +9 -0
  165. data/gemfiles/sequel-3.44.0.gemfile.lock +33 -0
  166. data/lib/state_machine.rb +6 -0
  167. data/lib/state_machine/branch.rb +9 -8
  168. data/lib/state_machine/callback.rb +2 -2
  169. data/lib/state_machine/core.rb +10 -0
  170. data/lib/state_machine/core_ext.rb +1 -0
  171. data/lib/state_machine/eval_helpers.rb +5 -3
  172. data/lib/state_machine/event.rb +17 -6
  173. data/lib/state_machine/graph.rb +92 -0
  174. data/lib/state_machine/integrations.rb +13 -1
  175. data/lib/state_machine/integrations/active_model.rb +14 -20
  176. data/lib/state_machine/integrations/active_model/observer.rb +3 -3
  177. data/lib/state_machine/integrations/active_model/observer_update.rb +42 -0
  178. data/lib/state_machine/integrations/active_record.rb +52 -25
  179. data/lib/state_machine/integrations/active_record/locale.rb +1 -1
  180. data/lib/state_machine/integrations/active_record/versions.rb +1 -17
  181. data/lib/state_machine/integrations/base.rb +15 -6
  182. data/lib/state_machine/integrations/data_mapper.rb +98 -35
  183. data/lib/state_machine/integrations/data_mapper/versions.rb +46 -8
  184. data/lib/state_machine/integrations/mongo_mapper.rb +39 -12
  185. data/lib/state_machine/integrations/mongo_mapper/locale.rb +1 -1
  186. data/lib/state_machine/integrations/mongo_mapper/versions.rb +3 -20
  187. data/lib/state_machine/integrations/mongoid.rb +52 -14
  188. data/lib/state_machine/integrations/mongoid/locale.rb +1 -1
  189. data/lib/state_machine/integrations/mongoid/versions.rb +52 -26
  190. data/lib/state_machine/integrations/sequel.rb +82 -33
  191. data/lib/state_machine/integrations/sequel/versions.rb +19 -44
  192. data/lib/state_machine/machine.rb +99 -59
  193. data/lib/state_machine/machine_collection.rb +1 -2
  194. data/lib/state_machine/macro_methods.rb +29 -0
  195. data/lib/state_machine/node_collection.rb +1 -1
  196. data/lib/state_machine/state.rb +18 -10
  197. data/lib/state_machine/state_context.rb +2 -2
  198. data/lib/state_machine/transition.rb +8 -1
  199. data/lib/state_machine/transition_collection.rb +2 -1
  200. data/lib/state_machine/version.rb +1 -1
  201. data/lib/state_machine/yard.rb +8 -0
  202. data/lib/state_machine/yard/handlers.rb +12 -0
  203. data/lib/state_machine/yard/handlers/base.rb +32 -0
  204. data/lib/state_machine/yard/handlers/event.rb +25 -0
  205. data/lib/state_machine/yard/handlers/machine.rb +344 -0
  206. data/lib/state_machine/yard/handlers/state.rb +25 -0
  207. data/lib/state_machine/yard/handlers/transition.rb +47 -0
  208. data/lib/state_machine/yard/templates.rb +3 -0
  209. data/lib/state_machine/yard/templates/default/class/html/setup.rb +30 -0
  210. data/lib/state_machine/yard/templates/default/class/html/state_machines.erb +12 -0
  211. data/lib/tasks/state_machine.rb +2 -1
  212. data/lib/yard-state_machine.rb +2 -0
  213. data/state_machine.gemspec +4 -3
  214. data/test/files/switch.rb +4 -0
  215. data/test/test_helper.rb +5 -0
  216. data/test/unit/branch_test.rb +117 -36
  217. data/test/unit/callback_test.rb +5 -2
  218. data/test/unit/eval_helpers_test.rb +49 -1
  219. data/test/unit/event_collection_test.rb +3 -1
  220. data/test/unit/event_test.rb +182 -12
  221. data/test/unit/graph_test.rb +98 -0
  222. data/test/unit/integrations/active_model_test.rb +82 -12
  223. data/test/unit/integrations/active_record_test.rb +393 -37
  224. data/test/unit/integrations/base_test.rb +7 -2
  225. data/test/unit/integrations/data_mapper_test.rb +326 -72
  226. data/test/unit/integrations/mongo_mapper_test.rb +338 -44
  227. data/test/unit/integrations/mongoid_test.rb +606 -98
  228. data/test/unit/integrations/sequel_test.rb +429 -102
  229. data/test/unit/integrations_test.rb +28 -6
  230. data/test/unit/machine_collection_test.rb +6 -2
  231. data/test/unit/machine_test.rb +134 -82
  232. data/test/unit/node_collection_test.rb +2 -2
  233. data/test/unit/path_test.rb +1 -1
  234. data/test/unit/state_test.rb +65 -21
  235. data/test/unit/transition_collection_test.rb +43 -23
  236. data/test/unit/transition_test.rb +8 -2
  237. metadata +303 -221
  238. data/gemfiles/active_model-3.2.0.gemfile.lock +0 -32
  239. data/gemfiles/active_record-3.2.0.gemfile.lock +0 -43
  240. data/gemfiles/graphviz-0.9.0.gemfile.lock +0 -26
@@ -283,6 +283,22 @@ module StateMachine
283
283
  # end
284
284
  # end
285
285
  #
286
+ # === Callback Order
287
+ #
288
+ # Callbacks occur in the following order. Callbacks specific to state_machine
289
+ # are bolded. The remaining callbacks are part of Mongoid.
290
+ #
291
+ # * (-) save
292
+ # * (1) *before_transition*
293
+ # * (-) valid
294
+ # * (2) before_validation
295
+ # * (3) after_validation
296
+ # * (4) before_save
297
+ # * (5) before_create
298
+ # * (6) after_create
299
+ # * (7) after_save
300
+ # * (8) *after_transition*
301
+ #
286
302
  # == Internationalization
287
303
  #
288
304
  # Any error message that is generated from performing invalid transitions
@@ -341,17 +357,10 @@ module StateMachine
341
357
  # The default options to use for state machines using this integration
342
358
  @defaults = {:action => :save}
343
359
 
344
- # Whether this integration is available. Only true if Mongoid::Document
345
- # is defined.
346
- def self.available?
347
- defined?(::Mongoid::Document)
348
- end
349
-
350
- # Should this integration be used for state machines in the given class?
351
360
  # Classes that include Mongoid::Document will automatically use the
352
361
  # Mongoid integration.
353
- def self.matches?(klass)
354
- klass <= ::Mongoid::Document
362
+ def self.matching_ancestors
363
+ %w(Mongoid::Document)
355
364
  end
356
365
 
357
366
  def self.extended(base) #:nodoc:
@@ -365,33 +374,62 @@ module StateMachine
365
374
  action == :save
366
375
  end
367
376
 
377
+ # Gets the db default for the machine's attribute
378
+ def owner_class_attribute_default
379
+ attribute_field && attribute_field.default_val
380
+ end
381
+
382
+ # Gets the field for this machine's attribute (if it exists)
383
+ def attribute_field
384
+ owner_class.fields[attribute.to_s] || owner_class.fields[owner_class.aliased_fields[attribute.to_s]]
385
+ end
386
+
368
387
  # Defines an initialization hook into the owner class for setting the
369
388
  # initial state of the machine *before* any attributes are set on the
370
389
  # object
371
390
  def define_state_initializer
372
391
  define_helper :instance, <<-end_eval, __FILE__, __LINE__ + 1
373
392
  def initialize(*)
374
- @attributes ||= {}
375
- self.class.state_machines.initialize_states(self, :dynamic => false)
376
-
377
393
  super do |*args|
378
394
  self.class.state_machines.initialize_states(self, :static => false)
379
395
  yield(*args) if block_given?
380
396
  end
381
397
  end
398
+
399
+ def apply_pre_processed_defaults
400
+ defaults = {}
401
+ self.class.state_machines.initialize_states(self, :static => :force, :dynamic => false, :to => defaults)
402
+ defaults.each do |attr, value|
403
+ send(:"\#{attr}=", value) unless attributes.include?(attr)
404
+ end
405
+ super
406
+ end
382
407
  end_eval
383
408
  end
384
409
 
385
410
  # Skips defining reader/writer methods since this is done automatically
386
411
  def define_state_accessor
387
- owner_class.field(attribute, :type => String) unless owner_class.fields.include?(attribute.to_s)
412
+ owner_class.field(attribute, :type => String) unless attribute_field
388
413
  super
389
414
  end
390
415
 
391
416
  # Uses around callbacks to run state events if using the :save hook
392
417
  def define_action_hook
393
418
  if action_hook == :save
394
- owner_class.set_callback(:save, :around, self, :prepend => true)
419
+ define_helper :instance, <<-end_eval, __FILE__, __LINE__ + 1
420
+ def insert(*)
421
+ self.class.state_machine(#{name.inspect}).send(:around_save, self) { super.persisted? }
422
+ self
423
+ end
424
+
425
+ def update(*)
426
+ self.class.state_machine(#{name.inspect}).send(:around_save, self) { super }
427
+ end
428
+
429
+ def upsert(*)
430
+ self.class.state_machine(#{name.inspect}).send(:around_save, self) { super }
431
+ end
432
+ end_eval
395
433
  else
396
434
  super
397
435
  end
@@ -1,4 +1,4 @@
1
1
  filename = "#{File.dirname(__FILE__)}/../active_model/locale.rb"
2
- translations = eval(IO.read(filename), binding, filename)
2
+ translations = eval(IO.read(File.expand_path(filename)), binding, filename)
3
3
  translations[:en][:mongoid] = translations[:en].delete(:activemodel)
4
4
  translations
@@ -1,53 +1,79 @@
1
1
  module StateMachine
2
2
  module Integrations #:nodoc:
3
3
  module Mongoid
4
- version '2.0.x - 2.2.x' do
4
+ version '2.x' do
5
5
  def self.active?
6
- ::Mongoid::VERSION =~ /^2\.[0-2]\./
6
+ ::Mongoid::VERSION =~ /^2\./
7
7
  end
8
8
 
9
9
  def define_state_initializer
10
10
  define_helper :instance, <<-end_eval, __FILE__, __LINE__ + 1
11
- # Initializes dynamic states
12
11
  def initialize(*)
12
+ @attributes ||= {}
13
+ self.class.state_machines.initialize_states(self, :static => :force, :dynamic => false)
14
+
13
15
  super do |*args|
14
16
  self.class.state_machines.initialize_states(self, :static => false)
15
17
  yield(*args) if block_given?
16
18
  end
17
19
  end
18
-
19
- # Initializes static states
20
- def apply_default_attributes(*)
21
- result = super
22
- self.class.state_machines.initialize_states(self, :dynamic => false, :to => result) if new_record?
23
- result
24
- end
25
20
  end_eval
26
21
  end
27
22
 
23
+ def owner_class_attribute_default
24
+ attribute_field && attribute_field.default
25
+ end
26
+
28
27
  def define_action_hook
29
- # +around+ callbacks don't have direct access to results until AS 3.1
30
- owner_class.set_callback(:save, :after, 'value', :prepend => true) if action_hook == :save
31
- super
28
+ if action_hook == :save
29
+ define_helper :instance, <<-end_eval, __FILE__, __LINE__ + 1
30
+ def insert(*)
31
+ self.class.state_machine(#{name.inspect}).send(:around_save, self) { super.persisted? }
32
+ self
33
+ end
34
+
35
+ def update(*)
36
+ self.class.state_machine(#{name.inspect}).send(:around_save, self) { super }
37
+ end
38
+ end_eval
39
+ else
40
+ super
41
+ end
32
42
  end
33
43
  end
34
44
 
35
- version '2.0.x' do
45
+ version '2.0.x - 2.3.x' do
36
46
  def self.active?
37
- ::Mongoid::VERSION =~ /^2\.0\./
47
+ ::Mongoid::VERSION =~ /^2\.[0-3]\./
38
48
  end
39
49
 
40
- # Forces the change in state to be recognized regardless of whether the
41
- # state value actually changed
42
- def write(object, attribute, value, *args)
43
- result = super
44
-
45
- if (attribute == :state || attribute == :event && value) && !object.send("#{self.attribute}_changed?")
46
- current = read(object, :state)
47
- object.changes[self.attribute.to_s] = [attribute == :event ? current : value, current]
48
- end
49
-
50
- result
50
+ def attribute_field
51
+ owner_class.fields[attribute.to_s]
52
+ end
53
+ end
54
+
55
+ version '2.0.x - 2.2.x' do
56
+ def self.active?
57
+ ::Mongoid::VERSION =~ /^2\.[0-2]\./
58
+ end
59
+
60
+ def define_state_initializer
61
+ define_helper :instance, <<-end_eval, __FILE__, __LINE__ + 1
62
+ # Initializes dynamic states
63
+ def initialize(*)
64
+ super do |*args|
65
+ self.class.state_machines.initialize_states(self, :static => false)
66
+ yield(*args) if block_given?
67
+ end
68
+ end
69
+
70
+ # Initializes static states
71
+ def apply_default_attributes(*)
72
+ result = super
73
+ self.class.state_machines.initialize_states(self, :static => :force, :dynamic => false, :to => result) if new_record?
74
+ result
75
+ end
76
+ end_eval
51
77
  end
52
78
  end
53
79
  end
@@ -134,18 +134,6 @@ module StateMachine
134
134
  # end
135
135
  # end
136
136
  #
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+. 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
148
- #
149
137
  # == Validation errors
150
138
  #
151
139
  # If an event fails to successfully fire because there are no matching
@@ -265,26 +253,38 @@ module StateMachine
265
253
  # The +TransitionLog+ model uses a second connection to the database that
266
254
  # allows new records to be saved without being affected by rollbacks in the
267
255
  # +Vehicle+ model's transaction.
256
+ #
257
+ # === Callback Order
258
+ #
259
+ # Callbacks occur in the following order. Callbacks specific to state_machine
260
+ # are bolded. The remaining callbacks are part of Sequel.
261
+ #
262
+ # * (-) save
263
+ # * (-) begin transaction (if enabled)
264
+ # * (1) *before_transition*
265
+ # * (2) before_validation
266
+ # * (-) validate
267
+ # * (3) after_validation
268
+ # * (4) before_save
269
+ # * (5) before_create
270
+ # * (-) create
271
+ # * (6) after_create
272
+ # * (7) after_save
273
+ # * (8) *after_transition*
274
+ # * (-) end transaction (if enabled)
275
+ # * (9) after_commit
268
276
  module Sequel
269
277
  include Base
270
278
 
271
279
  require 'state_machine/integrations/sequel/versions'
272
280
 
273
281
  # The default options to use for state machines using this integration
274
- class << self; attr_reader :defaults; end
275
282
  @defaults = {:action => :save}
276
283
 
277
- # Whether this integration is available. Only true if Sequel::Model is
278
- # defined.
279
- def self.available?
280
- defined?(::Sequel::Model)
281
- end
282
-
283
- # Should this integration be used for state machines in the given class?
284
284
  # Classes that include Sequel::Model will automatically use the Sequel
285
285
  # integration.
286
- def self.matches?(klass)
287
- klass <= ::Sequel::Model
286
+ def self.matching_ancestors
287
+ %w(Sequel::Model)
288
288
  end
289
289
 
290
290
  # Forces the change in state to be recognized regardless of whether the
@@ -331,7 +331,6 @@ module StateMachine
331
331
 
332
332
  # Loads all of the Sequel plugins necessary to run
333
333
  def load_plugins
334
- owner_class.plugin(:validation_class_methods)
335
334
  owner_class.plugin(:hook_class_methods)
336
335
  end
337
336
 
@@ -346,18 +345,20 @@ module StateMachine
346
345
  def define_state_initializer
347
346
  define_helper :instance, <<-end_eval, __FILE__, __LINE__ + 1
348
347
  def initialize_set(*)
349
- self.class.state_machines.initialize_states(self) { super }
348
+ self.class.state_machines.initialize_states(self, :static => :force) { super }
350
349
  end
351
350
  end_eval
352
351
  end
353
352
 
354
353
  # Skips defining reader/writer methods since this is done automatically
355
354
  def define_state_accessor
356
- name = self.name
357
- owner_class.validates_each(attribute) do |record, attr, value|
358
- machine = record.class.state_machine(name)
359
- machine.invalidate(record, :state, :invalid) unless machine.states.match(record)
360
- end
355
+ define_helper :instance, <<-end_eval, __FILE__, __LINE__ + 1
356
+ def validate(*)
357
+ super
358
+ machine = self.class.state_machine(#{name.inspect})
359
+ machine.invalidate(self, :state, :invalid) unless machine.states.match(self)
360
+ end
361
+ end_eval
361
362
  end
362
363
 
363
364
  # Defines validation hooks if the machine's action is to save the model
@@ -366,6 +367,43 @@ module StateMachine
366
367
  define_validation_hook if action == :save
367
368
  end
368
369
 
370
+ # Uses around callbacks to run state events if using the :save hook
371
+ def define_action_hook
372
+ if action == :save
373
+ define_helper :instance, <<-end_eval, __FILE__, __LINE__ + 1
374
+ def #{action_hook}(*args)
375
+ opts = args.last.is_a?(Hash) ? args.last : {}
376
+ yielded = false
377
+ result = self.class.state_machine(#{name.inspect}).send(:around_save, self) do
378
+ yielded = true
379
+ super
380
+ end
381
+
382
+ if yielded || result
383
+ result
384
+ else
385
+ #{handle_save_failure}
386
+ end
387
+ end
388
+ end_eval
389
+ else
390
+ super
391
+ end
392
+ end
393
+
394
+ # Handles how save failures (due to invalid transitions) are raised
395
+ def handle_save_failure
396
+ 'raise_hook_failure(:before_transition) if raise_on_failure?(opts)'
397
+ end
398
+
399
+ # Runs state events around the machine's :save action
400
+ def around_save(object)
401
+ result = transaction(object) do
402
+ object.class.state_machines.transitions(object, action).perform { yield }
403
+ end
404
+ result
405
+ end
406
+
369
407
  # Adds hooks into validation for automatically firing events
370
408
  def define_validation_hook
371
409
  define_helper :instance, <<-end_eval, __FILE__, __LINE__ + 1
@@ -375,9 +413,16 @@ module StateMachine
375
413
  end_eval
376
414
  end
377
415
 
378
- # Uses internal save hooks if using the :save action
379
- def action_hook
380
- action == :save ? :around_save : super
416
+ # Gets the db default for the machine's attribute
417
+ def owner_class_attribute_default
418
+ if owner_class.db.table_exists?(owner_class.table_name) && column = owner_class.db_schema[attribute.to_sym]
419
+ column[:default]
420
+ end
421
+ end
422
+
423
+ # Uses the DB literal to match the default against the specified state
424
+ def owner_class_attribute_default_matches?(state)
425
+ owner_class.db.literal(state.value) == owner_class_attribute_default
381
426
  end
382
427
 
383
428
  # Creates a scope for finding records *with* a particular state or
@@ -421,7 +466,11 @@ module StateMachine
421
466
  # Runs a new database transaction, rolling back any changes if the
422
467
  # yielded block fails (i.e. returns false).
423
468
  def transaction(object)
424
- object.db.transaction {raise ::Sequel::Error::Rollback unless yield}
469
+ result = nil
470
+ object.db.transaction do
471
+ raise ::Sequel::Error::Rollback unless result = yield
472
+ end
473
+ result
425
474
  end
426
475
 
427
476
  # Creates a new callback in the callback chain, always ensuring that
@@ -17,7 +17,7 @@ module StateMachine
17
17
  end
18
18
 
19
19
  def set(*)
20
- self.class.state_machines.initialize_states(self, :dynamic => false) if values.empty?
20
+ self.class.state_machines.initialize_states(self, :static => :force, :dynamic => false) if values.empty?
21
21
  super
22
22
  end
23
23
  end_eval
@@ -26,6 +26,7 @@ module StateMachine
26
26
  def define_validation_hook
27
27
  define_helper :instance, <<-end_eval, __FILE__, __LINE__ + 1
28
28
  def valid?(*args)
29
+ opts = args.first.is_a?(Hash) ? args.first : {}
29
30
  yielded = false
30
31
  result = self.class.state_machines.transitions(self, :save, :after => false).perform do
31
32
  yielded = true
@@ -40,31 +41,19 @@ module StateMachine
40
41
  end
41
42
  end_eval
42
43
  end
44
+ end
45
+
46
+ version '2.8.x - 3.13.x' do
47
+ def self.active?
48
+ !defined?(::Sequel::MAJOR) || ::Sequel::MAJOR == 2 || ::Sequel::MAJOR == 3 && ::Sequel::MINOR <= 13
49
+ end
43
50
 
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
51
+ def handle_validation_failure
52
+ 'raise_on_save_failure ? save_failure(:validation) : result'
64
53
  end
65
54
 
66
- def action_hook
67
- action == :save ? :_save : super
55
+ def handle_save_failure
56
+ 'save_failure(:save) if raise_on_save_failure'
68
57
  end
69
58
  end
70
59
 
@@ -79,26 +68,16 @@ module StateMachine
79
68
  def load_inflector
80
69
  end
81
70
 
82
- def action_hook
83
- action == :save ? :save : super
84
- end
85
-
86
71
  def model_from_dataset(dataset)
87
72
  dataset.model_classes[nil]
88
73
  end
89
- end
90
-
91
- version '2.8.x - 3.13.x' do
92
- def self.active?
93
- !defined?(::Sequel::MAJOR) || ::Sequel::MAJOR == 2 || ::Sequel::MAJOR == 3 && ::Sequel::MINOR <= 13
94
- end
95
-
96
- def handle_validation_failure
97
- 'raise_on_save_failure ? save_failure(:validation) : result'
98
- end
99
74
 
100
- def handle_save_failure
101
- 'save_failure(:save)'
75
+ def define_state_accessor
76
+ name = self.name
77
+ owner_class.validates_each(attribute) do |record, attr, value|
78
+ machine = record.class.state_machine(name)
79
+ machine.invalidate(record, :state, :invalid) unless machine.states.match(record)
80
+ end
102
81
  end
103
82
  end
104
83
 
@@ -108,11 +87,7 @@ module StateMachine
108
87
  end
109
88
 
110
89
  def handle_validation_failure
111
- 'raise_on_failure?(args.first || {}) ? raise_hook_failure(:validation) : result'
112
- end
113
-
114
- def handle_save_failure
115
- 'raise_hook_failure(:save)'
90
+ 'raise_on_failure?(opts) ? raise_hook_failure(:validation) : result'
116
91
  end
117
92
  end
118
93
  end