baby_bots 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.
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +11 -0
- data/Rakefile +2 -0
- data/baby_bots.gemspec +18 -0
- data/lib/baby_bots/baby_bot.rb +98 -0
- data/lib/baby_bots/state.rb +22 -0
- data/lib/baby_bots/version.rb +3 -0
- data/lib/baby_bots.rb +3 -0
- metadata +56 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012, Justin Hamilton
|
2
|
+
All rights reserved.
|
3
|
+
|
4
|
+
Redistribution and use in source and binary forms, with or without
|
5
|
+
modification, are permitted provided that the following conditions are met:
|
6
|
+
|
7
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
8
|
+
list of conditions and the following disclaimer.
|
9
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
10
|
+
this list of conditions and the following disclaimer in the documentation
|
11
|
+
and/or other materials provided with the distribution.
|
12
|
+
|
13
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
14
|
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
15
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
16
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
17
|
+
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
18
|
+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
19
|
+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
20
|
+
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
21
|
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
22
|
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/README.md
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
Baby Bots
|
2
|
+
=========
|
3
|
+
|
4
|
+
A small finite-state automata library
|
5
|
+
-------------------------------------
|
6
|
+
|
7
|
+
This is a small finite-state automata library written in Ruby both for a
|
8
|
+
project, as well as to teach me how to build gems (I'm not really much of
|
9
|
+
a Ruby guy).
|
10
|
+
|
11
|
+
Let me know of any improviments, thank you!
|
data/Rakefile
ADDED
data/baby_bots.gemspec
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/baby_bots/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Justin Hamilton"]
|
6
|
+
gem.email = ["justinanthonyhamilton@gmail.com"]
|
7
|
+
gem.description = %q{A tiny finite-state automata library.}
|
8
|
+
gem.summary = %q{While there are many fsa libraries out there, I wanted to implement my own so I could learn how to create a module/gem, as I am not really a Ruby guy and have no idea how.}
|
9
|
+
gem.homepage = "https://github.com/jamiltron/BabyBots"
|
10
|
+
gem.email = "justinanthonyhamilton@gmail.com"
|
11
|
+
|
12
|
+
gem.files = `git ls-files`.split($\)
|
13
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
14
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
15
|
+
gem.name = "baby_bots"
|
16
|
+
gem.require_paths = ["lib"]
|
17
|
+
gem.version = BabyBots::VERSION
|
18
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
module BabyBots
|
2
|
+
|
3
|
+
class NoSuchStateException < Exception
|
4
|
+
end
|
5
|
+
|
6
|
+
class NoSuchTransitionException < Exception
|
7
|
+
end
|
8
|
+
|
9
|
+
class BabyBot
|
10
|
+
attr_accessor :curr, :states, :start
|
11
|
+
|
12
|
+
def initialize(states={})
|
13
|
+
@states = states
|
14
|
+
@start = nil
|
15
|
+
@curr = nil
|
16
|
+
end
|
17
|
+
|
18
|
+
# add a new state to the state machine
|
19
|
+
def add_state(state, start=nil)
|
20
|
+
# key on state names to the actual state hash
|
21
|
+
@states[state.state] = state
|
22
|
+
if start
|
23
|
+
@start = state
|
24
|
+
@curr = @start
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
# accepts a hash of hashes consiting of {state => {event => transition, ...} ...}
|
30
|
+
# structure, and adds these states to the state machine, assumes first state
|
31
|
+
# is the starting state
|
32
|
+
def build(table)
|
33
|
+
first_state = true
|
34
|
+
|
35
|
+
# iterate through the provided {state => {event => transition, ...}, ...}
|
36
|
+
table.each do |state, state_table|
|
37
|
+
temp_state = State.new(state)
|
38
|
+
|
39
|
+
# iterate through each event and transition, building up the transitions
|
40
|
+
state_table.each do |event, transition|
|
41
|
+
temp_state.add_transition(event, transition)
|
42
|
+
end
|
43
|
+
|
44
|
+
# finally, add the state to the machine, and since we've already
|
45
|
+
# added a start state, clear the first_state flag
|
46
|
+
add_state(temp_state, first_state)
|
47
|
+
first_state = false
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# this is the driving function behind the fsa, process
|
52
|
+
# will check the current state, doing any processing necessary
|
53
|
+
# on the input based on whether or not this
|
54
|
+
|
55
|
+
def process(event=nil)
|
56
|
+
# get the current state
|
57
|
+
curr_state = @curr
|
58
|
+
|
59
|
+
# check if we need to preprocess the event
|
60
|
+
if respond_to?("pre_#{@curr.state}")
|
61
|
+
event = send("pre_#{@curr.state}", event)
|
62
|
+
end
|
63
|
+
|
64
|
+
next_state = @states[curr.table[event]]
|
65
|
+
if next_state.nil?
|
66
|
+
next_state = @states[curr.table[:else]]
|
67
|
+
end
|
68
|
+
|
69
|
+
# if the event is nil, and there is no :else clause,
|
70
|
+
# throw an exception
|
71
|
+
if next_state.nil?
|
72
|
+
raise NoSuchTransitionException,
|
73
|
+
"No valid transition #{event} for #{@curr.state}"
|
74
|
+
end
|
75
|
+
|
76
|
+
# check if we need to postprocess the event, this will act
|
77
|
+
# as the "return" from any state transition (even self-looping transitions)
|
78
|
+
if respond_to?("post_#{@curr.state}")
|
79
|
+
ret_val = send("post_#{@curr.state}", event)
|
80
|
+
end
|
81
|
+
|
82
|
+
|
83
|
+
# actually transition, and make sure such a transition exists
|
84
|
+
@curr = next_state
|
85
|
+
if @curr.nil?
|
86
|
+
raise NoSuchStateException,
|
87
|
+
"No valid state #{@curr} for transition #{event} from #{curr_state}"
|
88
|
+
end
|
89
|
+
|
90
|
+
return ret_val
|
91
|
+
end
|
92
|
+
|
93
|
+
def restart
|
94
|
+
@curr = @start
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module BabyBots
|
2
|
+
|
3
|
+
class State
|
4
|
+
attr_reader :state, :table
|
5
|
+
|
6
|
+
def initialize(state, table={})
|
7
|
+
@state = state
|
8
|
+
@table = table
|
9
|
+
end
|
10
|
+
|
11
|
+
def add_transition(event, transition, &callback)
|
12
|
+
@table[event] = transition
|
13
|
+
end
|
14
|
+
|
15
|
+
# the idea behind build is that you can call multiple
|
16
|
+
# :event :transition and the build method
|
17
|
+
# will parse them out and add them to state
|
18
|
+
def build(*args)
|
19
|
+
args.map {|k,v| add_transition(k, v) }
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/lib/baby_bots.rb
ADDED
metadata
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: baby_bots
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Justin Hamilton
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-04-30 00:00:00.000000000Z
|
13
|
+
dependencies: []
|
14
|
+
description: A tiny finite-state automata library.
|
15
|
+
email: justinanthonyhamilton@gmail.com
|
16
|
+
executables: []
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- .gitignore
|
21
|
+
- Gemfile
|
22
|
+
- LICENSE
|
23
|
+
- README.md
|
24
|
+
- Rakefile
|
25
|
+
- baby_bots.gemspec
|
26
|
+
- lib/baby_bots.rb
|
27
|
+
- lib/baby_bots/baby_bot.rb
|
28
|
+
- lib/baby_bots/state.rb
|
29
|
+
- lib/baby_bots/version.rb
|
30
|
+
homepage: https://github.com/jamiltron/BabyBots
|
31
|
+
licenses: []
|
32
|
+
post_install_message:
|
33
|
+
rdoc_options: []
|
34
|
+
require_paths:
|
35
|
+
- lib
|
36
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
37
|
+
none: false
|
38
|
+
requirements:
|
39
|
+
- - ! '>='
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '0'
|
42
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
43
|
+
none: false
|
44
|
+
requirements:
|
45
|
+
- - ! '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
requirements: []
|
49
|
+
rubyforge_project:
|
50
|
+
rubygems_version: 1.8.10
|
51
|
+
signing_key:
|
52
|
+
specification_version: 3
|
53
|
+
summary: While there are many fsa libraries out there, I wanted to implement my own
|
54
|
+
so I could learn how to create a module/gem, as I am not really a Ruby guy and have
|
55
|
+
no idea how.
|
56
|
+
test_files: []
|