state_machines 0.1.1 → 0.1.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6c8f59bcc8fc3bd9bc6c1cabb0872b9c408b6bfd
4
- data.tar.gz: 07144b06a3bb7e8f20e6c23bfc04369c4dec5e4a
3
+ metadata.gz: 65309f4d9983b6dccdc1b0b166250118764346dd
4
+ data.tar.gz: 6a47d10426af28dc338a77b731396c06fab6c84a
5
5
  SHA512:
6
- metadata.gz: 34091a04ce15920471155299080ec48c4a6494a0cf95512c669b312f2ba907e888ee8c0c61fd36c05948c45cfaf1283ec21b408a747338c8afbad1911f1b6873
7
- data.tar.gz: 325b5a778bc84a6cbb0553c51229f90be54952c10c8362c493763fd23ac1df2b54ce60de8a6bb66563ab8a00462f2df0ddcabb74998fb74e607a9062c4af26ec
6
+ metadata.gz: 10077a16141c29c4a6af5e8caf8a17b5e5548ab5138a70b7bb387970f4f8d6361a4f6a8ef8d9e1217aaa2cc783951ca52ec39b798e78077fe7ece8790bfcd19d
7
+ data.tar.gz: 457e4419ab4037322ce5b5d06e8dcbb6111a4874cfe4d5927dfd48948be2ed778ae5a83a1768dab3c15762402c1c8758a89ede19fdb061724533555bcea1c00c
data/README.md CHANGED
@@ -20,7 +20,556 @@ Or install it yourself as:
20
20
 
21
21
  ## Usage
22
22
 
23
+ ### Example
23
24
 
25
+ Below is an example of many of the features offered by this plugin, including:
26
+
27
+ * Initial states
28
+ * Namespaced states
29
+ * Transition callbacks
30
+ * Conditional transitions
31
+ * State-driven instance behavior
32
+ * Customized state values
33
+ * Parallel events
34
+ * Path analysis
35
+
36
+ Class definition:
37
+
38
+ ```ruby
39
+ class Vehicle
40
+ attr_accessor :seatbelt_on, :time_used, :auto_shop_busy
41
+
42
+ state_machine state, initial: :parked do
43
+ before_transition parked: :any - :parked, do: :put_on_seatbelt
44
+
45
+ after_transition on: :crash, do: :tow
46
+ after_transition on: :repair, :do: :fix
47
+ after_transition any => :parked do |vehicle, transition|
48
+ vehicle.seatbelt_on = false
49
+ end
50
+
51
+ after_failure on: :ignite, do: :log_start_failure
52
+
53
+ around_transition do |vehicle, transition, block|
54
+ start = Time.now
55
+ block.call
56
+ vehicle.time_used += Time.now - start
57
+ end
58
+
59
+ event :park do
60
+ transition [:idling, :first_gear] => :parked
61
+ end
62
+
63
+ event :ignite do
64
+ transition stalled: same, parked: :idling
65
+ end
66
+
67
+ event :idle do
68
+ transition first_gear: :idling
69
+ end
70
+
71
+ event :shift_up do
72
+ transition idling: :first_gear, first_gear: :second_gear, second_gear: :third_gear
73
+ end
74
+
75
+ event :shift_down do
76
+ transition third_gear: :second_gear, second_gear: :first_gear
77
+ end
78
+
79
+ event :crash do
80
+ transition all - [:parked, :stalled] => :stalled, if: ->(vehicle) {!vehicle.passed_inspection?}
81
+ end
82
+
83
+ event :repair do
84
+ # The first transition that matches the state and passes its conditions
85
+ # will be used
86
+ transition stalled: parked, unless: :auto_shop_busy
87
+ transition stalled: same
88
+ end
89
+
90
+ state :parked do
91
+ def speed
92
+ 0
93
+ end
94
+ end
95
+
96
+ state :idling, :first_gear do
97
+ def speed
98
+ 10
99
+ end
100
+ end
101
+
102
+ state all - [:parked, :stalled, :idling] do
103
+ def moving?
104
+ true
105
+ end
106
+ end
107
+
108
+ state :parked, :stalled, :idling do
109
+ def moving?
110
+ false
111
+ end
112
+ end
113
+ end
114
+
115
+ state_machine :alarm_state, initial: :active, namespace: :'alarm' do
116
+ event :enable do
117
+ transition all => :active
118
+ end
119
+
120
+ event :disable do
121
+ transition all => :off
122
+ end
123
+
124
+ state :active, :value => 1
125
+ state :off, :value => 0
126
+ end
127
+
128
+ def initialize
129
+ @seatbelt_on = false
130
+ @time_used = 0
131
+ @auto_shop_busy = true
132
+ super() # NOTE: This *must* be called, otherwise states won't get initialized
133
+ end
134
+
135
+ def put_on_seatbelt
136
+ @seatbelt_on = true
137
+ end
138
+
139
+ def passed_inspection?
140
+ false
141
+ end
142
+
143
+ def tow
144
+ # tow the vehicle
145
+ end
146
+
147
+ def fix
148
+ # get the vehicle fixed by a mechanic
149
+ end
150
+
151
+ def log_start_failure
152
+ # log a failed attempt to start the vehicle
153
+ end
154
+ end
155
+ ```
156
+
157
+ **Note** the comment made on the `initialize` method in the class. In order for
158
+ state machine attributes to be properly initialized, `super()` must be called.
159
+ See `StateMachines:MacroMethods` for more information about this.
160
+
161
+ Using the above class as an example, you can interact with the state machine
162
+ like so:
163
+
164
+ ```ruby
165
+ vehicle = Vehicle.new # => #<Vehicle:0xb7cf4eac @state="parked", @seatbelt_on=false>
166
+ vehicle.state # => "parked"
167
+ vehicle.state_name # => :parked
168
+ vehicle.human_state_name # => "parked"
169
+ vehicle.parked? # => true
170
+ vehicle.can_ignite? # => true
171
+ vehicle.ignite_transition # => #<StateMachines:Transition attribute=:state event=:ignite from="parked" from_name=:parked to="idling" to_name=:idling>
172
+ vehicle.state_events # => [:ignite]
173
+ vehicle.state_transitions # => [#<StateMachines:Transition attribute=:state event=:ignite from="parked" from_name=:parked to="idling" to_name=:idling>]
174
+ vehicle.speed # => 0
175
+ vehicle.moving? # => false
176
+
177
+ vehicle.ignite # => true
178
+ vehicle.parked? # => false
179
+ vehicle.idling? # => true
180
+ vehicle.speed # => 10
181
+ vehicle # => #<Vehicle:0xb7cf4eac @state="idling", @seatbelt_on=true>
182
+
183
+ vehicle.shift_up # => true
184
+ vehicle.speed # => 10
185
+ vehicle.moving? # => true
186
+ vehicle # => #<Vehicle:0xb7cf4eac @state="first_gear", @seatbelt_on=true>
187
+
188
+ # A generic event helper is available to fire without going through the event's instance method
189
+ vehicle.fire_state_event(:shift_up) # => true
190
+
191
+ # Call state-driven behavior that's undefined for the state raises a NoMethodError
192
+ vehicle.speed # => NoMethodError: super: no superclass method `speed' for #<Vehicle:0xb7cf4eac>
193
+ vehicle # => #<Vehicle:0xb7cf4eac @state="second_gear", @seatbelt_on=true>
194
+
195
+ # The bang (!) operator can raise exceptions if the event fails
196
+ vehicle.park! # => StateMachines:InvalidTransition: Cannot transition state via :park from :second_gear
197
+
198
+ # Generic state predicates can raise exceptions if the value does not exist
199
+ vehicle.state?(:parked) # => false
200
+ vehicle.state?(:invalid) # => IndexError: :invalid is an invalid name
201
+
202
+ # Namespaced machines have uniquely-generated methods
203
+ vehicle.alarm_state # => 1
204
+ vehicle.alarm_state_name # => :active
205
+
206
+ vehicle.can_disable_alarm? # => true
207
+ vehicle.disable_alarm # => true
208
+ vehicle.alarm_state # => 0
209
+ vehicle.alarm_state_name # => :off
210
+ vehicle.can_enable_alarm? # => true
211
+
212
+ vehicle.alarm_off? # => true
213
+ vehicle.alarm_active? # => false
214
+
215
+ # Events can be fired in parallel
216
+ vehicle.fire_events(:shift_down, :enable_alarm) # => true
217
+ vehicle.state_name # => :first_gear
218
+ vehicle.alarm_state_name # => :active
219
+
220
+ vehicle.fire_events!(:ignite, :enable_alarm) # => StateMachines:InvalidTransition: Cannot run events in parallel: ignite, enable_alarm
221
+
222
+ # Human-friendly names can be accessed for states/events
223
+ Vehicle.human_state_name(:first_gear) # => "first gear"
224
+ Vehicle.human_alarm_state_name(:active) # => "active"
225
+
226
+ Vehicle.human_state_event_name(:shift_down) # => "shift down"
227
+ Vehicle.human_alarm_state_event_name(:enable) # => "enable"
228
+
229
+ # States / events can also be references by the string version of their name
230
+ Vehicle.human_state_name('first_gear') # => "first gear"
231
+ Vehicle.human_state_event_name('shift_down') # => "shift down"
232
+
233
+ # Available transition paths can be analyzed for an object
234
+ vehicle.state_paths # => [[#<StateMachines:Transition ...], [#<StateMachines:Transition ...], ...]
235
+ vehicle.state_paths.to_states # => [:parked, :idling, :first_gear, :stalled, :second_gear, :third_gear]
236
+ vehicle.state_paths.events # => [:park, :ignite, :shift_up, :idle, :crash, :repair, :shift_down]
237
+
238
+ # Find all paths that start and end on certain states
239
+ vehicle.state_paths(:from => :parked, :to => :first_gear) # => [[
240
+ # #<StateMachines:Transition attribute=:state event=:ignite from="parked" ...>,
241
+ # #<StateMachines:Transition attribute=:state event=:shift_up from="idling" ...>
242
+ # ]]
243
+ # Skipping state_machine and writing to attributes directly
244
+ vehicle.state = "parked"
245
+ vehicle.state # => "parked"
246
+ vehicle.state_name # => :parked
247
+
248
+ # *Note* that the following is not supported (see StateMachines:MacroMethods#state_machine):
249
+ # vehicle.state = :parked
250
+ ```
251
+
252
+ ## Additional Topics
253
+
254
+ ### Explicit vs. Implicit Event Transitions
255
+
256
+ Every event defined for a state machine generates an instance method on the
257
+ class that allows the event to be explicitly triggered. Most of the examples in
258
+ the state_machine documentation use this technique. However, with some types of
259
+ integrations, like ActiveRecord, you can also *implicitly* fire events by
260
+ setting a special attribute on the instance.
261
+
262
+ Suppose you're using the ActiveRecord integration and the following model is
263
+ defined:
264
+
265
+ ```ruby
266
+ class Vehicle < ActiveRecord::Base
267
+ state_machine initial: :parked do
268
+ event :ignite do
269
+ transition parked: :idling
270
+ end
271
+ end
272
+ end
273
+ ```
274
+
275
+ To trigger the `ignite` event, you would typically call the `Vehicle#ignite`
276
+ method like so:
277
+
278
+ ```ruby
279
+ vehicle = Vehicle.create # => #<Vehicle id=1 state="parked">
280
+ vehicle.ignite # => true
281
+ vehicle.state # => "idling"
282
+ ```
283
+
284
+ This is referred to as an *explicit* event transition. The same behavior can
285
+ also be achieved *implicitly* by setting the state event attribute and invoking
286
+ the action associated with the state machine. For example:
287
+
288
+ ```ruby
289
+ vehicle = Vehicle.create # => #<Vehicle id=1 state="parked">
290
+ vehicle.state_event = 'ignite' # => 'ignite'
291
+ vehicle.save # => true
292
+ vehicle.state # => 'idling'
293
+ vehicle.state_event # => nil
294
+ ```
295
+
296
+ As you can see, the `ignite` event was automatically triggered when the `save`
297
+ action was called. This is particularly useful if you want to allow users to
298
+ drive the state transitions from a web API.
299
+
300
+ See each integration's API documentation for more information on the implicit
301
+ approach.
302
+
303
+ ### Symbols vs. Strings
304
+
305
+ In all of the examples used throughout the documentation, you'll notice that
306
+ states and events are almost always referenced as symbols. This isn't a
307
+ requirement, but rather a suggested best practice.
308
+
309
+ You can very well define your state machine with Strings like so:
310
+
311
+ ```ruby
312
+ class Vehicle
313
+ state_machine initial: 'parked' do
314
+ event 'ignite' do
315
+ transition 'parked' => 'idling'
316
+ end
317
+
318
+ # ...
319
+ end
320
+ end
321
+ ```
322
+
323
+ You could even use numbers as your state / event names. The **important** thing
324
+ to keep in mind is that the type being used for referencing states / events in
325
+ your machine definition must be **consistent**. If you're using Symbols, then
326
+ all states / events must use Symbols. Otherwise you'll encounter the following
327
+ error:
328
+
329
+ ```ruby
330
+ class Vehicle
331
+ state_machine do
332
+ event :ignite do
333
+ transition parked: 'idling'
334
+ end
335
+ end
336
+ end
337
+
338
+ # => ArgumentError: "idling" state defined as String, :parked defined as Symbol; all states must be consistent
339
+ ```
340
+
341
+ There **is** an exception to this rule. The consistency is only required within
342
+ the definition itself. However, when the machine's helper methods are called
343
+ with input from external sources, such as a web form, state_machine will map
344
+ that input to a String / Symbol. For example:
345
+
346
+ ```ruby
347
+ class Vehicle
348
+ state_machine initial: :parked do
349
+ event :ignite do
350
+ transition parked: :idling
351
+ end
352
+ end
353
+ end
354
+
355
+ v = Vehicle.new # => #<Vehicle:0xb71da5f8 @state="parked">
356
+ v.state?('parked') # => true
357
+ v.state?(:parked) # => true
358
+ ```
359
+
360
+ **Note** that none of this actually has to do with the type of the value that
361
+ gets stored. By default, all state values are assumed to be string -- regardless
362
+ of whether the state names are symbols or strings. If you want to store states
363
+ as symbols instead you'll have to be explicit about it:
364
+
365
+ ```ruby
366
+ class Vehicle
367
+ state_machine initial: :parked do
368
+ event :ignite do
369
+ transition parked: :idling
370
+ end
371
+
372
+ states.each do |state|
373
+ self.state(state.name, :value => state.name.to_sym)
374
+ end
375
+ end
376
+ end
377
+
378
+ v = Vehicle.new # => #<Vehicle:0xb71da5f8 @state=:parked>
379
+ v.state?('parked') # => true
380
+ v.state?(:parked) # => true
381
+ ```
382
+
383
+ ### Syntax flexibility
384
+
385
+ Although state_machine introduces a simplified syntax, it still remains
386
+ backwards compatible with previous versions and other state-related libraries by
387
+ providing some flexibility around how transitions are defined. See below for an
388
+ overview of these syntaxes.
389
+
390
+ #### Verbose syntax
391
+
392
+ In general, it's recommended that state machines use the implicit syntax for
393
+ transitions. However, you can be a little more explicit and verbose about
394
+ transitions by using the `:from`, `:except_from`, `:to`,
395
+ and `:except_to` options.
396
+
397
+ For example, transitions and callbacks can be defined like so:
398
+
399
+ ```ruby
400
+ class Vehicle
401
+ state_machine initial: :parked do
402
+ before_transition from: :parked, except_to: :parked, do: :put_on_seatbelt
403
+ after_transition to: :parked do |transition|
404
+ self.seatbelt = 'off' # self is the record
405
+ end
406
+
407
+ event :ignite do
408
+ transition from: :parked, to: :idling
409
+ end
410
+ end
411
+ end
412
+ ```
413
+
414
+ #### Transition context
415
+
416
+ Some flexibility is provided around the context in which transitions can be
417
+ defined. In almost all examples throughout the documentation, transitions are
418
+ defined within the context of an event. If you prefer to have state machines
419
+ defined in the context of a **state** either out of preference or in order to
420
+ easily migrate from a different library, you can do so as shown below:
421
+
422
+ ```ruby
423
+ class Vehicle
424
+ state_machine initial: :parked do
425
+ ...
426
+
427
+ state :parked do
428
+ transition to::idling, :on => [:ignite, :shift_up], if: :seatbelt_on?
429
+
430
+ def speed
431
+ 0
432
+ end
433
+ end
434
+
435
+ state :first_gear do
436
+ transition to: :second_gear, on: :shift_up
437
+
438
+ def speed
439
+ 10
440
+ end
441
+ end
442
+
443
+ state :idling, :first_gear do
444
+ transition to: :parked, on: :park
445
+ end
446
+ end
447
+ end
448
+ ```
449
+
450
+ In the above example, there's no need to specify the `from` state for each
451
+ transition since it's inferred from the context.
452
+
453
+ You can also define transitions completely outside the context of a particular
454
+ state / event. This may be useful in cases where you're building a state
455
+ machine from a data store instead of part of the class definition. See the
456
+ example below:
457
+
458
+ ```ruby
459
+ class Vehicle
460
+ state_machine initial: :parked do
461
+ ...
462
+
463
+ transition parked: :idling, :on => [:ignite, :shift_up]
464
+ transition first_gear: :second_gear, second_gear: :third_gear, on: :shift_up
465
+ transition [:idling, :first_gear] => :parked, on: :park
466
+ transition [:idling, :first_gear] => :parked, on: :park
467
+ transition all - [:parked, :stalled]: :stalled, unless: :auto_shop_busy?
468
+ end
469
+ end
470
+ ```
471
+
472
+ Notice that in these alternative syntaxes:
473
+
474
+ * You can continue to configure `:if` and `:unless` conditions
475
+ * You can continue to define `from` states (when in the machine context) using
476
+ the `all`, `any`, and `same` helper methods
477
+
478
+ ### Static / Dynamic definitions
479
+
480
+ In most cases, the definition of a state machine is **static**. That is to say,
481
+ the states, events and possible transitions are known ahead of time even though
482
+ they may depend on data that's only known at runtime. For example, certain
483
+ transitions may only be available depending on an attribute on that object it's
484
+ being run on. All of the documentation in this library define static machines
485
+ like so:
486
+
487
+ ```ruby
488
+ class Vehicle
489
+ state_machine :state, initial: :parked do
490
+ event :park do
491
+ transition [:idling, :first_gear] => :parked
492
+ end
493
+
494
+ ...
495
+ end
496
+ end
497
+ ```
498
+
499
+ However, there may be cases where the definition of a state machine is **dynamic**.
500
+ This means that you don't know the possible states or events for a machine until
501
+ runtime. For example, you may allow users in your application to manage the
502
+ state machine of a project or task in your system. This means that the list of
503
+ transitions (and their associated states / events) could be stored externally,
504
+ such as in a database. In a case like this, you can define dynamically-generated
505
+ state machines like so:
506
+
507
+ ```ruby
508
+ class Vehicle
509
+ attr_accessor :state
510
+
511
+ # Make sure the machine gets initialized so the initial state gets set properly
512
+ def initialize(*)
513
+ super
514
+ machine
515
+ end
516
+
517
+ # Replace this with an external source (like a db)
518
+ def transitions
519
+ [
520
+ {parked: :idling, on: :ignite},
521
+ {idling: :first_gear, first_gear: :second_gear, on: :shift_up}
522
+ # ...
523
+ ]
524
+ end
525
+
526
+ # Create a state machine for this vehicle instance dynamically based on the
527
+ # transitions defined from the source above
528
+ def machine
529
+ vehicle = self
530
+ @machine ||= Machine.new(vehicle, initial: :parked, action: :save) do
531
+ vehicle.transitions.each {|attrs| transition(attrs)}
532
+ end
533
+ end
534
+
535
+ def save
536
+ # Save the state change...
537
+ true
538
+ end
539
+ end
540
+
541
+ # Generic class for building machines
542
+ class Machine
543
+ def self.new(object, *args, &block)
544
+ machine_class = Class.new
545
+ machine = machine_class.state_machine(*args, &block)
546
+ attribute = machine.attribute
547
+ action = machine.action
548
+
549
+ # Delegate attributes
550
+ machine_class.class_eval do
551
+ define_method(:definition) { machine }
552
+ define_method(attribute) { object.send(attribute) }
553
+ define_method("#{attribute}=") {|value| object.send("#{attribute}=", value) }
554
+ define_method(action) { object.send(action) } if action
555
+ end
556
+
557
+ machine_class.new
558
+ end
559
+ end
560
+
561
+ vehicle = Vehicle.new # => #<Vehicle:0xb708412c @state="parked" ...>
562
+ vehicle.state # => "parked"
563
+ vehicle.machine.ignite # => true
564
+ vehicle.machine.state # => "idling
565
+ vehicle.state # => "idling"
566
+ vehicle.machine.state_transitions # => [#<StateMachines:Transition ...>]
567
+ vehicle.machine.definition.states.keys # => :first_gear, :second_gear, :parked, :idling
568
+ ```
569
+
570
+ As you can see, state_machine provides enough flexibility for you to be able
571
+ to create new machine definitions on the fly based on an external source of
572
+ transitions.
24
573
 
25
574
  ## Dependencies
26
575
 
@@ -41,8 +590,8 @@ For documenting state machines:
41
590
 
42
591
  ## TODO
43
592
 
44
- Add matchers/assertions for rspec and minitest
45
- Fix integration dependency
593
+ * Add matchers/assertions for rspec and minitest
594
+ * Fix integrations dependency
46
595
 
47
596
  ## Contributing
48
597
 
@@ -14,8 +14,27 @@ module StateMachines
14
14
  # An invalid integration was specified
15
15
  class IntegrationNotFound < Error
16
16
  def initialize(name)
17
- valid_integrations_name = Integrations.list.collect(&:integration_name)
18
- super(nil, "#{name.inspect} is an invalid integration. Valid integrations are: #{valid_integrations_name.join(', ')} ")
17
+ super(nil, "#{name.inspect} is an invalid integration. #{error_message}")
18
+ end
19
+
20
+ def valid_integrations
21
+ "Valid integrations are: #{valid_integrations_name}"
22
+ end
23
+
24
+ def valid_integrations_name
25
+ Integrations.list.collect(&:integration_name)
26
+ end
27
+
28
+ def no_integrations
29
+ 'No integrations registered'
30
+ end
31
+
32
+ def error_message
33
+ if Integrations.list.size.zero?
34
+ no_integrations
35
+ else
36
+ valid_integrations
37
+ end
19
38
  end
20
39
  end
21
40
 
@@ -40,8 +40,8 @@ module StateMachines
40
40
  end
41
41
 
42
42
  def reset #:nodoc:#
43
- name_spaced_integrations
44
43
  @integrations = Set.new
44
+ name_spaced_integrations
45
45
  true
46
46
  end
47
47
 
@@ -59,6 +59,7 @@ module StateMachines
59
59
  name_spaced_integrations
60
60
  @integrations
61
61
  end
62
+
62
63
  alias_method :list, :integrations
63
64
 
64
65
 
@@ -115,7 +116,7 @@ module StateMachines
115
116
 
116
117
  def name_spaced_integrations
117
118
  # FIXME, Integrations should be add before their dependencies.
118
- self.constants.reverse.each do |const|
119
+ self.constants.reject{ |i| i==:Base }.each do |const|
119
120
  integration = self.const_get(const)
120
121
  add(integration)
121
122
  end
@@ -17,13 +17,6 @@ module StateMachines
17
17
  end
18
18
  end
19
19
 
20
- # Whether this integration is available for the current library. This
21
- # is only true if the ORM that the integration is for is currently
22
- # defined.
23
- def available?
24
- matching_ancestors.any? && Object.const_defined?(matching_ancestors[0].split('::')[0])
25
- end
26
-
27
20
  # The list of ancestor names that cause this integration to matched.
28
21
  def matching_ancestors
29
22
  []
@@ -1,3 +1,3 @@
1
1
  module StateMachines
2
- VERSION = '0.1.1'
2
+ VERSION = '0.1.2'
3
3
  end
@@ -5,12 +5,12 @@ class IntegrationFinderTest < StateMachinesTest
5
5
  StateMachines::Integrations.reset
6
6
  end
7
7
 
8
- def test_should_find_base
9
- assert_equal StateMachines::Integrations::Base, StateMachines::Integrations.find_by_name(:base)
10
- end
11
-
12
8
  def test_should_raise_an_exception_if_invalid
13
9
  exception = assert_raises(StateMachines::IntegrationNotFound) { StateMachines::Integrations.find_by_name(:invalid) }
14
- assert_equal ':invalid is an invalid integration. Valid integrations are: base ', exception.message
10
+ assert_equal ':invalid is an invalid integration. No integrations registered', exception.message
11
+ end
12
+
13
+ def test_should_have_no_integrations
14
+ assert_equal(Set.new, StateMachines::Integrations.list)
15
15
  end
16
16
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: state_machines
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Abdelkader Boudih