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
@@ -1,5 +1,5 @@
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][:activerecord] = translations[:en].delete(:activemodel)
4
4
 
5
5
  # Only ActiveRecord 2.3.5+ can pull i18n >= 0.1.3 from system-wide gems (and
@@ -10,7 +10,7 @@ module StateMachine
10
10
  define_helper :instance, <<-end_eval, __FILE__, __LINE__ + 1
11
11
  def attributes_from_column_definition(*)
12
12
  result = super
13
- self.class.state_machines.initialize_states(self, :dynamic => false, :to => result)
13
+ self.class.state_machines.initialize_states(self, :static => :force, :dynamic => false, :to => result)
14
14
  result
15
15
  end
16
16
  end_eval
@@ -81,10 +81,6 @@ module StateMachine
81
81
  :activerecord
82
82
  end
83
83
 
84
- def action_hook
85
- action == :save ? :create_or_update : super
86
- end
87
-
88
84
  def load_observer_extensions
89
85
  super
90
86
  ::ActiveRecord::Observer.class_eval do
@@ -122,18 +118,6 @@ module StateMachine
122
118
  klass.self_and_descendants_from_active_record
123
119
  end
124
120
  end
125
-
126
- version '3.0.x' do
127
- def self.active?
128
- ::ActiveRecord::VERSION::MAJOR == 3 && ::ActiveRecord::VERSION::MINOR == 0
129
- end
130
-
131
- def define_action_hook
132
- # +around+ callbacks don't have direct access to results until AS 3.1
133
- owner_class.set_callback(:save, :after, 'value', :prepend => true) if action_hook == :save
134
- super
135
- end
136
- end
137
121
  end
138
122
  end
139
123
  end
@@ -18,16 +18,25 @@ module StateMachine
18
18
  end
19
19
 
20
20
  # Whether this integration is available for the current library. This
21
- # is usually only true if the ORM that the integration is for is
22
- # currently defined. Default is false.
21
+ # is only true if the ORM that the integration is for is currently
22
+ # defined.
23
23
  def available?
24
- false
24
+ matching_ancestors.any? && Object.const_defined?(matching_ancestors[0].split('::')[0])
25
25
  end
26
26
 
27
- # Whether the integration should be used for the given class. Default
28
- # is false.
27
+ # The list of ancestor names that cause this integration to matched.
28
+ def matching_ancestors
29
+ []
30
+ end
31
+
32
+ # Whether the integration should be used for the given class.
29
33
  def matches?(klass)
30
- false
34
+ matches_ancestors?(klass.ancestors.map {|ancestor| ancestor.name})
35
+ end
36
+
37
+ # Whether the integration should be used for the given list of ancestors.
38
+ def matches_ancestors?(ancestors)
39
+ (ancestors & matching_ancestors).any?
31
40
  end
32
41
 
33
42
  # Tracks the various version overrides for an integration
@@ -108,6 +108,42 @@ module StateMachine
108
108
  # end
109
109
  # end
110
110
  #
111
+ # === Within DataMapper Hooks
112
+ #
113
+ # DataMapper protects against the potential for system stack errors resulting
114
+ # from infinite loops by preventing records from being saved multiple times
115
+ # within save hooks. You need to be acutely aware of this when interacting
116
+ # with state_machine within save hooks. There are two things to keep in mind:
117
+ #
118
+ # 1. You cannot run a state_machine event during an `after :save/:create` hook.
119
+ # 2. If you need to run a state_machine event during a `before :save/:create/etc.`
120
+ # hook, then you have to force the machine's action to be skipped by passing
121
+ # `false` in as an argument to the event.
122
+ #
123
+ # For example:
124
+ #
125
+ # class Vehicle
126
+ # include DataMapper::Resource
127
+ # ...
128
+ #
129
+ # state_machine :initial => :parked do
130
+ # event :ignite do
131
+ # transition :parked => :idling
132
+ # end
133
+ # end
134
+ #
135
+ # # This will allow the event to transition without attempting to save a second time
136
+ # before :create { ignite(false) }
137
+ #
138
+ # # This will never work because DataMapper will refuse to save the
139
+ # # changes since we're still inside of a transaction
140
+ # # after :create { ignite }
141
+ # end
142
+ #
143
+ # While the above will work, in reality you should typically just set the
144
+ # `state_event` attribute in `#initialize` to automatically transition an
145
+ # object on creation.
146
+ #
111
147
  # == Transactions
112
148
  #
113
149
  # By default, the use of transactions during an event transition is
@@ -146,21 +182,6 @@ module StateMachine
146
182
  # end
147
183
  # end
148
184
  #
149
- # If using the +save+ action for the machine, this option will be ignored as
150
- # the transaction behavior will depend on the +save+ implementation within
151
- # DataMapper. To avoid this, use a different action like so:
152
- #
153
- # class Vehicle
154
- # include DataMapper::Resource
155
- # ...
156
- #
157
- # state_machine :initial => :parked, :use_transactions => false, :action => :save_state do
158
- # ...
159
- # end
160
- #
161
- # alias_method :save_state, :save
162
- # end
163
- #
164
185
  # == Validation errors
165
186
  #
166
187
  # If an event fails to successfully fire because there are no matching
@@ -302,26 +323,38 @@ module StateMachine
302
323
  # The failure callback creates +TransitionLog+ records using a second
303
324
  # connection to the database, allowing them to be saved without being
304
325
  # affected by rollbacks in the +Vehicle+ resource's transaction.
326
+ #
327
+ # === Callback Order
328
+ #
329
+ # Callbacks occur in the following order. Callbacks specific to state_machine
330
+ # are bolded. The remaining callbacks are part of ActiveRecord.
331
+ #
332
+ # * (-) save
333
+ # * (-) begin transaction (if enabled)
334
+ # * (1) *before_transition*
335
+ # * (2) before :valid?
336
+ # * (-) valid?
337
+ # * (3) after :valid?
338
+ # * (4) before :save
339
+ # * (-) save
340
+ # * (5) before :create
341
+ # * (-) create
342
+ # * (6) after :create
343
+ # * (7) after :save
344
+ # * (8) *after_transition*
345
+ # * (-) end transaction (if enabled)
305
346
  module DataMapper
306
347
  include Base
307
348
 
308
349
  require 'state_machine/integrations/data_mapper/versions'
309
350
 
310
351
  # The default options to use for state machines using this integration
311
- class << self; attr_reader :defaults; end
312
352
  @defaults = {:action => :save, :use_transactions => false}
313
353
 
314
- # Whether this integration is available. Only true if DataMapper::Resource
315
- # is defined.
316
- def self.available?
317
- defined?(::DataMapper::Resource)
318
- end
319
-
320
- # Should this integration be used for state machines in the given class?
321
354
  # Classes that include DataMapper::Resource will automatically use the
322
355
  # DataMapper integration.
323
- def self.matches?(klass)
324
- klass <= ::DataMapper::Resource
356
+ def self.matching_ancestors
357
+ %w(DataMapper::Resource)
325
358
  end
326
359
 
327
360
  # Loads additional files specific to DataMapper
@@ -371,6 +404,16 @@ module StateMachine
371
404
  @supports_validations ||= ::DataMapper.const_defined?('Validate')
372
405
  end
373
406
 
407
+ # Gets the db default for the machine's attribute
408
+ def owner_class_attribute_default
409
+ attribute_property && attribute_property.default
410
+ end
411
+
412
+ # Gets the property for this machine's attribute (if it exists)
413
+ def attribute_property
414
+ owner_class.properties.detect {|property| property.name == attribute}
415
+ end
416
+
374
417
  # Pluralizes the name using the built-in inflector
375
418
  def pluralize(word)
376
419
  ::DataMapper::Inflector.pluralize(word.to_s)
@@ -382,14 +425,14 @@ module StateMachine
382
425
  def define_state_initializer
383
426
  define_helper :instance, <<-end_eval, __FILE__, __LINE__ + 1
384
427
  def initialize(*args)
385
- self.class.state_machines.initialize_states(self) { super }
428
+ self.class.state_machines.initialize_states(self, :static => :force) { super }
386
429
  end
387
430
  end_eval
388
431
  end
389
432
 
390
433
  # Skips defining reader/writer methods since this is done automatically
391
434
  def define_state_accessor
392
- owner_class.property(attribute, String) unless owner_class.properties.detect {|property| property.name == attribute}
435
+ owner_class.property(attribute, String) unless attribute_property
393
436
 
394
437
  if supports_validations?
395
438
  name = self.name
@@ -402,20 +445,40 @@ module StateMachine
402
445
 
403
446
  # Adds hooks into validation for automatically firing events
404
447
  def define_action_helpers
405
- super
406
-
407
- if action == :save && supports_validations?
448
+ if action_hook == :save
408
449
  define_helper :instance, <<-end_eval, __FILE__, __LINE__ + 1
409
- def valid?(*)
410
- self.class.state_machines.transitions(self, :save, :after => false).perform { super }
450
+ def save(*)
451
+ result = self.class.state_machines.transitions(self, :save).perform { super }
452
+ assert_save_successful(:save, result)
453
+ result
454
+ end
455
+
456
+ def save!(*)
457
+ result = self.class.state_machines.transitions(self, :save).perform { super }
458
+ assert_save_successful(:save!, result)
459
+ result
460
+ end
461
+
462
+ def save_self(*)
463
+ self.class.state_machines.transitions(self, :save).perform { super }
411
464
  end
412
465
  end_eval
466
+
467
+ define_validation_hook
468
+ else
469
+ super
413
470
  end
414
471
  end
415
472
 
416
- # Uses internal save hooks if using the :save action
417
- def action_hook
418
- action == :save ? :save_self : super
473
+ # Adds hooks into validation for automatically firing events
474
+ def define_validation_hook
475
+ if supports_validations?
476
+ define_helper :instance, <<-end_eval, __FILE__, __LINE__ + 1
477
+ def valid?(*)
478
+ self.class.state_machines.transitions(self, :save, :after => false).perform { super }
479
+ end
480
+ end_eval
481
+ end
419
482
  end
420
483
 
421
484
  # Creates a scope for finding records *with* a particular state or
@@ -1,23 +1,33 @@
1
1
  module StateMachine
2
2
  module Integrations #:nodoc:
3
3
  module DataMapper
4
- version '0.9.x' do
4
+ version '0.9.x - 0.10.x' do
5
5
  def self.active?
6
- ::DataMapper::VERSION =~ /^0\.9\./
6
+ ::DataMapper::VERSION =~ /^0\.\d\./ || ::DataMapper::VERSION =~ /^0\.10\./
7
7
  end
8
8
 
9
- def action_hook
10
- action
9
+ def pluralize(word)
10
+ ::Extlib::Inflection.pluralize(word.to_s)
11
11
  end
12
12
  end
13
13
 
14
- version '0.9.x - 0.10.x' do
14
+ version '0.9.x' do
15
15
  def self.active?
16
- ::DataMapper::VERSION =~ /^0\.\d\./ || ::DataMapper::VERSION =~ /^0\.10\./
16
+ ::DataMapper::VERSION =~ /^0\.9\./
17
17
  end
18
18
 
19
- def pluralize(word)
20
- ::Extlib::Inflection.pluralize(word.to_s)
19
+ def define_action_helpers
20
+ if action_hook == :save
21
+ define_helper :instance, <<-end_eval, __FILE__, __LINE__ + 1
22
+ def save(*)
23
+ self.class.state_machines.transitions(self, :save).perform { super }
24
+ end
25
+ end_eval
26
+
27
+ define_validation_hook
28
+ else
29
+ super
30
+ end
21
31
  end
22
32
  end
23
33
 
@@ -33,6 +43,34 @@ module StateMachine
33
43
  end
34
44
  end
35
45
 
46
+ version '0.10.x' do
47
+ def self.active?
48
+ ::DataMapper::VERSION =~ /^0\.10\./
49
+ end
50
+
51
+ def define_action_helpers
52
+ if action_hook == :save
53
+ define_helper :instance, <<-end_eval, __FILE__, __LINE__ + 1
54
+ def save(*)
55
+ self.class.state_machines.transitions(self, :save).perform { super }
56
+ end
57
+
58
+ def save!(*)
59
+ self.class.state_machines.transitions(self, :save).perform { super }
60
+ end
61
+
62
+ def save_self(*)
63
+ self.class.state_machines.transitions(self, :save).perform { super }
64
+ end
65
+ end_eval
66
+
67
+ define_validation_hook
68
+ else
69
+ super
70
+ end
71
+ end
72
+ end
73
+
36
74
  version '1.0.0' do
37
75
  def self.active?
38
76
  ::DataMapper::VERSION == '1.0.0'
@@ -233,6 +233,22 @@ module StateMachine
233
233
  # Note, also, that the transition can be accessed by simply defining
234
234
  # additional arguments in the callback block.
235
235
  #
236
+ # === Callback Order
237
+ #
238
+ # Callbacks occur in the following order. Callbacks specific to state_machine
239
+ # are bolded. The remaining callbacks are part of MongoMapper.
240
+ #
241
+ # * (-) save
242
+ # * (1) *before_transition*
243
+ # * (-) valid
244
+ # * (2) before_validation
245
+ # * (3) after_validation
246
+ # * (4) before_save
247
+ # * (5) before_create
248
+ # * (6) after_create
249
+ # * (7) after_save
250
+ # * (8) *after_transition*
251
+ #
236
252
  # == Internationalization
237
253
  #
238
254
  # Any error message that is generated from performing invalid transitions
@@ -291,17 +307,10 @@ module StateMachine
291
307
  # The default options to use for state machines using this integration
292
308
  @defaults = {:action => :save}
293
309
 
294
- # Whether this integration is available. Only true if MongoMapper::Document
295
- # is defined.
296
- def self.available?
297
- defined?(::MongoMapper::Document)
298
- end
299
-
300
- # Should this integration be used for state machines in the given class?
301
310
  # Classes that include MongoMapper::Document will automatically use the
302
311
  # MongoMapper integration.
303
- def self.matches?(klass)
304
- klass <= ::MongoMapper::Document
312
+ def self.matching_ancestors
313
+ %w(MongoMapper::Document)
305
314
  end
306
315
 
307
316
  protected
@@ -310,27 +319,45 @@ module StateMachine
310
319
  action == :save
311
320
  end
312
321
 
322
+ # Gets the db default for the machine's attribute
323
+ def owner_class_attribute_default
324
+ attribute_key && attribute_key.default_value
325
+ end
326
+
327
+ # Gets the Mongoid key for this machine's attribute (if it exists)
328
+ def attribute_key
329
+ owner_class.keys[attribute.to_s]
330
+ end
331
+
313
332
  # Defines an initialization hook into the owner class for setting the
314
333
  # initial state of the machine *before* any attributes are set on the
315
334
  # object
316
335
  def define_state_initializer
317
336
  define_helper :instance, <<-end_eval, __FILE__, __LINE__ + 1
318
337
  def initialize(*args)
319
- self.class.state_machines.initialize_states(self) { super }
338
+ self.class.state_machines.initialize_states(self, :static => :force) { super }
320
339
  end
321
340
  end_eval
322
341
  end
323
342
 
324
343
  # Skips defining reader/writer methods since this is done automatically
325
344
  def define_state_accessor
326
- owner_class.key(attribute, String) unless owner_class.keys.include?(attribute.to_s)
345
+ owner_class.key(attribute, String) unless attribute_key
327
346
  super
328
347
  end
329
348
 
330
349
  # Uses around callbacks to run state events if using the :save hook
331
350
  def define_action_hook
332
351
  if action_hook == :save
333
- owner_class.set_callback(:save, :around, self, :prepend => true)
352
+ define_helper :instance, <<-end_eval, __FILE__, __LINE__ + 1
353
+ def save(*)
354
+ self.class.state_machine(#{name.inspect}).send(:around_save, self) { super }
355
+ end
356
+
357
+ def save!(*)
358
+ self.class.state_machine(#{name.inspect}).send(:around_save, self) { super } || raise(::MongoMapper::DocumentNotValid.new(self))
359
+ end
360
+ end_eval
334
361
  else
335
362
  super
336
363
  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][:mongo_mapper] = translations[:en].delete(:activemodel)
4
4
  translations
@@ -14,7 +14,7 @@ module StateMachine
14
14
  define_helper :instance, <<-end_eval, __FILE__, __LINE__ + 1
15
15
  def initialize(*args)
16
16
  attrs, * = args
17
- attrs && attrs.stringify_keys.key?('_id') ? super : self.class.state_machines.initialize_states(self) { super }
17
+ attrs && attrs.stringify_keys.key?('_id') ? super : self.class.state_machines.initialize_states(self, :static => :force) { super }
18
18
  end
19
19
  end_eval
20
20
  end
@@ -49,10 +49,6 @@ module StateMachine
49
49
  })
50
50
  end
51
51
 
52
- def action_hook
53
- action == :save ? :create_or_update : super
54
- end
55
-
56
52
  def load_locale
57
53
  end
58
54
 
@@ -82,25 +78,12 @@ module StateMachine
82
78
  def define_state_initializer
83
79
  define_helper :instance, <<-end_eval, __FILE__, __LINE__ + 1
84
80
  def initialize(*args)
85
- attrs, from_db = args
86
- from_db ? super : self.class.state_machines.initialize_states(self) { super }
81
+ from_db = args[1]
82
+ from_db ? super : self.class.state_machines.initialize_states(self, :static => :force) { super }
87
83
  end
88
84
  end_eval
89
85
  end
90
86
  end
91
-
92
- # Assumes MongoMapper 0.10+ uses ActiveModel 3.1+
93
- version '0.9.x' do
94
- def self.active?
95
- defined?(::MongoMapper::Version) && ::MongoMapper::Version =~ /^0\.9\./
96
- end
97
-
98
- def define_action_hook
99
- # +around+ callbacks don't have direct access to results until AS 3.1
100
- owner_class.set_callback(:save, :after, 'value', :prepend => true) if action_hook == :save
101
- super
102
- end
103
- end
104
87
  end
105
88
  end
106
89
  end