state_machine 1.1.2 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (240) hide show
  1. data/.gitignore +7 -11
  2. data/.travis.yml +49 -7
  3. data/Appraisals +255 -87
  4. data/CHANGELOG.md +30 -0
  5. data/README.md +142 -21
  6. data/Rakefile +1 -11
  7. data/examples/Gemfile +5 -0
  8. data/examples/Gemfile.lock +14 -0
  9. data/examples/auto_shop.rb +2 -0
  10. data/examples/car.rb +2 -0
  11. data/examples/doc/AutoShop.html +2856 -0
  12. data/examples/doc/AutoShop_state.png +0 -0
  13. data/examples/doc/Car.html +919 -0
  14. data/examples/doc/Car_state.png +0 -0
  15. data/examples/doc/TrafficLight.html +2230 -0
  16. data/examples/doc/TrafficLight_state.png +0 -0
  17. data/examples/doc/Vehicle.html +7921 -0
  18. data/examples/doc/Vehicle_state.png +0 -0
  19. data/examples/doc/_index.html +136 -0
  20. data/examples/doc/class_list.html +47 -0
  21. data/examples/doc/css/common.css +1 -0
  22. data/examples/doc/css/full_list.css +55 -0
  23. data/examples/doc/css/style.css +322 -0
  24. data/examples/doc/file_list.html +46 -0
  25. data/examples/doc/frames.html +13 -0
  26. data/examples/doc/index.html +136 -0
  27. data/examples/doc/js/app.js +205 -0
  28. data/examples/doc/js/full_list.js +173 -0
  29. data/examples/doc/js/jquery.js +16 -0
  30. data/examples/doc/method_list.html +734 -0
  31. data/examples/doc/top-level-namespace.html +105 -0
  32. data/examples/rails-rest/migration.rb +1 -5
  33. data/examples/rails-rest/view__form.html.erb +34 -0
  34. data/examples/rails-rest/view_edit.html.erb +2 -21
  35. data/examples/rails-rest/view_index.html.erb +6 -4
  36. data/examples/rails-rest/view_new.html.erb +2 -11
  37. data/examples/rails-rest/view_show.html.erb +5 -3
  38. data/examples/traffic_light.rb +2 -0
  39. data/examples/vehicle.rb +2 -0
  40. data/gemfiles/active_model-3.0.0.gemfile.lock +9 -6
  41. data/gemfiles/active_model-3.0.5.gemfile.lock +10 -7
  42. data/gemfiles/active_model-3.1.1.gemfile.lock +12 -10
  43. data/gemfiles/{active_model-3.2.0.gemfile → active_model-3.2.1.gemfile} +1 -1
  44. data/gemfiles/{graphviz-0.9.0.gemfile → active_model-3.2.12.gemfile} +1 -1
  45. data/gemfiles/active_model-3.2.12.gemfile.lock +36 -0
  46. data/gemfiles/{active_record-3.2.0.gemfile → active_model-3.2.13.rc1.gemfile} +1 -2
  47. data/gemfiles/active_model-3.2.13.rc1.gemfile.lock +36 -0
  48. data/gemfiles/active_model-4.0.0.gemfile +9 -0
  49. data/gemfiles/active_model-4.0.0.gemfile.lock +78 -0
  50. data/gemfiles/active_record-2.0.0.gemfile +2 -1
  51. data/gemfiles/active_record-2.0.0.gemfile.lock +15 -6
  52. data/gemfiles/active_record-2.0.5.gemfile +2 -1
  53. data/gemfiles/active_record-2.0.5.gemfile.lock +15 -6
  54. data/gemfiles/active_record-2.1.0.gemfile +2 -1
  55. data/gemfiles/active_record-2.1.0.gemfile.lock +15 -6
  56. data/gemfiles/active_record-2.1.2.gemfile +2 -1
  57. data/gemfiles/active_record-2.1.2.gemfile.lock +15 -6
  58. data/gemfiles/active_record-2.2.3.gemfile +2 -1
  59. data/gemfiles/active_record-2.2.3.gemfile.lock +15 -6
  60. data/gemfiles/active_record-2.3.12.gemfile +2 -1
  61. data/gemfiles/active_record-2.3.12.gemfile.lock +15 -6
  62. data/gemfiles/active_record-2.3.5.gemfile +9 -0
  63. data/gemfiles/active_record-2.3.5.gemfile.lock +39 -0
  64. data/gemfiles/active_record-3.0.0.gemfile +2 -1
  65. data/gemfiles/active_record-3.0.0.gemfile.lock +18 -11
  66. data/gemfiles/active_record-3.0.5.gemfile +2 -1
  67. data/gemfiles/active_record-3.0.5.gemfile.lock +19 -12
  68. data/gemfiles/active_record-3.1.1.gemfile +2 -1
  69. data/gemfiles/active_record-3.1.1.gemfile.lock +22 -16
  70. data/gemfiles/active_record-3.2.12.gemfile +9 -0
  71. data/gemfiles/active_record-3.2.12.gemfile.lock +51 -0
  72. data/gemfiles/active_record-3.2.13.rc1.gemfile +9 -0
  73. data/gemfiles/active_record-3.2.13.rc1.gemfile.lock +51 -0
  74. data/gemfiles/active_record-4.0.0.gemfile +11 -0
  75. data/gemfiles/active_record-4.0.0.gemfile.lock +83 -0
  76. data/gemfiles/data_mapper-0.10.2.gemfile +1 -0
  77. data/gemfiles/data_mapper-0.10.2.gemfile.lock +13 -9
  78. data/gemfiles/data_mapper-0.9.11.gemfile +1 -0
  79. data/gemfiles/data_mapper-0.9.11.gemfile.lock +31 -7
  80. data/gemfiles/data_mapper-0.9.4.gemfile.lock +25 -14
  81. data/gemfiles/data_mapper-0.9.7.gemfile +1 -0
  82. data/gemfiles/data_mapper-0.9.7.gemfile.lock +27 -15
  83. data/gemfiles/data_mapper-1.0.0.gemfile.lock +20 -17
  84. data/gemfiles/data_mapper-1.0.1.gemfile.lock +20 -17
  85. data/gemfiles/data_mapper-1.0.2.gemfile.lock +20 -17
  86. data/gemfiles/data_mapper-1.1.0.gemfile.lock +19 -16
  87. data/gemfiles/data_mapper-1.2.0.gemfile.lock +19 -16
  88. data/gemfiles/default.gemfile.lock +8 -5
  89. data/gemfiles/graphviz-0.9.17.gemfile +7 -0
  90. data/gemfiles/graphviz-0.9.17.gemfile.lock +29 -0
  91. data/gemfiles/graphviz-0.9.21.gemfile.lock +7 -4
  92. data/gemfiles/graphviz-1.0.0.gemfile.lock +7 -4
  93. data/gemfiles/graphviz-1.0.3.gemfile +7 -0
  94. data/gemfiles/graphviz-1.0.3.gemfile.lock +29 -0
  95. data/gemfiles/graphviz-1.0.8.gemfile +7 -0
  96. data/gemfiles/graphviz-1.0.8.gemfile.lock +29 -0
  97. data/gemfiles/mongo_mapper-0.10.0.gemfile +1 -0
  98. data/gemfiles/mongo_mapper-0.10.0.gemfile.lock +14 -11
  99. data/gemfiles/mongo_mapper-0.11.1.gemfile +7 -0
  100. data/gemfiles/mongo_mapper-0.11.1.gemfile.lock +44 -0
  101. data/gemfiles/mongo_mapper-0.11.2.gemfile +9 -0
  102. data/gemfiles/mongo_mapper-0.11.2.gemfile.lock +48 -0
  103. data/gemfiles/mongo_mapper-0.12.0.gemfile +9 -0
  104. data/gemfiles/mongo_mapper-0.12.0.gemfile.lock +48 -0
  105. data/gemfiles/mongo_mapper-0.5.5.gemfile.lock +7 -4
  106. data/gemfiles/mongo_mapper-0.5.8.gemfile.lock +7 -4
  107. data/gemfiles/mongo_mapper-0.6.0.gemfile.lock +7 -4
  108. data/gemfiles/mongo_mapper-0.6.10.gemfile.lock +7 -4
  109. data/gemfiles/mongo_mapper-0.7.0.gemfile.lock +7 -4
  110. data/gemfiles/mongo_mapper-0.7.5.gemfile.lock +7 -4
  111. data/gemfiles/mongo_mapper-0.8.0.gemfile.lock +7 -4
  112. data/gemfiles/mongo_mapper-0.8.3.gemfile.lock +7 -4
  113. data/gemfiles/mongo_mapper-0.8.4.gemfile.lock +7 -4
  114. data/gemfiles/mongo_mapper-0.8.6.gemfile.lock +7 -4
  115. data/gemfiles/mongo_mapper-0.9.0.gemfile.lock +7 -4
  116. data/gemfiles/mongoid-2.0.0.gemfile +2 -0
  117. data/gemfiles/mongoid-2.0.0.gemfile.lock +22 -18
  118. data/gemfiles/mongoid-2.1.4.gemfile +2 -0
  119. data/gemfiles/mongoid-2.1.4.gemfile.lock +21 -17
  120. data/gemfiles/mongoid-2.2.4.gemfile +2 -0
  121. data/gemfiles/mongoid-2.2.4.gemfile.lock +21 -17
  122. data/gemfiles/mongoid-2.3.3.gemfile +2 -0
  123. data/gemfiles/mongoid-2.3.3.gemfile.lock +21 -17
  124. data/gemfiles/mongoid-2.4.0.gemfile +9 -0
  125. data/gemfiles/mongoid-2.4.0.gemfile.lock +47 -0
  126. data/gemfiles/mongoid-2.4.10.gemfile +9 -0
  127. data/gemfiles/mongoid-2.4.10.gemfile.lock +47 -0
  128. data/gemfiles/mongoid-2.5.2.gemfile +9 -0
  129. data/gemfiles/mongoid-2.5.2.gemfile.lock +47 -0
  130. data/gemfiles/mongoid-2.6.0.gemfile +9 -0
  131. data/gemfiles/mongoid-2.6.0.gemfile.lock +47 -0
  132. data/gemfiles/mongoid-3.0.0.gemfile +8 -0
  133. data/gemfiles/mongoid-3.0.0.gemfile.lock +45 -0
  134. data/gemfiles/mongoid-3.0.22.gemfile +8 -0
  135. data/gemfiles/mongoid-3.0.22.gemfile.lock +45 -0
  136. data/gemfiles/mongoid-3.1.0.gemfile +8 -0
  137. data/gemfiles/mongoid-3.1.0.gemfile.lock +45 -0
  138. data/gemfiles/sequel-2.11.0.gemfile +2 -1
  139. data/gemfiles/sequel-2.11.0.gemfile.lock +11 -6
  140. data/gemfiles/sequel-2.12.0.gemfile +2 -1
  141. data/gemfiles/sequel-2.12.0.gemfile.lock +11 -6
  142. data/gemfiles/sequel-2.8.0.gemfile +2 -1
  143. data/gemfiles/sequel-2.8.0.gemfile.lock +11 -6
  144. data/gemfiles/sequel-3.0.0.gemfile +2 -1
  145. data/gemfiles/sequel-3.0.0.gemfile.lock +11 -6
  146. data/gemfiles/sequel-3.10.0.gemfile +9 -0
  147. data/gemfiles/sequel-3.10.0.gemfile.lock +33 -0
  148. data/gemfiles/sequel-3.13.0.gemfile +2 -1
  149. data/gemfiles/sequel-3.13.0.gemfile.lock +11 -6
  150. data/gemfiles/sequel-3.14.0.gemfile +2 -1
  151. data/gemfiles/sequel-3.14.0.gemfile.lock +11 -6
  152. data/gemfiles/sequel-3.23.0.gemfile +2 -1
  153. data/gemfiles/sequel-3.23.0.gemfile.lock +11 -6
  154. data/gemfiles/sequel-3.24.0.gemfile +2 -1
  155. data/gemfiles/sequel-3.24.0.gemfile.lock +11 -6
  156. data/gemfiles/sequel-3.29.0.gemfile +2 -1
  157. data/gemfiles/sequel-3.29.0.gemfile.lock +11 -6
  158. data/gemfiles/sequel-3.34.0.gemfile +9 -0
  159. data/gemfiles/sequel-3.34.0.gemfile.lock +33 -0
  160. data/gemfiles/sequel-3.35.0.gemfile +9 -0
  161. data/gemfiles/sequel-3.35.0.gemfile.lock +33 -0
  162. data/gemfiles/sequel-3.4.0.gemfile +9 -0
  163. data/gemfiles/sequel-3.4.0.gemfile.lock +33 -0
  164. data/gemfiles/sequel-3.44.0.gemfile +9 -0
  165. data/gemfiles/sequel-3.44.0.gemfile.lock +33 -0
  166. data/lib/state_machine.rb +6 -0
  167. data/lib/state_machine/branch.rb +9 -8
  168. data/lib/state_machine/callback.rb +2 -2
  169. data/lib/state_machine/core.rb +10 -0
  170. data/lib/state_machine/core_ext.rb +1 -0
  171. data/lib/state_machine/eval_helpers.rb +5 -3
  172. data/lib/state_machine/event.rb +17 -6
  173. data/lib/state_machine/graph.rb +92 -0
  174. data/lib/state_machine/integrations.rb +13 -1
  175. data/lib/state_machine/integrations/active_model.rb +14 -20
  176. data/lib/state_machine/integrations/active_model/observer.rb +3 -3
  177. data/lib/state_machine/integrations/active_model/observer_update.rb +42 -0
  178. data/lib/state_machine/integrations/active_record.rb +52 -25
  179. data/lib/state_machine/integrations/active_record/locale.rb +1 -1
  180. data/lib/state_machine/integrations/active_record/versions.rb +1 -17
  181. data/lib/state_machine/integrations/base.rb +15 -6
  182. data/lib/state_machine/integrations/data_mapper.rb +98 -35
  183. data/lib/state_machine/integrations/data_mapper/versions.rb +46 -8
  184. data/lib/state_machine/integrations/mongo_mapper.rb +39 -12
  185. data/lib/state_machine/integrations/mongo_mapper/locale.rb +1 -1
  186. data/lib/state_machine/integrations/mongo_mapper/versions.rb +3 -20
  187. data/lib/state_machine/integrations/mongoid.rb +52 -14
  188. data/lib/state_machine/integrations/mongoid/locale.rb +1 -1
  189. data/lib/state_machine/integrations/mongoid/versions.rb +52 -26
  190. data/lib/state_machine/integrations/sequel.rb +82 -33
  191. data/lib/state_machine/integrations/sequel/versions.rb +19 -44
  192. data/lib/state_machine/machine.rb +99 -59
  193. data/lib/state_machine/machine_collection.rb +1 -2
  194. data/lib/state_machine/macro_methods.rb +29 -0
  195. data/lib/state_machine/node_collection.rb +1 -1
  196. data/lib/state_machine/state.rb +18 -10
  197. data/lib/state_machine/state_context.rb +2 -2
  198. data/lib/state_machine/transition.rb +8 -1
  199. data/lib/state_machine/transition_collection.rb +2 -1
  200. data/lib/state_machine/version.rb +1 -1
  201. data/lib/state_machine/yard.rb +8 -0
  202. data/lib/state_machine/yard/handlers.rb +12 -0
  203. data/lib/state_machine/yard/handlers/base.rb +32 -0
  204. data/lib/state_machine/yard/handlers/event.rb +25 -0
  205. data/lib/state_machine/yard/handlers/machine.rb +344 -0
  206. data/lib/state_machine/yard/handlers/state.rb +25 -0
  207. data/lib/state_machine/yard/handlers/transition.rb +47 -0
  208. data/lib/state_machine/yard/templates.rb +3 -0
  209. data/lib/state_machine/yard/templates/default/class/html/setup.rb +30 -0
  210. data/lib/state_machine/yard/templates/default/class/html/state_machines.erb +12 -0
  211. data/lib/tasks/state_machine.rb +2 -1
  212. data/lib/yard-state_machine.rb +2 -0
  213. data/state_machine.gemspec +4 -3
  214. data/test/files/switch.rb +4 -0
  215. data/test/test_helper.rb +5 -0
  216. data/test/unit/branch_test.rb +117 -36
  217. data/test/unit/callback_test.rb +5 -2
  218. data/test/unit/eval_helpers_test.rb +49 -1
  219. data/test/unit/event_collection_test.rb +3 -1
  220. data/test/unit/event_test.rb +182 -12
  221. data/test/unit/graph_test.rb +98 -0
  222. data/test/unit/integrations/active_model_test.rb +82 -12
  223. data/test/unit/integrations/active_record_test.rb +393 -37
  224. data/test/unit/integrations/base_test.rb +7 -2
  225. data/test/unit/integrations/data_mapper_test.rb +326 -72
  226. data/test/unit/integrations/mongo_mapper_test.rb +338 -44
  227. data/test/unit/integrations/mongoid_test.rb +606 -98
  228. data/test/unit/integrations/sequel_test.rb +429 -102
  229. data/test/unit/integrations_test.rb +28 -6
  230. data/test/unit/machine_collection_test.rb +6 -2
  231. data/test/unit/machine_test.rb +134 -82
  232. data/test/unit/node_collection_test.rb +2 -2
  233. data/test/unit/path_test.rb +1 -1
  234. data/test/unit/state_test.rb +65 -21
  235. data/test/unit/transition_collection_test.rb +43 -23
  236. data/test/unit/transition_test.rb +8 -2
  237. metadata +303 -221
  238. data/gemfiles/active_model-3.2.0.gemfile.lock +0 -32
  239. data/gemfiles/active_record-3.2.0.gemfile.lock +0 -43
  240. data/gemfiles/graphviz-0.9.0.gemfile.lock +0 -26
@@ -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