state_machines 0.1.1 → 0.1.2

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