finist 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 3ef4a139c70308a595e49e7ba7053a64631c33e0
4
+ data.tar.gz: 6a640ada556919739c4bc01de30d4aa3e9fba735
5
+ SHA512:
6
+ metadata.gz: c5720d02f6093f0eeb9bd20161f7b7b617f70c20ded4a85d8305cb2f61e5e38c50e2b3c18ea33c225fb35841cc6d0f28b88bbe75dc5e409a4297698965bbbd11
7
+ data.tar.gz: 2cc188dd3d8a6d7fd58bdab1c9727765ca7ddbb346132e687364617ece39a41d5127df78382838a4e333736cbe32241892c7fd4f4ece7f7cf75085a2cd2f6cae
data/.gems ADDED
@@ -0,0 +1,2 @@
1
+ redic -v 1.2.0
2
+ cutest -v 1.2.2
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2015 Michel Martens
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,112 @@
1
+ Finist
2
+ ======
3
+
4
+ Redis based Finite State Machine.
5
+
6
+ Description
7
+ -----------
8
+
9
+ Finist is a finite state machine that is defined and persisted in
10
+ [Redis][redis].
11
+
12
+ Community
13
+ ---------
14
+
15
+ Meet us on IRC: [#lesscode](irc://chat.freenode.net/#lesscode) on
16
+ [freenode.net](http://freenode.net/).
17
+
18
+ Related projects
19
+ ----------------
20
+
21
+ There's an [alternative implementation for Lua][finist.lua].
22
+
23
+ Getting started
24
+ ---------------
25
+
26
+ Install [Redis][redis]. On most platforms it's as easy as grabbing
27
+ the sources, running make and then putting the `redis-server` binary
28
+ in the PATH.
29
+
30
+ Once you have it installed, you can execute `redis-server` and it
31
+ will run on `localhost:6379` by default. Check the `redis.conf`
32
+ file that comes with the sources if you want to change some settings.
33
+
34
+ Usage
35
+ -----
36
+
37
+ Finist requires a [Redic][redic] compatible client. To make things
38
+ easier, `redic` is listed as a runtime dependency so the examples
39
+ in this document will work.
40
+
41
+ ```ruby
42
+ require "finist"
43
+
44
+ # Initialize with a Redis client, the name of the machine and the
45
+ # initial state. In this example, the machine is called "order" and
46
+ # the initial status is "pending". The Redis client is connected to
47
+ # the default host (127.0.0.1:6379).
48
+ machine = Finist.new(Redic.new, "order", "pending")
49
+
50
+ # Available transitions are defined with the `on` method
51
+ # `machine.on(<event>, <initial_state>, <final_state>)`
52
+ machine.on("approve", "pending", "approved")
53
+ machine.on("cancel", "pending", "cancelled")
54
+ machine.on("cancel", "approved", "cancelled")
55
+ machine.on("reset", "cancelled", "pending")
56
+ ```
57
+
58
+ Now that the possible transitions are defined, we can check the
59
+ current state:
60
+
61
+ ```ruby
62
+ machine.state
63
+ # => "pending"
64
+ ```
65
+
66
+ And we can trigger an event:
67
+
68
+ ```ruby
69
+ machine.trigger("approve")
70
+ # => [true, "approved"]
71
+ ```
72
+
73
+ The `trigger` method returns an array of two values: the first
74
+ represents whether a transition occurred, and the second represents
75
+ the current state.
76
+
77
+ Here's what happens if an event doesn't cause a transition:
78
+
79
+ ```ruby
80
+ machine.trigger("reset")
81
+ # => [false, "approved"]
82
+ ```
83
+
84
+ Here's a convenient way to use this flag:
85
+
86
+ ```ruby
87
+ changed, state = machine.trigger("reset")
88
+
89
+ if changed
90
+ printf("State changed to %s\n", state)
91
+ end
92
+ ```
93
+
94
+ If you need to remove all the transitions for a given event, you
95
+ can use `rm`:
96
+
97
+ ```ruby
98
+ machine.rm("reset")
99
+ ```
100
+
101
+ Note that every change is persisted in Redis.
102
+
103
+ Installation
104
+ ------------
105
+
106
+ ```
107
+ $ gem install finist
108
+ ```
109
+
110
+ [redis]: http://redis.io
111
+ [redic]: https://github.com/amakawa/redic
112
+ [finist.lua]: https://github.com/soveran/finist.lua
data/finist.gemspec ADDED
@@ -0,0 +1,15 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "finist"
3
+ s.version = "0.0.1"
4
+ s.summary = %{Redis based Finite State Machine}
5
+ s.description = %Q{Finist is a finite state machine that is defined and persisted in Redis.}
6
+ s.authors = ["Michel Martens"]
7
+ s.email = ["michel@soveran.com"]
8
+ s.homepage = "https://github.com/soveran/finist"
9
+ s.license = "MIT"
10
+
11
+ s.files = `git ls-files`.split("\n")
12
+
13
+ s.add_dependency "redic"
14
+ s.add_development_dependency "cutest"
15
+ end
data/lib/finist.rb ADDED
@@ -0,0 +1,53 @@
1
+ # encoding: UTF-8
2
+
3
+ require "redic"
4
+
5
+ class Finist
6
+ SCRIPT = <<-EOS
7
+ local curr = redis.call("GET", KEYS[1])
8
+ local next = redis.call("HGET", KEYS[2], curr)
9
+
10
+ if next then
11
+ redis.call("SET", KEYS[1], next)
12
+ return { next, true }
13
+ else
14
+ return { curr, false }
15
+ end
16
+ EOS
17
+
18
+ def initialize(redis, name, init)
19
+ @name = sprintf("finist:%s", name)
20
+ @redis = redis
21
+ @redis.call("SET", @name, init, "NX")
22
+ end
23
+
24
+ def event_key(ev)
25
+ sprintf("%s:%s", @name, ev)
26
+ end
27
+
28
+ def on(ev, curr_state, next_state)
29
+ @redis.call("HSET", event_key(ev), curr_state, next_state)
30
+ end
31
+
32
+ def rm(ev)
33
+ @redis.call("DEL", event_key(ev))
34
+ end
35
+
36
+ def state
37
+ @redis.call("GET", @name)
38
+ end
39
+
40
+ def send_event(ev)
41
+ @redis.call("EVAL", SCRIPT, "2", @name, event_key(ev))
42
+ end
43
+
44
+ def trigger(ev)
45
+ result = send_event(ev)
46
+
47
+ if result[1]
48
+ return true, result[0]
49
+ else
50
+ return false, result[0]
51
+ end
52
+ end
53
+ end
data/makefile ADDED
@@ -0,0 +1,4 @@
1
+ .PHONY: test
2
+
3
+ test:
4
+ cutest -r ./test/helper.rb ./test/*.rb
data/test/all.rb ADDED
@@ -0,0 +1,60 @@
1
+ require_relative "helper"
2
+
3
+ test do
4
+ c = Redic.new
5
+
6
+ fsm = Finist.new(c, "myfsm", "pending")
7
+
8
+ fsm.on("approve", "pending", "approved")
9
+ fsm.on("cancel", "pending", "cancelled")
10
+ fsm.on("cancel", "approved", "cancelled")
11
+ fsm.on("reset", "cancelled", "pending")
12
+
13
+ # Verify initial state
14
+ assert_equal("pending", fsm.state)
15
+
16
+ # Send an event
17
+ fsm.trigger("approve")
18
+
19
+ # Verify transition to "approved"
20
+ assert_equal("approved", fsm.state)
21
+
22
+ # Send an event
23
+ fsm.trigger("cancel")
24
+
25
+ # Verify transition to "cancelled"
26
+ assert_equal("cancelled", fsm.state)
27
+
28
+ # Send an event
29
+ fsm.trigger("approve")
30
+
31
+ # Verify state remains as "cancelled"
32
+ assert_equal("cancelled", fsm.state)
33
+
34
+ # Create a different fsm with client
35
+ fsm2 = Finist.new(c, "myfsm", "pending")
36
+
37
+ # Verify state remains as "cancelled"
38
+ assert_equal("cancelled", fsm2.state)
39
+
40
+ # A successful event returns true
41
+ changed, state = fsm.trigger("reset")
42
+
43
+ assert_equal(true, changed)
44
+ assert_equal("pending", state)
45
+
46
+ # An unsuccessful event returns false
47
+ changed, state = fsm.trigger("reset")
48
+
49
+ assert_equal(false, changed)
50
+ assert_equal("pending", state)
51
+
52
+ # Delete an event
53
+ fsm.rm("approve")
54
+
55
+ # Non existent events return false
56
+ changed, state = fsm.trigger("approve")
57
+
58
+ assert_equal(false, changed)
59
+ assert_equal("pending", state)
60
+ end
data/test/helper.rb ADDED
@@ -0,0 +1 @@
1
+ require_relative "../lib/finist"
metadata ADDED
@@ -0,0 +1,80 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: finist
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Michel Martens
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-02-03 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: redic
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: cutest
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ description: Finist is a finite state machine that is defined and persisted in Redis.
42
+ email:
43
+ - michel@soveran.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - .gems
49
+ - LICENSE
50
+ - README.md
51
+ - finist.gemspec
52
+ - lib/finist.rb
53
+ - makefile
54
+ - test/all.rb
55
+ - test/helper.rb
56
+ homepage: https://github.com/soveran/finist
57
+ licenses:
58
+ - MIT
59
+ metadata: {}
60
+ post_install_message:
61
+ rdoc_options: []
62
+ require_paths:
63
+ - lib
64
+ required_ruby_version: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ required_rubygems_version: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - '>='
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
74
+ requirements: []
75
+ rubyforge_project:
76
+ rubygems_version: 2.0.14
77
+ signing_key:
78
+ specification_version: 4
79
+ summary: Redis based Finite State Machine
80
+ test_files: []