simple_states 0.0.1 → 0.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.
- data/README.md +65 -2
- data/lib/simple_states/event.rb +17 -7
- data/lib/simple_states/version.rb +1 -1
- metadata +9 -9
data/README.md
CHANGED
@@ -1,5 +1,68 @@
|
|
1
1
|
# simple\_states
|
2
2
|
|
3
|
-
A super-slim (~200 loc) statemachine-like support library focussed on use in
|
3
|
+
A super-slim (~200 loc) statemachine-like support library focussed on use in
|
4
|
+
Travis CI.
|
4
5
|
|
5
|
-
|
6
|
+
## Usage
|
7
|
+
|
8
|
+
Define states and events like this:
|
9
|
+
|
10
|
+
class Foo
|
11
|
+
include SimpleStates
|
12
|
+
|
13
|
+
states :created, :started, :finished
|
14
|
+
|
15
|
+
event :start, :from => :created, :to => :started, :if => :startable?
|
16
|
+
event :finish, :to => :finished, :after => :cleanup
|
17
|
+
|
18
|
+
attr_accessor :state
|
19
|
+
|
20
|
+
def start
|
21
|
+
# start foo
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
Including the SimpleStates module to your class is currently required. We'll add
|
26
|
+
hooks for ActiveRecord etc later.
|
27
|
+
|
28
|
+
SimpleStates expects your model to support attribute accessors for `:state`.
|
29
|
+
|
30
|
+
Event options have the following well-known meanings:
|
31
|
+
|
32
|
+
:from # valid states to transition from
|
33
|
+
:to # target state to transition to
|
34
|
+
:if # only proceed if the given method returns true
|
35
|
+
:unless # only proceed if the given method returns false
|
36
|
+
:before # run the given method before running `super` and setting the new state
|
37
|
+
:after # run the given method at the very end
|
38
|
+
|
39
|
+
All of these options except for `:to` can be given as a single symbol or string or
|
40
|
+
as an Array of symbols or strings.
|
41
|
+
|
42
|
+
Calling `event` will effectively add methods to a proxy module which is
|
43
|
+
included to the singleton class of your class' instances. E.g. declaring `event
|
44
|
+
:start` in the example above will add a method `start` to a module included to
|
45
|
+
the singleton class of instances of `Foo`.
|
46
|
+
|
47
|
+
This method will
|
48
|
+
|
49
|
+
1. check if `:if`/`:except` conditions apply (if given) and just return from the method otherwise
|
50
|
+
2. check if the object currently is in a valid `:from` state (if given) and raise an exception otherwise
|
51
|
+
3. run `:before` callbacks (if given)
|
52
|
+
4. call `super` if Foo defines the current method (i.e. call `start` but not `finish` in the example above)
|
53
|
+
5. add the object's current state to its `past_states` history
|
54
|
+
6. set the object's `state` to the target state given as `:to`
|
55
|
+
7. set the object's `[state]_at` attribute to `Time.now` if the object defines a writer for it
|
56
|
+
8. run `:after` callbacks (if given)
|
57
|
+
|
58
|
+
You can define options for all events like so:
|
59
|
+
|
60
|
+
event :finish, :to => :finished, :after => :cleanup
|
61
|
+
event :all, :after => :notify
|
62
|
+
|
63
|
+
This will call :cleanup first and then :notify on :finish.
|
64
|
+
|
65
|
+
If no target state was given for an event then SimpleStates will try to derive
|
66
|
+
it from the states list. I.e. for an event `start` it will check the states
|
67
|
+
list for a state `started` and use it. If it can not find a target state this
|
68
|
+
way then it will raise an exception.
|
data/lib/simple_states/event.rb
CHANGED
@@ -43,16 +43,12 @@ module SimpleStates
|
|
43
43
|
def skip?(object, args)
|
44
44
|
result = false
|
45
45
|
result ||= !send_methods(object, options.if, args) if options.if?
|
46
|
-
result ||= send_methods(object, options.
|
46
|
+
result ||= send_methods(object, options.unless, args) if options.unless?
|
47
47
|
result
|
48
48
|
end
|
49
49
|
|
50
50
|
def can_transition?(object)
|
51
|
-
object.state && Array(options.from).include?(object.state)
|
52
|
-
end
|
53
|
-
|
54
|
-
def raise_invalid_transition(object)
|
55
|
-
raise TransitionException, "#{object.inspect} can not receive event #{name.inspect} while in state #{object.state.inspect}."
|
51
|
+
!options.from || object.state && Array(options.from).include?(object.state)
|
56
52
|
end
|
57
53
|
|
58
54
|
def run_callbacks(object, type, args)
|
@@ -60,7 +56,7 @@ module SimpleStates
|
|
60
56
|
end
|
61
57
|
|
62
58
|
def set_state(object)
|
63
|
-
if state =
|
59
|
+
if state = target_state(object)
|
64
60
|
object.past_states << object.state if object.state
|
65
61
|
object.state = state.to_sym
|
66
62
|
object.send(:"#{state}_at=", Time.now) if object.respond_to?(:"#{state}_at=")
|
@@ -68,6 +64,12 @@ module SimpleStates
|
|
68
64
|
end
|
69
65
|
end
|
70
66
|
|
67
|
+
def target_state(object)
|
68
|
+
options.to || :"#{name}ed".tap do |state|
|
69
|
+
raise_unknown_target_state(object) unless object.class.states.include?(state)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
71
73
|
def send_methods(object, methods, args)
|
72
74
|
Array(methods).inject(false) { |result, method| result | send_method(object, method, args) } if methods
|
73
75
|
end
|
@@ -83,5 +85,13 @@ module SimpleStates
|
|
83
85
|
def arity(object, method)
|
84
86
|
object.class.instance_method(method).arity rescue 0
|
85
87
|
end
|
88
|
+
|
89
|
+
def raise_invalid_transition(object)
|
90
|
+
raise TransitionException, "#{object.inspect} can not receive event #{name.inspect} while in state #{object.state.inspect}."
|
91
|
+
end
|
92
|
+
|
93
|
+
def raise_unknown_target_state(object)
|
94
|
+
raise TransitionException, "can not find target state for #{object.inspect} for event #{name.inspect}."
|
95
|
+
end
|
86
96
|
end
|
87
97
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: simple_states
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -13,7 +13,7 @@ date: 2011-08-04 00:00:00.000000000Z
|
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activesupport
|
16
|
-
requirement: &
|
16
|
+
requirement: &70366831755540 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *70366831755540
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: hashr
|
27
|
-
requirement: &
|
27
|
+
requirement: &70366831754780 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ~>
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: 0.0.10
|
33
33
|
type: :runtime
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *70366831754780
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: test_declarative
|
38
|
-
requirement: &
|
38
|
+
requirement: &70366831754080 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ! '>='
|
@@ -43,10 +43,10 @@ dependencies:
|
|
43
43
|
version: '0'
|
44
44
|
type: :development
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *70366831754080
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: mocha
|
49
|
-
requirement: &
|
49
|
+
requirement: &70366831752860 !ruby/object:Gem::Requirement
|
50
50
|
none: false
|
51
51
|
requirements:
|
52
52
|
- - ! '>='
|
@@ -54,7 +54,7 @@ dependencies:
|
|
54
54
|
version: '0'
|
55
55
|
type: :development
|
56
56
|
prerelease: false
|
57
|
-
version_requirements: *
|
57
|
+
version_requirements: *70366831752860
|
58
58
|
description: ! '[description]'
|
59
59
|
email: svenfuchs@artweb-design.de
|
60
60
|
executables: []
|