machina 0.0.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 +7 -0
- data/.gitignore +1 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +17 -0
- data/Makefile +13 -0
- data/README.md +22 -0
- data/lib/machina.rb +91 -0
- data/machina.gemspec +14 -0
- data/spec/machina_spec.rb +71 -0
- data/spec/spec_helper.rb +2 -0
- metadata +53 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: d67cfd0648b85a02e58ce0f287c24a5f2a2424ff50ac949b1272f44885cb3f77
|
4
|
+
data.tar.gz: 341433286e327508944bd8fac79640b0be466288629fe02f4c296ba3f3ea01cd
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 8e34227ae96d02e0682e7c3a489d318baffcb95b91d6068b740010e4640f1f3bad6da62e03683ce259bfd8db02032aae4a23392d54c951140ee6fd185b2e24d3
|
7
|
+
data.tar.gz: edefe97cd25753f5d7458502cc625817b3204c0957e5351eb3a2ef9889dea8e72f22b0ac315ef521ccc8ba94b52aff0c7f28b2c3693a7ea0d428cc1c4ec3c371
|
data/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
*.gem
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
data/Makefile
ADDED
data/README.md
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# Machina
|
2
|
+
|
3
|
+
_a god introduced by means of a crane in ancient Greek and Roman drama to decide the final outcome_
|
4
|
+
|
5
|
+

|
6
|
+
|
7
|
+
```ruby
|
8
|
+
fsm = Machina.new(:unconfirmed)
|
9
|
+
|
10
|
+
fsm.when[:welcome_email] = -> { send_welcome_email }
|
11
|
+
fsm.when[:enable_account] = -> (user) { user.enabled! }
|
12
|
+
|
13
|
+
fsm[:confirm] = {
|
14
|
+
:unconfirmed => [:welcome_email, :enable_account, :confirmed],
|
15
|
+
}
|
16
|
+
|
17
|
+
fsm.on[:confirm] = -> (user) { puts "Starting confirmation process for #{user.id}." }
|
18
|
+
|
19
|
+
fsm.state # => :unconfirmed
|
20
|
+
fsm.trigger!(:confirm, user)
|
21
|
+
fsm.state # => :confirmed
|
22
|
+
```
|
data/lib/machina.rb
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Machina
|
4
|
+
InvalidEventError = Class.new(StandardError)
|
5
|
+
InvalidStateChangeError = Class.new(StandardError)
|
6
|
+
|
7
|
+
EVENT = -> (h, k) { h[k] = ->(*args) {} }
|
8
|
+
|
9
|
+
attr_reader :state, :events
|
10
|
+
|
11
|
+
# Initializes Machina
|
12
|
+
#
|
13
|
+
# @param initial_state [Symbol] the initial state of the state machine
|
14
|
+
# @return [Machina] the initialized Machina
|
15
|
+
#
|
16
|
+
def initialize(initial_state)
|
17
|
+
@state = initial_state
|
18
|
+
@events = Hash.new { |h, k| h[k] = [] }
|
19
|
+
end
|
20
|
+
|
21
|
+
# Triggers an event.
|
22
|
+
#
|
23
|
+
# @param event [Symbol] the event to trigger
|
24
|
+
# @param args [Array<Object>] the objects that the callback will receive
|
25
|
+
# @return [void]
|
26
|
+
#
|
27
|
+
def trigger(event, *args)
|
28
|
+
return unless trigger?(event)
|
29
|
+
|
30
|
+
Array(events[event][state]).each do |candidate_state|
|
31
|
+
begin
|
32
|
+
self.when[candidate_state].call(*args)
|
33
|
+
rescue StandardError
|
34
|
+
break
|
35
|
+
end
|
36
|
+
|
37
|
+
@state = candidate_state
|
38
|
+
end
|
39
|
+
|
40
|
+
on[event].call(*args)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Triggers an event and raises on error.
|
44
|
+
#
|
45
|
+
# @param event [Symbol] the event to trigger
|
46
|
+
# @param args [Array<Object>] the objects that the callback will receive
|
47
|
+
# @return [void]
|
48
|
+
#
|
49
|
+
def trigger!(event, *args)
|
50
|
+
raise InvalidStateChangeError, "Event #{event} missing #{state} step" unless trigger?(event)
|
51
|
+
|
52
|
+
trigger(event, *args)
|
53
|
+
end
|
54
|
+
|
55
|
+
# Checks if an event can be triggered
|
56
|
+
#
|
57
|
+
# @param event [Symbol] the event to trigger
|
58
|
+
# @return [Boolean] the chance posibility
|
59
|
+
#
|
60
|
+
def trigger?(event)
|
61
|
+
raise InvalidEventError, "Event #{event} not found." unless events.key?(event)
|
62
|
+
|
63
|
+
events[event].key?(state)
|
64
|
+
end
|
65
|
+
|
66
|
+
# Defines the list of possible callbacks when an event is triggered
|
67
|
+
#
|
68
|
+
# @return [Hash]
|
69
|
+
#
|
70
|
+
def on
|
71
|
+
@on ||= Hash.new(&EVENT)
|
72
|
+
end
|
73
|
+
|
74
|
+
# Defines the list of possible callbacks when a state is reached
|
75
|
+
#
|
76
|
+
# @return [Hash]
|
77
|
+
#
|
78
|
+
def when
|
79
|
+
@when ||= Hash.new(&EVENT)
|
80
|
+
end
|
81
|
+
|
82
|
+
# Defines the list of possible events and their transitions
|
83
|
+
#
|
84
|
+
# @param key [Symbol] the event name
|
85
|
+
# @param states [Hash] the event transitions
|
86
|
+
# @return [void]
|
87
|
+
#
|
88
|
+
def []=(key, states)
|
89
|
+
events[key] = states
|
90
|
+
end
|
91
|
+
end
|
data/machina.gemspec
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = "machina"
|
5
|
+
s.version = "0.0.1"
|
6
|
+
s.summary = "Simple state machine"
|
7
|
+
s.description = "Really really simple state machine"
|
8
|
+
s.authors = ["elcuervo"]
|
9
|
+
s.licenses = %w[MIT HUGWARE]
|
10
|
+
s.email = ["elcuervo@elcuervo.net"]
|
11
|
+
s.homepage = "http://github.com/elcuervo/machina"
|
12
|
+
s.files = `git ls-files`.split("\n")
|
13
|
+
s.test_files = `git ls-files test`.split("\n")
|
14
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
|
4
|
+
Post = Struct.new(:title, :notified, :published)
|
5
|
+
|
6
|
+
describe Machina do
|
7
|
+
attr_reader :fsm
|
8
|
+
|
9
|
+
before { @fsm = Machina.new(:start) }
|
10
|
+
|
11
|
+
it "basic" do
|
12
|
+
fsm[:begin] = { :start => :step1 }
|
13
|
+
|
14
|
+
assert_equal :start, fsm.state
|
15
|
+
|
16
|
+
fsm.trigger(:begin)
|
17
|
+
|
18
|
+
assert_equal :step1, fsm.state
|
19
|
+
end
|
20
|
+
|
21
|
+
it "complex" do
|
22
|
+
post = Post.new("Something HN worthy", false, false)
|
23
|
+
completed = false
|
24
|
+
|
25
|
+
fsm.when[:send_notification] = -> (post) { post.notified = true }
|
26
|
+
fsm.when[:published] = -> (post) { post.published = true }
|
27
|
+
|
28
|
+
fsm[:complete] = {
|
29
|
+
:start => [:review, :send_notification, :published],
|
30
|
+
:review => [:send_notification, :published],
|
31
|
+
:reset => :start
|
32
|
+
}
|
33
|
+
|
34
|
+
fsm.on[:complete] = -> (_post) { completed = true }
|
35
|
+
|
36
|
+
assert_equal :start, fsm.state
|
37
|
+
assert_equal false, completed
|
38
|
+
assert_equal false, post.notified
|
39
|
+
assert_equal false, post.published
|
40
|
+
|
41
|
+
fsm.trigger(:complete, post)
|
42
|
+
|
43
|
+
assert_equal :published, fsm.state
|
44
|
+
assert_equal true, completed
|
45
|
+
assert_equal true, post.notified
|
46
|
+
assert_equal true, post.published
|
47
|
+
end
|
48
|
+
|
49
|
+
it "stops on error" do
|
50
|
+
fsm.when[:stop] = -> { raise }
|
51
|
+
|
52
|
+
fsm[:act] = {
|
53
|
+
:start => [:a, :b, :stop, :final]
|
54
|
+
}
|
55
|
+
|
56
|
+
fsm.trigger(:act)
|
57
|
+
|
58
|
+
assert_equal :b, fsm.state
|
59
|
+
end
|
60
|
+
|
61
|
+
it "trigger!" do
|
62
|
+
fsm[:one] = {
|
63
|
+
:not_start => :reset
|
64
|
+
}
|
65
|
+
|
66
|
+
assert_raises(Machina::InvalidStateChangeError) { fsm.trigger!(:one) }
|
67
|
+
assert_raises(Machina::InvalidEventError) { fsm.trigger!(:two) }
|
68
|
+
|
69
|
+
assert_equal :start, fsm.state
|
70
|
+
end
|
71
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: machina
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- elcuervo
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2021-02-04 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: Really really simple state machine
|
14
|
+
email:
|
15
|
+
- elcuervo@elcuervo.net
|
16
|
+
executables: []
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- ".gitignore"
|
21
|
+
- Gemfile
|
22
|
+
- Gemfile.lock
|
23
|
+
- Makefile
|
24
|
+
- README.md
|
25
|
+
- lib/machina.rb
|
26
|
+
- machina.gemspec
|
27
|
+
- spec/machina_spec.rb
|
28
|
+
- spec/spec_helper.rb
|
29
|
+
homepage: http://github.com/elcuervo/machina
|
30
|
+
licenses:
|
31
|
+
- MIT
|
32
|
+
- HUGWARE
|
33
|
+
metadata: {}
|
34
|
+
post_install_message:
|
35
|
+
rdoc_options: []
|
36
|
+
require_paths:
|
37
|
+
- lib
|
38
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
39
|
+
requirements:
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: '0'
|
43
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
requirements: []
|
49
|
+
rubygems_version: 3.0.3
|
50
|
+
signing_key:
|
51
|
+
specification_version: 4
|
52
|
+
summary: Simple state machine
|
53
|
+
test_files: []
|