strict_machine 0.2.0 → 0.2.1
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:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: af5b761971dfb358c71f10ac26f27fe6e8bd5d2d
|
4
|
+
data.tar.gz: c09dd654724097dc1db5c1ca9d4106460a3c8303
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fb18988d67119ceab8121d57c61c12ab65dd7ba1b55a1ad009d24343488b33a1557185c3f4938cc66671a2425ec9cf3157ef11b7c014cb1093683e7bbd949aad
|
7
|
+
data.tar.gz: 28c3aa5e21a28d1247c4fee56e9b3d0864be384dd831251d9f685cb556bca515c69db7fa81826afa8560d6aa67d56d8f64d9f48bcee5958504a223262b6bc59a
|
data/README.md
CHANGED
@@ -20,17 +20,134 @@ Or install it yourself as:
|
|
20
20
|
|
21
21
|
## Usage
|
22
22
|
|
23
|
-
|
23
|
+
You can use this gem in two ways - either by embedding a state machine onto
|
24
|
+
your class, or by mounting a separate class.
|
25
|
+
|
26
|
+
### Embedded
|
27
|
+
|
28
|
+
```ruby
|
29
|
+
class A < StrictMachine::Base
|
30
|
+
strict_machine do
|
31
|
+
state :initial do
|
32
|
+
on hop: :middle
|
33
|
+
end
|
34
|
+
state :middle do
|
35
|
+
on hop: :final
|
36
|
+
end
|
37
|
+
state :final
|
38
|
+
on_transition do |from, to, trigger_event, duration|
|
39
|
+
log from, to, trigger_event, duration
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def log(from, to, trigger_event, duration)
|
44
|
+
# ...
|
45
|
+
end
|
46
|
+
end
|
47
|
+
```
|
48
|
+
|
49
|
+
### Mounting
|
50
|
+
|
51
|
+
(using same class A as previous example, minus the `log` method)
|
52
|
+
|
53
|
+
```ruby
|
54
|
+
class B
|
55
|
+
include StrictMachine::MountStateMachine
|
56
|
+
|
57
|
+
mount_state_machine A
|
58
|
+
|
59
|
+
def log(from, to, trigger_event, duration)
|
60
|
+
# ...
|
61
|
+
end
|
62
|
+
end
|
63
|
+
```
|
64
|
+
|
65
|
+
**NOTE**: when mounting, the methods referenced should always be on the class doing the mounting, not on the state machine one!
|
66
|
+
|
67
|
+
### Object extensions
|
68
|
+
|
69
|
+
Whether embedding or mounting, an object instance will have the following
|
70
|
+
methods added to it:
|
71
|
+
|
72
|
+
- `#state` returns the current state's name
|
73
|
+
- `#trigger(*transitions)` triggers the given transition(s) by name
|
74
|
+
- `#state_attr` the name of the state attribute being used
|
75
|
+
- `#states` list of `State` objects in the machine's definition
|
76
|
+
|
77
|
+
and an object's class will have:
|
78
|
+
|
79
|
+
- `#definition` the DSL evaluation context object
|
80
|
+
- `#strict_machine_attr` the name of the attribute to store state in
|
81
|
+
- `#strict_machine_class` either the class containing the state machine, when
|
82
|
+
mounting, or `self` when embedding
|
83
|
+
- `#strict_machine` the DSL hook
|
84
|
+
|
85
|
+
### Internals
|
86
|
+
|
87
|
+
The gem works by storing, at the object's class level, the state machine's
|
88
|
+
definition and the name of the state storage attribute. Every instance of the class embedding or mounting a state machine will have its own state (stored in said attribute (by default `state`), and upon transitions, the current state and the transition requested will be passed to `#change_state` in
|
89
|
+
`InstanceMethods` and a new state will be reached (and `true` returned), or
|
90
|
+
an exception will be raised. Existing exceptions:
|
91
|
+
|
92
|
+
- `StateNotFoundError` tried to transition to a non-existing state
|
93
|
+
- `TransitionNotFoundError` the transition requested does not exist
|
94
|
+
- `GuardedTransitionError` the guard condition for the transition was not met
|
95
|
+
|
96
|
+
You can bypass the guard condition on a transition by adding a bang `!` to the
|
97
|
+
transition's name, i.e.,
|
98
|
+
|
99
|
+
```ruby
|
100
|
+
obj.trigger('transition!') # bypass guards
|
101
|
+
```
|
102
|
+
|
103
|
+
## The DSL
|
104
|
+
|
105
|
+
Here's a complete example:
|
106
|
+
|
107
|
+
```ruby
|
108
|
+
strict_machine do
|
109
|
+
state :new do
|
110
|
+
on submit: :awaiting_review
|
111
|
+
end
|
112
|
+
|
113
|
+
state :awaiting_review do
|
114
|
+
on review: :under_review
|
115
|
+
end
|
116
|
+
|
117
|
+
state :under_review do
|
118
|
+
on_entry { |previous, trigger| some_method(previous, trigger) }
|
119
|
+
on accept: :accepted, if: :cool_article?
|
120
|
+
on reject: :rejected, if: :bad_article?
|
121
|
+
end
|
122
|
+
|
123
|
+
state :accepted
|
124
|
+
state :rejected
|
125
|
+
|
126
|
+
on_transition do |from, to, trigger_event, duration|
|
127
|
+
log from, to, trigger_event, duration
|
128
|
+
end
|
129
|
+
end
|
130
|
+
```
|
131
|
+
|
132
|
+
Guards (the `:if`s on `on` calls) must return `true` in order to pass.
|
133
|
+
|
134
|
+
When mounting, you can specify the state attribute's name with:
|
135
|
+
|
136
|
+
`mount_state_machine SomeClass, state: "meh"`
|
137
|
+
|
138
|
+
And when embedding:
|
139
|
+
|
140
|
+
`strict_machine("meh") do ....`
|
24
141
|
|
25
142
|
## Development
|
26
143
|
|
27
144
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
28
145
|
|
29
|
-
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `
|
146
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `strict_machine.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
30
147
|
|
31
148
|
## Contributing
|
32
149
|
|
33
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/
|
150
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/sardaukar/strict_machine.
|
34
151
|
|
35
152
|
|
36
153
|
## License
|
@@ -31,5 +31,11 @@ module StrictMachine
|
|
31
31
|
def add_on_entry(proc)
|
32
32
|
@on_entry << proc
|
33
33
|
end
|
34
|
+
|
35
|
+
def run_on_entries(instance, current_state_name, trigger)
|
36
|
+
@on_entry.each do |proc|
|
37
|
+
instance.instance_exec(current_state_name, trigger, &proc)
|
38
|
+
end
|
39
|
+
end
|
34
40
|
end
|
35
41
|
end
|
@@ -21,6 +21,14 @@ module StrictMachine
|
|
21
21
|
@states.first.name
|
22
22
|
end
|
23
23
|
|
24
|
+
def run_transitions(instance, current_state, new_state, trigger, duration)
|
25
|
+
@transitions.each do |proc|
|
26
|
+
instance.instance_exec(
|
27
|
+
current_state, new_state, trigger, duration, &proc
|
28
|
+
)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
24
32
|
### DSL
|
25
33
|
|
26
34
|
def state(name, &block)
|
@@ -1,8 +1,10 @@
|
|
1
|
+
require "benchmark"
|
2
|
+
|
1
3
|
module StrictMachine
|
2
4
|
module MountStateMachine
|
3
5
|
module InstanceMethods
|
4
6
|
def trigger(*transitions)
|
5
|
-
transitions.map {|t| change_state(t, state) }
|
7
|
+
transitions.map { |t| change_state(t, state) }
|
6
8
|
|
7
9
|
true
|
8
10
|
end
|
@@ -14,7 +16,7 @@ module StrictMachine
|
|
14
16
|
end
|
15
17
|
|
16
18
|
def state_attr
|
17
|
-
self.class.strict_machine_attr.to_s.
|
19
|
+
self.class.strict_machine_attr.to_s.delete("@")
|
18
20
|
end
|
19
21
|
|
20
22
|
def states
|
@@ -44,31 +46,29 @@ module StrictMachine
|
|
44
46
|
end
|
45
47
|
|
46
48
|
def change_state(trigger, current_state_name)
|
47
|
-
|
48
|
-
current_state = definition.get_state_by_name(current_state_name)
|
49
|
-
is_bang, transition = current_state.get_transition(trigger)
|
49
|
+
new_state = nil
|
50
50
|
|
51
|
-
|
52
|
-
|
53
|
-
transition.guard
|
54
|
-
)
|
55
|
-
end
|
51
|
+
duration = Benchmark.measure do
|
52
|
+
is_bang, transition = current_state_obj.get_transition(trigger)
|
56
53
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
end
|
54
|
+
if transition.guarded? && !is_bang
|
55
|
+
raise GuardedTransitionError unless public_send(transition.guard)
|
56
|
+
end
|
61
57
|
|
62
|
-
|
58
|
+
new_state = definition.get_state_by_name(transition.to)
|
59
|
+
new_state.run_on_entries(self, current_state_name, trigger.to_sym)
|
60
|
+
end.real
|
63
61
|
|
64
|
-
definition.
|
65
|
-
self.
|
66
|
-
|
67
|
-
)
|
68
|
-
end
|
62
|
+
definition.run_transitions(
|
63
|
+
self, current_state_name, new_state.name, trigger.to_sym, duration
|
64
|
+
)
|
69
65
|
|
70
66
|
write_state(new_state.name)
|
71
67
|
end
|
68
|
+
|
69
|
+
def current_state_obj
|
70
|
+
definition.get_state_by_name(current_state_attr_value)
|
71
|
+
end
|
72
72
|
end
|
73
73
|
end
|
74
74
|
end
|
data/lib/strict_machine.rb
CHANGED
@@ -1,13 +1,12 @@
|
|
1
1
|
require_relative "ext/object"
|
2
2
|
|
3
3
|
module StrictMachine
|
4
|
-
VERSION = "0.2.
|
4
|
+
VERSION = "0.2.1".freeze
|
5
5
|
|
6
6
|
class << self; attr_accessor :list; end
|
7
7
|
end
|
8
8
|
|
9
9
|
class StateNotFoundError < StandardError; end
|
10
|
-
class InvalidTransitionError < StandardError; end
|
11
10
|
class TransitionNotFoundError < StandardError; end
|
12
11
|
class GuardedTransitionError < StandardError; end
|
13
12
|
|
data/strict_machine.gemspec
CHANGED
@@ -11,7 +11,7 @@ Gem::Specification.new do |spec|
|
|
11
11
|
spec.email = ["sardaukar.siet@gmail.com"]
|
12
12
|
|
13
13
|
spec.summary = "State machine functionality for Ruby classes"
|
14
|
-
spec.homepage = "https://
|
14
|
+
spec.homepage = "https://github.com/sardaukar/strict_machine"
|
15
15
|
spec.license = "MIT"
|
16
16
|
|
17
17
|
spec.files = `git ls-files -z`
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: strict_machine
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Bruno Antunes
|
@@ -101,7 +101,7 @@ files:
|
|
101
101
|
- spec/mountable_machine_spec.rb
|
102
102
|
- spec/spec_helper.rb
|
103
103
|
- strict_machine.gemspec
|
104
|
-
homepage: https://
|
104
|
+
homepage: https://github.com/sardaukar/strict_machine
|
105
105
|
licenses:
|
106
106
|
- MIT
|
107
107
|
metadata: {}
|