james 0.0.6-universal-darwin-10 → 0.0.7-universal-darwin-10

Sign up to get free protection for your applications and to get access to all the features.
data/bin/james CHANGED
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env ruby
2
2
  #
3
3
 
4
- # james joke phonebook quote
4
+ # james joke phonebook quote # Uses just the described ones.
5
5
  # OR
6
- # james # Uses Dir.pwd
6
+ # james # Uses all it can find.
7
7
  #
8
8
 
9
9
  begin
@@ -1,13 +1,27 @@
1
1
  module James; end
2
2
 
3
- require File.expand_path '../james/timer', __FILE__
3
+ # require File.expand_path '../james/timer', __FILE__
4
+
5
+ require File.expand_path '../james/state_api', __FILE__
6
+ require File.expand_path '../james/state_internals', __FILE__
7
+
4
8
  require File.expand_path '../james/visitor', __FILE__
5
9
  require File.expand_path '../james/visitors', __FILE__
6
- require File.expand_path '../james/dialogues', __FILE__
7
10
 
8
11
  require File.expand_path '../james/dialogue_api', __FILE__
9
12
  require File.expand_path '../james/dialogue_internals', __FILE__
10
13
 
14
+ require File.expand_path '../james/dialogues', __FILE__
15
+
16
+ require File.expand_path '../james/inputs/base', __FILE__
17
+ require File.expand_path '../james/inputs/audio', __FILE__
18
+ require File.expand_path '../james/inputs/terminal', __FILE__
19
+
20
+ require File.expand_path '../james/outputs/audio', __FILE__
21
+ require File.expand_path '../james/outputs/terminal', __FILE__
22
+
23
+ require File.expand_path '../james/builtin/core_dialogue', __FILE__
24
+
11
25
  require File.expand_path '../james/controller', __FILE__
12
26
 
13
27
  module James
@@ -1,22 +1,43 @@
1
+ # This is the core dialogue every dialogue will be hooking into.
2
+ #
3
+ # Eventually, the design should be such that everyone can use
4
+ # the design and core dialogue they like best.
5
+ #
6
+ # But to get going, this suffices for now.
7
+ #
1
8
  class CoreDialogue
2
9
 
3
10
  include James::Dialogue
4
11
 
12
+ # The alert state.
13
+ # When James is in this state, he should be
14
+ # open for user dialogues.
15
+ #
5
16
  state :awake do
6
- chainable # If James is awake, he offers more dialogues on this state, if there are any.
17
+ # If James is awake, he offers more dialogues
18
+ # on this state, if there are any hooked into this state.
19
+ #
20
+ chainable
7
21
 
8
- hear 'I need some time alone, James.' => :away,
9
- "Good night, James." => :exit,
10
- ["Thank you, James.", "Thanks, James."] => :awake
22
+ hear 'I need some time alone, James.' => :away,
23
+ "Good night, James." => :exit,
24
+ "Thank you, James." => :awake
11
25
  into { "Sir?" }
12
26
  end
13
27
 
28
+ # The away state. James does not listen to any
29
+ # user dialogue hooks, but only for his name
30
+ # or the good night, i.e. exit phrase.
31
+ #
14
32
  state :away do
15
33
  hear 'James?' => :awake,
16
34
  "Good night, James." => :exit
17
35
  into { "Of course, Sir!" }
18
36
  end
19
37
 
38
+ # This is not a real state. It just exists to Kernel.exit
39
+ # James when he enters this state.
40
+ #
20
41
  state :exit do
21
42
  into do
22
43
  puts "James: Exits through a side door."
@@ -1,14 +1,5 @@
1
1
  framework 'AppKit'
2
2
 
3
- require File.expand_path '../inputs/base', __FILE__
4
- require File.expand_path '../inputs/audio', __FILE__
5
- require File.expand_path '../inputs/terminal', __FILE__
6
-
7
- require File.expand_path '../outputs/audio', __FILE__
8
- require File.expand_path '../outputs/terminal', __FILE__
9
-
10
- require File.expand_path '../builtin/core_dialogue', __FILE__
11
-
12
3
  module James
13
4
 
14
5
  class Controller
@@ -36,18 +27,7 @@ module James
36
27
  # Load voices from yaml.
37
28
  #
38
29
  def load_voices
39
- # yaml_voices = ''
40
- # File.open('voices.yml') do |f| yaml_voices << f.read end
41
- # voices = YAML.load(yaml_voices)
42
- # @male_voice = voices['male']
43
- # @female_voice = voices['female']
44
- # Commented voices are Apple built-in voices. Can be changed by replacing the last part e.g.'Vicki' with e.g.'Laura'
45
- # much better female voice from iVox:
46
- # female: com.acapela.iVox.voice.iVoxHeather22k
47
- # much better male voice from iVox:
48
- # male: com.acapela.iVox.voice.iVoxRyan22k
49
- # female: com.apple.speech.synthesis.voice.Vicki
50
- # male: com.apple.speech.synthesis.voice.Bruce
30
+ # TODO
51
31
  end
52
32
 
53
33
  # Initialize and "parse" the
@@ -61,7 +41,7 @@ module James
61
41
  # Start recognizing words.
62
42
  #
63
43
  def start_input
64
- @input = Inputs::Audio.new self
44
+ @input = Inputs::Terminal.new self
65
45
  @input.listen
66
46
  end
67
47
  # Start speaking.
@@ -16,6 +16,10 @@ module James
16
16
  #
17
17
  module Dialogue; end
18
18
 
19
+ # We don't care about the spelling.
20
+ #
21
+ Dialog = Dialogue
22
+
19
23
  class << self
20
24
 
21
25
  def dialogue &block
@@ -1,7 +1,3 @@
1
- require File.expand_path '../state_api', __FILE__
2
- require File.expand_path '../state_internals', __FILE__
3
- require File.expand_path '../dialogues', __FILE__
4
-
5
1
  module James
6
2
 
7
3
  # A dialogue is just a container object
@@ -54,8 +50,4 @@ module James
54
50
 
55
51
  end
56
52
 
57
- # We don't care about the spelling.
58
- #
59
- Dialog = Dialogue
60
-
61
53
  end
@@ -1,5 +1,3 @@
1
- require File.expand_path '../visitor', __FILE__
2
-
3
1
  module James
4
2
 
5
3
  # Registers all dialogues and connects their states.
@@ -42,6 +40,8 @@ module James
42
40
  end
43
41
  end
44
42
 
43
+ # Hook all user dialogues into the initial state.
44
+ #
45
45
  initial.hear resolved_entries
46
46
  end
47
47
 
@@ -26,6 +26,8 @@ module James
26
26
 
27
27
  # Callback method from the speech interface.
28
28
  #
29
+ # Note: Uses a MacRuby only form.
30
+ #
29
31
  def speechRecognizer sender, didRecognizeCommand: command
30
32
  heard command
31
33
  end
@@ -10,6 +10,8 @@ module James
10
10
  @controller = controller
11
11
  end
12
12
 
13
+ # Call this method if you heard something in the subclass.
14
+ #
13
15
  def heard command
14
16
  # Call dialogue.
15
17
  #
@@ -7,6 +7,7 @@ module James
7
7
  class Terminal < Base
8
8
 
9
9
  def listen
10
+ sleep 2
10
11
  loop do
11
12
  possibilities = controller.expects
12
13
  puts "Possibilities:\n #{possibilities.join("\n ")}"
@@ -43,7 +43,7 @@ module James
43
43
  #
44
44
  def hear transitions
45
45
  transitions = { transitions => name } unless transitions.respond_to?(:to_hash)
46
- @transitions = expand transitions
46
+ @transitions.merge! expand(transitions)
47
47
  end
48
48
 
49
49
  # Execute this block when entering this state.
@@ -13,11 +13,14 @@ module James
13
13
 
14
14
  # Returns the next state for the given phrase.
15
15
  #
16
- # It accesses the context (Dialog(ue)) to get a full object state.
16
+ # It accesses the context (aka Dialog(ue)) to get a full object state.
17
+ #
18
+ # If it is a Symbol, James will try to get the real state.
19
+ # If not, it will just return it (a State already, or lambda).
17
20
  #
18
21
  def next_for phrase
19
22
  state = self.transitions[phrase]
20
- state.respond_to?(:phrases) ? state : context.state_for(state)
23
+ state.respond_to?(:id2name) ? context.state_for(state) : state
21
24
  end
22
25
 
23
26
  def __into__
@@ -1,5 +1,3 @@
1
- require File.expand_path '../timer', __FILE__
2
-
3
1
  module James
4
2
 
5
3
  # The visitor knows where in the conversation we are.
@@ -46,10 +44,15 @@ module James
46
44
  result
47
45
  end
48
46
  def transition phrase
49
- self.current = current.next_for phrase
47
+ state_or_lambda = current.next_for phrase
48
+ if state_or_lambda.respond_to?(:call)
49
+ state_or_lambda.call # Don't transition.
50
+ else
51
+ self.current = state_or_lambda
52
+ end
50
53
  end
51
54
  def check
52
- escape && yield("That led nowhere.") unless current
55
+ escape && yield("That led nowhere.") unless self.current
53
56
  end
54
57
  def hear phrase, &block
55
58
  return unless hears? phrase
@@ -2,6 +2,8 @@
2
2
  #
3
3
  require File.expand_path '../../../../lib/james/dialogue_api', __FILE__
4
4
  require File.expand_path '../../../../lib/james/dialogue_internals', __FILE__
5
+ require File.expand_path '../../../../lib/james/builtin/core_dialogue', __FILE__
6
+ require File.expand_path '../../../../lib/james/dialogues', __FILE__
5
7
 
6
8
  describe James::Dialogue do
7
9
 
@@ -76,6 +76,17 @@ describe James::State do
76
76
  end
77
77
  end
78
78
 
79
+ context 'with a returning transition' do
80
+ let(:state) do
81
+ described_class.new :some_name, @context do
82
+ hear 'transition one' => lambda { "I do this and return to :some_name" }
83
+ end
84
+ end
85
+ describe 'phrases' do
86
+ it { state.phrases.should == ['transition one'] }
87
+ end
88
+ end
89
+
79
90
  context 'with 1 transition and into and exit' do
80
91
  let(:state) do
81
92
  described_class.new :some_name, @context do
@@ -106,12 +117,12 @@ describe James::State do
106
117
  end
107
118
  end
108
119
 
109
- context 'with multiple transition' do
120
+ context 'with multiple transition and separate hears' do
110
121
  let(:state) do
111
122
  described_class.new :some_name, @context do
112
123
  hear 'transition one' => :next_state1,
113
- 'transition two' => :next_state2,
114
- 'transition three' => :next_state3
124
+ 'transition two' => :next_state2
125
+ hear 'transition three' => :next_state3
115
126
  end
116
127
  end
117
128
  describe 'phrases' do
@@ -121,6 +132,24 @@ describe James::State do
121
132
  it { state.to_s.should == 'James::State(some_name, some_context, {"transition one"=>:next_state1, "transition two"=>:next_state2, "transition three"=>:next_state3})' }
122
133
  end
123
134
  it { state.next_for('transition two').should == :some_state_object2 }
135
+ it { state.next_for('transition three').should == :some_state_object3 }
136
+ it { state.next_for('non-existent').should == nil }
137
+ end
138
+
139
+ context 'with self-transitions' do
140
+ some_proc = ->(){ "Going back to where I came from" }
141
+ let(:state) do
142
+ described_class.new :some_name, @context do
143
+ hear 'transition one' => some_proc
144
+ end
145
+ end
146
+ describe 'phrases' do
147
+ it { state.phrases.should == ['transition one'] }
148
+ end
149
+ describe 'to_s' do
150
+ it { state.to_s.should == "James::State(some_name, some_context, {\"transition one\"=>#{some_proc}})" }
151
+ end
152
+ it { state.next_for('transition one').should == some_proc }
124
153
  it { state.next_for('non-existent').should == nil }
125
154
  end
126
155
 
@@ -45,11 +45,20 @@ describe James::Visitors do
45
45
  end
46
46
 
47
47
  describe 'expects' do
48
- before(:each) do
49
- first.stub! :expects => [:a, :b]
50
- second.stub! :expects => [:c, :d, :e]
48
+ context 'chainable' do
49
+ before(:each) do
50
+ first.stub! :expects => [:a, :b], :chainable? => true
51
+ second.stub! :expects => [:c, :d, :e], :chainable? => true
52
+ end
53
+ it { visitors.expects.should == [:a, :b, :c, :d, :e] }
54
+ end
55
+ context 'not chainable' do
56
+ before(:each) do
57
+ first.stub! :expects => [:a, :b], :chainable? => false
58
+ second.stub! :expects => [:c, :d, :e], :chainable? => false
59
+ end
60
+ it { visitors.expects.should == [:a, :b] }
51
61
  end
52
- it { visitors.expects.should == [:a, :b, :c, :d, :e] }
53
62
  end
54
63
 
55
64
  end
metadata CHANGED
@@ -1,38 +1,32 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: james
3
3
  version: !ruby/object:Gem::Version
4
- prerelease: false
5
- segments:
6
- - 0
7
- - 0
8
- - 6
9
- version: 0.0.6
4
+ prerelease:
5
+ version: 0.0.7
10
6
  platform: universal-darwin-10
11
7
  authors:
12
8
  - Florian Hanke
13
9
  autorequire:
14
10
  bindir: bin
15
11
  cert_chain: []
16
- date: 2011-05-10 00:00:00 +10:00
12
+ date: 2011-05-13 00:00:00 +10:00
17
13
  default_executable: james
18
14
  dependencies:
19
15
  - !ruby/object:Gem::Dependency
20
16
  name: rspec
21
17
  prerelease: false
22
18
  requirement: !ruby/object:Gem::Requirement
19
+ none: false
23
20
  requirements:
24
21
  - - '>='
25
22
  - !ruby/object:Gem::Version
26
- segments:
27
- - 0
28
23
  version: "0"
29
24
  type: :development
30
25
  version_requirements: !ruby/object:Gem::Requirement
26
+ none: false
31
27
  requirements:
32
28
  - - '>='
33
29
  - !ruby/object:Gem::Version
34
- segments:
35
- - 0
36
30
  version: "0"
37
31
  description: Modular Electronic Butler. Using a simple dialogue system where you can
38
32
  easily add more dialogues.
@@ -58,9 +52,15 @@ files:
58
52
  - lib/james/visitor.rb
59
53
  - lib/james/visitors.rb
60
54
  - lib/james.rb
61
- - lib/old/main.rb
62
- - lib/old/main_dialogue.rb
63
55
  - 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/dialogue_spec.rb
60
+ - spec/lib/james/state_spec.rb
61
+ - spec/lib/james/visitor_spec.rb
62
+ - spec/lib/james/visitors_spec.rb
63
+ - bin/james
64
64
  has_rdoc: true
65
65
  homepage: http://floere.github.com/james
66
66
  licenses: []
@@ -69,22 +69,20 @@ rdoc_options: []
69
69
  require_paths:
70
70
  - lib
71
71
  required_ruby_version: !ruby/object:Gem::Requirement
72
+ none: false
72
73
  requirements:
73
74
  - - '>='
74
75
  - !ruby/object:Gem::Version
75
- segments:
76
- - 0
77
76
  version: "0"
78
77
  required_rubygems_version: !ruby/object:Gem::Requirement
78
+ none: false
79
79
  requirements:
80
80
  - - '>='
81
81
  - !ruby/object:Gem::Version
82
- segments:
83
- - 0
84
82
  version: "0"
85
83
  requirements: []
86
84
  rubyforge_project:
87
- rubygems_version: 1.3.6
85
+ rubygems_version: 1.4.2
88
86
  signing_key:
89
87
  specification_version: 3
90
88
  summary: 'James: Modular Electronic Butler with modular Dialogues.'
@@ -1,30 +0,0 @@
1
- # Instead of this, check for __main__.
2
- #
3
-
4
- require File.expand_path '../../james', __FILE__
5
-
6
- app = NSApplication.sharedApplication
7
- app.delegate = James::Controller.new
8
-
9
- # window = NSWindow.alloc.initWithContentRect([200, 300, 300, 100],
10
- # styleMask:NSTitledWindowMask|NSClosableWindowMask|NSMiniaturizableWindowMask,
11
- # backing:NSBackingStoreBuffered,
12
- # defer:false)
13
- # window.title = 'MacRuby: The Definitive Guide'
14
- # window.level = 3
15
- # window.delegate = app.delegate
16
- #
17
- # button = NSButton.alloc.initWithFrame([80, 10, 120, 80])
18
- # button.bezelStyle = 4
19
- # button.title = 'Hello World!'
20
- # button.target = app.delegate
21
- # button.action = 'say_hello:'
22
- #
23
- # window.contentView.addSubview(button)
24
- #
25
- # window.display
26
- # window.orderFrontRegardless
27
-
28
- app.delegate.applicationDidFinishLaunching nil
29
-
30
- app.run
@@ -1,47 +0,0 @@
1
- module James
2
-
3
- # This is the default main dialogue.
4
- #
5
- # It is a proxy to the internal dialogues.
6
- #
7
- class MainDialogue
8
- include Dialogue
9
-
10
- hear 'wake up james' => :awake
11
-
12
- # Create Protostate with block. Then, create instance instance_evaling block.
13
- #
14
- state :awake do
15
- hear 'sleep james' => :sleeping
16
- hear 'my options?' { |the_next| the_next.phrases.join ' ' }
17
- into { "At your service" }
18
- exit { "Right away" }
19
- end
20
-
21
- state :sleeping do
22
- hear 'wake up james' => :awake
23
- into { "Good night, Sir" }
24
- end
25
-
26
- # state :awake, {
27
- # 'sleep james' => :sleeping
28
- # # 'james, my options?' => lambda { || } # stays in this state but executes.
29
- # }
30
- # state :sleeping, {
31
- # 'wake up james' => :awake
32
- # }
33
- # entry 'wake up james' => :awake
34
- #
35
- # def enter_sleeping
36
- # "Good night, Sir"
37
- # end
38
- # def enter_awake
39
- # "At your service"
40
- # end
41
- # def exit_awake
42
- # "Right away"
43
- # end
44
-
45
- end
46
-
47
- end