state_machine 1.1.2 → 1.2.0

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 (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