pretty_state_machine 0.1.0

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: d85359c4d2c29e4e2a1aa08e0120fc04e71eb542
4
+ data.tar.gz: 4067a1ff77c21c1a703b5cdc3c0e47c10bcbe5c4
5
+ SHA512:
6
+ metadata.gz: fb7cda2d6b96642c4929b66d836135d1b52e0693a0465fc6104f13bb109b6392b15b39364a6a4efaaec0342c01c6f7ff95f127d12a508deac0f009f7a9516d66
7
+ data.tar.gz: 33a6319bbbb9ac550e0b15f27f35e30c52f07164a2995c98da8753de74ef65e8bd624ff1ef39909bf0856e04b85b0f10a728a8493156c06f6d8234a57a99c626
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2013 YOURNAME
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,36 @@
1
+ [![Build Status](https://travis-ci.org/ags/pretty_state_machine.png?branch=master)](https://travis-ci.org/ags/pretty_state_machine)
2
+
3
+ Pretty State Machine
4
+ ====================
5
+
6
+ Simple FSM class-oriented DSL. Somewhere between [MicroMachine](https://github.com/soveran/micromachine) and [state_machine](https://github.com/pluginaweek/state_machine/).
7
+
8
+ Usage
9
+ -----
10
+
11
+ ```ruby
12
+ class Reznor < PrettyStateMachine::Machine
13
+ state :up_above_it, initial: true
14
+
15
+ state :down_in_it
16
+
17
+ state :gave_up
18
+
19
+ transition :get_down! do
20
+ from :up_above_it
21
+ to :down_in_it
22
+ end
23
+
24
+ transition :give_up! do
25
+ from :up_above_it, :down_in_it
26
+ to :gave_up
27
+ end
28
+ end
29
+
30
+ >> machine = Reznor.new
31
+ >> machine.state.name
32
+ => :up_above_it
33
+ >> machine.get_down!
34
+ >> machine.state.name
35
+ => :down_in_it
36
+ ```
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env rake
2
+ require 'rubygems'
3
+ require 'bundler/setup'
4
+ require 'bundler/gem_tasks'
5
+ require 'rspec/core/rake_task'
6
+
7
+ RSpec::Core::RakeTask.new(:spec)
8
+
9
+ task default: :spec
@@ -0,0 +1,9 @@
1
+ require_relative 'pretty_state_machine/machine'
2
+ require_relative 'pretty_state_machine/state'
3
+ require_relative 'pretty_state_machine/transition'
4
+
5
+ module PrettyStateMachine
6
+ InvalidMachine = Class.new(Exception)
7
+ InvalidTransition = Class.new(Exception)
8
+ InvalidState = Class.new(Exception)
9
+ end
@@ -0,0 +1,66 @@
1
+ module PrettyStateMachine
2
+ class Machine
3
+
4
+ attr_reader :state
5
+
6
+ def self.states
7
+ @_states ||= {}
8
+ end
9
+
10
+ def self.transitions
11
+ @_transitions ||= {}
12
+ end
13
+
14
+ def self.initial_state
15
+ states.values.find(&:initial?)
16
+ end
17
+
18
+ def self.state(name, options={})
19
+ initial = options.fetch(:initial) { false }
20
+ state = State.new(name, initial: initial)
21
+ states[name] = state
22
+ end
23
+
24
+ def self.transition(name, &block)
25
+ transition = Transition.new(self, name)
26
+ transition.instance_eval(&block)
27
+
28
+ if transition.to_state.nil?
29
+ raise InvalidTransition, "transition '#{name}' requires an end state"
30
+ end
31
+
32
+ define_method(name) do
33
+ if transition.permitted_from?(@state)
34
+ @state = transition.to_state
35
+ else
36
+ raise InvalidTransition, "cannot transition to '#{transition.to_state}' via '#{name}' from '#{@state}'"
37
+ end
38
+ end
39
+
40
+ transitions[name] = transition
41
+ end
42
+
43
+ def initialize(state=self.class.initial_state)
44
+ raise InvalidMachine.new('an initial state is required') if state.nil?
45
+ @state = State(state)
46
+ end
47
+
48
+ protected
49
+
50
+ def self.state_from_name(state_name)
51
+ states.fetch(state_name) do
52
+ raise InvalidState.new("'#{state_name}' is an invalid state")
53
+ end
54
+ end
55
+
56
+ def State(arg)
57
+ case arg
58
+ when State then arg
59
+ when Symbol then self.class.state_from_name(arg)
60
+ else
61
+ raise TypeError, "cannot convert #{arg.inspect} to State"
62
+ end
63
+ end
64
+
65
+ end
66
+ end
@@ -0,0 +1,21 @@
1
+ require 'forwardable'
2
+
3
+ module PrettyStateMachine
4
+ class State
5
+ extend Forwardable
6
+
7
+ attr_reader :name
8
+
9
+ def_delegator :name, :to_s
10
+
11
+ def initialize(name, options={})
12
+ @name = name
13
+ @initial = options.fetch(:initial) { false }
14
+ end
15
+
16
+ def initial?
17
+ @initial
18
+ end
19
+
20
+ end
21
+ end
@@ -0,0 +1,26 @@
1
+ module PrettyStateMachine
2
+ class Transition
3
+ attr_reader :to_state
4
+
5
+ def initialize(machine_class, name)
6
+ @machine_class = machine_class
7
+ @name = name
8
+ @from_states = []
9
+ end
10
+
11
+ def from(*state_names)
12
+ @from_states = state_names.flatten.compact.map { |state_name|
13
+ @machine_class.state_from_name(state_name)
14
+ }
15
+ end
16
+
17
+ def to(state_name)
18
+ @to_state = @machine_class.state_from_name(state_name)
19
+ end
20
+
21
+ def permitted_from?(state)
22
+ @from_states.include?(state)
23
+ end
24
+ end
25
+
26
+ end
@@ -0,0 +1,3 @@
1
+ module PrettyStateMachine
2
+ VERSION = '0.1.0'
3
+ end
@@ -0,0 +1,74 @@
1
+ require './lib/pretty_state_machine'
2
+
3
+ describe PrettyStateMachine::Machine do
4
+ class Empty < PrettyStateMachine::Machine; end
5
+
6
+ class Reznor < PrettyStateMachine::Machine
7
+ state :up_above_it, initial: true
8
+
9
+ state :down_in_it
10
+
11
+ state :gave_up
12
+
13
+ transition :get_down! do
14
+ from :up_above_it
15
+ to :down_in_it
16
+ end
17
+
18
+ transition :give_up! do
19
+ from :down_in_it
20
+ to :gave_up
21
+ end
22
+ end
23
+
24
+ it "sets state to provided initial state on creation" do
25
+ expect(Reznor.new.state.name).to eq(:up_above_it)
26
+ end
27
+
28
+ it "initial state can be overriden on initialization" do
29
+ expect(Reznor.new(:down_in_it).state.name).to eq(:down_in_it)
30
+ end
31
+
32
+ it "raises InvalidMachine without an initial state" do
33
+ expect do
34
+ Empty.new
35
+ end.to raise_error(PrettyStateMachine::InvalidMachine,
36
+ 'an initial state is required')
37
+ end
38
+
39
+ it "defines transition methods that change state" do
40
+ machine = Reznor.new
41
+ expect do
42
+ machine.get_down!
43
+ end.to change { machine.state.name }.from(:up_above_it).to(:down_in_it)
44
+ end
45
+
46
+ it "does not allow definition of transitions without a to" do
47
+ expect do
48
+ class NoTransitionTo < PrettyStateMachine::Machine
49
+ state :foo, initial: true
50
+ transition :bar do from :foo end
51
+ end
52
+ end.to raise_error(PrettyStateMachine::InvalidTransition,
53
+ "transition 'bar' requires an end state")
54
+ end
55
+
56
+ it "enforces transition from declarations" do
57
+ machine = Reznor.new
58
+ expect do
59
+ machine.give_up!
60
+ end.to raise_error(PrettyStateMachine::InvalidTransition,
61
+ "cannot transition to 'gave_up' via 'give_up!' from 'up_above_it'")
62
+ expect(machine.state.name).to eq(:up_above_it)
63
+ end
64
+
65
+ context "when given an invalid initial state" do
66
+ it "raises PrettyStateMachine::InvalidState" do
67
+ expect do
68
+ Reznor.new(:foobar)
69
+ end.to raise_error(PrettyStateMachine::InvalidState,
70
+ "'foobar' is an invalid state")
71
+ end
72
+ end
73
+
74
+ end
@@ -0,0 +1,22 @@
1
+ require './lib/pretty_state_machine'
2
+
3
+ describe PrettyStateMachine::State do
4
+ describe "#initial?" do
5
+ it "is false by default" do
6
+ state = PrettyStateMachine::State.new(:foobar)
7
+ expect(PrettyStateMachine::State.new(:foobar).initial?).to be_false
8
+ end
9
+
10
+ it "is true when state is created with 'initial' parameter" do
11
+ state = PrettyStateMachine::State.new(:foobar, initial: true)
12
+ expect(state.initial?).to be_true
13
+ end
14
+ end
15
+
16
+ describe "#to_s" do
17
+ it "returns the state name" do
18
+ state = PrettyStateMachine::State.new(:foobar, initial: true)
19
+ expect(state.to_s).to eq('foobar')
20
+ end
21
+ end
22
+ end
metadata ADDED
@@ -0,0 +1,98 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pretty_state_machine
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Alex Smith
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-05-30 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
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
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: Simple DSL for state machine classes
56
+ email:
57
+ - alex@thatalexguy.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - lib/pretty_state_machine/machine.rb
63
+ - lib/pretty_state_machine/state.rb
64
+ - lib/pretty_state_machine/transition.rb
65
+ - lib/pretty_state_machine/version.rb
66
+ - lib/pretty_state_machine.rb
67
+ - MIT-LICENSE
68
+ - Rakefile
69
+ - README.md
70
+ - spec/lib/pretty_state_machine/machine_spec.rb
71
+ - spec/lib/pretty_state_machine/state_spec.rb
72
+ homepage: https://github.com/ags/pretty_state_machine
73
+ licenses:
74
+ - MIT
75
+ metadata: {}
76
+ post_install_message:
77
+ rdoc_options: []
78
+ require_paths:
79
+ - lib
80
+ required_ruby_version: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - '>='
83
+ - !ruby/object:Gem::Version
84
+ version: '0'
85
+ required_rubygems_version: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ requirements: []
91
+ rubyforge_project:
92
+ rubygems_version: 2.0.0
93
+ signing_key:
94
+ specification_version: 4
95
+ summary: Simple DSL for state machine classes
96
+ test_files:
97
+ - spec/lib/pretty_state_machine/machine_spec.rb
98
+ - spec/lib/pretty_state_machine/state_spec.rb