branston 0.6.1 → 0.6.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (188) hide show
  1. data/README.rdoc +1 -1
  2. data/lib/branston/Gemfile +25 -0
  3. data/lib/branston/Gemfile.lock +76 -0
  4. data/lib/branston/app/controllers/application_controller.rb +1 -1
  5. data/lib/branston/app/controllers/outcomes_controller.rb +2 -0
  6. data/lib/branston/app/controllers/stories_controller.rb +82 -86
  7. data/lib/branston/app/controllers/users_controller.rb +69 -11
  8. data/lib/branston/app/helpers/iterations_helper.rb +13 -13
  9. data/lib/branston/app/models/iteration.rb +3 -1
  10. data/lib/branston/app/models/release.rb +0 -1
  11. data/lib/branston/app/models/story.rb +30 -28
  12. data/lib/branston/app/models/user.rb +46 -1
  13. data/lib/branston/app/views/layouts/_header.html.erb +8 -3
  14. data/lib/branston/app/views/layouts/user_roles.html.erb +5 -5
  15. data/lib/branston/app/views/sessions/new.html.erb +8 -14
  16. data/lib/branston/app/views/users/_admin_controls.html.erb +14 -0
  17. data/lib/branston/app/views/users/_form.html.erb +27 -0
  18. data/lib/branston/app/views/users/edit.html.erb +9 -0
  19. data/lib/branston/app/views/users/index.html.erb +14 -0
  20. data/lib/branston/app/views/users/new.html.erb +3 -22
  21. data/lib/branston/config/boot.rb +20 -0
  22. data/lib/branston/config/environment.rb +2 -7
  23. data/lib/branston/config/environments/test.rb +0 -8
  24. data/lib/branston/config/preinitializer.rb +21 -0
  25. data/lib/branston/config/routes.rb +15 -10
  26. data/lib/branston/db/development.sqlite3 +0 -0
  27. data/lib/branston/db/development_structure.sql +21 -8
  28. data/lib/branston/db/migrate/20100723161424_add_state_to_user.rb +12 -0
  29. data/lib/branston/db/migrate/20100726150322_add_activation_fields_to_user.rb +12 -0
  30. data/lib/branston/db/migrate/20100729125551_set_default_user_state_to_pending.rb +10 -0
  31. data/lib/branston/db/migrate/20100812133837_add_is_admin_property_to_user.rb +10 -0
  32. data/lib/branston/db/migrate/20100812140532_set_default_user_state_to_active.rb +10 -0
  33. data/lib/branston/db/migrate/20100812143455_add_default_admin_user.rb +17 -0
  34. data/lib/branston/db/migrate/20110408162438_remove_is_admin_property_and_add_role_instead.rb +12 -0
  35. data/lib/branston/db/pristine.sqlite3 +0 -0
  36. data/lib/branston/db/schema.rb +6 -8
  37. data/lib/branston/db/test.sqlite3 +0 -0
  38. data/lib/branston/log/development.log +1181 -433
  39. data/lib/branston/log/test.log +145306 -52026
  40. data/lib/branston/test/blueprints.rb +22 -28
  41. data/lib/branston/test/functional/iterations_controller_test.rb +149 -113
  42. data/lib/branston/test/functional/outcomes_controller_test.rb +94 -60
  43. data/lib/branston/test/functional/preconditions_controller_test.rb +101 -67
  44. data/lib/branston/test/functional/releases_controller_test.rb +85 -49
  45. data/lib/branston/test/functional/scenarios_controller_test.rb +104 -70
  46. data/lib/branston/test/functional/stories_controller_test.rb +41 -12
  47. data/lib/branston/test/functional/users_controller_test.rb +364 -43
  48. data/lib/branston/test/unit/iteration_test.rb +37 -6
  49. data/lib/branston/test/unit/outcome_test.rb +2 -2
  50. data/lib/branston/test/unit/participation_test.rb +2 -2
  51. data/lib/branston/test/unit/precondition_test.rb +3 -3
  52. data/lib/branston/test/unit/release_test.rb +4 -0
  53. data/lib/branston/test/unit/scenario_test.rb +4 -4
  54. data/lib/branston/test/unit/story_test.rb +62 -40
  55. data/lib/branston/test/unit/user_test.rb +195 -5
  56. metadata +136 -156
  57. data/lib/branston/app/controllers/user_roles_controller.rb +0 -105
  58. data/lib/branston/app/helpers/user_roles_helper.rb +0 -2
  59. data/lib/branston/app/models/user_role.rb +0 -21
  60. data/lib/branston/app/views/layouts/outcomes.html.erb +0 -17
  61. data/lib/branston/app/views/layouts/preconditions.html.erb +0 -17
  62. data/lib/branston/app/views/layouts/releases.html.erb +0 -17
  63. data/lib/branston/app/views/user_roles/edit.html.erb +0 -16
  64. data/lib/branston/app/views/user_roles/index.html.erb +0 -20
  65. data/lib/branston/app/views/user_roles/new.html.erb +0 -15
  66. data/lib/branston/app/views/user_roles/show.html.erb +0 -8
  67. data/lib/branston/coverage/app-controllers-application_controller_rb.html +0 -231
  68. data/lib/branston/coverage/app-controllers-iterations_controller_rb.html +0 -801
  69. data/lib/branston/coverage/app-controllers-outcomes_controller_rb.html +0 -759
  70. data/lib/branston/coverage/app-controllers-preconditions_controller_rb.html +0 -783
  71. data/lib/branston/coverage/app-controllers-releases_controller_rb.html +0 -705
  72. data/lib/branston/coverage/app-controllers-scenarios_controller_rb.html +0 -777
  73. data/lib/branston/coverage/app-controllers-sessions_controller_rb.html +0 -411
  74. data/lib/branston/coverage/app-controllers-stories_controller_rb.html +0 -1071
  75. data/lib/branston/coverage/app-controllers-user_roles_controller_rb.html +0 -693
  76. data/lib/branston/coverage/app-controllers-users_controller_rb.html +0 -315
  77. data/lib/branston/coverage/app-helpers-application_helper_rb.html +0 -327
  78. data/lib/branston/coverage/app-helpers-iterations_helper_rb.html +0 -363
  79. data/lib/branston/coverage/app-helpers-outcomes_helper_rb.html +0 -75
  80. data/lib/branston/coverage/app-helpers-preconditions_helper_rb.html +0 -75
  81. data/lib/branston/coverage/app-helpers-releases_helper_rb.html +0 -75
  82. data/lib/branston/coverage/app-helpers-sessions_helper_rb.html +0 -75
  83. data/lib/branston/coverage/app-helpers-stories_helper_rb.html +0 -75
  84. data/lib/branston/coverage/app-helpers-user_roles_helper_rb.html +0 -75
  85. data/lib/branston/coverage/app-models-iteration_rb.html +0 -321
  86. data/lib/branston/coverage/app-models-outcome_rb.html +0 -243
  87. data/lib/branston/coverage/app-models-participation_rb.html +0 -189
  88. data/lib/branston/coverage/app-models-precondition_rb.html +0 -243
  89. data/lib/branston/coverage/app-models-release_rb.html +0 -195
  90. data/lib/branston/coverage/app-models-scenario_rb.html +0 -231
  91. data/lib/branston/coverage/app-models-story_rb.html +0 -621
  92. data/lib/branston/coverage/app-models-user_rb.html +0 -513
  93. data/lib/branston/coverage/app-models-user_role_rb.html +0 -189
  94. data/lib/branston/coverage/index.html +0 -570
  95. data/lib/branston/coverage/jquery-1.3.2.min.js +0 -19
  96. data/lib/branston/coverage/jquery.tablesorter.min.js +0 -15
  97. data/lib/branston/coverage/lib-client_rb.html +0 -537
  98. data/lib/branston/coverage/lib-faker_extras_rb.html +0 -207
  99. data/lib/branston/coverage/lib-story_generator_rb.html +0 -873
  100. data/lib/branston/coverage/print.css +0 -12
  101. data/lib/branston/coverage/rcov.js +0 -42
  102. data/lib/branston/coverage/screen.css +0 -270
  103. data/lib/branston/db/migrate/20091127131037_create_user_roles.rb +0 -13
  104. data/lib/branston/db/migrate/20091127172950_add_story_id_to_user_role.rb +0 -10
  105. data/lib/branston/test/functional/user_roles_controller_test.rb +0 -71
  106. data/lib/branston/test/unit/helpers/user_roles_helper_test.rb +0 -4
  107. data/lib/branston/test/unit/user_role_test.rb +0 -9
  108. data/lib/branston/tmp/performance/BrowsingTest#test_homepage_process_time_flat.txt +0 -8
  109. data/lib/branston/tmp/performance/BrowsingTest#test_homepage_process_time_graph.html +0 -6718
  110. data/lib/branston/tmp/performance/BrowsingTest#test_homepage_process_time_tree.txt +0 -9942
  111. data/lib/branston/vendor/plugins/state_machine/CHANGELOG.rdoc +0 -298
  112. data/lib/branston/vendor/plugins/state_machine/LICENSE +0 -20
  113. data/lib/branston/vendor/plugins/state_machine/README.rdoc +0 -466
  114. data/lib/branston/vendor/plugins/state_machine/Rakefile +0 -98
  115. data/lib/branston/vendor/plugins/state_machine/examples/AutoShop_state.png +0 -0
  116. data/lib/branston/vendor/plugins/state_machine/examples/Car_state.png +0 -0
  117. data/lib/branston/vendor/plugins/state_machine/examples/TrafficLight_state.png +0 -0
  118. data/lib/branston/vendor/plugins/state_machine/examples/Vehicle_state.png +0 -0
  119. data/lib/branston/vendor/plugins/state_machine/examples/auto_shop.rb +0 -11
  120. data/lib/branston/vendor/plugins/state_machine/examples/car.rb +0 -19
  121. data/lib/branston/vendor/plugins/state_machine/examples/merb-rest/controller.rb +0 -51
  122. data/lib/branston/vendor/plugins/state_machine/examples/merb-rest/model.rb +0 -28
  123. data/lib/branston/vendor/plugins/state_machine/examples/merb-rest/view_edit.html.erb +0 -24
  124. data/lib/branston/vendor/plugins/state_machine/examples/merb-rest/view_index.html.erb +0 -23
  125. data/lib/branston/vendor/plugins/state_machine/examples/merb-rest/view_new.html.erb +0 -13
  126. data/lib/branston/vendor/plugins/state_machine/examples/merb-rest/view_show.html.erb +0 -17
  127. data/lib/branston/vendor/plugins/state_machine/examples/rails-rest/controller.rb +0 -43
  128. data/lib/branston/vendor/plugins/state_machine/examples/rails-rest/migration.rb +0 -11
  129. data/lib/branston/vendor/plugins/state_machine/examples/rails-rest/model.rb +0 -23
  130. data/lib/branston/vendor/plugins/state_machine/examples/rails-rest/view_edit.html.erb +0 -25
  131. data/lib/branston/vendor/plugins/state_machine/examples/rails-rest/view_index.html.erb +0 -23
  132. data/lib/branston/vendor/plugins/state_machine/examples/rails-rest/view_new.html.erb +0 -14
  133. data/lib/branston/vendor/plugins/state_machine/examples/rails-rest/view_show.html.erb +0 -17
  134. data/lib/branston/vendor/plugins/state_machine/examples/traffic_light.rb +0 -7
  135. data/lib/branston/vendor/plugins/state_machine/examples/vehicle.rb +0 -31
  136. data/lib/branston/vendor/plugins/state_machine/init.rb +0 -1
  137. data/lib/branston/vendor/plugins/state_machine/lib/state_machine.rb +0 -388
  138. data/lib/branston/vendor/plugins/state_machine/lib/state_machine/assertions.rb +0 -36
  139. data/lib/branston/vendor/plugins/state_machine/lib/state_machine/callback.rb +0 -189
  140. data/lib/branston/vendor/plugins/state_machine/lib/state_machine/condition_proxy.rb +0 -94
  141. data/lib/branston/vendor/plugins/state_machine/lib/state_machine/eval_helpers.rb +0 -67
  142. data/lib/branston/vendor/plugins/state_machine/lib/state_machine/event.rb +0 -252
  143. data/lib/branston/vendor/plugins/state_machine/lib/state_machine/event_collection.rb +0 -122
  144. data/lib/branston/vendor/plugins/state_machine/lib/state_machine/extensions.rb +0 -149
  145. data/lib/branston/vendor/plugins/state_machine/lib/state_machine/guard.rb +0 -230
  146. data/lib/branston/vendor/plugins/state_machine/lib/state_machine/integrations.rb +0 -68
  147. data/lib/branston/vendor/plugins/state_machine/lib/state_machine/integrations/active_record.rb +0 -492
  148. data/lib/branston/vendor/plugins/state_machine/lib/state_machine/integrations/active_record/locale.rb +0 -11
  149. data/lib/branston/vendor/plugins/state_machine/lib/state_machine/integrations/active_record/observer.rb +0 -41
  150. data/lib/branston/vendor/plugins/state_machine/lib/state_machine/integrations/data_mapper.rb +0 -351
  151. data/lib/branston/vendor/plugins/state_machine/lib/state_machine/integrations/data_mapper/observer.rb +0 -139
  152. data/lib/branston/vendor/plugins/state_machine/lib/state_machine/integrations/sequel.rb +0 -322
  153. data/lib/branston/vendor/plugins/state_machine/lib/state_machine/machine.rb +0 -1467
  154. data/lib/branston/vendor/plugins/state_machine/lib/state_machine/machine_collection.rb +0 -155
  155. data/lib/branston/vendor/plugins/state_machine/lib/state_machine/matcher.rb +0 -123
  156. data/lib/branston/vendor/plugins/state_machine/lib/state_machine/matcher_helpers.rb +0 -54
  157. data/lib/branston/vendor/plugins/state_machine/lib/state_machine/node_collection.rb +0 -152
  158. data/lib/branston/vendor/plugins/state_machine/lib/state_machine/state.rb +0 -249
  159. data/lib/branston/vendor/plugins/state_machine/lib/state_machine/state_collection.rb +0 -112
  160. data/lib/branston/vendor/plugins/state_machine/lib/state_machine/transition.rb +0 -394
  161. data/lib/branston/vendor/plugins/state_machine/state_machine.gemspec +0 -30
  162. data/lib/branston/vendor/plugins/state_machine/tasks/state_machine.rake +0 -1
  163. data/lib/branston/vendor/plugins/state_machine/tasks/state_machine.rb +0 -30
  164. data/lib/branston/vendor/plugins/state_machine/test/classes/switch.rb +0 -11
  165. data/lib/branston/vendor/plugins/state_machine/test/functional/state_machine_test.rb +0 -941
  166. data/lib/branston/vendor/plugins/state_machine/test/test_helper.rb +0 -4
  167. data/lib/branston/vendor/plugins/state_machine/test/unit/assertions_test.rb +0 -40
  168. data/lib/branston/vendor/plugins/state_machine/test/unit/callback_test.rb +0 -455
  169. data/lib/branston/vendor/plugins/state_machine/test/unit/condition_proxy_test.rb +0 -328
  170. data/lib/branston/vendor/plugins/state_machine/test/unit/eval_helpers_test.rb +0 -120
  171. data/lib/branston/vendor/plugins/state_machine/test/unit/event_collection_test.rb +0 -326
  172. data/lib/branston/vendor/plugins/state_machine/test/unit/event_test.rb +0 -743
  173. data/lib/branston/vendor/plugins/state_machine/test/unit/guard_test.rb +0 -908
  174. data/lib/branston/vendor/plugins/state_machine/test/unit/integrations/active_record_test.rb +0 -1367
  175. data/lib/branston/vendor/plugins/state_machine/test/unit/integrations/data_mapper_test.rb +0 -962
  176. data/lib/branston/vendor/plugins/state_machine/test/unit/integrations/sequel_test.rb +0 -859
  177. data/lib/branston/vendor/plugins/state_machine/test/unit/integrations_test.rb +0 -42
  178. data/lib/branston/vendor/plugins/state_machine/test/unit/invalid_event_test.rb +0 -7
  179. data/lib/branston/vendor/plugins/state_machine/test/unit/invalid_transition_test.rb +0 -7
  180. data/lib/branston/vendor/plugins/state_machine/test/unit/machine_collection_test.rb +0 -938
  181. data/lib/branston/vendor/plugins/state_machine/test/unit/machine_test.rb +0 -2004
  182. data/lib/branston/vendor/plugins/state_machine/test/unit/matcher_helpers_test.rb +0 -37
  183. data/lib/branston/vendor/plugins/state_machine/test/unit/matcher_test.rb +0 -155
  184. data/lib/branston/vendor/plugins/state_machine/test/unit/node_collection_test.rb +0 -207
  185. data/lib/branston/vendor/plugins/state_machine/test/unit/state_collection_test.rb +0 -280
  186. data/lib/branston/vendor/plugins/state_machine/test/unit/state_machine_test.rb +0 -31
  187. data/lib/branston/vendor/plugins/state_machine/test/unit/state_test.rb +0 -795
  188. data/lib/branston/vendor/plugins/state_machine/test/unit/transition_test.rb +0 -1212
@@ -1,68 +0,0 @@
1
- # Load each available integration
2
- Dir["#{File.dirname(__FILE__)}/integrations/*.rb"].sort.each do |path|
3
- require "state_machine/integrations/#{File.basename(path)}"
4
- end
5
-
6
- module StateMachine
7
- # Integrations allow state machines to take advantage of features within the
8
- # context of a particular library. This is currently most useful with
9
- # database libraries. For example, the various database integrations allow
10
- # state machines to hook into features like:
11
- # * Saving
12
- # * Transactions
13
- # * Observers
14
- # * Scopes
15
- # * Callbacks
16
- # * Validation errors
17
- #
18
- # This type of integration allows the user to work with state machines in a
19
- # fashion similar to other object models in their application.
20
- #
21
- # The integration interface is loosely defined by various unimplemented
22
- # methods in the StateMachine::Machine class. See that class or the various
23
- # built-in integrations for more information about how to define additional
24
- # integrations.
25
- module Integrations
26
- # Attempts to find an integration that matches the given class. This will
27
- # look through all of the built-in integrations under the StateMachine::Integrations
28
- # namespace and find one that successfully matches the class.
29
- #
30
- # == Examples
31
- #
32
- # class Vehicle
33
- # end
34
- #
35
- # class ARVehicle < ActiveRecord::Base
36
- # end
37
- #
38
- # class DMVehicle
39
- # include DataMapper::Resource
40
- # end
41
- #
42
- # class SequelVehicle < Sequel::Model
43
- # end
44
- #
45
- # StateMachine::Integrations.match(Vehicle) # => nil
46
- # StateMachine::Integrations.match(ARVehicle) # => StateMachine::Integrations::ActiveRecord
47
- # StateMachine::Integrations.match(DMVehicle) # => StateMachine::Integrations::DataMapper
48
- # StateMachine::Integrations.match(SequelVehicle) # => StateMachine::Integrations::Sequel
49
- def self.match(klass)
50
- if integration = constants.find {|name| const_get(name).matches?(klass)}
51
- find(integration)
52
- end
53
- end
54
-
55
- # Finds an integration with the given name. If the integration cannot be
56
- # found, then a NameError exception will be raised.
57
- #
58
- # == Examples
59
- #
60
- # StateMachine::Integrations.find(:active_record) # => StateMachine::Integrations::ActiveRecord
61
- # StateMachine::Integrations.find(:data_mapper) # => StateMachine::Integrations::DataMapper
62
- # StateMachine::Integrations.find(:sequel) # => StateMachine::Integrations::Sequel
63
- # StateMachine::Integrations.find(:invalid) # => NameError: wrong constant name Invalid
64
- def self.find(name)
65
- const_get(name.to_s.gsub(/(?:^|_)(.)/) {$1.upcase})
66
- end
67
- end
68
- end
@@ -1,492 +0,0 @@
1
- module StateMachine
2
- module Integrations #:nodoc:
3
- # Adds support for integrating state machines with ActiveRecord models.
4
- #
5
- # == Examples
6
- #
7
- # Below is an example of a simple state machine defined within an
8
- # ActiveRecord model:
9
- #
10
- # class Vehicle < ActiveRecord::Base
11
- # state_machine :initial => :parked do
12
- # event :ignite do
13
- # transition :parked => :idling
14
- # end
15
- # end
16
- # end
17
- #
18
- # The examples in the sections below will use the above class as a
19
- # reference.
20
- #
21
- # == Actions
22
- #
23
- # By default, the action that will be invoked when a state is transitioned
24
- # is the +save+ action. This will cause the record to save the changes
25
- # made to the state machine's attribute. *Note* that if any other changes
26
- # were made to the record prior to transition, then those changes will
27
- # be saved as well.
28
- #
29
- # For example,
30
- #
31
- # vehicle = Vehicle.create # => #<Vehicle id: 1, name: nil, state: "parked">
32
- # vehicle.name = 'Ford Explorer'
33
- # vehicle.ignite # => true
34
- # vehicle.reload # => #<Vehicle id: 1, name: "Ford Explorer", state: "idling">
35
- #
36
- # == Events
37
- #
38
- # As described in StateMachine::InstanceMethods#state_machine, event
39
- # attributes are created for every machine that allow transitions to be
40
- # performed automatically when the object's action (in this case, :save)
41
- # is called.
42
- #
43
- # In ActiveRecord, these automated events are run in the following order:
44
- # * before validation - Run before callbacks and persist new states, then validate
45
- # * before save - If validation was skipped, run before callbacks and persist new states, then save
46
- # * after save - Run after callbacks
47
- #
48
- # For example,
49
- #
50
- # vehicle = Vehicle.create # => #<Vehicle id: 1, name: nil, state: "parked">
51
- # vehicle.state_event # => nil
52
- # vehicle.state_event = 'invalid'
53
- # vehicle.valid? # => false
54
- # vehicle.errors.full_messages # => ["State event is invalid"]
55
- #
56
- # vehicle.state_event = 'ignite'
57
- # vehicle.valid? # => true
58
- # vehicle.save # => true
59
- # vehicle.state # => "idling"
60
- # vehicle.state_event # => nil
61
- #
62
- # Note that this can also be done on a mass-assignment basis:
63
- #
64
- # vehicle = Vehicle.create(:state_event => 'ignite') # => #<Vehicle id: 1, name: nil, state: "idling">
65
- # vehicle.state # => "idling"
66
- #
67
- # === Security implications
68
- #
69
- # Beware that public event attributes mean that events can be fired
70
- # whenever mass-assignment is being used. If you want to prevent malicious
71
- # users from tampering with events through URLs / forms, the attribute
72
- # should be protected like so:
73
- #
74
- # class Vehicle < ActiveRecord::Base
75
- # attr_protected :state_event
76
- # # attr_accessible ... # Alternative technique
77
- #
78
- # state_machine do
79
- # ...
80
- # end
81
- # end
82
- #
83
- # If you want to only have *some* events be able to fire via mass-assignment,
84
- # you can build two state machines (one public and one protected) like so:
85
- #
86
- # class Vehicle < ActiveRecord::Base
87
- # attr_protected :state_event # Prevent access to events in the first machine
88
- #
89
- # state_machine do
90
- # # Define private events here
91
- # end
92
- #
93
- # # Public machine targets the same state as the private machine
94
- # state_machine :public_state, :attribute => :state do
95
- # # Define public events here
96
- # end
97
- # end
98
- #
99
- # == Transactions
100
- #
101
- # In order to ensure that any changes made during transition callbacks
102
- # are rolled back during a failed attempt, every transition is wrapped
103
- # within a transaction.
104
- #
105
- # For example,
106
- #
107
- # class Message < ActiveRecord::Base
108
- # end
109
- #
110
- # Vehicle.state_machine do
111
- # before_transition do |vehicle, transition|
112
- # Message.create(:content => transition.inspect)
113
- # false
114
- # end
115
- # end
116
- #
117
- # vehicle = Vehicle.create # => #<Vehicle id: 1, name: nil, state: "parked">
118
- # vehicle.ignite # => false
119
- # Message.count # => 0
120
- #
121
- # *Note* that only before callbacks that halt the callback chain and
122
- # failed attempts to save the record will result in the transaction being
123
- # rolled back. If an after callback halts the chain, the previous result
124
- # still applies and the transaction is *not* rolled back.
125
- #
126
- # To turn off transactions:
127
- #
128
- # class Vehicle < ActiveRecord::Base
129
- # state_machine :initial => :parked, :use_transactions => false do
130
- # ...
131
- # end
132
- # end
133
- #
134
- # == Validation errors
135
- #
136
- # If an event fails to successfully fire because there are no matching
137
- # transitions for the current record, a validation error is added to the
138
- # record's state attribute to help in determining why it failed and for
139
- # reporting via the UI.
140
- #
141
- # For example,
142
- #
143
- # vehicle = Vehicle.create(:state => 'idling') # => #<Vehicle id: 1, name: nil, state: "idling">
144
- # vehicle.ignite # => false
145
- # vehicle.errors.full_messages # => ["State cannot transition via \"ignite\""]
146
- #
147
- # If an event fails to fire because of a validation error on the record and
148
- # *not* because a matching transition was not available, no error messages
149
- # will be added to the state attribute.
150
- #
151
- # == Scopes
152
- #
153
- # To assist in filtering models with specific states, a series of named
154
- # scopes are defined on the model for finding records with or without a
155
- # particular set of states.
156
- #
157
- # These named scopes are essentially the functional equivalent of the
158
- # following definitions:
159
- #
160
- # class Vehicle < ActiveRecord::Base
161
- # named_scope :with_states, lambda {|*states| {:conditions => {:state => states}}}
162
- # # with_states also aliased to with_state
163
- #
164
- # named_scope :without_states, lambda {|*states| {:conditions => ['state NOT IN (?)', states]}}
165
- # # without_states also aliased to without_state
166
- # end
167
- #
168
- # *Note*, however, that the states are converted to their stored values
169
- # before being passed into the query.
170
- #
171
- # Because of the way named scopes work in ActiveRecord, they can be
172
- # chained like so:
173
- #
174
- # Vehicle.with_state(:parked).all(:order => 'id DESC')
175
- #
176
- # == Callbacks
177
- #
178
- # All before/after transition callbacks defined for ActiveRecord models
179
- # behave in the same way that other ActiveRecord callbacks behave. The
180
- # object involved in the transition is passed in as an argument.
181
- #
182
- # For example,
183
- #
184
- # class Vehicle < ActiveRecord::Base
185
- # state_machine :initial => :parked do
186
- # before_transition any => :idling do |vehicle|
187
- # vehicle.put_on_seatbelt
188
- # end
189
- #
190
- # before_transition do |vehicle, transition|
191
- # # log message
192
- # end
193
- #
194
- # event :ignite do
195
- # transition :parked => :idling
196
- # end
197
- # end
198
- #
199
- # def put_on_seatbelt
200
- # ...
201
- # end
202
- # end
203
- #
204
- # Note, also, that the transition can be accessed by simply defining
205
- # additional arguments in the callback block.
206
- #
207
- # == Observers
208
- #
209
- # In addition to support for ActiveRecord-like hooks, there is additional
210
- # support for ActiveRecord observers. Because of the way ActiveRecord
211
- # observers are designed, there is less flexibility around the specific
212
- # transitions that can be hooked in. However, a large number of hooks
213
- # *are* supported. For example, if a transition for a record's +state+
214
- # attribute changes the state from +parked+ to +idling+ via the +ignite+
215
- # event, the following observer methods are supported:
216
- # * before/after_ignite_from_parked_to_idling
217
- # * before/after_ignite_from_parked
218
- # * before/after_ignite_to_idling
219
- # * before/after_ignite
220
- # * before/after_transition_state_from_parked_to_idling
221
- # * before/after_transition_state_from_parked
222
- # * before/after_transition_state_to_idling
223
- # * before/after_transition_state
224
- # * before/after_transition
225
- #
226
- # The following class shows an example of some of these hooks:
227
- #
228
- # class VehicleObserver < ActiveRecord::Observer
229
- # def before_save(vehicle)
230
- # # log message
231
- # end
232
- #
233
- # # Callback for :ignite event *before* the transition is performed
234
- # def before_ignite(vehicle, transition)
235
- # # log message
236
- # end
237
- #
238
- # # Callback for :ignite event *after* the transition has been performed
239
- # def after_ignite(vehicle, transition)
240
- # # put on seatbelt
241
- # end
242
- #
243
- # # Generic transition callback *before* the transition is performed
244
- # def after_transition(vehicle, transition)
245
- # Audit.log(vehicle, transition)
246
- # end
247
- # end
248
- #
249
- # More flexible transition callbacks can be defined directly within the
250
- # model as described in StateMachine::Machine#before_transition
251
- # and StateMachine::Machine#after_transition.
252
- #
253
- # To define a single observer for multiple state machines:
254
- #
255
- # class StateMachineObserver < ActiveRecord::Observer
256
- # observe Vehicle, Switch, Project
257
- #
258
- # def after_transition(record, transition)
259
- # Audit.log(record, transition)
260
- # end
261
- # end
262
- module ActiveRecord
263
- # The default options to use for state machines using this integration
264
- class << self; attr_reader :defaults; end
265
- @defaults = {:action => :save}
266
-
267
- # Should this integration be used for state machines in the given class?
268
- # Classes that inherit from ActiveRecord::Base will automatically use
269
- # the ActiveRecord integration.
270
- def self.matches?(klass)
271
- defined?(::ActiveRecord::Base) && klass <= ::ActiveRecord::Base
272
- end
273
-
274
- # Loads additional files specific to ActiveRecord
275
- def self.extended(base) #:nodoc:
276
- require 'state_machine/integrations/active_record/observer'
277
-
278
- if Object.const_defined?(:I18n)
279
- locale = "#{File.dirname(__FILE__)}/active_record/locale.rb"
280
- I18n.load_path << locale unless I18n.load_path.include?(locale)
281
- end
282
- end
283
-
284
- # Forces the change in state to be recognized regardless of whether the
285
- # state value actually changed
286
- def write(object, attribute, value)
287
- result = super
288
- object.send("#{self.attribute}_will_change!") if attribute == :state && object.respond_to?("#{self.attribute}_will_change!")
289
- result
290
- end
291
-
292
- # Adds a validation error to the given object
293
- def invalidate(object, attribute, message, values = [])
294
- attribute = self.attribute(attribute)
295
-
296
- if Object.const_defined?(:I18n)
297
- options = values.inject({}) {|options, (key, value)| options[key] = value; options}
298
- object.errors.add(attribute, message, options.merge(
299
- :default => @messages[message]
300
- ))
301
- else
302
- object.errors.add(attribute, generate_message(message, values))
303
- end
304
- end
305
-
306
- # Resets any errors previously added when invalidating the given object
307
- def reset(object)
308
- object.errors.clear
309
- end
310
-
311
- protected
312
- # Adds the default callbacks for notifying ActiveRecord observers
313
- # before/after a transition has been performed.
314
- def after_initialize
315
- callbacks[:before] << Callback.new {|object, transition| notify(:before, object, transition)}
316
- callbacks[:after] << Callback.new {|object, transition| notify(:after, object, transition)}
317
- end
318
-
319
- # Defines an initialization hook into the owner class for setting the
320
- # initial state of the machine *before* any attributes are set on the
321
- # object
322
- def define_state_initializer
323
- @instance_helper_module.class_eval <<-end_eval, __FILE__, __LINE__
324
- # Ensure that the attributes setter gets used to force initialization
325
- # of the state machines
326
- def initialize(attributes = nil, *args)
327
- attributes ||= {}
328
- super
329
- end
330
-
331
- # Hooks in to attribute initialization to set the states *prior*
332
- # to the attributes being set
333
- def attributes=(new_attributes, *args)
334
- if new_record? && !@initialized_state_machines
335
- @initialized_state_machines = true
336
-
337
- if new_attributes
338
- attributes = new_attributes.dup
339
- attributes.stringify_keys!
340
- ignore = remove_attributes_protected_from_mass_assignment(attributes).keys
341
- else
342
- ignore = []
343
- end
344
-
345
- initialize_state_machines(:dynamic => false, :ignore => ignore)
346
- super
347
- initialize_state_machines(:dynamic => true, :ignore => ignore)
348
- else
349
- super
350
- end
351
- end
352
- end_eval
353
- end
354
-
355
- # Skips defining reader/writer methods since this is done automatically
356
- def define_state_accessor
357
- name = self.name
358
-
359
- owner_class.validates_each(attribute) do |record, attr, value|
360
- machine = record.class.state_machine(name)
361
- machine.invalidate(record, :state, :invalid) unless machine.states.match(record)
362
- end
363
- end
364
-
365
- # Adds support for defining the attribute predicate, while providing
366
- # compatibility with the default predicate which determines whether
367
- # *anything* is set for the attribute's value
368
- def define_state_predicate
369
- name = self.name
370
-
371
- # Still use class_eval here instance of define_instance_method since
372
- # we need to be able to call +super+
373
- @instance_helper_module.class_eval do
374
- define_method("#{name}?") do |*args|
375
- args.empty? ? super(*args) : self.class.state_machine(name).states.matches?(self, *args)
376
- end
377
- end
378
- end
379
-
380
- # Adds hooks into validation for automatically firing events
381
- def define_action_helpers
382
- if action == :save
383
- if super(:create_or_update)
384
- @instance_helper_module.class_eval do
385
- define_method(:valid?) do |*args|
386
- self.class.state_machines.fire_event_attributes(self, :save, false) { super(*args) }
387
- end
388
- end
389
- end
390
- else
391
- super
392
- end
393
- end
394
-
395
- # Creates a scope for finding records *with* a particular state or
396
- # states for the attribute
397
- def create_with_scope(name)
398
- attribute = self.attribute
399
- define_scope(name, lambda {|values| {:conditions => {attribute => values}}})
400
- end
401
-
402
- # Creates a scope for finding records *without* a particular state or
403
- # states for the attribute
404
- def create_without_scope(name)
405
- attribute = self.attribute
406
- define_scope(name, lambda {|values| {:conditions => ["#{attribute} NOT IN (?)", values]}})
407
- end
408
-
409
- # Runs a new database transaction, rolling back any changes by raising
410
- # an ActiveRecord::Rollback exception if the yielded block fails
411
- # (i.e. returns false).
412
- def transaction(object)
413
- object.class.transaction {raise ::ActiveRecord::Rollback unless yield}
414
- end
415
-
416
- # Creates a new callback in the callback chain, always inserting it
417
- # before the default Observer callbacks that were created after
418
- # initialization.
419
- def add_callback(type, options, &block)
420
- options[:terminator] = @terminator ||= lambda {|result| result == false}
421
- @callbacks[type].insert(-2, callback = Callback.new(options, &block))
422
- add_states(callback.known_states)
423
-
424
- callback
425
- end
426
-
427
- private
428
- # Defines a new named scope with the given name. Since ActiveRecord
429
- # does not allow direct access to the model being used within the
430
- # evaluation of a dynamic named scope, the scope must be generated
431
- # manually. It's necessary to have access to the model so that the
432
- # state names can be translated to their associated values and so that
433
- # inheritance is respected properly.
434
- def define_scope(name, scope)
435
- if owner_class.respond_to?(:named_scope)
436
- name = name.to_sym
437
- machine_name = self.name
438
-
439
- # Create the scope and then override it with state translation
440
- owner_class.named_scope(name)
441
- owner_class.scopes[name] = lambda do |klass, *states|
442
- machine_states = klass.state_machine(machine_name).states
443
- values = states.flatten.map {|state| machine_states.fetch(state).value}
444
-
445
- ::ActiveRecord::NamedScope::Scope.new(klass, scope.call(values))
446
- end
447
- end
448
-
449
- # Prevent the Machine class from wrapping the scope
450
- false
451
- end
452
-
453
- # Notifies observers on the given object that a callback occurred
454
- # involving the given transition. This will attempt to call the
455
- # following methods on observers:
456
- # * #{type}_#{qualified_event}_from_#{from}_to_#{to}
457
- # * #{type}_#{qualified_event}_from_#{from}
458
- # * #{type}_#{qualified_event}_to_#{to}
459
- # * #{type}_#{qualified_event}
460
- # * #{type}_transition_#{machine_name}_from_#{from}_to_#{to}
461
- # * #{type}_transition_#{machine_name}_from_#{from}
462
- # * #{type}_transition_#{machine_name}_to_#{to}
463
- # * #{type}_transition_#{machine_name}
464
- # * #{type}_transition
465
- #
466
- # This will always return true regardless of the results of the
467
- # callbacks.
468
- def notify(type, object, transition)
469
- name = self.name
470
- event = transition.qualified_event
471
- from = transition.from_name
472
- to = transition.to_name
473
-
474
- # Machine-specific updates
475
- ["#{type}_#{event}", "#{type}_transition_#{name}"].each do |event_segment|
476
- ["_from_#{from}", nil].each do |from_segment|
477
- ["_to_#{to}", nil].each do |to_segment|
478
- object.class.changed
479
- object.class.notify_observers([event_segment, from_segment, to_segment].join, object, transition)
480
- end
481
- end
482
- end
483
-
484
- # Generic updates
485
- object.class.changed
486
- object.class.notify_observers("#{type}_transition", object, transition)
487
-
488
- true
489
- end
490
- end
491
- end
492
- end