nanomachine 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ -Ilib
2
+ --require ./spec/spec_helper
@@ -0,0 +1,8 @@
1
+ rvm:
2
+ - "1.9.3"
3
+ - "1.9.2"
4
+ - "jruby-19mode"
5
+ - "rbx-19mode"
6
+ - "1.8.7"
7
+ - "jruby-18mode"
8
+ - "rbx-18mode"
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
4
+
5
+ gem "yard"
@@ -0,0 +1,76 @@
1
+ # Nanomachine
2
+
3
+ A really tiny state machine for ruby. No events, only accepted transitions and transition callbacks.
4
+
5
+ The difference between Nanomachine, and otherwise known Micromachine (https://rubygems.org/gems/micromachine) is that
6
+ Micromachine transitions to new states in response to events; multiple events can transition between the two same states.
7
+ Nanomachine, on the other hand, does not care about events, and only needs the state you want to be in after successful
8
+ transition.
9
+
10
+ Nanomachine can be used in any ruby project, and have no runtime dependencies.
11
+
12
+ ## Installation
13
+
14
+ Install the gem:
15
+
16
+ ```shell
17
+ gem install nanomachine
18
+ ```
19
+
20
+ or add it to your Gemfile, if you are using [Bundler][]:
21
+
22
+ ```ruby
23
+ gem "nanomachine", "~> 1.0"
24
+ ```
25
+
26
+ [Bundler]: http://gembundler.com/
27
+
28
+ ## Example
29
+
30
+ ```ruby
31
+ state_machine = Nanomachine.new("unpublished") do |fsm|
32
+ fsm.transition("published", %w[unpublished processing removed])
33
+ fsm.transition("unpublished", %w[published processing removed])
34
+ fsm.transition("processing", %w[published unpublished])
35
+ fsm.transition("removed", []) # defined for being explicit
36
+
37
+ fsm.on_transition(:to => "processing") do |(previous_state, _), id|
38
+ Worker.schedule(id, previous_state)
39
+ end
40
+
41
+ fsm.on_transition do |(from_state, to_state)|
42
+ update_column(:state, to_state)
43
+ end
44
+ end
45
+
46
+ if state_machine.transition_to("published")
47
+ puts "Publish success!"
48
+ else
49
+ puts "Publish failure! We’re in #{state_machine.state}."
50
+ end
51
+ ```
52
+
53
+ ## License
54
+
55
+ Copyright (c) 2012 Ivan Navarrete and Kim Burgestrand
56
+
57
+ MIT License
58
+
59
+ Permission is hereby granted, free of charge, to any person obtaining
60
+ a copy of this software and associated documentation files (the
61
+ "Software"), to deal in the Software without restriction, including
62
+ without limitation the rights to use, copy, modify, merge, publish,
63
+ distribute, sublicense, and/or sell copies of the Software, and to
64
+ permit persons to whom the Software is furnished to do so, subject to
65
+ the following conditions:
66
+
67
+ The above copyright notice and this permission notice shall be
68
+ included in all copies or substantial portions of the Software.
69
+
70
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
71
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
72
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
73
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
74
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
75
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
76
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,34 @@
1
+ begin
2
+ require "bundler/gem_tasks"
3
+ rescue LoadError
4
+ # bundler not required
5
+ end
6
+
7
+ begin
8
+ require "yard"
9
+ YARD::Rake::YardocTask.new("yard:doc") do |task|
10
+ task.options = ["--no-stats"]
11
+ end
12
+
13
+ desc "List undocumented methods and constants"
14
+ task "yard:stats" do
15
+ YARD::CLI::Stats.run("--list-undoc")
16
+ end
17
+
18
+ desc "Generate documentation and show documentation stats"
19
+ task :yard => ["yard:doc", "yard:stats"]
20
+ rescue LoadError
21
+ puts "WARN: YARD not available. You may install documentation dependencies via bundler."
22
+ end
23
+
24
+ desc "Start an IRB session with Nanomachine loaded"
25
+ task :console do
26
+ exec "irb", "-Ilib", "-rnanomachine"
27
+ end
28
+
29
+ require "rspec/core/rake_task"
30
+ RSpec::Core::RakeTask.new do |spec|
31
+ spec.ruby_opts = ["-W"]
32
+ end
33
+
34
+ task :default => :spec
@@ -0,0 +1,183 @@
1
+ require "nanomachine/version"
2
+ require "set"
3
+
4
+ # A minimal state machine where you transition between states, instead
5
+ # of transition by input symbols or events.
6
+ #
7
+ # @example
8
+ # state_machine = Nanomachine.new("unpublished") do |fsm|
9
+ # fsm.transition("published", %w[unpublished processing removed])
10
+ # fsm.transition("unpublished", %w[published processing removed])
11
+ # fsm.transition("processing", %w[published unpublished])
12
+ # fsm.transition("removed", []) # defined for being explicit
13
+ #
14
+ # fsm.on_transition do |(from_state, to_state)|
15
+ # update_column(:state, to_state)
16
+ # end
17
+ # end
18
+ #
19
+ # if state_machine.transition_to("published")
20
+ # puts "Publish success!"
21
+ # else
22
+ # puts "Publish failure! We’re in #{state_machine.state}."
23
+ # end
24
+ #
25
+ #
26
+ class Nanomachine
27
+ # Raised when a transition cannot be performed.
28
+ InvalidTransitionError = Class.new(StandardError)
29
+
30
+ # Raised when a given state cannot be accepted.
31
+ InvalidStateError = Class.new(StandardError)
32
+
33
+ # Construct a Nanomachine with an initial state.
34
+ #
35
+ # @example initialization with a block
36
+ # machine = Nanomachine.new("initial") do |fsm|
37
+ # fsm.transition("initial", %w[green orange])
38
+ # fsm.transition("green", %w[orange error])
39
+ # fsm.transition("orange", %w[green error])
40
+ # # error is a dead state, no transition out of it
41
+ # # so not necessary to define the transitions for it
42
+ #
43
+ # fsm.on_transition(to: "error") do |(from_state, to_state), message|
44
+ # notifier.notify_error(message)
45
+ # end
46
+ #
47
+ # fsm.on_transition do |(from_state, to_state)|
48
+ # object.update_state(to_state)
49
+ # end
50
+ # end
51
+ #
52
+ # @param [#to_s] initial_state state the machine is in after initialization
53
+ # @raise [InvalidStateError] if initial state is nil
54
+ # @yield [self] yields the machine for easy definition of states
55
+ # @yieldparam [Nanomachine] self
56
+ def initialize(initial_state)
57
+ if initial_state.nil?
58
+ raise InvalidStateError, "initial state cannot be nil"
59
+ end
60
+
61
+ @state = initial_state.to_s
62
+ @transitions = Hash.new(Set.new)
63
+ @callbacks = Hash.new { |h, k| h[k] = [] }
64
+ yield self if block_given?
65
+ end
66
+
67
+ # @return [String] current state of the state machine.
68
+ attr_reader :state
69
+
70
+ # @example
71
+ # {"initial"=>#<Set: {"green", "orange"}>,
72
+ # "green"=>#<Set: {"orange", "error"}>,
73
+ # "orange"=>#<Set: {"green", "error"}>}
74
+ #
75
+ # @return [Hash<String, Set>] mapping of state to possible transition targets
76
+ attr_reader :transitions
77
+
78
+ # Define possible state transitions from the source state.
79
+ #
80
+ # @example
81
+ # fsm.transition("green", %w[orange red])
82
+ # fsm.transition("orange", %w[red])
83
+ # fsm.transition(:error, [:nowhere])
84
+ #
85
+ # @param [#to_s] from
86
+ # @param [#each] to each target state must respond to #to_s
87
+ def transition(from, to)
88
+ transitions[from.to_s] = Set.new(to).map!(&:to_s)
89
+ end
90
+
91
+ # Define a callback to be executed on transition.
92
+ #
93
+ # @example callback executed on any transition
94
+ # fsm.on_transition do |(from_state, to_state), *args, &block|
95
+ # # executed on any transition
96
+ # end
97
+ #
98
+ # @example callback executed on transition from a given state only
99
+ # fsm.on_transition(from: "green") do |(from_state, to_state), *args, &block|
100
+ # # executed only on transitions *from* green state
101
+ # end
102
+ #
103
+ # @example callback executed on transition to a given state only
104
+ # fsm.on_transition(to: "green") do |(from_state, to_state), *args, &block|
105
+ # # executed only on transitions *to* green state
106
+ # end
107
+ #
108
+ # @example callback executed on transition between two states only
109
+ # fsm.on_transition(from: "green", to: "red") do |(from_state, to_state), *args, &block|
110
+ # # executed only on transitions between green and red
111
+ # end
112
+ #
113
+ # @param [Hash] options constraint on when callback is to be executed
114
+ # @option options [#to_s, nil] :from (nil) only match when transitioning from the given state, nil for any
115
+ # @option options [#to_s, nil] :to (nil) only match when transitioning to the given state, nil for any
116
+ # @yield [transition, *args, &block] transition states (from, to), and parameters given to {#transition_to} on transition
117
+ # @yieldparam [Array<from_state, to_state>] transition
118
+ # @yieldparam *args arguments passed to {#transition_to}
119
+ # @yieldparam &block block passed to {#transition_to}
120
+ # @raise [ArgumentError] when given unknown options
121
+ # @raise [LocalJumpError] when no callback block is supplied
122
+ def on_transition(options = {}, &block)
123
+ unless block_given?
124
+ raise LocalJumpError, "no block given"
125
+ end
126
+
127
+ from = options.delete(:from)
128
+ from &&= from.to_s
129
+
130
+ to = options.delete(:to)
131
+ to &&= to.to_s
132
+
133
+ unless options.empty?
134
+ raise ArgumentError, "unknown options: #{options.keys.join(", ")}"
135
+ end
136
+
137
+ @callbacks[[from, to]] << block
138
+ end
139
+
140
+ # Transition the state machine from the current state to a target state.
141
+ #
142
+ # @example transition to error state with a message given to any callbacks
143
+ # if previous_state = fsm.transition_to("error", "something went really wrong")
144
+ # puts "Transition from #{previous_state} to #{fsm.state} successful!"
145
+ # else
146
+ # puts "Transition failed."
147
+ # end
148
+ #
149
+ # @param [#to_s] other_state new state to transition to
150
+ # @param args any number of arguments, passed to callbacks defined with {#on_transition}
151
+ # @param block passed to callbacks defined with {#on_transition}
152
+ # @return [String, false] state the machine was in before transition, or false if transition is not allowed
153
+ def transition_to(other_state, *args, &block)
154
+ other_state &&= other_state.to_s
155
+ if transitions[state].include?(other_state)
156
+ previous_state, @state = @state, other_state
157
+ [[nil, nil], [previous_state, nil], [nil, @state], [previous_state, @state]].each do |combo|
158
+ @callbacks[combo].each do |callback|
159
+ callback.call([previous_state, @state], *args, &block)
160
+ end
161
+ end
162
+ previous_state
163
+ else
164
+ false
165
+ end
166
+ end
167
+
168
+ # Same as {#transition_to}, but raises an error if the transition is not allowed.
169
+ #
170
+ # @example
171
+ # fsm.transition_to!("bogus state") # => InvalidTransitionError
172
+ #
173
+ # @param (see #transition_to)
174
+ # @return [String] the state the state machine was in before transition
175
+ # @raise [InvalidTransitionError] if the state machine cannot transition from current state to target state
176
+ def transition_to!(other_state)
177
+ if previous_state = transition_to(other_state)
178
+ previous_state
179
+ else
180
+ raise InvalidTransitionError, "cannot transition from #{state.inspect} to #{other_state.inspect}"
181
+ end
182
+ end
183
+ end
@@ -0,0 +1,4 @@
1
+ class Nanomachine
2
+ # @see http://semver.org/
3
+ VERSION = "1.0.0"
4
+ end
@@ -0,0 +1,52 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "nanomachine/version"
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "nanomachine"
8
+ gem.summary = "A really tiny state machine for ruby. No events, only acceptable transitions and transition callbacks."
9
+ gem.description = <<-DESCRIPTION.gsub(/^ */, "")
10
+ A really tiny state machine for ruby. No events, only accepted transitions and transition callbacks.
11
+
12
+ The difference between Nanomachine, and otherwise known Micromachine (https://rubygems.org/gems/micromachine) is that
13
+ Micromachine transitions to new states in response to events; multiple events can transition between the two same states.
14
+ Nanomachine, on the other hand, does not care about events, and only needs the state you want to be in after successful
15
+ transition.
16
+
17
+ Nanomachine can be used in any ruby project, and have no runtime dependencies.
18
+
19
+ Example:
20
+ state_machine = Nanomachine.new("unpublished") do |fsm|
21
+ fsm.transition("published", %w[unpublished processing removed])
22
+ fsm.transition("unpublished", %w[published processing removed])
23
+ fsm.transition("processing", %w[published unpublished])
24
+ fsm.transition("removed", []) # defined for being explicit
25
+
26
+ fsm.on_transition do |(from_state, to_state)|
27
+ update_column(:state, to_state)
28
+ end
29
+ end
30
+
31
+ if state_machine.transition_to("published")
32
+ puts "Publish success!"
33
+ else
34
+ puts "Publish failure! We’re in \#{state_machine.state}."
35
+ end
36
+ DESCRIPTION
37
+
38
+ gem.version = Nanomachine::VERSION
39
+
40
+ gem.homepage = "https://github.com/elabs/nanomachine"
41
+ gem.authors = ["Ivan Navarrete", "Kim Burgestrand"]
42
+ gem.email = ["crzivn@gmail.com", "kim@burgestrand.se"]
43
+ gem.license = "MIT License"
44
+
45
+ gem.files = `git ls-files`.split($/)
46
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
47
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
48
+ gem.require_paths = ["lib"]
49
+
50
+ gem.add_development_dependency "rake"
51
+ gem.add_development_dependency "rspec", "~> 2.0"
52
+ end
@@ -0,0 +1,163 @@
1
+ describe "Nanomachine state machine" do
2
+ before do
3
+ @callbacks = []
4
+ end
5
+
6
+ let(:fsm) do
7
+ Nanomachine.new("A") do |m|
8
+ m.transition("A", %w[B C E X])
9
+ m.transition("B", %w[A])
10
+ m.transition(:C, [:A, :D])
11
+ m.transition("D", %w[])
12
+ m.transition("E", %w[B C])
13
+
14
+ m.on_transition(:to => "B") do |*args, &block|
15
+ @callbacks << [:to, args, block]
16
+ end
17
+
18
+ m.on_transition(:from => "A") do |*args, &block|
19
+ @callbacks << [:from, args, block]
20
+ end
21
+
22
+ m.on_transition(:from => "A", :to => "B") do |*args, &block|
23
+ @callbacks << [:from_to, args, block]
24
+ end
25
+
26
+ m.on_transition(:from => "E", :to => "C") do |*args, &block|
27
+ @callbacks << [:from_to_e, args, block]
28
+ end
29
+
30
+ m.on_transition do |*args, &block|
31
+ @callbacks << [:any, args, block]
32
+ end
33
+ end
34
+ end
35
+
36
+ describe "VERSION" do
37
+ specify { Nanomachine::VERSION.should be_a String }
38
+ end
39
+
40
+ describe "#initialize" do
41
+ it "raises an error if given an invalid initial state" do
42
+ expect { Nanomachine.new(nil) }.to raise_error(Nanomachine::InvalidStateError, /initial state/)
43
+ end
44
+ end
45
+
46
+ describe "#state" do
47
+ it "returns the current state" do
48
+ fsm.state.should eq("A")
49
+ end
50
+ end
51
+
52
+ describe "#transitions" do
53
+ it "returns the available transitions" do
54
+ fsm.transitions.should eq({"A" => Set.new(%w[B C E X]),
55
+ "B" => Set.new(%w[A]),
56
+ "C" => Set.new(%w[A D]),
57
+ "D" => Set.new(),
58
+ "E" => Set.new(%w[B C])})
59
+ end
60
+ end
61
+
62
+ describe "#on_transition" do
63
+ it "raises an error when given unknown options" do
64
+ expect { fsm.on_transition(:bad_option => "foo") { } }.to raise_error(ArgumentError, /bad_option/)
65
+ end
66
+
67
+ it "raises an error when given no block" do
68
+ expect { fsm.on_transition }.to raise_error(LocalJumpError, /no block given/)
69
+ end
70
+ end
71
+
72
+ describe "#transition_to" do
73
+ it "transitions to the new state" do
74
+ expect { fsm.transition_to("B") }.to change { fsm.state }.from("A").to("B")
75
+ end
76
+
77
+ it "does not transition when the transition is undefined" do
78
+ fsm.transition_to("X")
79
+ expect { fsm.transition_to("A").should be_false }.to_not change { fsm.state }
80
+ end
81
+
82
+ it "returns the previous state" do
83
+ fsm.transition_to("B").should eq("A")
84
+ end
85
+
86
+ it "returns false if transition failed" do
87
+ fsm.transition_to("D").should be_false
88
+ end
89
+
90
+ context "callbacks" do
91
+ it "executes all callbacks in the correct order, most generic first" do
92
+ block = proc {}
93
+ args = [1, [3, 4]]
94
+
95
+ fsm.transition_to("B", *args, &block)
96
+
97
+ @callbacks.should eq([
98
+ [:any, [["A", "B"], 1, [3, 4]], block],
99
+ [:from, [["A", "B"], 1, [3, 4]], block],
100
+ [:to, [["A", "B"], 1, [3, 4]], block],
101
+ [:from_to, [["A", "B"], 1, [3, 4]], block]
102
+ ])
103
+ end
104
+
105
+ it "executes callbacks reacting to any transition" do
106
+ fsm.transition_to("C")
107
+ @callbacks.clear
108
+ fsm.transition_to("D")
109
+
110
+ @callbacks.should eq([
111
+ [:any, [["C", "D"]], nil],
112
+ ])
113
+ end
114
+
115
+ it "executes callbacks for the from-transition" do
116
+ fsm.transition_to("C")
117
+
118
+ @callbacks.should eq([
119
+ [:any, [["A", "C"]], nil],
120
+ [:from, [["A", "C"]], nil],
121
+ ])
122
+ end
123
+
124
+ it "executes callbacks for the to-transition" do
125
+ fsm.transition_to("E")
126
+ @callbacks.clear
127
+ fsm.transition_to("B")
128
+
129
+ @callbacks.should eq([
130
+ [:any, [["E", "B"]], nil],
131
+ [:to, [["E", "B"]], nil],
132
+ ])
133
+ end
134
+
135
+ it "executes callbacks for the from-to-transition" do
136
+ fsm.transition_to("E")
137
+ @callbacks.clear
138
+ fsm.transition_to("C")
139
+
140
+ @callbacks.should eq([
141
+ [:any, [["E", "C"]], nil],
142
+ [:from_to_e, [["E", "C"]], nil],
143
+ ])
144
+ end
145
+
146
+ it "executes no callbacks on failed transitions" do
147
+ fsm.transition_to("D").should be_false
148
+ @callbacks.should be_empty
149
+ end
150
+ end
151
+ end
152
+
153
+ describe "#transition_to!" do
154
+ it "raises an error on an invalid transition" do
155
+ fsm.should_receive(:transition_to).and_return(false)
156
+ expect { fsm.transition_to!("D") }.to raise_error(Nanomachine::InvalidTransitionError, /cannot transition/)
157
+ end
158
+
159
+ it "returns the previous state on success" do
160
+ fsm.transition_to!("B").should eq "A"
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,2 @@
1
+ require "rspec"
2
+ require "nanomachine"
metadata ADDED
@@ -0,0 +1,136 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: nanomachine
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Ivan Navarrete
9
+ - Kim Burgestrand
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2012-11-15 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rake
17
+ requirement: &2170833800 !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ! '>='
21
+ - !ruby/object:Gem::Version
22
+ version: '0'
23
+ type: :development
24
+ prerelease: false
25
+ version_requirements: *2170833800
26
+ - !ruby/object:Gem::Dependency
27
+ name: rspec
28
+ requirement: &2170833240 !ruby/object:Gem::Requirement
29
+ none: false
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '2.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: *2170833240
37
+ description: ! 'A really tiny state machine for ruby. No events, only accepted transitions
38
+ and transition callbacks.
39
+
40
+
41
+ The difference between Nanomachine, and otherwise known Micromachine (https://rubygems.org/gems/micromachine)
42
+ is that
43
+
44
+ Micromachine transitions to new states in response to events; multiple events can
45
+ transition between the two same states.
46
+
47
+ Nanomachine, on the other hand, does not care about events, and only needs the state
48
+ you want to be in after successful
49
+
50
+ transition.
51
+
52
+
53
+ Nanomachine can be used in any ruby project, and have no runtime dependencies.
54
+
55
+
56
+ Example:
57
+
58
+ state_machine = Nanomachine.new("unpublished") do |fsm|
59
+
60
+ fsm.transition("published", %w[unpublished processing removed])
61
+
62
+ fsm.transition("unpublished", %w[published processing removed])
63
+
64
+ fsm.transition("processing", %w[published unpublished])
65
+
66
+ fsm.transition("removed", []) # defined for being explicit
67
+
68
+
69
+ fsm.on_transition do |(from_state, to_state)|
70
+
71
+ update_column(:state, to_state)
72
+
73
+ end
74
+
75
+ end
76
+
77
+
78
+ if state_machine.transition_to("published")
79
+
80
+ puts "Publish success!"
81
+
82
+ else
83
+
84
+ puts "Publish failure! We’re in #{state_machine.state}."
85
+
86
+ end
87
+
88
+ '
89
+ email:
90
+ - crzivn@gmail.com
91
+ - kim@burgestrand.se
92
+ executables: []
93
+ extensions: []
94
+ extra_rdoc_files: []
95
+ files:
96
+ - .gitignore
97
+ - .rspec
98
+ - .travis.yml
99
+ - Gemfile
100
+ - README.md
101
+ - Rakefile
102
+ - lib/nanomachine.rb
103
+ - lib/nanomachine/version.rb
104
+ - nanomachine.gemspec
105
+ - spec/nano_machine_spec.rb
106
+ - spec/spec_helper.rb
107
+ homepage: https://github.com/elabs/nanomachine
108
+ licenses:
109
+ - MIT License
110
+ post_install_message:
111
+ rdoc_options: []
112
+ require_paths:
113
+ - lib
114
+ required_ruby_version: !ruby/object:Gem::Requirement
115
+ none: false
116
+ requirements:
117
+ - - ! '>='
118
+ - !ruby/object:Gem::Version
119
+ version: '0'
120
+ required_rubygems_version: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ! '>='
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ requirements: []
127
+ rubyforge_project:
128
+ rubygems_version: 1.8.10
129
+ signing_key:
130
+ specification_version: 3
131
+ summary: A really tiny state machine for ruby. No events, only acceptable transitions
132
+ and transition callbacks.
133
+ test_files:
134
+ - spec/nano_machine_spec.rb
135
+ - spec/spec_helper.rb
136
+ has_rdoc: