state_machine 1.0.1 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
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