baby_bots 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|