james 0.1.1-universal-darwin-10 → 0.2.0-universal-darwin-10
Sign up to get free protection for your applications and to get access to all the features.
- data/aux/james/cli.rb +21 -4
- data/lib/james/builtin/core_dialog.rb +4 -0
- data/lib/james/controller.rb +60 -51
- data/lib/james/conversation.rb +62 -0
- data/lib/james/dialog_internals.rb +62 -9
- data/lib/james/dialogs.rb +6 -28
- data/lib/james/markers/current.rb +34 -0
- data/lib/james/markers/marker.rb +92 -0
- data/lib/james/markers/memory.rb +37 -0
- data/lib/james/state_api.rb +39 -11
- data/lib/james/state_internals.rb +35 -14
- data/lib/james.rb +5 -5
- metadata +7 -23
- data/lib/james/visitor.rb +0 -80
- data/lib/james/visitors.rb +0 -62
- data/spec/aux/james/cli_spec.rb +0 -19
- data/spec/integration/test_dialogue_spec.rb +0 -67
- data/spec/lib/james/controller_spec.rb +0 -35
- data/spec/lib/james/dialog_spec.rb +0 -90
- data/spec/lib/james/inputs/audio_spec.rb +0 -18
- data/spec/lib/james/inputs/terminal_spec.rb +0 -12
- data/spec/lib/james/state_spec.rb +0 -156
- data/spec/lib/james/visitor_spec.rb +0 -101
- data/spec/lib/james/visitors_spec.rb +0 -64
@@ -2,18 +2,28 @@ module James
|
|
2
2
|
|
3
3
|
class State
|
4
4
|
|
5
|
-
|
5
|
+
# Transitions are internal transitions & external transitions.
|
6
|
+
#
|
7
|
+
def transitions
|
8
|
+
@transitions
|
9
|
+
end
|
6
10
|
|
7
|
-
# Returns all possible phrases that lead
|
8
|
-
# away from this state.
|
9
11
|
#
|
10
|
-
|
12
|
+
#
|
13
|
+
def expects
|
11
14
|
transitions.keys
|
12
15
|
end
|
13
16
|
|
17
|
+
#
|
18
|
+
#
|
19
|
+
def internal_expects
|
20
|
+
transitions.select { |phrase, target| target.respond_to?(:to_sym) || target == self.context }.keys
|
21
|
+
end
|
22
|
+
|
23
|
+
|
14
24
|
# Returns the next state for the given phrase.
|
15
25
|
#
|
16
|
-
# It accesses the context (aka Dialog
|
26
|
+
# It accesses the context (aka Dialog) to get a full object state.
|
17
27
|
#
|
18
28
|
# If it is a Symbol, James will try to get the real state.
|
19
29
|
# If not, it will just return it (a State already, or lambda).
|
@@ -23,15 +33,26 @@ module James
|
|
23
33
|
state.respond_to?(:id2name) ? context.state_for(state) : state
|
24
34
|
end
|
25
35
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
36
|
+
# The naughty privates.
|
37
|
+
#
|
38
|
+
|
39
|
+
# Called by the visitor visiting this state.
|
40
|
+
#
|
41
|
+
def __into__
|
42
|
+
@into_block && context.instance_eval(&@into_block)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Called by the visitor visiting this state.
|
46
|
+
#
|
47
|
+
def __exit__
|
48
|
+
@exit_block && context.instance_eval(&@exit_block)
|
49
|
+
end
|
50
|
+
|
51
|
+
# Called by the visitor visiting this state.
|
52
|
+
#
|
53
|
+
def __transition__ &block
|
54
|
+
context.instance_eval &block
|
55
|
+
end
|
35
56
|
|
36
57
|
end
|
37
58
|
|
data/lib/james.rb
CHANGED
@@ -3,8 +3,10 @@ module James; end
|
|
3
3
|
require File.expand_path '../james/state_api', __FILE__
|
4
4
|
require File.expand_path '../james/state_internals', __FILE__
|
5
5
|
|
6
|
-
require File.expand_path '../james/
|
7
|
-
require File.expand_path '../james/
|
6
|
+
require File.expand_path '../james/markers/marker', __FILE__
|
7
|
+
require File.expand_path '../james/markers/current', __FILE__
|
8
|
+
require File.expand_path '../james/markers/memory', __FILE__
|
9
|
+
require File.expand_path '../james/conversation', __FILE__
|
8
10
|
|
9
11
|
require File.expand_path '../james/dialog_api', __FILE__
|
10
12
|
require File.expand_path '../james/dialog_internals', __FILE__
|
@@ -30,13 +32,11 @@ module James
|
|
30
32
|
# If called twice or more, will just add more dialogs.
|
31
33
|
#
|
32
34
|
def self.use *dialogs
|
33
|
-
dialogs.each { |dialog| controller
|
35
|
+
dialogs.each { |dialog| controller << dialog}
|
34
36
|
end
|
35
37
|
|
36
38
|
# Start listening.
|
37
39
|
#
|
38
|
-
# Will not listen again if already listening.
|
39
|
-
#
|
40
40
|
def self.listen options
|
41
41
|
controller.listen options
|
42
42
|
end
|
metadata
CHANGED
@@ -2,14 +2,14 @@
|
|
2
2
|
name: james
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.
|
5
|
+
version: 0.2.0
|
6
6
|
platform: universal-darwin-10
|
7
7
|
authors:
|
8
8
|
- Florian Hanke
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2011-
|
12
|
+
date: 2011-06-08 00:00:00 +10:00
|
13
13
|
default_executable: james
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -38,6 +38,7 @@ extra_rdoc_files: []
|
|
38
38
|
files:
|
39
39
|
- lib/james/builtin/core_dialog.rb
|
40
40
|
- lib/james/controller.rb
|
41
|
+
- lib/james/conversation.rb
|
41
42
|
- lib/james/dialog_api.rb
|
42
43
|
- lib/james/dialog_internals.rb
|
43
44
|
- lib/james/dialogs.rb
|
@@ -45,23 +46,15 @@ files:
|
|
45
46
|
- lib/james/inputs/audio.rb
|
46
47
|
- lib/james/inputs/base.rb
|
47
48
|
- lib/james/inputs/terminal.rb
|
49
|
+
- lib/james/markers/current.rb
|
50
|
+
- lib/james/markers/marker.rb
|
51
|
+
- lib/james/markers/memory.rb
|
48
52
|
- lib/james/outputs/audio.rb
|
49
53
|
- lib/james/outputs/terminal.rb
|
50
54
|
- lib/james/state_api.rb
|
51
55
|
- lib/james/state_internals.rb
|
52
|
-
- lib/james/visitor.rb
|
53
|
-
- lib/james/visitors.rb
|
54
56
|
- lib/james.rb
|
55
57
|
- aux/james/cli.rb
|
56
|
-
- spec/aux/james/cli_spec.rb
|
57
|
-
- spec/integration/test_dialogue_spec.rb
|
58
|
-
- spec/lib/james/controller_spec.rb
|
59
|
-
- spec/lib/james/dialog_spec.rb
|
60
|
-
- spec/lib/james/inputs/audio_spec.rb
|
61
|
-
- spec/lib/james/inputs/terminal_spec.rb
|
62
|
-
- spec/lib/james/state_spec.rb
|
63
|
-
- spec/lib/james/visitor_spec.rb
|
64
|
-
- spec/lib/james/visitors_spec.rb
|
65
58
|
- bin/james
|
66
59
|
has_rdoc: true
|
67
60
|
homepage: http://floere.github.com/james
|
@@ -88,13 +81,4 @@ rubygems_version: 1.4.2
|
|
88
81
|
signing_key:
|
89
82
|
specification_version: 3
|
90
83
|
summary: 'James: Modular Electronic Butler with modular Dialogs.'
|
91
|
-
test_files:
|
92
|
-
- spec/aux/james/cli_spec.rb
|
93
|
-
- spec/integration/test_dialogue_spec.rb
|
94
|
-
- spec/lib/james/controller_spec.rb
|
95
|
-
- spec/lib/james/dialog_spec.rb
|
96
|
-
- spec/lib/james/inputs/audio_spec.rb
|
97
|
-
- spec/lib/james/inputs/terminal_spec.rb
|
98
|
-
- spec/lib/james/state_spec.rb
|
99
|
-
- spec/lib/james/visitor_spec.rb
|
100
|
-
- spec/lib/james/visitors_spec.rb
|
84
|
+
test_files: []
|
data/lib/james/visitor.rb
DELETED
@@ -1,80 +0,0 @@
|
|
1
|
-
module James
|
2
|
-
|
3
|
-
# The visitor knows where in the conversation we are.
|
4
|
-
#
|
5
|
-
# It also remembers where it has to go back to if
|
6
|
-
# too much time passes without input.
|
7
|
-
#
|
8
|
-
# Note: A visitor should generally be very stupid.
|
9
|
-
#
|
10
|
-
class Visitor
|
11
|
-
|
12
|
-
attr_reader :initial, :timer
|
13
|
-
attr_accessor :current
|
14
|
-
|
15
|
-
# Pass in an initial state to start from.
|
16
|
-
#
|
17
|
-
def initialize initial
|
18
|
-
@current = initial
|
19
|
-
|
20
|
-
@initial = initial
|
21
|
-
end
|
22
|
-
|
23
|
-
# Resets the current state back to the initial.
|
24
|
-
#
|
25
|
-
def reset
|
26
|
-
self.current = initial
|
27
|
-
end
|
28
|
-
|
29
|
-
# We hear a phrase.
|
30
|
-
#
|
31
|
-
# Also used to start the whole process.
|
32
|
-
#
|
33
|
-
def enter
|
34
|
-
result = current.__into__
|
35
|
-
yield result if result && block_given?
|
36
|
-
result
|
37
|
-
end
|
38
|
-
def exit
|
39
|
-
result = current.__exit__
|
40
|
-
yield result if result && block_given?
|
41
|
-
result
|
42
|
-
end
|
43
|
-
def transition phrase
|
44
|
-
state_or_lambda = current.next_for phrase
|
45
|
-
if state_or_lambda.respond_to?(:call)
|
46
|
-
current.__transition__ &state_or_lambda # Don't transition.
|
47
|
-
else
|
48
|
-
self.current = state_or_lambda
|
49
|
-
end
|
50
|
-
end
|
51
|
-
def check
|
52
|
-
reset && yield("Whoops. That led nowhere. Perhaps you didn't define the target state?") unless self.current
|
53
|
-
end
|
54
|
-
def hear phrase, &block
|
55
|
-
return unless hears? phrase
|
56
|
-
exit_text = exit &block
|
57
|
-
transition phrase
|
58
|
-
check &block
|
59
|
-
into_text = enter &block
|
60
|
-
exit_text || into_text
|
61
|
-
end
|
62
|
-
def hears? phrase
|
63
|
-
expects.include? phrase
|
64
|
-
end
|
65
|
-
def expects
|
66
|
-
current.phrases
|
67
|
-
end
|
68
|
-
# Does the current state allow penetration into another dialog?
|
69
|
-
#
|
70
|
-
def chainable?
|
71
|
-
current.chainable?
|
72
|
-
end
|
73
|
-
|
74
|
-
def to_s
|
75
|
-
"#{self.class.name}(#{initial}, current: #{current})"
|
76
|
-
end
|
77
|
-
|
78
|
-
end
|
79
|
-
|
80
|
-
end
|
data/lib/james/visitors.rb
DELETED
@@ -1,62 +0,0 @@
|
|
1
|
-
module James
|
2
|
-
|
3
|
-
# The visitors class has a number of visitors, whose
|
4
|
-
# dialogs are visited in order of preference.
|
5
|
-
#
|
6
|
-
# Why?
|
7
|
-
# Discussions have multiple points where they can be.
|
8
|
-
# (Politics, then this joke, then back again, finally "Oh, bye I have to go!")
|
9
|
-
#
|
10
|
-
# In James, though, it is much simpler.
|
11
|
-
# We just have a visitor in an entry scenario
|
12
|
-
# (James!, Sleep, Wake up, Something completely different)
|
13
|
-
# and one in any specific user-given scenario.
|
14
|
-
#
|
15
|
-
# Visitors is a proxy object for visitors.
|
16
|
-
#
|
17
|
-
class Visitors
|
18
|
-
|
19
|
-
attr_reader :visitors
|
20
|
-
|
21
|
-
def initialize *visitors
|
22
|
-
@visitors = visitors
|
23
|
-
end
|
24
|
-
|
25
|
-
# Hear tries all visitors in order
|
26
|
-
# until one hears a phrase he knows.
|
27
|
-
#
|
28
|
-
# After that, all remaining visitors are reset.
|
29
|
-
#
|
30
|
-
def hear phrase, &block
|
31
|
-
enumerator = visitors.dup
|
32
|
-
|
33
|
-
while visitor = enumerator.shift
|
34
|
-
visitor.hear phrase, &block and break
|
35
|
-
end
|
36
|
-
|
37
|
-
enumerator.each do |visitor|
|
38
|
-
visitor.reset
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
# Enter enters the first visitor.
|
43
|
-
#
|
44
|
-
def enter
|
45
|
-
visitors.first.enter
|
46
|
-
end
|
47
|
-
|
48
|
-
# Simply returns the sum of what phrases all dialogs do expect, front-to-back.
|
49
|
-
#
|
50
|
-
# Stops as soon as a visitor is not in a chainable state anymore.
|
51
|
-
#
|
52
|
-
def expects
|
53
|
-
visitors.inject([]) do |expects, visitor|
|
54
|
-
total = visitor.expects + expects
|
55
|
-
break total unless visitor.chainable?
|
56
|
-
total
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
end
|
61
|
-
|
62
|
-
end
|
data/spec/aux/james/cli_spec.rb
DELETED
@@ -1,19 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
#
|
3
|
-
require File.expand_path '../../../../aux/james/cli', __FILE__
|
4
|
-
|
5
|
-
describe James::CLI do
|
6
|
-
|
7
|
-
before(:each) do
|
8
|
-
Dir.stub! :[] => ['test_dialog.rb', 'test_dialog.rb', 'test/test_dialog.rb']
|
9
|
-
end
|
10
|
-
|
11
|
-
let(:cli) { James::CLI.new }
|
12
|
-
|
13
|
-
describe 'find_dialogs' do
|
14
|
-
it 'returns the right ones' do
|
15
|
-
cli.find_dialogs.should == ['test_dialog.rb', 'test_dialog.rb', 'test/test_dialog.rb']
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
end
|
@@ -1,67 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
#
|
3
|
-
require File.expand_path '../../../lib/james', __FILE__
|
4
|
-
|
5
|
-
describe 'TestDialog' do
|
6
|
-
|
7
|
-
context 'unit' do
|
8
|
-
let(:dialog) do
|
9
|
-
|
10
|
-
Class.new do
|
11
|
-
include James::Dialog
|
12
|
-
|
13
|
-
hear ['test1', 'test2'] => :first
|
14
|
-
|
15
|
-
state :first do
|
16
|
-
hear 'go' => :second, 'stay' => :first
|
17
|
-
end
|
18
|
-
|
19
|
-
state :second do
|
20
|
-
hear 'go' => :third, 'back' => :first
|
21
|
-
end
|
22
|
-
end.new
|
23
|
-
|
24
|
-
end
|
25
|
-
let(:visitor) do
|
26
|
-
James::Visitor.new dialog.state_for(:first)
|
27
|
-
end
|
28
|
-
|
29
|
-
describe "integration" do
|
30
|
-
it 'works correctly' do
|
31
|
-
visitor.current.name.should == :first
|
32
|
-
visitor.hear('go') {}
|
33
|
-
visitor.current.name.should == :second
|
34
|
-
visitor.hear('back') {}
|
35
|
-
visitor.current.name.should == :first
|
36
|
-
visitor.hear('stay') {}
|
37
|
-
visitor.current.name.should == :first
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
# context 'integration' do
|
43
|
-
# let(:dialog) do
|
44
|
-
# dialog = Class.new do
|
45
|
-
# include James::Dialog
|
46
|
-
#
|
47
|
-
# hear ['test1', 'test2'] => :first
|
48
|
-
# state :first do
|
49
|
-
# hear 'go' => :second, 'stay' => :first
|
50
|
-
# end
|
51
|
-
# state :second do
|
52
|
-
# hear 'go' => :third, 'back' => :first
|
53
|
-
# end
|
54
|
-
# end.new
|
55
|
-
# end
|
56
|
-
# it 'works correctly' do
|
57
|
-
# dialog.state.name.should == :awake
|
58
|
-
# dialog.hear 'sleep'
|
59
|
-
# dialog.state.name.should == :sleeping
|
60
|
-
# end
|
61
|
-
# it 'delegates correctly' do
|
62
|
-
# dialog.state.name.should == :awake
|
63
|
-
# dialog.hear 'test1'
|
64
|
-
# end
|
65
|
-
# end
|
66
|
-
|
67
|
-
end
|
@@ -1,35 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
#
|
3
|
-
require File.expand_path '../../../../lib/james/dialog_api', __FILE__
|
4
|
-
require File.expand_path '../../../../lib/james/dialog_internals', __FILE__
|
5
|
-
require File.expand_path '../../../../lib/james/builtin/core_dialog', __FILE__
|
6
|
-
require File.expand_path '../../../../lib/james/visitors', __FILE__
|
7
|
-
require File.expand_path '../../../../lib/james/visitor', __FILE__
|
8
|
-
require File.expand_path '../../../../lib/james/state_api', __FILE__
|
9
|
-
require File.expand_path '../../../../lib/james/dialogs', __FILE__
|
10
|
-
require File.expand_path '../../../../lib/james/inputs/base', __FILE__
|
11
|
-
require File.expand_path '../../../../lib/james/inputs/terminal', __FILE__
|
12
|
-
require File.expand_path '../../../../lib/james/inputs/audio', __FILE__
|
13
|
-
require File.expand_path '../../../../lib/james/controller', __FILE__
|
14
|
-
|
15
|
-
describe James::Controller do
|
16
|
-
|
17
|
-
let(:controller) { described_class.new }
|
18
|
-
|
19
|
-
describe 'listening?' do
|
20
|
-
it 'is correct' do
|
21
|
-
controller.listening?.should == false
|
22
|
-
end
|
23
|
-
end
|
24
|
-
describe 'expects' do
|
25
|
-
it 'delegates' do
|
26
|
-
visitor = stub! :visitor
|
27
|
-
controller.stub! :visitor => visitor
|
28
|
-
|
29
|
-
visitor.should_receive(:expects).once.with()
|
30
|
-
|
31
|
-
controller.expects
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
end
|
@@ -1,90 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
#
|
3
|
-
require File.expand_path '../../../../lib/james', __FILE__
|
4
|
-
require File.expand_path '../../../../lib/james/state_api', __FILE__
|
5
|
-
require File.expand_path '../../../../lib/james/dialog_api', __FILE__
|
6
|
-
require File.expand_path '../../../../lib/james/dialog_internals', __FILE__
|
7
|
-
require File.expand_path '../../../../lib/james/builtin/core_dialog', __FILE__
|
8
|
-
require File.expand_path '../../../../lib/james/dialogs', __FILE__
|
9
|
-
|
10
|
-
describe James::Dialog do
|
11
|
-
|
12
|
-
context 'units' do
|
13
|
-
let(:dialog) do
|
14
|
-
James.use_dialog do
|
15
|
-
|
16
|
-
hear 'something' => :first
|
17
|
-
|
18
|
-
state :first do
|
19
|
-
hear 'something else' => :second
|
20
|
-
into {}
|
21
|
-
exit {}
|
22
|
-
end
|
23
|
-
|
24
|
-
state :second do
|
25
|
-
hear 'yet something else' => :first
|
26
|
-
into {}
|
27
|
-
exit {}
|
28
|
-
end
|
29
|
-
|
30
|
-
end
|
31
|
-
end
|
32
|
-
describe 'state_for' do
|
33
|
-
it 'delegates to the class, adding itself' do
|
34
|
-
new_dialog = dialog.new
|
35
|
-
dialog.should_receive(:state_for).once.with :some_name, new_dialog
|
36
|
-
|
37
|
-
new_dialog.state_for :some_name
|
38
|
-
end
|
39
|
-
it 'returns nil on not found' do
|
40
|
-
dialog.new.state_for(:nonexistent).should == nil
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
describe 'hear with lambda' do
|
46
|
-
let(:dialog) do
|
47
|
-
class Test
|
48
|
-
include James::Dialog
|
49
|
-
|
50
|
-
def initialize
|
51
|
-
@bla = 'some bla'
|
52
|
-
end
|
53
|
-
|
54
|
-
state :test do
|
55
|
-
hear 'bla' => ->(){ @bla }
|
56
|
-
end
|
57
|
-
end
|
58
|
-
Test.new
|
59
|
-
end
|
60
|
-
it "is instance eval'd" do
|
61
|
-
test_state = dialog.state_for :test
|
62
|
-
test_state.hear 'bla' do |result|
|
63
|
-
result.should == 'some bla'
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
describe 'initialization' do
|
69
|
-
it 'can be included' do
|
70
|
-
expect do
|
71
|
-
class Test
|
72
|
-
include James::Dialog
|
73
|
-
|
74
|
-
hear 'something' => :some_state
|
75
|
-
end
|
76
|
-
end.to_not raise_error
|
77
|
-
end
|
78
|
-
|
79
|
-
it 'can be defined' do
|
80
|
-
expect do
|
81
|
-
James.dialog do
|
82
|
-
|
83
|
-
hear 'something' => :some_state
|
84
|
-
|
85
|
-
end
|
86
|
-
end.to_not raise_error
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
end
|
@@ -1,18 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
#
|
3
|
-
require File.expand_path '../../../../../lib/james/inputs/base', __FILE__
|
4
|
-
require File.expand_path '../../../../../lib/james/inputs/audio', __FILE__
|
5
|
-
|
6
|
-
describe James::Inputs::Audio do
|
7
|
-
|
8
|
-
let(:input) { described_class.new }
|
9
|
-
|
10
|
-
describe 'speechRecognizer' do
|
11
|
-
it 'does something' do
|
12
|
-
sender = stub :sender
|
13
|
-
|
14
|
-
input.speechRecognizer sender
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
end
|
@@ -1,12 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
#
|
3
|
-
require File.expand_path '../../../../../lib/james/inputs/base', __FILE__
|
4
|
-
require File.expand_path '../../../../../lib/james/inputs/terminal', __FILE__
|
5
|
-
|
6
|
-
describe James::Inputs::Terminal do
|
7
|
-
|
8
|
-
let(:input) { described_class.new }
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
end
|