james 0.1.1-universal-darwin-10 → 0.2.0-universal-darwin-10
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/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
|