aasm 2.1.1 → 5.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (275) hide show
  1. checksums.yaml +7 -0
  2. data/.document +6 -0
  3. data/.github/ISSUE_TEMPLATE/bug_report.md +27 -0
  4. data/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
  5. data/.gitignore +20 -0
  6. data/.travis.yml +82 -0
  7. data/API +34 -0
  8. data/Appraisals +67 -0
  9. data/CHANGELOG.md +453 -0
  10. data/CODE_OF_CONDUCT.md +13 -0
  11. data/CONTRIBUTING.md +24 -0
  12. data/Dockerfile +44 -0
  13. data/Gemfile +6 -0
  14. data/Gemfile.lock_old +151 -0
  15. data/HOWTO +12 -0
  16. data/{MIT-LICENSE → LICENSE} +1 -1
  17. data/PLANNED_CHANGES.md +11 -0
  18. data/README.md +1524 -0
  19. data/README_FROM_VERSION_3_TO_4.md +240 -0
  20. data/Rakefile +20 -84
  21. data/TESTING.md +25 -0
  22. data/aasm.gemspec +37 -0
  23. data/docker-compose.yml +40 -0
  24. data/gemfiles/norails.gemfile +10 -0
  25. data/gemfiles/rails_4.2.gemfile +17 -0
  26. data/gemfiles/rails_4.2_mongoid_5.gemfile +12 -0
  27. data/gemfiles/rails_4.2_nobrainer.gemfile +9 -0
  28. data/gemfiles/rails_5.0.gemfile +14 -0
  29. data/gemfiles/rails_5.0_nobrainer.gemfile +9 -0
  30. data/gemfiles/rails_5.1.gemfile +14 -0
  31. data/gemfiles/rails_5.2.gemfile +14 -0
  32. data/lib/aasm/aasm.rb +160 -137
  33. data/lib/aasm/base.rb +290 -0
  34. data/lib/aasm/configuration.rb +48 -0
  35. data/lib/aasm/core/event.rb +177 -0
  36. data/lib/aasm/core/invoker.rb +129 -0
  37. data/lib/aasm/core/invokers/base_invoker.rb +75 -0
  38. data/lib/aasm/core/invokers/class_invoker.rb +52 -0
  39. data/lib/aasm/core/invokers/literal_invoker.rb +47 -0
  40. data/lib/aasm/core/invokers/proc_invoker.rb +59 -0
  41. data/lib/aasm/core/state.rb +91 -0
  42. data/lib/aasm/core/transition.rb +83 -0
  43. data/lib/aasm/dsl_helper.rb +32 -0
  44. data/lib/aasm/errors.rb +21 -0
  45. data/lib/aasm/instance_base.rb +133 -0
  46. data/lib/aasm/localizer.rb +64 -0
  47. data/lib/aasm/minitest/allow_event.rb +13 -0
  48. data/lib/aasm/minitest/allow_transition_to.rb +13 -0
  49. data/lib/aasm/minitest/have_state.rb +13 -0
  50. data/lib/aasm/minitest/transition_from.rb +21 -0
  51. data/lib/aasm/minitest.rb +5 -0
  52. data/lib/aasm/minitest_spec.rb +15 -0
  53. data/lib/aasm/persistence/active_record_persistence.rb +108 -173
  54. data/lib/aasm/persistence/base.rb +89 -0
  55. data/lib/aasm/persistence/core_data_query_persistence.rb +94 -0
  56. data/lib/aasm/persistence/dynamoid_persistence.rb +92 -0
  57. data/lib/aasm/persistence/mongoid_persistence.rb +126 -0
  58. data/lib/aasm/persistence/no_brainer_persistence.rb +105 -0
  59. data/lib/aasm/persistence/orm.rb +154 -0
  60. data/lib/aasm/persistence/plain_persistence.rb +26 -0
  61. data/lib/aasm/persistence/redis_persistence.rb +112 -0
  62. data/lib/aasm/persistence/sequel_persistence.rb +83 -0
  63. data/lib/aasm/persistence.rb +48 -10
  64. data/lib/aasm/rspec/allow_event.rb +26 -0
  65. data/lib/aasm/rspec/allow_transition_to.rb +26 -0
  66. data/lib/aasm/rspec/have_state.rb +22 -0
  67. data/lib/aasm/rspec/transition_from.rb +36 -0
  68. data/lib/aasm/rspec.rb +5 -0
  69. data/lib/aasm/state_machine.rb +40 -22
  70. data/lib/aasm/state_machine_store.rb +76 -0
  71. data/lib/aasm/version.rb +3 -0
  72. data/lib/aasm.rb +21 -1
  73. data/lib/generators/aasm/aasm_generator.rb +16 -0
  74. data/lib/generators/aasm/orm_helpers.rb +41 -0
  75. data/lib/generators/active_record/aasm_generator.rb +40 -0
  76. data/lib/generators/active_record/templates/migration.rb +8 -0
  77. data/lib/generators/active_record/templates/migration_existing.rb +5 -0
  78. data/lib/generators/mongoid/aasm_generator.rb +28 -0
  79. data/lib/generators/nobrainer/aasm_generator.rb +28 -0
  80. data/lib/motion-aasm.rb +37 -0
  81. data/spec/database.rb +57 -0
  82. data/spec/database.yml +3 -0
  83. data/spec/en.yml +9 -0
  84. data/spec/generators/active_record_generator_spec.rb +53 -0
  85. data/spec/generators/mongoid_generator_spec.rb +31 -0
  86. data/spec/generators/no_brainer_generator_spec.rb +29 -0
  87. data/spec/localizer_test_model_deprecated_style.yml +13 -0
  88. data/spec/localizer_test_model_new_style.yml +11 -0
  89. data/spec/models/active_record/active_record_callback.rb +93 -0
  90. data/spec/models/active_record/basic_active_record_two_state_machines_example.rb +25 -0
  91. data/spec/models/active_record/complex_active_record_example.rb +37 -0
  92. data/spec/models/active_record/derivate_new_dsl.rb +7 -0
  93. data/spec/models/active_record/false_state.rb +35 -0
  94. data/spec/models/active_record/gate.rb +39 -0
  95. data/spec/models/active_record/instance_level_skip_validation_example.rb +19 -0
  96. data/spec/models/active_record/invalid_persistor.rb +29 -0
  97. data/spec/models/active_record/localizer_test_model.rb +42 -0
  98. data/spec/models/active_record/namespaced.rb +16 -0
  99. data/spec/models/active_record/no_direct_assignment.rb +21 -0
  100. data/spec/models/active_record/no_scope.rb +21 -0
  101. data/spec/models/active_record/persisted_state.rb +12 -0
  102. data/spec/models/active_record/person.rb +23 -0
  103. data/spec/models/active_record/provided_and_persisted_state.rb +24 -0
  104. data/spec/models/active_record/reader.rb +7 -0
  105. data/spec/models/active_record/readme_job.rb +21 -0
  106. data/spec/models/active_record/silent_persistor.rb +29 -0
  107. data/spec/models/active_record/simple_new_dsl.rb +32 -0
  108. data/spec/models/active_record/thief.rb +29 -0
  109. data/spec/models/active_record/timestamp_example.rb +16 -0
  110. data/spec/models/active_record/transactor.rb +124 -0
  111. data/spec/models/active_record/transient.rb +6 -0
  112. data/spec/models/active_record/validator.rb +118 -0
  113. data/spec/models/active_record/with_enum.rb +39 -0
  114. data/spec/models/active_record/with_enum_without_column.rb +38 -0
  115. data/spec/models/active_record/with_false_enum.rb +31 -0
  116. data/spec/models/active_record/with_true_enum.rb +39 -0
  117. data/spec/models/active_record/work.rb +3 -0
  118. data/spec/models/active_record/worker.rb +2 -0
  119. data/spec/models/active_record/writer.rb +6 -0
  120. data/spec/models/basic_two_state_machines_example.rb +25 -0
  121. data/spec/models/callbacks/basic.rb +98 -0
  122. data/spec/models/callbacks/basic_multiple.rb +75 -0
  123. data/spec/models/callbacks/guard_within_block.rb +67 -0
  124. data/spec/models/callbacks/guard_within_block_multiple.rb +66 -0
  125. data/spec/models/callbacks/multiple_transitions_transition_guard.rb +66 -0
  126. data/spec/models/callbacks/multiple_transitions_transition_guard_multiple.rb +65 -0
  127. data/spec/models/callbacks/private_method.rb +44 -0
  128. data/spec/models/callbacks/private_method_multiple.rb +44 -0
  129. data/spec/models/callbacks/with_args.rb +62 -0
  130. data/spec/models/callbacks/with_args_multiple.rb +61 -0
  131. data/spec/models/callbacks/with_state_arg.rb +34 -0
  132. data/spec/models/callbacks/with_state_arg_multiple.rb +29 -0
  133. data/spec/models/complex_example.rb +222 -0
  134. data/spec/models/conversation.rb +93 -0
  135. data/spec/models/default_state.rb +12 -0
  136. data/spec/models/double_definer.rb +21 -0
  137. data/spec/models/dynamoid/complex_dynamoid_example.rb +37 -0
  138. data/spec/models/dynamoid/dynamoid_multiple.rb +18 -0
  139. data/spec/models/dynamoid/dynamoid_simple.rb +18 -0
  140. data/spec/models/foo.rb +106 -0
  141. data/spec/models/foo_callback_multiple.rb +45 -0
  142. data/spec/models/guard_arguments_check.rb +17 -0
  143. data/spec/models/guard_with_params.rb +24 -0
  144. data/spec/models/guard_with_params_multiple.rb +18 -0
  145. data/spec/models/guardian.rb +58 -0
  146. data/spec/models/guardian_multiple.rb +48 -0
  147. data/spec/models/guardian_without_from_specified.rb +18 -0
  148. data/spec/models/initial_state_proc.rb +31 -0
  149. data/spec/models/mongoid/complex_mongoid_example.rb +37 -0
  150. data/spec/models/mongoid/invalid_persistor_mongoid.rb +39 -0
  151. data/spec/models/mongoid/mongoid_relationships.rb +26 -0
  152. data/spec/models/mongoid/no_scope_mongoid.rb +21 -0
  153. data/spec/models/mongoid/silent_persistor_mongoid.rb +39 -0
  154. data/spec/models/mongoid/simple_mongoid.rb +23 -0
  155. data/spec/models/mongoid/simple_new_dsl_mongoid.rb +25 -0
  156. data/spec/models/mongoid/timestamp_example_mongoid.rb +20 -0
  157. data/spec/models/mongoid/validator_mongoid.rb +100 -0
  158. data/spec/models/multi_transitioner.rb +34 -0
  159. data/spec/models/multiple_transitions_that_differ_only_by_guard.rb +31 -0
  160. data/spec/models/namespaced_multiple_example.rb +42 -0
  161. data/spec/models/no_initial_state.rb +25 -0
  162. data/spec/models/nobrainer/complex_no_brainer_example.rb +36 -0
  163. data/spec/models/nobrainer/invalid_persistor_no_brainer.rb +39 -0
  164. data/spec/models/nobrainer/no_scope_no_brainer.rb +21 -0
  165. data/spec/models/nobrainer/nobrainer_relationships.rb +25 -0
  166. data/spec/models/nobrainer/silent_persistor_no_brainer.rb +39 -0
  167. data/spec/models/nobrainer/simple_new_dsl_nobrainer.rb +25 -0
  168. data/spec/models/nobrainer/simple_no_brainer.rb +23 -0
  169. data/spec/models/nobrainer/validator_no_brainer.rb +98 -0
  170. data/spec/models/not_auto_loaded/process.rb +21 -0
  171. data/spec/models/parametrised_event.rb +42 -0
  172. data/spec/models/parametrised_event_multiple.rb +29 -0
  173. data/spec/models/process_with_new_dsl.rb +31 -0
  174. data/spec/models/provided_state.rb +24 -0
  175. data/spec/models/redis/complex_redis_example.rb +40 -0
  176. data/spec/models/redis/redis_multiple.rb +20 -0
  177. data/spec/models/redis/redis_simple.rb +20 -0
  178. data/spec/models/sequel/complex_sequel_example.rb +46 -0
  179. data/spec/models/sequel/invalid_persistor.rb +52 -0
  180. data/spec/models/sequel/sequel_multiple.rb +25 -0
  181. data/spec/models/sequel/sequel_simple.rb +26 -0
  182. data/spec/models/sequel/silent_persistor.rb +50 -0
  183. data/spec/models/sequel/transactor.rb +112 -0
  184. data/spec/models/sequel/validator.rb +93 -0
  185. data/spec/models/sequel/worker.rb +12 -0
  186. data/spec/models/silencer.rb +27 -0
  187. data/spec/models/simple_custom_example.rb +53 -0
  188. data/spec/models/simple_example.rb +23 -0
  189. data/spec/models/simple_example_with_guard_args.rb +17 -0
  190. data/spec/models/simple_multiple_example.rb +42 -0
  191. data/spec/models/state_machine_with_failed_event.rb +20 -0
  192. data/spec/models/states_on_one_line_example.rb +8 -0
  193. data/spec/models/sub_class.rb +41 -0
  194. data/spec/models/sub_class_with_more_states.rb +18 -0
  195. data/spec/models/sub_classing.rb +3 -0
  196. data/spec/models/super_class.rb +46 -0
  197. data/spec/models/this_name_better_not_be_in_use.rb +11 -0
  198. data/spec/models/timestamps_example.rb +19 -0
  199. data/spec/models/timestamps_with_named_machine_example.rb +13 -0
  200. data/spec/models/valid_state_name.rb +23 -0
  201. data/spec/spec_helper.rb +41 -0
  202. data/spec/spec_helpers/active_record.rb +8 -0
  203. data/spec/spec_helpers/dynamoid.rb +35 -0
  204. data/spec/spec_helpers/mongoid.rb +26 -0
  205. data/spec/spec_helpers/nobrainer.rb +15 -0
  206. data/spec/spec_helpers/redis.rb +18 -0
  207. data/spec/spec_helpers/remove_warnings.rb +1 -0
  208. data/spec/spec_helpers/sequel.rb +7 -0
  209. data/spec/unit/abstract_class_spec.rb +27 -0
  210. data/spec/unit/api_spec.rb +104 -0
  211. data/spec/unit/basic_two_state_machines_example_spec.rb +10 -0
  212. data/spec/unit/callback_multiple_spec.rb +304 -0
  213. data/spec/unit/callbacks_spec.rb +521 -0
  214. data/spec/unit/complex_example_spec.rb +93 -0
  215. data/spec/unit/complex_multiple_example_spec.rb +115 -0
  216. data/spec/unit/edge_cases_spec.rb +16 -0
  217. data/spec/unit/event_multiple_spec.rb +73 -0
  218. data/spec/unit/event_naming_spec.rb +16 -0
  219. data/spec/unit/event_spec.rb +394 -0
  220. data/spec/unit/exception_spec.rb +11 -0
  221. data/spec/unit/guard_arguments_check_spec.rb +9 -0
  222. data/spec/unit/guard_multiple_spec.rb +60 -0
  223. data/spec/unit/guard_spec.rb +89 -0
  224. data/spec/unit/guard_with_params_multiple_spec.rb +10 -0
  225. data/spec/unit/guard_with_params_spec.rb +14 -0
  226. data/spec/unit/guard_without_from_specified_spec.rb +10 -0
  227. data/spec/unit/initial_state_multiple_spec.rb +15 -0
  228. data/spec/unit/initial_state_spec.rb +12 -0
  229. data/spec/unit/inspection_multiple_spec.rb +205 -0
  230. data/spec/unit/inspection_spec.rb +153 -0
  231. data/spec/unit/invoker_spec.rb +189 -0
  232. data/spec/unit/invokers/base_invoker_spec.rb +72 -0
  233. data/spec/unit/invokers/class_invoker_spec.rb +95 -0
  234. data/spec/unit/invokers/literal_invoker_spec.rb +86 -0
  235. data/spec/unit/invokers/proc_invoker_spec.rb +86 -0
  236. data/spec/unit/localizer_spec.rb +109 -0
  237. data/spec/unit/memory_leak_spec.rb +38 -0
  238. data/spec/unit/multiple_transitions_that_differ_only_by_guard_spec.rb +14 -0
  239. data/spec/unit/namespaced_multiple_example_spec.rb +75 -0
  240. data/spec/unit/new_dsl_spec.rb +12 -0
  241. data/spec/unit/override_warning_spec.rb +94 -0
  242. data/spec/unit/persistence/active_record_persistence_multiple_spec.rb +635 -0
  243. data/spec/unit/persistence/active_record_persistence_spec.rb +852 -0
  244. data/spec/unit/persistence/dynamoid_persistence_multiple_spec.rb +135 -0
  245. data/spec/unit/persistence/dynamoid_persistence_spec.rb +84 -0
  246. data/spec/unit/persistence/mongoid_persistence_multiple_spec.rb +200 -0
  247. data/spec/unit/persistence/mongoid_persistence_spec.rb +177 -0
  248. data/spec/unit/persistence/no_brainer_persistence_multiple_spec.rb +198 -0
  249. data/spec/unit/persistence/no_brainer_persistence_spec.rb +158 -0
  250. data/spec/unit/persistence/redis_persistence_multiple_spec.rb +88 -0
  251. data/spec/unit/persistence/redis_persistence_spec.rb +53 -0
  252. data/spec/unit/persistence/sequel_persistence_multiple_spec.rb +148 -0
  253. data/spec/unit/persistence/sequel_persistence_spec.rb +368 -0
  254. data/spec/unit/readme_spec.rb +41 -0
  255. data/spec/unit/reloading_spec.rb +15 -0
  256. data/spec/unit/rspec_matcher_spec.rb +88 -0
  257. data/spec/unit/simple_custom_example_spec.rb +39 -0
  258. data/spec/unit/simple_example_spec.rb +57 -0
  259. data/spec/unit/simple_multiple_example_spec.rb +91 -0
  260. data/spec/unit/state_spec.rb +105 -0
  261. data/spec/unit/states_on_one_line_example_spec.rb +16 -0
  262. data/spec/unit/subclassing_multiple_spec.rb +74 -0
  263. data/spec/unit/subclassing_spec.rb +46 -0
  264. data/spec/unit/timestamps_spec.rb +32 -0
  265. data/spec/unit/transition_spec.rb +436 -0
  266. data/test/minitest_helper.rb +57 -0
  267. data/test/unit/minitest_matcher_test.rb +80 -0
  268. metadata +607 -60
  269. data/CHANGELOG +0 -33
  270. data/README.rdoc +0 -122
  271. data/TODO +0 -9
  272. data/doc/jamis.rb +0 -591
  273. data/lib/aasm/event.rb +0 -76
  274. data/lib/aasm/state.rb +0 -35
  275. data/lib/aasm/state_transition.rb +0 -36
data/README.md ADDED
@@ -0,0 +1,1524 @@
1
+ # AASM - Ruby state machines
2
+
3
+ [![Gem Version](https://badge.fury.io/rb/aasm.svg)](http://badge.fury.io/rb/aasm)
4
+ [![Build Status](https://travis-ci.org/aasm/aasm.svg?branch=master)](https://travis-ci.org/aasm/aasm)
5
+ [![Code Climate](https://codeclimate.com/github/aasm/aasm/badges/gpa.svg)](https://codeclimate.com/github/aasm/aasm)
6
+ [![codecov](https://codecov.io/gh/aasm/aasm/branch/master/graph/badge.svg)](https://codecov.io/gh/aasm/aasm)
7
+
8
+ ## Index
9
+ - [Upgrade from version 3 to 4](#upgrade-from-version-3-to-4)
10
+ - [Usage](#usage)
11
+ - [Callbacks](#callbacks)
12
+ - [Lifecycle](#lifecycle)
13
+ - [The current event triggered](#the-current-event-triggered)
14
+ - [Guards](#guards)
15
+ - [Transitions](#transitions)
16
+ - [Multiple state machines per class](#multiple-state-machines-per-class)
17
+ - [Handling naming conflicts between multiple state machines](#handling-naming-conflicts-between-multiple-state-machines)
18
+ - [Binding event](#binding-event)
19
+ - [Auto-generated Status Constants](#auto-generated-status-constants)
20
+ - [Extending AASM](#extending-aasm)
21
+ - [ActiveRecord](#activerecord)
22
+ - [Bang events](#bang-events)
23
+ - [Timestamps](#timestamps)
24
+ - [ActiveRecord enums](#activerecord-enums)
25
+ - [Sequel](#sequel)
26
+ - [Dynamoid](#dynamoid)
27
+ - [Mongoid](#mongoid)
28
+ - [Nobrainer](#nobrainer)
29
+ - [Redis](#redis)
30
+ - [Automatic Scopes](#automatic-scopes)
31
+ - [Transaction support](#transaction-support)
32
+ - [Pessimistic Locking](#pessimistic-locking)
33
+ - [Column name & migration](#column-name--migration)
34
+ - [Log State Changes](#log-state-changes)
35
+ - [Inspection](#inspection)
36
+ - [Warning output](#warning-output)
37
+ - [RubyMotion support](#rubymotion-support)
38
+ - [Testing](#testing)
39
+ - [RSpec](#rspec)
40
+ - [Minitest](#minitest)
41
+ - [Assertions](#assertions)
42
+ - [Expectations](#expectations)
43
+ - [Installation](#installation)
44
+ - [Manually from RubyGems.org](#manually-from-rubygemsorg)
45
+ - [Bundler](#or-if-you-are-using-bundler)
46
+ - [Building your own gems](#building-your-own-gems)
47
+ - [Generators](#generators)
48
+ - [Test suite with Docker](#docker)
49
+ - [Latest changes](#latest-changes)
50
+ - [Questions?](#questions)
51
+ - [Maintainers](#maintainers)
52
+ - [Contributing](CONTRIBUTING.md)
53
+ - [Warranty](#warranty)
54
+ - [License](#license)
55
+
56
+ This package contains AASM, a library for adding finite state machines to Ruby classes.
57
+
58
+ AASM started as the *acts_as_state_machine* plugin but has evolved into a more generic library
59
+ that no longer targets only ActiveRecord models. It currently provides adapters for many
60
+ ORMs but it can be used for any Ruby class, no matter what parent class it has (if any).
61
+
62
+ ## Upgrade from version 3 to 4
63
+
64
+ Take a look at the [README_FROM_VERSION_3_TO_4](https://github.com/aasm/aasm/blob/master/README_FROM_VERSION_3_TO_4.md) for details how to switch from version 3.x to 4.0 of _AASM_.
65
+
66
+ ## Usage
67
+
68
+ Adding a state machine is as simple as including the AASM module and start defining
69
+ **states** and **events** together with their **transitions**:
70
+
71
+ ```ruby
72
+ class Job
73
+ include AASM
74
+
75
+ aasm do
76
+ state :sleeping, initial: true
77
+ state :running, :cleaning
78
+
79
+ event :run do
80
+ transitions from: :sleeping, to: :running
81
+ end
82
+
83
+ event :clean do
84
+ transitions from: :running, to: :cleaning
85
+ end
86
+
87
+ event :sleep do
88
+ transitions from: [:running, :cleaning], to: :sleeping
89
+ end
90
+ end
91
+
92
+ end
93
+ ```
94
+
95
+ This provides you with a couple of public methods for instances of the class `Job`:
96
+
97
+ ```ruby
98
+ job = Job.new
99
+ job.sleeping? # => true
100
+ job.may_run? # => true
101
+ job.run
102
+ job.running? # => true
103
+ job.sleeping? # => false
104
+ job.may_run? # => false
105
+ job.run # => raises AASM::InvalidTransition
106
+ ```
107
+
108
+ If you don't like exceptions and prefer a simple `true` or `false` as response, tell
109
+ AASM not to be *whiny*:
110
+
111
+ ```ruby
112
+ class Job
113
+ ...
114
+ aasm whiny_transitions: false do
115
+ ...
116
+ end
117
+ end
118
+
119
+ job.running? # => true
120
+ job.may_run? # => false
121
+ job.run # => false
122
+ ```
123
+
124
+ When firing an event, you can pass a block to the method, it will be called only if
125
+ the transition succeeds :
126
+
127
+ ```ruby
128
+ job.run do
129
+ job.user.notify_job_ran # Will be called if job.may_run? is true
130
+ end
131
+ ```
132
+
133
+ ### Callbacks
134
+
135
+ You can define a number of callbacks for your events, transitions and states. These methods, Procs or classes will be
136
+ called when certain criteria are met, like entering a particular state:
137
+
138
+ ```ruby
139
+ class Job
140
+ include AASM
141
+
142
+ aasm do
143
+ state :sleeping, initial: true, before_enter: :do_something
144
+ state :running, before_enter: Proc.new { do_something && notify_somebody }
145
+ state :finished
146
+
147
+ after_all_transitions :log_status_change
148
+
149
+ event :run, after: :notify_somebody do
150
+ before do
151
+ log('Preparing to run')
152
+ end
153
+
154
+ transitions from: :sleeping, to: :running, after: Proc.new {|*args| set_process(*args) }
155
+ transitions from: :running, to: :finished, after: LogRunTime
156
+ end
157
+
158
+ event :sleep do
159
+ after do
160
+ ...
161
+ end
162
+ error do |e|
163
+ ...
164
+ end
165
+ transitions from: :running, to: :sleeping
166
+ end
167
+ end
168
+
169
+ def log_status_change
170
+ puts "changing from #{aasm.from_state} to #{aasm.to_state} (event: #{aasm.current_event})"
171
+ end
172
+
173
+ def set_process(name)
174
+ ...
175
+ end
176
+
177
+ def do_something
178
+ ...
179
+ end
180
+
181
+ def notify_somebody
182
+ ...
183
+ end
184
+
185
+ end
186
+
187
+ class LogRunTime
188
+ def call
189
+ log "Job was running for X seconds"
190
+ end
191
+ end
192
+ ```
193
+
194
+ In this case `do_something` is called before actually entering the state `sleeping`,
195
+ while `notify_somebody` is called after the transition `run` (from `sleeping` to `running`)
196
+ is finished.
197
+
198
+ AASM will also initialize `LogRunTime` and run the `call` method for you after the transition from `running` to `finished` in the example above. You can pass arguments to the class by defining an initialize method on it, like this:
199
+
200
+ Note that Procs are executed in the context of a record, it means that you don't need to expect the record as an argument, just call the methods you need.
201
+
202
+ ```ruby
203
+ class LogRunTime
204
+ # optional args parameter can be omitted, but if you define initialize
205
+ # you must accept the model instance as the first parameter to it.
206
+ def initialize(job, args = {})
207
+ @job = job
208
+ end
209
+
210
+ def call
211
+ log "Job was running for #{@job.run_time} seconds"
212
+ end
213
+ end
214
+ ```
215
+
216
+ #### Parameters
217
+ You can pass parameters to events:
218
+
219
+ ```ruby
220
+ job = Job.new
221
+ job.run(:defragmentation)
222
+ ```
223
+
224
+ All guards and after callbacks will receive these parameters. In this case `set_process` would be called with
225
+ `:defragmentation` argument.
226
+
227
+ If the first argument to the event is a state (e.g. `:running` or `:finished`), the first argument is consumed and
228
+ the state machine will attempt to transition to that state. Add comma separated parameter for gaurds and callbacks
229
+
230
+ ```ruby
231
+ job = Job.new
232
+ job.run(:running, :defragmentation)
233
+ ```
234
+ In this case `set_process` won't be called, job will transition to running state and callback will receive
235
+ :defragmentation as parameter
236
+
237
+ #### Error Handling
238
+ In case of an error during the event processing the error is rescued and passed to `:error`
239
+ callback, which can handle it or re-raise it for further propagation.
240
+
241
+ Also, you can define a method that will be called if any event fails:
242
+
243
+ ```ruby
244
+ def aasm_event_failed(event_name, old_state_name)
245
+ # use custom exception/messages, report metrics, etc
246
+ end
247
+ ```
248
+
249
+ During the transition's `:after` callback (and reliably only then, or in the global
250
+ `after_all_transitions` callback) you can access the originating state (the from-state)
251
+ and the target state (the to state), like this:
252
+
253
+ ```ruby
254
+ def set_process(name)
255
+ logger.info "from #{aasm.from_state} to #{aasm.to_state}"
256
+ end
257
+ ```
258
+
259
+ #### Lifecycle
260
+
261
+ Here you can see a list of all possible callbacks, together with their order of calling:
262
+
263
+ ```ruby
264
+ begin
265
+ event before_all_events
266
+ event before
267
+ event guards
268
+ transition guards
269
+ old_state before_exit
270
+ old_state exit
271
+ after_all_transitions
272
+ transition after
273
+ new_state before_enter
274
+ new_state enter
275
+ ...update state...
276
+ event before_success # if persist successful
277
+ transition success # if persist successful, database update not guaranteed
278
+ event success # if persist successful, database update not guaranteed
279
+ old_state after_exit
280
+ new_state after_enter
281
+ event after
282
+ event after_all_events
283
+ rescue
284
+ event error
285
+ event error_on_all_events
286
+ ensure
287
+ event ensure
288
+ event ensure_on_all_events
289
+ end
290
+ ```
291
+
292
+ Use event's `after_commit` callback if it should be fired after database update.
293
+
294
+ #### The current event triggered
295
+
296
+ While running the callbacks you can easily retrieve the name of the event triggered
297
+ by using `aasm.current_event`:
298
+
299
+ ```ruby
300
+ # taken the example callback from above
301
+ def do_something
302
+ puts "triggered #{aasm.current_event}"
303
+ end
304
+ ```
305
+
306
+ and then
307
+
308
+ ```ruby
309
+ job = Job.new
310
+
311
+ # without bang
312
+ job.sleep # => triggered :sleep
313
+
314
+ # with bang
315
+ job.sleep! # => triggered :sleep!
316
+ ```
317
+
318
+
319
+ ### Guards
320
+
321
+ Let's assume you want to allow particular transitions only if a defined condition is
322
+ given. For this you can set up a guard per transition, which will run before actually
323
+ running the transition. If the guard returns `false` the transition will be
324
+ denied (raising `AASM::InvalidTransition` or returning `false` itself):
325
+
326
+ ```ruby
327
+ class Cleaner
328
+ include AASM
329
+
330
+ aasm do
331
+ state :idle, initial: true
332
+ state :cleaning
333
+
334
+ event :clean do
335
+ transitions from: :idle, to: :cleaning, guard: :cleaning_needed?
336
+ end
337
+
338
+ event :clean_if_needed do
339
+ transitions from: :idle, to: :cleaning do
340
+ guard do
341
+ cleaning_needed?
342
+ end
343
+ end
344
+ transitions from: :idle, to: :idle
345
+ end
346
+
347
+ event :clean_if_dirty do
348
+ transitions from: :idle, to: :cleaning, guard: :if_dirty?
349
+ end
350
+ end
351
+
352
+ def cleaning_needed?
353
+ false
354
+ end
355
+
356
+ def if_dirty?(status)
357
+ status == :dirty
358
+ end
359
+ end
360
+
361
+ job = Cleaner.new
362
+ job.may_clean? # => false
363
+ job.clean # => raises AASM::InvalidTransition
364
+ job.may_clean_if_needed? # => true
365
+ job.clean_if_needed! # idle
366
+
367
+ job.clean_if_dirty(:clean) # => false
368
+ job.clean_if_dirty(:dirty) # => true
369
+ ```
370
+
371
+ You can even provide a number of guards, which all have to succeed to proceed
372
+
373
+ ```ruby
374
+ def walked_the_dog?; ...; end
375
+
376
+ event :sleep do
377
+ transitions from: :running, to: :sleeping, guards: [:cleaning_needed?, :walked_the_dog?]
378
+ end
379
+ ```
380
+
381
+ If you want to provide guards for all transitions within an event, you can use event guards
382
+
383
+ ```ruby
384
+ event :sleep, guards: [:walked_the_dog?] do
385
+ transitions from: :running, to: :sleeping, guards: [:cleaning_needed?]
386
+ transitions from: :cleaning, to: :sleeping
387
+ end
388
+ ```
389
+
390
+ If you prefer a more Ruby-like guard syntax, you can use `if` and `unless` as well:
391
+
392
+ ```ruby
393
+ event :clean do
394
+ transitions from: :running, to: :cleaning, if: :cleaning_needed?
395
+ end
396
+
397
+ event :sleep do
398
+ transitions from: :running, to: :sleeping, unless: :cleaning_needed?
399
+ end
400
+ end
401
+ ```
402
+
403
+ You can invoke a Class instead a method since this Class responds to `call`
404
+
405
+ ```ruby
406
+ event :sleep do
407
+ transitions from: :running, to: :sleeping, guards: Dog
408
+ end
409
+ ```
410
+ ```ruby
411
+ class Dog
412
+ def call
413
+ cleaning_needed? && walked?
414
+ end
415
+ ...
416
+ end
417
+ ```
418
+
419
+ ### Transitions
420
+
421
+ In the event of having multiple transitions for an event, the first transition that successfully completes will stop other transitions in the same event from being processed.
422
+
423
+ ```ruby
424
+ require 'aasm'
425
+
426
+ class Job
427
+ include AASM
428
+
429
+ aasm do
430
+ state :stage1, initial: true
431
+ state :stage2
432
+ state :stage3
433
+ state :completed
434
+
435
+ event :stage1_completed do
436
+ transitions from: :stage1, to: :stage3, guard: :stage2_completed?
437
+ transitions from: :stage1, to: :stage2
438
+ end
439
+ end
440
+
441
+ def stage2_completed?
442
+ true
443
+ end
444
+ end
445
+
446
+ job = Job.new
447
+ job.stage1_completed
448
+ job.aasm.current_state # stage3
449
+ ```
450
+
451
+ You can define transition from any defined state by omitting `from`:
452
+
453
+ ```ruby
454
+ event :abort do
455
+ transitions to: :aborted
456
+ end
457
+ ```
458
+
459
+ ### Display name for state
460
+
461
+ You can define display name for state using :display option
462
+
463
+ ```ruby
464
+ class Job
465
+ include AASM
466
+
467
+ aasm do
468
+ state :stage1, initial: true, display: 'First Stage'
469
+ state :stage2
470
+ state :stage3
471
+ end
472
+ end
473
+
474
+ job = Job.new
475
+ job.aasm.human_state
476
+
477
+ ```
478
+
479
+ ### Multiple state machines per class
480
+
481
+ Multiple state machines per class are supported. Be aware though that _AASM_ has been
482
+ built with one state machine per class in mind. Nonetheless, here's how to do it (see below). Please note that you will need to specify database columns for where your pertinent states will be stored - we have specified two columns `move_state` and `work_state` in the example below. See the [Column name & migration](https://github.com/aasm/aasm#column-name--migration) section for further info.
483
+
484
+ ```ruby
485
+ class SimpleMultipleExample
486
+ include AASM
487
+ aasm(:move, column: 'move_state') do
488
+ state :standing, initial: true
489
+ state :walking
490
+ state :running
491
+
492
+ event :walk do
493
+ transitions from: :standing, to: :walking
494
+ end
495
+ event :run do
496
+ transitions from: [:standing, :walking], to: :running
497
+ end
498
+ event :hold do
499
+ transitions from: [:walking, :running], to: :standing
500
+ end
501
+ end
502
+
503
+ aasm(:work, column: 'work_state') do
504
+ state :sleeping, initial: true
505
+ state :processing
506
+
507
+ event :start do
508
+ transitions from: :sleeping, to: :processing
509
+ end
510
+ event :stop do
511
+ transitions from: :processing, to: :sleeping
512
+ end
513
+ end
514
+ end
515
+
516
+ simple = SimpleMultipleExample.new
517
+
518
+ simple.aasm(:move).current_state
519
+ # => :standing
520
+ simple.aasm(:work).current_state
521
+ # => :sleeping
522
+
523
+ simple.start
524
+ simple.aasm(:move).current_state
525
+ # => :standing
526
+ simple.aasm(:work).current_state
527
+ # => :processing
528
+
529
+ ```
530
+
531
+ #### Handling naming conflicts between multiple state machines
532
+
533
+ _AASM_ doesn't prohibit to define the same event in more than one state
534
+ machine. If no namespace is provided, the latest definition "wins" and
535
+ overrides previous definitions. Nonetheless, a warning is issued:
536
+ `SimpleMultipleExample: overriding method 'run'!`.
537
+
538
+ Alternatively, you can provide a namespace for each state machine:
539
+
540
+ ```ruby
541
+ class NamespacedMultipleExample
542
+ include AASM
543
+ aasm(:status) do
544
+ state :unapproved, initial: true
545
+ state :approved
546
+
547
+ event :approve do
548
+ transitions from: :unapproved, to: :approved
549
+ end
550
+
551
+ event :unapprove do
552
+ transitions from: :approved, to: :unapproved
553
+ end
554
+ end
555
+
556
+ aasm(:review_status, namespace: :review) do
557
+ state :unapproved, initial: true
558
+ state :approved
559
+
560
+ event :approve do
561
+ transitions from: :unapproved, to: :approved
562
+ end
563
+
564
+ event :unapprove do
565
+ transitions from: :approved, to: :unapproved
566
+ end
567
+ end
568
+ end
569
+
570
+ namespaced = NamespacedMultipleExample.new
571
+
572
+ namespaced.aasm(:status).current_state
573
+ # => :unapproved
574
+ namespaced.aasm(:review_status).current_state
575
+ # => :unapproved
576
+ namespaced.approve_review
577
+ namespaced.aasm(:review_status).current_state
578
+ # => :approved
579
+ ```
580
+
581
+ All _AASM_ class- and instance-level `aasm` methods accept a state machine selector.
582
+ So, for example, to use inspection on a class level, you have to use
583
+
584
+ ```ruby
585
+ SimpleMultipleExample.aasm(:move).states.map(&:name)
586
+ # => [:standing, :walking, :running]
587
+ ```
588
+
589
+ ### Binding event
590
+
591
+ Allow an event to be bound to another
592
+ ```ruby
593
+ class Example
594
+ include AASM
595
+
596
+ aasm(:work) do
597
+ state :sleeping, initial: true
598
+ state :processing
599
+
600
+ event :start do
601
+ transitions from: :sleeping, to: :processing
602
+ end
603
+ event :stop do
604
+ transitions from: :processing, to: :sleeping
605
+ end
606
+ end
607
+
608
+ aasm(:question) do
609
+ state :answered, initial: true
610
+ state :asked
611
+
612
+ event :ask, binding_event: :start do
613
+ transitions from: :answered, to: :asked
614
+ end
615
+ event :answer, binding_event: :stop do
616
+ transitions from: :asked, to: :answered
617
+ end
618
+ end
619
+ end
620
+
621
+ example = Example.new
622
+ example.aasm(:work).current_state #=> :sleeping
623
+ example.aasm(:question).current_state #=> :answered
624
+ example.ask
625
+ example.aasm(:work).current_state #=> :processing
626
+ example.aasm(:question).current_state #=> :asked
627
+ ```
628
+
629
+ ### Auto-generated Status Constants
630
+
631
+ AASM automatically [generates constants](https://github.com/aasm/aasm/pull/60)
632
+ for each status so you don't have to explicitly define them.
633
+
634
+ ```ruby
635
+ class Foo
636
+ include AASM
637
+
638
+ aasm do
639
+ state :initialized
640
+ state :calculated
641
+ state :finalized
642
+ end
643
+ end
644
+
645
+ > Foo::STATE_INITIALIZED
646
+ #=> :initialized
647
+ > Foo::STATE_CALCULATED
648
+ #=> :calculated
649
+ ```
650
+
651
+ ### Extending AASM
652
+
653
+ AASM allows you to easily extend `AASM::Base` for your own application purposes.
654
+
655
+ Let's suppose we have common logic across many AASM models. We can embody this logic in a sub-class of `AASM::Base`.
656
+
657
+ ```ruby
658
+ class CustomAASMBase < AASM::Base
659
+ # A custom transiton that we want available across many AASM models.
660
+ def count_transitions!
661
+ klass.class_eval do
662
+ aasm with_klass: CustomAASMBase do
663
+ after_all_transitions :increment_transition_count
664
+ end
665
+ end
666
+ end
667
+
668
+ # A custom annotation that we want available across many AASM models.
669
+ def requires_guards!
670
+ klass.class_eval do
671
+ attr_reader :authorizable_called,
672
+ :transition_count,
673
+ :fillable_called
674
+
675
+ def authorizable?
676
+ @authorizable_called = true
677
+ end
678
+
679
+ def fillable?
680
+ @fillable_called = true
681
+ end
682
+
683
+ def increment_transition_count
684
+ @transition_count ||= 0
685
+ @transition_count += 1
686
+ end
687
+ end
688
+ end
689
+ end
690
+ ```
691
+
692
+ When we declare our model that has an AASM state machine, we simply declare the AASM block with a `:with_klass` key to our own class.
693
+
694
+ ```ruby
695
+ class SimpleCustomExample
696
+ include AASM
697
+
698
+ # Let's build an AASM state machine with our custom class.
699
+ aasm with_klass: CustomAASMBase do
700
+ requires_guards!
701
+ count_transitions!
702
+
703
+ state :initialised, initial: true
704
+ state :filled_out
705
+ state :authorised
706
+
707
+ event :fill_out do
708
+ transitions from: :initialised, to: :filled_out, guard: :fillable?
709
+ end
710
+ event :authorise do
711
+ transitions from: :filled_out, to: :authorised, guard: :authorizable?
712
+ end
713
+ end
714
+ end
715
+ ```
716
+
717
+
718
+ ### ActiveRecord
719
+
720
+ AASM comes with support for ActiveRecord and allows automatic persisting of the object's
721
+ state in the database.
722
+
723
+ Add `gem 'after_commit_everywhere', '~> 1.0'` to your Gemfile.
724
+
725
+ ```ruby
726
+ class Job < ActiveRecord::Base
727
+ include AASM
728
+
729
+ aasm do # default column: aasm_state
730
+ state :sleeping, initial: true
731
+ state :running
732
+
733
+ event :run do
734
+ transitions from: :sleeping, to: :running
735
+ end
736
+
737
+ event :sleep do
738
+ transitions from: :running, to: :sleeping
739
+ end
740
+ end
741
+
742
+ end
743
+ ```
744
+
745
+ ### Bang events
746
+
747
+ You can tell AASM to auto-save the object or leave it unsaved
748
+
749
+ ```ruby
750
+ job = Job.new
751
+ job.run # not saved
752
+ job.run! # saved
753
+
754
+ # or
755
+ job.aasm.fire(:run) # not saved
756
+ job.aasm.fire!(:run) # saved
757
+ ```
758
+
759
+ Saving includes running all validations on the `Job` class. If
760
+ `whiny_persistence` flag is set to `true`, exception is raised in case of
761
+ failure. If `whiny_persistence` flag is set to false, methods with a bang return
762
+ `true` if the state transition is successful or `false` if an error occurs.
763
+
764
+ If you want make sure the state gets saved without running validations (and
765
+ thereby maybe persisting an invalid object state), simply tell AASM to skip the
766
+ validations. Be aware that when skipping validations, only the state column will
767
+ be updated in the database (just like ActiveRecord `update_column` is working).
768
+
769
+ ```ruby
770
+ class Job < ActiveRecord::Base
771
+ include AASM
772
+
773
+ aasm skip_validation_on_save: true do
774
+ state :sleeping, initial: true
775
+ state :running
776
+
777
+ event :run do
778
+ transitions from: :sleeping, to: :running
779
+ end
780
+
781
+ event :sleep do
782
+ transitions from: :running, to: :sleeping
783
+ end
784
+ end
785
+
786
+ end
787
+ ```
788
+
789
+ Also You can skip the validation at instance level with `some_event_name_without_validation!` method.
790
+ With this you have the flexibility of having validation for all your transitions by default and then skip it wherever required.
791
+ Please note that only state column will be updated as mentioned in the above example.
792
+
793
+ ```ruby
794
+ job.run_without_validation!
795
+ ```
796
+
797
+ If you want to make sure that the _AASM_ column for storing the state is not directly assigned,
798
+ configure _AASM_ to not allow direct assignment, like this:
799
+
800
+ ```ruby
801
+ class Job < ActiveRecord::Base
802
+ include AASM
803
+
804
+ aasm no_direct_assignment: true do
805
+ state :sleeping, initial: true
806
+ state :running
807
+
808
+ event :run do
809
+ transitions from: :sleeping, to: :running
810
+ end
811
+ end
812
+
813
+ end
814
+ ```
815
+
816
+ resulting in this:
817
+
818
+ ```ruby
819
+ job = Job.create
820
+ job.aasm_state # => 'sleeping'
821
+ job.aasm_state = :running # => raises AASM::NoDirectAssignmentError
822
+ job.aasm_state # => 'sleeping'
823
+ ```
824
+
825
+ ### Timestamps
826
+
827
+ You can tell _AASM_ to try to write a timestamp whenever a new state is entered.
828
+ If `timestamps: true` is set, _AASM_ will look for a field named like the new state plus `_at` and try to fill it:
829
+
830
+ ```ruby
831
+ class Job < ActiveRecord::Base
832
+ include AASM
833
+
834
+ aasm timestamps: true do
835
+ state :sleeping, initial: true
836
+ state :running
837
+
838
+ event :run do
839
+ transitions from: :sleeping, to: :running
840
+ end
841
+ end
842
+ end
843
+ ```
844
+
845
+ resulting in this:
846
+
847
+ ```ruby
848
+ job = Job.create
849
+ job.running_at # => nil
850
+ job.run!
851
+ job.running_at # => 2020-02-20 20:00:00
852
+ ```
853
+
854
+ Missing timestamp fields are silently ignored, so it is not necessary to have setters (such as ActiveRecord columns) for *all* states when using this option.
855
+
856
+ #### ActiveRecord enums
857
+
858
+ You can use
859
+ [enumerations](http://edgeapi.rubyonrails.org/classes/ActiveRecord/Enum.html)
860
+ in Rails 4.1+ for your state column:
861
+
862
+ ```ruby
863
+ class Job < ActiveRecord::Base
864
+ include AASM
865
+
866
+ enum state: {
867
+ sleeping: 5,
868
+ running: 99
869
+ }
870
+
871
+ aasm column: :state, enum: true do
872
+ state :sleeping, initial: true
873
+ state :running
874
+ end
875
+ end
876
+ ```
877
+
878
+ You can explicitly pass the name of the method which provides access
879
+ to the enumeration mapping as a value of ```enum```, or you can simply
880
+ set it to ```true```. In the latter case AASM will try to use
881
+ pluralized column name to access possible enum states.
882
+
883
+ Furthermore, if your column has integer type (which is normally the
884
+ case when you're working with Rails enums), you can omit ```:enum```
885
+ setting --- AASM auto-detects this situation and enabled enum
886
+ support. If anything goes wrong, you can disable enum functionality
887
+ and fall back to the default behavior by setting ```:enum```
888
+ to ```false```.
889
+
890
+ ### Sequel
891
+
892
+ AASM also supports [Sequel](http://sequel.jeremyevans.net/) besides _ActiveRecord_, and _Mongoid_.
893
+
894
+ ```ruby
895
+ class Job < Sequel::Model
896
+ include AASM
897
+
898
+ aasm do # default column: aasm_state
899
+ ...
900
+ end
901
+ end
902
+ ```
903
+
904
+ However it's not yet as feature complete as _ActiveRecord_. For example, there are
905
+ scopes defined yet. See [Automatic Scopes](#automatic-scopes).
906
+
907
+ ### Dynamoid
908
+
909
+ Since version `4.8.0` _AASM_ also supports [Dynamoid](http://joshsymonds.com/Dynamoid/) as
910
+ persistence ORM.
911
+
912
+ ### Mongoid
913
+
914
+ AASM also supports persistence to Mongodb if you're using Mongoid. Make sure
915
+ to include Mongoid::Document before you include AASM.
916
+
917
+ ```ruby
918
+ class Job
919
+ include Mongoid::Document
920
+ include AASM
921
+ field :aasm_state
922
+ aasm do
923
+ ...
924
+ end
925
+ end
926
+ ```
927
+
928
+ ### NoBrainer
929
+
930
+ AASM also supports persistence to [RethinkDB](https://www.rethinkdb.com/)
931
+ if you're using [Nobrainer](http://nobrainer.io/).
932
+ Make sure to include NoBrainer::Document before you include AASM.
933
+
934
+ ```ruby
935
+ class Job
936
+ include NoBrainer::Document
937
+ include AASM
938
+ field :aasm_state
939
+ aasm do
940
+ ...
941
+ end
942
+ end
943
+ ```
944
+
945
+ ### Redis
946
+
947
+ AASM also supports persistence in Redis via
948
+ [Redis::Objects](https://github.com/nateware/redis-objects).
949
+ Make sure to include Redis::Objects before you include AASM. Note that non-bang
950
+ events will work as bang events, persisting the changes on every call.
951
+
952
+ ```ruby
953
+ class User
954
+ include Redis::Objects
955
+ include AASM
956
+
957
+ aasm do
958
+ end
959
+ end
960
+ ```
961
+
962
+ ### Automatic Scopes
963
+
964
+ AASM will automatically create scope methods for each state in the model.
965
+
966
+ ```ruby
967
+ class Job < ActiveRecord::Base
968
+ include AASM
969
+
970
+ aasm do
971
+ state :sleeping, initial: true
972
+ state :running
973
+ state :cleaning
974
+ end
975
+
976
+ def self.sleeping
977
+ "This method name is already in use"
978
+ end
979
+ end
980
+ ```
981
+
982
+ ```ruby
983
+ class JobsController < ApplicationController
984
+ def index
985
+ @running_jobs = Job.running
986
+ @recent_cleaning_jobs = Job.cleaning.where('created_at >= ?', 3.days.ago)
987
+
988
+ # @sleeping_jobs = Job.sleeping #=> "This method name is already in use"
989
+ end
990
+ end
991
+ ```
992
+
993
+ If you don't need scopes (or simply don't want them), disable their creation when
994
+ defining the `AASM` states, like this:
995
+
996
+ ```ruby
997
+ class Job < ActiveRecord::Base
998
+ include AASM
999
+
1000
+ aasm create_scopes: false do
1001
+ state :sleeping, initial: true
1002
+ state :running
1003
+ state :cleaning
1004
+ end
1005
+ end
1006
+ ```
1007
+
1008
+
1009
+ ### Transaction support
1010
+
1011
+ Since version *3.0.13* AASM supports ActiveRecord transactions. So whenever a transition
1012
+ callback or the state update fails, all changes to any database record are rolled back.
1013
+ Mongodb does not support transactions.
1014
+
1015
+ There are currently 3 transactional callbacks that can be handled on the event, and 2 transactional callbacks for all events.
1016
+
1017
+ ```ruby
1018
+ event before_all_transactions
1019
+ event before_transaction
1020
+ event aasm_fire_event (within transaction)
1021
+ event after_commit (if event successful)
1022
+ event after_transaction
1023
+ event after_all_transactions
1024
+ ```
1025
+
1026
+ If you want to make sure a depending action happens only after the transaction is committed,
1027
+ use the `after_commit` callback along with the auto-save (bang) methods, like this:
1028
+
1029
+ ```ruby
1030
+ class Job < ActiveRecord::Base
1031
+ include AASM
1032
+
1033
+ aasm do
1034
+ state :sleeping, initial: true
1035
+ state :running
1036
+
1037
+ event :run, after_commit: :notify_about_running_job do
1038
+ transitions from: :sleeping, to: :running
1039
+ end
1040
+ end
1041
+
1042
+ def notify_about_running_job
1043
+ ...
1044
+ end
1045
+ end
1046
+
1047
+ job = Job.where(state: 'sleeping').first!
1048
+ job.run! # Saves the model and triggers the after_commit callback
1049
+ ```
1050
+
1051
+ Note that the following will not run the `after_commit` callbacks because
1052
+ the auto-save method is not used:
1053
+
1054
+ ```ruby
1055
+ job = Job.where(state: 'sleeping').first!
1056
+ job.run
1057
+ job.save! #notify_about_running_job is not run
1058
+ ```
1059
+
1060
+ Please note that `:after_commit` AASM callbacks behaves around custom implementation
1061
+ of transaction pattern rather than a real-life DB transaction. This fact still causes
1062
+ the race conditions and redundant callback calls within nested transaction. In order
1063
+ to fix that it's highly recommended to add `gem 'after_commit_everywhere', '~> 1.0'`
1064
+ to your `Gemfile`.
1065
+
1066
+ If you want to encapsulate state changes within an own transaction, the behavior
1067
+ of this nested transaction might be confusing. Take a look at
1068
+ [ActiveRecord Nested Transactions](http://api.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html)
1069
+ if you want to know more about this. Nevertheless, AASM by default requires a new transaction
1070
+ `transaction(requires_new: true)`. You can override this behavior by changing
1071
+ the configuration
1072
+
1073
+ ```ruby
1074
+ class Job < ActiveRecord::Base
1075
+ include AASM
1076
+
1077
+ aasm requires_new_transaction: false do
1078
+ ...
1079
+ end
1080
+
1081
+ ...
1082
+ end
1083
+ ```
1084
+
1085
+ which then leads to `transaction(requires_new: false)`, the Rails default.
1086
+
1087
+ Additionally, if you do not want any of your active record actions to be
1088
+ wrapped in a transaction, you can specify the `use_transactions` flag. This can
1089
+ be useful if you want want to persist things to the database that happen as a
1090
+ result of a transaction or callback, even when some error occurs. The
1091
+ `use_transactions` flag is true by default.
1092
+
1093
+ ```ruby
1094
+ class Job < ActiveRecord::Base
1095
+ include AASM
1096
+
1097
+ aasm use_transactions: false do
1098
+ ...
1099
+ end
1100
+
1101
+ ...
1102
+ end
1103
+ ```
1104
+
1105
+ ### Pessimistic Locking
1106
+
1107
+ AASM supports [Active Record pessimistic locking via `with_lock`](http://api.rubyonrails.org/classes/ActiveRecord/Locking/Pessimistic.html#method-i-with_lock) for database persistence layers.
1108
+
1109
+ | Option | Purpose |
1110
+ | ------ | ------- |
1111
+ | `false` (default) | No lock is obtained | |
1112
+ | `true` | Obtain a blocking pessimistic lock e.g. `FOR UPDATE` |
1113
+ | String | Obtain a lock based on the SQL string e.g. `FOR UPDATE NOWAIT` |
1114
+
1115
+
1116
+ ```ruby
1117
+ class Job < ActiveRecord::Base
1118
+ include AASM
1119
+
1120
+ aasm requires_lock: true do
1121
+ ...
1122
+ end
1123
+
1124
+ ...
1125
+ end
1126
+ ```
1127
+
1128
+ ```ruby
1129
+ class Job < ActiveRecord::Base
1130
+ include AASM
1131
+
1132
+ aasm requires_lock: 'FOR UPDATE NOWAIT' do
1133
+ ...
1134
+ end
1135
+
1136
+ ...
1137
+ end
1138
+ ```
1139
+
1140
+
1141
+ ### Column name & migration
1142
+
1143
+ As a default AASM uses the column `aasm_state` to store the states. You can override
1144
+ this by defining your favorite column name, using `:column` like this:
1145
+
1146
+ ```ruby
1147
+ class Job < ActiveRecord::Base
1148
+ include AASM
1149
+
1150
+ aasm column: :my_state do
1151
+ ...
1152
+ end
1153
+
1154
+ aasm :another_state_machine, column: :second_state do
1155
+ ...
1156
+ end
1157
+ end
1158
+ ```
1159
+
1160
+ Whatever column name is used, make sure to add a migration to provide this column
1161
+ (of type `string`).
1162
+ Do not add default value for column at the database level. If you add default
1163
+ value in database then AASM callbacks on the initial state will not be fired upon
1164
+ instantiation of the model.
1165
+
1166
+ ```ruby
1167
+ class AddJobState < ActiveRecord::Migration
1168
+ def self.up
1169
+ add_column :jobs, :aasm_state, :string
1170
+ end
1171
+
1172
+ def self.down
1173
+ remove_column :jobs, :aasm_state
1174
+ end
1175
+ end
1176
+ ```
1177
+
1178
+ ### Log State Changes
1179
+
1180
+ Logging state change can be done using [paper_trail](https://github.com/paper-trail-gem/paper_trail) gem
1181
+
1182
+ Example of implementation can be found here [https://github.com/nitsujri/aasm-papertrail-example](https://github.com/nitsujri/aasm-papertrail-example)
1183
+
1184
+
1185
+ ### Inspection
1186
+
1187
+ AASM supports query methods for states and events
1188
+
1189
+ Given the following `Job` class:
1190
+
1191
+ ```ruby
1192
+ class Job
1193
+ include AASM
1194
+
1195
+ aasm do
1196
+ state :sleeping, initial: true
1197
+ state :running, :cleaning
1198
+
1199
+ event :run do
1200
+ transitions from: :sleeping, to: :running
1201
+ end
1202
+
1203
+ event :clean do
1204
+ transitions from: :running, to: :cleaning, guard: :cleaning_needed?
1205
+ end
1206
+
1207
+ event :sleep do
1208
+ transitions from: [:running, :cleaning], to: :sleeping
1209
+ end
1210
+ end
1211
+
1212
+ def cleaning_needed?
1213
+ false
1214
+ end
1215
+ end
1216
+ ```
1217
+
1218
+ ```ruby
1219
+ # show all states
1220
+ Job.aasm.states.map(&:name)
1221
+ #=> [:sleeping, :running, :cleaning]
1222
+
1223
+ job = Job.new
1224
+
1225
+ # show all permitted states (from initial state)
1226
+ job.aasm.states(permitted: true).map(&:name)
1227
+ #=> [:running]
1228
+
1229
+ # List all the permitted transitions(event and state pairs) from initial state
1230
+ job.aasm.permitted_transitions
1231
+ #=> [{ :event => :run, :state => :running }]
1232
+
1233
+ job.run
1234
+ job.aasm.states(permitted: true).map(&:name)
1235
+ #=> [:sleeping]
1236
+
1237
+ # show all non permitted states
1238
+ job.aasm.states(permitted: false).map(&:name)
1239
+ #=> [:cleaning]
1240
+
1241
+ # show all possible (triggerable) events from the current state
1242
+ job.aasm.events.map(&:name)
1243
+ #=> [:clean, :sleep]
1244
+
1245
+ # show all permitted events
1246
+ job.aasm.events(permitted: true).map(&:name)
1247
+ #=> [:sleep]
1248
+
1249
+ # show all non permitted events
1250
+ job.aasm.events(permitted: false).map(&:name)
1251
+ #=> [:clean]
1252
+
1253
+ # show all possible events except a specific one
1254
+ job.aasm.events(reject: :sleep).map(&:name)
1255
+ #=> [:clean]
1256
+
1257
+ # list states for select
1258
+ Job.aasm.states_for_select
1259
+ #=> [["Sleeping", "sleeping"], ["Running", "running"], ["Cleaning", "cleaning"]]
1260
+
1261
+ # show permitted states with guard parameter
1262
+ job.aasm.states({permitted: true}, guard_parameter).map(&:name)
1263
+ ```
1264
+
1265
+
1266
+ ### Warning output
1267
+
1268
+ Warnings are by default printed to `STDERR`. If you want to log those warnings to another output,
1269
+ use
1270
+
1271
+ ```ruby
1272
+ class Job
1273
+ include AASM
1274
+
1275
+ aasm logger: Rails.logger do
1276
+ ...
1277
+ end
1278
+ end
1279
+ ```
1280
+
1281
+ You can hide warnings by setting `AASM::Configuration.hide_warnings = true`
1282
+
1283
+ ### RubyMotion support
1284
+
1285
+ Now supports [CodeDataQuery](https://github.com/infinitered/cdq.git) !
1286
+ However I'm still in the process of submitting my compatibility updates to their repository.
1287
+ In the meantime you can use [my fork](https://github.com/Infotaku/cdq.git), there may still be some minor issues but I intend to extensively use it myself, so fixes should come fast.
1288
+
1289
+ Warnings:
1290
+ - Due to RubyMotion Proc's lack of 'source_location' method, it may be harder
1291
+ to find out the origin of a "cannot transition from" error. I would recommend using
1292
+ the 'instance method symbol / string' way whenever possible when defining guardians and callbacks.
1293
+
1294
+
1295
+ ### Testing
1296
+
1297
+ #### RSpec
1298
+
1299
+ AASM provides some matchers for [RSpec](http://rspec.info):
1300
+ * `transition_from`,
1301
+ * `have_state`, `allow_event`
1302
+ * and `allow_transition_to`.
1303
+
1304
+ ##### Installation Instructions:
1305
+ * Add `require 'aasm/rspec'` to your `spec_helper.rb` file.
1306
+
1307
+ ##### Examples Of Usage in Rspec:
1308
+
1309
+ ```ruby
1310
+ # classes with only the default state machine
1311
+ job = Job.new
1312
+ expect(job).to transition_from(:sleeping).to(:running).on_event(:run)
1313
+ expect(job).not_to transition_from(:sleeping).to(:cleaning).on_event(:run)
1314
+ expect(job).to have_state(:sleeping)
1315
+ expect(job).not_to have_state(:running)
1316
+ expect(job).to allow_event :run
1317
+ expect(job).to_not allow_event :clean
1318
+ expect(job).to allow_transition_to(:running)
1319
+ expect(job).to_not allow_transition_to(:cleaning)
1320
+ # on_event also accept multiple arguments
1321
+ expect(job).to transition_from(:sleeping).to(:running).on_event(:run, :defragmentation)
1322
+
1323
+ # classes with multiple state machine
1324
+ multiple = SimpleMultipleExample.new
1325
+ expect(multiple).to transition_from(:standing).to(:walking).on_event(:walk).on(:move)
1326
+ expect(multiple).to_not transition_from(:standing).to(:running).on_event(:walk).on(:move)
1327
+ expect(multiple).to have_state(:standing).on(:move)
1328
+ expect(multiple).not_to have_state(:walking).on(:move)
1329
+ expect(multiple).to allow_event(:walk).on(:move)
1330
+ expect(multiple).to_not allow_event(:hold).on(:move)
1331
+ expect(multiple).to allow_transition_to(:walking).on(:move)
1332
+ expect(multiple).to_not allow_transition_to(:running).on(:move)
1333
+ expect(multiple).to transition_from(:sleeping).to(:processing).on_event(:start).on(:work)
1334
+ expect(multiple).to_not transition_from(:sleeping).to(:sleeping).on_event(:start).on(:work)
1335
+ expect(multiple).to have_state(:sleeping).on(:work)
1336
+ expect(multiple).not_to have_state(:processing).on(:work)
1337
+ expect(multiple).to allow_event(:start).on(:move)
1338
+ expect(multiple).to_not allow_event(:stop).on(:move)
1339
+ expect(multiple).to allow_transition_to(:processing).on(:move)
1340
+ expect(multiple).to_not allow_transition_to(:sleeping).on(:move)
1341
+ # allow_event also accepts arguments
1342
+ expect(job).to allow_event(:run).with(:defragmentation)
1343
+
1344
+ ```
1345
+
1346
+ #### Minitest
1347
+
1348
+ AASM provides assertions and rspec-like expectations for [Minitest](https://github.com/seattlerb/minitest).
1349
+
1350
+ ##### Assertions
1351
+
1352
+ List of supported assertions: `assert_have_state`, `refute_have_state`, `assert_transitions_from`, `refute_transitions_from`, `assert_event_allowed`, `refute_event_allowed`, `assert_transition_to_allowed`, `refute_transition_to_allowed`.
1353
+
1354
+
1355
+ ##### Examples Of Usage (Minitest):
1356
+
1357
+ Add `require 'aasm/minitest'` to your `test_helper.rb` file and use them like this:
1358
+
1359
+ ```ruby
1360
+ # classes with only the default state machine
1361
+ job = Job.new
1362
+ assert_transitions_from job, :sleeping, to: :running, on_event: :run
1363
+ refute_transitions_from job, :sleeping, to: :cleaning, on_event: :run
1364
+ assert_have_state job, :sleeping
1365
+ refute_have_state job, :running
1366
+ assert_event_allowed job, :run
1367
+ refute_event_allowed job, :clean
1368
+ assert_transition_to_allowed job, :running
1369
+ refute_transition_to_allowed job, :cleaning
1370
+ # on_event also accept arguments
1371
+ assert_transitions_from job, :sleeping, :defragmentation, to: :running, on_event: :run
1372
+
1373
+ # classes with multiple state machine
1374
+ multiple = SimpleMultipleExample.new
1375
+ assert_transitions_from multiple, :standing, to: :walking, on_event: :walk, on: :move
1376
+ refute_transitions_from multiple, :standing, to: :running, on_event: :walk, on: :move
1377
+ assert_have_state multiple, :standing, on: :move
1378
+ refute_have_state multiple, :walking, on: :move
1379
+ assert_event_allowed multiple, :walk, on: :move
1380
+ refute_event_allowed multiple, :hold, on: :move
1381
+ assert_transition_to_allowed multiple, :walking, on: :move
1382
+ refute_transition_to_allowed multiple, :running, on: :move
1383
+ assert_transitions_from multiple, :sleeping, to: :processing, on_event: :start, on: :work
1384
+ refute_transitions_from multiple, :sleeping, to: :sleeping, on_event: :start, on: :work
1385
+ assert_have_state multiple, :sleeping, on: :work
1386
+ refute_have_state multiple, :processing, on: :work
1387
+ assert_event_allowed multiple, :start, on: :move
1388
+ refute_event_allowed multiple, :stop, on: :move
1389
+ assert_transition_to_allowed multiple, :processing, on: :move
1390
+ refute_transition_to_allowed multiple, :sleeping, on: :move
1391
+ ```
1392
+
1393
+ ##### Expectations
1394
+
1395
+ List of supported expectations: `must_transition_from`, `wont_transition_from`, `must_have_state`, `wont_have_state`, `must_allow_event`, `wont_allow_event`, `must_allow_transition_to`, `wont_allow_transition_to`.
1396
+
1397
+ Add `require 'aasm/minitest_spec'` to your `test_helper.rb` file and use them like this:
1398
+
1399
+ ```ruby
1400
+ # classes with only the default state machine
1401
+ job = Job.new
1402
+ job.must_transition_from :sleeping, to: :running, on_event: :run
1403
+ job.wont_transition_from :sleeping, to: :cleaning, on_event: :run
1404
+ job.must_have_state :sleeping
1405
+ job.wont_have_state :running
1406
+ job.must_allow_event :run
1407
+ job.wont_allow_event :clean
1408
+ job.must_allow_transition_to :running
1409
+ job.wont_allow_transition_to :cleaning
1410
+ # on_event also accept arguments
1411
+ job.must_transition_from :sleeping, :defragmentation, to: :running, on_event: :run
1412
+
1413
+ # classes with multiple state machine
1414
+ multiple = SimpleMultipleExample.new
1415
+ multiple.must_transition_from :standing, to: :walking, on_event: :walk, on: :move
1416
+ multiple.wont_transition_from :standing, to: :running, on_event: :walk, on: :move
1417
+ multiple.must_have_state :standing, on: :move
1418
+ multiple.wont_have_state :walking, on: :move
1419
+ multiple.must_allow_event :walk, on: :move
1420
+ multiple.wont_allow_event :hold, on: :move
1421
+ multiple.must_allow_transition_to :walking, on: :move
1422
+ multiple.wont_allow_transition_to :running, on: :move
1423
+ multiple.must_transition_from :sleeping, to: :processing, on_event: :start, on: :work
1424
+ multiple.wont_transition_from :sleeping, to: :sleeping, on_event: :start, on: :work
1425
+ multiple.must_have_state :sleeping, on: :work
1426
+ multiple.wont_have_state :processing, on: :work
1427
+ multiple.must_allow_event :start, on: :move
1428
+ multiple.wont_allow_event :stop, on: :move
1429
+ multiple.must_allow_transition_to :processing, on: :move
1430
+ multiple.wont_allow_transition_to :sleeping, on: :move
1431
+ ```
1432
+
1433
+ ## <a id="installation">Installation ##
1434
+
1435
+ ### Manually from RubyGems.org ###
1436
+
1437
+ ```sh
1438
+ % gem install aasm
1439
+ ```
1440
+
1441
+ ### Or if you are using Bundler ###
1442
+
1443
+ ```ruby
1444
+ # Gemfile
1445
+ gem 'aasm'
1446
+ ```
1447
+
1448
+ ### Building your own gems ###
1449
+
1450
+ ```sh
1451
+ % rake build
1452
+ % sudo gem install pkg/aasm-x.y.z.gem
1453
+ ```
1454
+
1455
+ ### Generators
1456
+
1457
+ After installing AASM you can run generator:
1458
+
1459
+ ```sh
1460
+ % rails generate aasm NAME [COLUMN_NAME]
1461
+ ```
1462
+ Replace NAME with the Model name, COLUMN_NAME is optional(default is 'aasm_state').
1463
+ This will create a model (if one does not exist) and configure it with aasm block.
1464
+ For Active record orm a migration file is added to add aasm state column to table.
1465
+
1466
+ ### Docker
1467
+
1468
+ Run test suite easily on docker
1469
+ ```
1470
+ 1. docker-compose build aasm
1471
+ 2. docker-compose run --rm aasm
1472
+ ```
1473
+
1474
+ ## Latest changes ##
1475
+
1476
+ Take a look at the [CHANGELOG](https://github.com/aasm/aasm/blob/master/CHANGELOG.md) for details about recent changes to the current version.
1477
+
1478
+ ## Questions? ##
1479
+
1480
+ Feel free to
1481
+
1482
+ * [create an issue on GitHub](https://github.com/aasm/aasm/issues)
1483
+ * [ask a question on StackOverflow](http://stackoverflow.com) (tag with `aasm`)
1484
+ * send us a tweet [@aasm](http://twitter.com/aasm)
1485
+
1486
+ ## Maintainers ##
1487
+
1488
+ * [Scott Barron](https://github.com/rubyist) (2006–2009, original author)
1489
+ * [Travis Tilley](https://github.com/ttilley) (2009–2011)
1490
+ * [Thorsten Böttger](http://github.com/alto) (since 2011)
1491
+ * [Anil Maurya](http://github.com/anilmaurya) (since 2016)
1492
+
1493
+
1494
+ ## [Contributing](CONTRIBUTING.md)
1495
+
1496
+ ## Warranty ##
1497
+
1498
+ This software is provided "as is" and without any express or
1499
+ implied warranties, including, without limitation, the implied
1500
+ warranties of merchantibility and fitness for a particular
1501
+ purpose.
1502
+
1503
+ ## License ##
1504
+
1505
+ Copyright (c) 2006-2017 Scott Barron
1506
+
1507
+ Permission is hereby granted, free of charge, to any person obtaining
1508
+ a copy of this software and associated documentation files (the
1509
+ "Software"), to deal in the Software without restriction, including
1510
+ without limitation the rights to use, copy, modify, merge, publish,
1511
+ distribute, sublicense, and/or sell copies of the Software, and to
1512
+ permit persons to whom the Software is furnished to do so, subject to
1513
+ the following conditions:
1514
+
1515
+ The above copyright notice and this permission notice shall be
1516
+ included in all copies or substantial portions of the Software.
1517
+
1518
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
1519
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
1520
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
1521
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
1522
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
1523
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
1524
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.