state_machine 1.0.1 → 1.0.2

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 (120) hide show
  1. data/.gitignore +11 -0
  2. data/.travis.yml +16 -0
  3. data/.yardopts +5 -0
  4. data/Appraisals +260 -0
  5. data/CHANGELOG.rdoc +15 -0
  6. data/Gemfile +3 -0
  7. data/README.rdoc +156 -29
  8. data/Rakefile +31 -57
  9. data/gemfiles/active_model-3.0.0.gemfile +7 -0
  10. data/gemfiles/active_model-3.0.0.gemfile.lock +32 -0
  11. data/gemfiles/active_model-3.0.5.gemfile +7 -0
  12. data/gemfiles/active_model-3.0.5.gemfile.lock +32 -0
  13. data/gemfiles/active_record-2.0.0.gemfile +8 -0
  14. data/gemfiles/active_record-2.0.0.gemfile.lock +30 -0
  15. data/gemfiles/active_record-2.0.5.gemfile +8 -0
  16. data/gemfiles/active_record-2.0.5.gemfile.lock +30 -0
  17. data/gemfiles/active_record-2.1.0.gemfile +8 -0
  18. data/gemfiles/active_record-2.1.0.gemfile.lock +30 -0
  19. data/gemfiles/active_record-2.1.2.gemfile +8 -0
  20. data/gemfiles/active_record-2.1.2.gemfile.lock +30 -0
  21. data/gemfiles/active_record-2.2.3.gemfile +8 -0
  22. data/gemfiles/active_record-2.2.3.gemfile.lock +30 -0
  23. data/gemfiles/active_record-2.3.12.gemfile +8 -0
  24. data/gemfiles/active_record-2.3.12.gemfile.lock +30 -0
  25. data/gemfiles/active_record-3.0.0.gemfile +8 -0
  26. data/gemfiles/active_record-3.0.0.gemfile.lock +44 -0
  27. data/gemfiles/active_record-3.0.5.gemfile +8 -0
  28. data/gemfiles/active_record-3.0.5.gemfile.lock +43 -0
  29. data/gemfiles/data_mapper-0.10.2.gemfile +12 -0
  30. data/gemfiles/data_mapper-0.10.2.gemfile.lock +45 -0
  31. data/gemfiles/data_mapper-0.9.11.gemfile +12 -0
  32. data/gemfiles/data_mapper-0.9.11.gemfile.lock +47 -0
  33. data/gemfiles/data_mapper-0.9.4.gemfile +12 -0
  34. data/gemfiles/data_mapper-0.9.4.gemfile.lock +61 -0
  35. data/gemfiles/data_mapper-0.9.7.gemfile +12 -0
  36. data/gemfiles/data_mapper-0.9.7.gemfile.lock +57 -0
  37. data/gemfiles/data_mapper-1.0.0.gemfile +12 -0
  38. data/gemfiles/data_mapper-1.0.0.gemfile.lock +53 -0
  39. data/gemfiles/data_mapper-1.0.1.gemfile +12 -0
  40. data/gemfiles/data_mapper-1.0.1.gemfile.lock +53 -0
  41. data/gemfiles/data_mapper-1.0.2.gemfile +12 -0
  42. data/gemfiles/data_mapper-1.0.2.gemfile.lock +53 -0
  43. data/gemfiles/data_mapper-1.1.0.gemfile +12 -0
  44. data/gemfiles/data_mapper-1.1.0.gemfile.lock +51 -0
  45. data/gemfiles/default.gemfile +7 -0
  46. data/gemfiles/default.gemfile.lock +24 -0
  47. data/gemfiles/mongo_mapper-0.5.5.gemfile +8 -0
  48. data/gemfiles/mongo_mapper-0.5.5.gemfile.lock +33 -0
  49. data/gemfiles/mongo_mapper-0.5.8.gemfile +8 -0
  50. data/gemfiles/mongo_mapper-0.5.8.gemfile.lock +33 -0
  51. data/gemfiles/mongo_mapper-0.6.0.gemfile +8 -0
  52. data/gemfiles/mongo_mapper-0.6.0.gemfile.lock +33 -0
  53. data/gemfiles/mongo_mapper-0.6.10.gemfile +8 -0
  54. data/gemfiles/mongo_mapper-0.6.10.gemfile.lock +33 -0
  55. data/gemfiles/mongo_mapper-0.7.0.gemfile +8 -0
  56. data/gemfiles/mongo_mapper-0.7.0.gemfile.lock +33 -0
  57. data/gemfiles/mongo_mapper-0.7.5.gemfile +8 -0
  58. data/gemfiles/mongo_mapper-0.7.5.gemfile.lock +36 -0
  59. data/gemfiles/mongo_mapper-0.8.0.gemfile +10 -0
  60. data/gemfiles/mongo_mapper-0.8.0.gemfile.lock +40 -0
  61. data/gemfiles/mongo_mapper-0.8.3.gemfile +10 -0
  62. data/gemfiles/mongo_mapper-0.8.3.gemfile.lock +40 -0
  63. data/gemfiles/mongo_mapper-0.8.4.gemfile +8 -0
  64. data/gemfiles/mongo_mapper-0.8.4.gemfile.lock +38 -0
  65. data/gemfiles/mongo_mapper-0.8.6.gemfile +8 -0
  66. data/gemfiles/mongo_mapper-0.8.6.gemfile.lock +38 -0
  67. data/gemfiles/mongo_mapper-0.9.0.gemfile +7 -0
  68. data/gemfiles/mongo_mapper-0.9.0.gemfile.lock +41 -0
  69. data/gemfiles/mongoid-2.0.0.gemfile +7 -0
  70. data/gemfiles/mongoid-2.0.0.gemfile.lock +42 -0
  71. data/gemfiles/mongoid-2.1.4.gemfile +7 -0
  72. data/gemfiles/mongoid-2.1.4.gemfile.lock +40 -0
  73. data/gemfiles/sequel-2.11.0.gemfile +8 -0
  74. data/gemfiles/sequel-2.11.0.gemfile.lock +28 -0
  75. data/gemfiles/sequel-2.12.0.gemfile +8 -0
  76. data/gemfiles/sequel-2.12.0.gemfile.lock +28 -0
  77. data/gemfiles/sequel-2.8.0.gemfile +8 -0
  78. data/gemfiles/sequel-2.8.0.gemfile.lock +28 -0
  79. data/gemfiles/sequel-3.0.0.gemfile +8 -0
  80. data/gemfiles/sequel-3.0.0.gemfile.lock +28 -0
  81. data/gemfiles/sequel-3.13.0.gemfile +8 -0
  82. data/gemfiles/sequel-3.13.0.gemfile.lock +28 -0
  83. data/gemfiles/sequel-3.14.0.gemfile +8 -0
  84. data/gemfiles/sequel-3.14.0.gemfile.lock +28 -0
  85. data/gemfiles/sequel-3.23.0.gemfile +8 -0
  86. data/gemfiles/sequel-3.23.0.gemfile.lock +28 -0
  87. data/gemfiles/sequel-3.24.0.gemfile +8 -0
  88. data/gemfiles/sequel-3.24.0.gemfile.lock +28 -0
  89. data/lib/state_machine/event.rb +13 -90
  90. data/lib/state_machine/helper_module.rb +17 -0
  91. data/lib/state_machine/integrations/active_model.rb +35 -0
  92. data/lib/state_machine/integrations/active_record.rb +41 -2
  93. data/lib/state_machine/integrations/data_mapper.rb +17 -2
  94. data/lib/state_machine/integrations/mongo_mapper.rb +34 -7
  95. data/lib/state_machine/integrations/mongoid.rb +34 -26
  96. data/lib/state_machine/integrations/mongoid/versions.rb +29 -3
  97. data/lib/state_machine/integrations/sequel.rb +22 -72
  98. data/lib/state_machine/integrations/sequel/versions.rb +87 -6
  99. data/lib/state_machine/machine.rb +279 -19
  100. data/lib/state_machine/state.rb +2 -2
  101. data/lib/state_machine/state_context.rb +133 -0
  102. data/lib/state_machine/version.rb +3 -0
  103. data/state_machine.gemspec +22 -0
  104. data/test/test_helper.rb +1 -3
  105. data/test/unit/branch_test.rb +1 -3
  106. data/test/unit/event_collection_test.rb +3 -3
  107. data/test/unit/event_test.rb +1 -3
  108. data/test/unit/helper_module_test.rb +17 -0
  109. data/test/unit/integrations/active_model_test.rb +0 -4
  110. data/test/unit/integrations/active_record_test.rb +50 -9
  111. data/test/unit/integrations/data_mapper_test.rb +267 -253
  112. data/test/unit/integrations/mongo_mapper_test.rb +47 -15
  113. data/test/unit/integrations/mongoid_test.rb +50 -8
  114. data/test/unit/integrations/sequel_test.rb +10 -6
  115. data/test/unit/machine_test.rb +206 -25
  116. data/test/unit/state_context_test.rb +421 -0
  117. data/test/unit/state_test.rb +20 -3
  118. metadata +303 -128
  119. data/lib/state_machine/condition_proxy.rb +0 -94
  120. data/test/unit/condition_proxy_test.rb +0 -328
@@ -1,94 +0,0 @@
1
- require 'state_machine/eval_helpers'
2
-
3
- module StateMachine
4
- # Represents a type of module in which class-level methods are proxied to
5
- # another class, injecting a custom <tt>:if</tt> condition along with method.
6
- #
7
- # This is used for being able to automatically include conditionals which
8
- # check the current state in class-level methods that have configuration
9
- # options.
10
- #
11
- # == Examples
12
- #
13
- # class Vehicle
14
- # class << self
15
- # attr_accessor :validations
16
- #
17
- # def validate(options, &block)
18
- # validations << options
19
- # end
20
- # end
21
- #
22
- # self.validations = []
23
- # attr_accessor :state, :simulate
24
- #
25
- # def moving?
26
- # self.class.validations.all? {|validation| validation[:if].call(self)}
27
- # end
28
- # end
29
- #
30
- # In the above class, a simple set of validation behaviors have been defined.
31
- # Each validation consists of a configuration like so:
32
- #
33
- # Vehicle.validate :unless => :simulate
34
- # Vehicle.validate :if => lambda {|vehicle| ...}
35
- #
36
- # In order to scope conditions, a condition proxy can be created to the
37
- # Vehicle class. For example,
38
- #
39
- # proxy = StateMachine::ConditionProxy.new(Vehicle, lambda {|vehicle| vehicle.state == 'first_gear'})
40
- # proxy.validate(:unless => :simulate)
41
- #
42
- # vehicle = Vehicle.new # => #<Vehicle:0xb7ce491c @simulate=nil, @state=nil>
43
- # vehicle.moving? # => false
44
- #
45
- # vehicle.state = 'first_gear'
46
- # vehicle.moving? # => true
47
- #
48
- # vehicle.simulate = true
49
- # vehicle.moving? # => false
50
- class ConditionProxy < Module
51
- include EvalHelpers
52
-
53
- # Creates a new proxy to the given class, merging in the given condition
54
- def initialize(klass, condition)
55
- @klass = klass
56
- @condition = condition
57
- end
58
-
59
- # Hooks in condition-merging to methods that don't exist in this module
60
- def method_missing(*args, &block)
61
- # Get the configuration
62
- if args.last.is_a?(Hash)
63
- options = args.last
64
- else
65
- args << options = {}
66
- end
67
-
68
- # Get any existing condition that may need to be merged
69
- if_condition = options.delete(:if)
70
- unless_condition = options.delete(:unless)
71
-
72
- # Provide scope access to configuration in case the block is evaluated
73
- # within the object instance
74
- proxy = self
75
- proxy_condition = @condition
76
-
77
- # Replace the configuration condition with the one configured for this
78
- # proxy, merging together any existing conditions
79
- options[:if] = lambda do |*args|
80
- # Block may be executed within the context of the actual object, so
81
- # it'll either be the first argument or the executing context
82
- object = args.first || self
83
-
84
- proxy.evaluate_method(object, proxy_condition) &&
85
- Array(if_condition).all? {|condition| proxy.evaluate_method(object, condition)} &&
86
- !Array(unless_condition).any? {|condition| proxy.evaluate_method(object, condition)}
87
- end
88
-
89
- # Evaluate the method on the original class with the condition proxied
90
- # through
91
- @klass.send(*args, &block)
92
- end
93
- end
94
- end
@@ -1,328 +0,0 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2
-
3
- class Validateable
4
- class << self
5
- def validate(*args, &block)
6
- args << block if block_given?
7
- args
8
- end
9
- end
10
- end
11
-
12
- class ConditionProxyTest < Test::Unit::TestCase
13
- def test_should_call_class_with_same_arguments
14
- options = {}
15
- condition_proxy = StateMachine::ConditionProxy.new(Validateable, lambda {})
16
- validation = condition_proxy.validate(:name, options)
17
-
18
- assert_equal [:name, options], validation
19
- end
20
-
21
- def test_should_pass_block_through_to_class
22
- options = {}
23
- condition_proxy = StateMachine::ConditionProxy.new(Validateable, lambda {})
24
-
25
- proxy_block = lambda {}
26
- validation = condition_proxy.validate(:name, options, &proxy_block)
27
-
28
- assert_equal [:name, options, proxy_block], validation
29
- end
30
-
31
- def test_should_pass_object_into_proxy_condition
32
- condition_args = []
33
- condition_proxy = StateMachine::ConditionProxy.new(Validateable, lambda {|*args| condition_args = args})
34
- options = condition_proxy.validate[0]
35
-
36
- object = Validateable.new
37
- options[:if].call(object)
38
-
39
- assert_equal [object], condition_args
40
- end
41
-
42
- def test_should_evaluate_symbol_condition
43
- klass = Class.new(Validateable) do
44
- attr_accessor :callback_called
45
-
46
- def callback
47
- @callback_called = true
48
- end
49
- end
50
-
51
- condition_proxy = StateMachine::ConditionProxy.new(klass, :callback)
52
- options = condition_proxy.validate[0]
53
-
54
- object = klass.new
55
- options[:if].call(object)
56
-
57
- assert object.callback_called
58
- end
59
-
60
- def test_should_evaluate_string_condition
61
- klass = Class.new(Validateable) do
62
- attr_accessor :callback_called
63
- end
64
-
65
- condition_proxy = StateMachine::ConditionProxy.new(klass, '@callback_called = true')
66
- options = condition_proxy.validate[0]
67
-
68
- object = klass.new
69
- options[:if].call(object)
70
-
71
- assert object.callback_called
72
- end
73
- end
74
-
75
- class ConditionProxyWithoutConditionsTest < Test::Unit::TestCase
76
- def setup
77
- @proxy_result = nil
78
- condition_proxy = StateMachine::ConditionProxy.new(Validateable, lambda {@proxy_result})
79
-
80
- @object = Validateable.new
81
- @options = condition_proxy.validate[0]
82
- end
83
-
84
- def test_should_have_options_configuration
85
- assert_instance_of Hash, @options
86
- end
87
-
88
- def test_should_have_if_option
89
- assert_not_nil @options[:if]
90
- end
91
-
92
- def test_should_be_false_if_proxy_condition_is_false
93
- @proxy_result = false
94
- assert !@options[:if].call(@object)
95
- end
96
-
97
- def test_should_be_true_if_proxy_condition_is_true
98
- @proxy_result = true
99
- assert @options[:if].call(@object)
100
- end
101
-
102
- def test_should_be_true_if_proxy_condition_is_not_true
103
- @proxy_result = 1
104
- assert @options[:if].call(@object)
105
- end
106
- end
107
-
108
- class ConditionProxyWithIfConditionTest < Test::Unit::TestCase
109
- def setup
110
- @proxy_result = nil
111
- condition_proxy = StateMachine::ConditionProxy.new(Validateable, lambda {@proxy_result})
112
-
113
- @object = Validateable.new
114
-
115
- @condition_result = nil
116
- @options = condition_proxy.validate(:if => lambda {@condition_result})[0]
117
- end
118
-
119
- def test_should_have_if_option
120
- assert_not_nil @options[:if]
121
- end
122
-
123
- def test_should_be_false_if_proxy_condition_is_false
124
- @proxy_result = false
125
- assert !@options[:if].call(@object)
126
- end
127
-
128
- def test_should_be_false_if_original_condition_is_false
129
- @condition_result = false
130
- assert !@options[:if].call(@object)
131
- end
132
-
133
- def test_should_be_true_if_proxy_and_original_condition_are_true
134
- @proxy_result = true
135
- @condition_result = true
136
- assert @options[:if].call(@object)
137
- end
138
-
139
- def test_should_evaluate_symbol_condition
140
- klass = Class.new(Validateable) do
141
- attr_accessor :callback
142
- end
143
-
144
- condition_proxy = StateMachine::ConditionProxy.new(klass, lambda {true})
145
- options = condition_proxy.validate(:if => :callback)[0]
146
-
147
- object = klass.new
148
- object.callback = false
149
- assert !options[:if].call(object)
150
-
151
- object.callback = true
152
- assert options[:if].call(object)
153
- end
154
-
155
- def test_should_evaluate_string_condition
156
- klass = Class.new(Validateable) do
157
- attr_accessor :callback
158
- end
159
-
160
- condition_proxy = StateMachine::ConditionProxy.new(klass, lambda {true})
161
- options = condition_proxy.validate(:if => '@callback')[0]
162
-
163
- object = klass.new
164
- object.callback = false
165
- assert !options[:if].call(object)
166
-
167
- object.callback = true
168
- assert options[:if].call(object)
169
- end
170
- end
171
-
172
- class ConditionProxyWithMultipleIfConditionsTest < Test::Unit::TestCase
173
- def setup
174
- condition_proxy = StateMachine::ConditionProxy.new(Validateable, lambda {true})
175
-
176
- @object = Validateable.new
177
-
178
- @first_condition_result = nil
179
- @second_condition_result = nil
180
- @options = condition_proxy.validate(:if => [lambda {@first_condition_result}, lambda {@second_condition_result}])[0]
181
- end
182
-
183
- def test_should_be_true_if_all_conditions_are_true
184
- @first_condition_result = true
185
- @second_condition_result = true
186
- assert @options[:if].call(@object)
187
- end
188
-
189
- def test_should_be_false_if_any_condition_is_false
190
- @first_condition_result = true
191
- @second_condition_result = false
192
- assert !@options[:if].call(@object)
193
-
194
- @first_condition_result = false
195
- @second_condition_result = true
196
- assert !@options[:if].call(@object)
197
- end
198
- end
199
-
200
- class ConditionProxyWithUnlessConditionTest < Test::Unit::TestCase
201
- def setup
202
- @proxy_result = nil
203
- condition_proxy = StateMachine::ConditionProxy.new(Validateable, lambda {@proxy_result})
204
-
205
- @object = Validateable.new
206
-
207
- @condition_result = nil
208
- @options = condition_proxy.validate(:unless => lambda {@condition_result})[0]
209
- end
210
-
211
- def test_should_have_if_option
212
- assert_not_nil @options[:if]
213
- end
214
-
215
- def test_should_be_false_if_proxy_condition_is_false
216
- @proxy_result = false
217
- assert !@options[:if].call(@object)
218
- end
219
-
220
- def test_should_be_false_if_original_condition_is_true
221
- @condition_result = true
222
- assert !@options[:if].call(@object)
223
- end
224
-
225
- def test_should_be_true_if_proxy_is_true_and_original_condition_is_false
226
- @proxy_result = true
227
- @condition_result = false
228
- assert @options[:if].call(@object)
229
- end
230
-
231
- def test_should_evaluate_symbol_condition
232
- klass = Class.new(Validateable) do
233
- attr_accessor :callback
234
- end
235
-
236
- condition_proxy = StateMachine::ConditionProxy.new(klass, lambda {true})
237
- options = condition_proxy.validate(:unless => :callback)[0]
238
-
239
- object = klass.new
240
- object.callback = true
241
- assert !options[:if].call(object)
242
-
243
- object.callback = false
244
- assert options[:if].call(object)
245
- end
246
-
247
- def test_should_evaluate_string_condition
248
- klass = Class.new(Validateable) do
249
- attr_accessor :callback
250
- end
251
-
252
- condition_proxy = StateMachine::ConditionProxy.new(klass, lambda {true})
253
- options = condition_proxy.validate(:unless => '@callback')[0]
254
-
255
- object = klass.new
256
- object.callback = true
257
- assert !options[:if].call(object)
258
-
259
- object.callback = false
260
- assert options[:if].call(object)
261
- end
262
- end
263
-
264
- class ConditionProxyWithMultipleUnlessConditionsTest < Test::Unit::TestCase
265
- def setup
266
- condition_proxy = StateMachine::ConditionProxy.new(Validateable, lambda {true})
267
-
268
- @object = Validateable.new
269
-
270
- @first_condition_result = nil
271
- @second_condition_result = nil
272
- @options = condition_proxy.validate(:unless => [lambda {@first_condition_result}, lambda {@second_condition_result}])[0]
273
- end
274
-
275
- def test_should_be_true_if_all_conditions_are_false
276
- @first_condition_result = false
277
- @second_condition_result = false
278
- assert @options[:if].call(@object)
279
- end
280
-
281
- def test_should_be_false_if_any_condition_is_true
282
- @first_condition_result = true
283
- @second_condition_result = false
284
- assert !@options[:if].call(@object)
285
-
286
- @first_condition_result = false
287
- @second_condition_result = true
288
- assert !@options[:if].call(@object)
289
- end
290
- end
291
-
292
- class ConditionProxyWithIfAndUnlessConditionsTest < Test::Unit::TestCase
293
- def setup
294
- condition_proxy = StateMachine::ConditionProxy.new(Validateable, lambda {true})
295
-
296
- @object = Validateable.new
297
-
298
- @if_condition_result = nil
299
- @unless_condition_result = nil
300
- @options = condition_proxy.validate(:if => lambda {@if_condition_result}, :unless => lambda {@unless_condition_result})[0]
301
- end
302
-
303
- def test_should_be_false_if_if_condition_is_false
304
- @if_condition_result = false
305
- @unless_condition_result = false
306
- assert !@options[:if].call(@object)
307
-
308
- @if_condition_result = false
309
- @unless_condition_result = true
310
- assert !@options[:if].call(@object)
311
- end
312
-
313
- def test_should_be_false_if_unless_condition_is_true
314
- @if_condition_result = false
315
- @unless_condition_result = true
316
- assert !@options[:if].call(@object)
317
-
318
- @if_condition_result = true
319
- @unless_condition_result = true
320
- assert !@options[:if].call(@object)
321
- end
322
-
323
- def test_should_be_true_if_if_condition_is_true_and_unless_condition_is_false
324
- @if_condition_result = true
325
- @unless_condition_result = false
326
- assert @options[:if].call(@object)
327
- end
328
- end