punchblock 0.8.0 → 0.8.1

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.md CHANGED
@@ -1,5 +1,8 @@
1
1
  # develop
2
2
 
3
+ # v0.8.1 - 2012-01-09
4
+ * Feature: Support DTMF Input components on Asterisk
5
+
3
6
  # v0.8.0 - 2012-01-06
4
7
  * Feature: Expose Blather's connection timeout config when creating a Connection::XMPP
5
8
  * Bugfix: Remove some deprecated Tropo extension components
@@ -315,6 +315,10 @@ module Punchblock
315
315
  read_attr :mode, :to_sym
316
316
  end
317
317
 
318
+ def mode=(other)
319
+ write_attr :mode, other
320
+ end
321
+
318
322
  ##
319
323
  # @return [Float] A measure of the confidence of the result, between 0-1
320
324
  #
@@ -322,23 +326,55 @@ module Punchblock
322
326
  read_attr :confidence, :to_f
323
327
  end
324
328
 
329
+ def confidence=(other)
330
+ write_attr :confidence, other
331
+ end
332
+
325
333
  ##
326
334
  # @return [String] An intelligent interpretation of the meaning of the response.
327
335
  #
328
336
  def interpretation
329
- find_first('//ns:interpretation', :ns => self.registered_ns).text
337
+ interpretation_node.text
338
+ end
339
+
340
+ def interpretation=(other)
341
+ interpretation_node.content = other
330
342
  end
331
343
 
332
344
  ##
333
345
  # @return [String] The exact response gained
334
346
  #
335
347
  def utterance
336
- find_first('//ns:utterance', :ns => self.registered_ns).text
348
+ utterance_node.text
349
+ end
350
+
351
+ def utterance=(other)
352
+ utterance_node.content = other
337
353
  end
338
354
 
339
355
  def inspect_attributes # :nodoc:
340
356
  [:mode, :confidence, :interpretation, :utterance] + super
341
357
  end
358
+
359
+ private
360
+
361
+ def interpretation_node
362
+ child_node_with_name 'interpretation'
363
+ end
364
+
365
+ def utterance_node
366
+ child_node_with_name 'utterance'
367
+ end
368
+
369
+ def child_node_with_name(name)
370
+ node = find_first "ns:#{name}", :ns => self.class.registered_ns
371
+
372
+ unless node
373
+ self << (node = RayoNode.new(name, self.document))
374
+ node.namespace = self.class.registered_ns
375
+ end
376
+ node
377
+ end
342
378
  end
343
379
 
344
380
  class NoMatch < Event::Complete::Reason
@@ -4,6 +4,7 @@ module Punchblock
4
4
  module Translator
5
5
  class Asterisk
6
6
  class Call
7
+ include HasGuardedHandlers
7
8
  include Celluloid
8
9
 
9
10
  attr_reader :id, :channel, :translator, :agi_env
@@ -42,6 +43,7 @@ module Punchblock
42
43
  pb_logger.debug "Could not find component for AMI event: #{ami_event}"
43
44
  end
44
45
  end
46
+ trigger_handler :ami, ami_event
45
47
  end
46
48
 
47
49
  def execute_command(command)
@@ -66,6 +68,8 @@ module Punchblock
66
68
  execute_agi_command command
67
69
  when Punchblock::Component::Output
68
70
  execute_component Component::Asterisk::Output, command
71
+ when Punchblock::Component::Input
72
+ execute_component Component::Asterisk::Input, command
69
73
  end
70
74
  end
71
75
 
@@ -0,0 +1,116 @@
1
+ module Punchblock
2
+ module Translator
3
+ class Asterisk
4
+ module Component
5
+ module Asterisk
6
+ class Input < Component
7
+
8
+ attr_reader :grammar, :buffer
9
+
10
+ def setup
11
+ @media_engine = call.translator.media_engine
12
+ @buffer = ""
13
+ end
14
+
15
+ def execute
16
+ initial_timeout = @component_node.initial_timeout || -1
17
+ @inter_digit_timeout = @component_node.inter_digit_timeout || -1
18
+
19
+ return with_error 'option error', 'A grammar document is required.' unless @component_node.grammar
20
+ return with_error 'option error', 'A mode value other than DTMF is unsupported on Asterisk.' unless @component_node.mode == :dtmf
21
+ return with_error 'option error', 'An initial timeout value that is negative (and not -1) is invalid.' unless initial_timeout >= -1
22
+ return with_error 'option error', 'An inter-digit timeout value that is negative (and not -1) is invalid.' unless @inter_digit_timeout >= -1
23
+
24
+ send_ref
25
+
26
+ case @media_engine
27
+ when :asterisk, nil
28
+ @grammar = @component_node.grammar.value.clone
29
+ grammar.inline!
30
+ grammar.tokenize!
31
+ grammar.normalize_whitespace
32
+
33
+ begin_initial_timer initial_timeout/1000 unless initial_timeout == -1
34
+
35
+ component = current_actor
36
+
37
+ @active = true
38
+
39
+ call.register_handler :ami, :name => 'DTMF' do |event|
40
+ component.process_dtmf! event['Digit'] if event['End'] == 'Yes'
41
+ end
42
+ end
43
+ end
44
+
45
+ def process_dtmf(digit)
46
+ return unless @active
47
+ pb_logger.trace "Processing incoming DTMF digit #{digit}"
48
+ buffer << digit
49
+ cancel_initial_timer
50
+ case (match = grammar.match buffer.dup)
51
+ when RubySpeech::GRXML::Match
52
+ pb_logger.trace "Found a match against buffer #{buffer}"
53
+ complete success_reason(match)
54
+ when RubySpeech::GRXML::NoMatch
55
+ pb_logger.trace "Buffer #{buffer} does not match grammar"
56
+ complete Punchblock::Component::Input::Complete::NoMatch.new
57
+ when RubySpeech::GRXML::PotentialMatch
58
+ pb_logger.trace "Buffer #{buffer} potentially matches grammar. Waiting..."
59
+ reset_inter_digit_timer
60
+ end
61
+ end
62
+
63
+ private
64
+
65
+ def begin_initial_timer(timeout)
66
+ pb_logger.trace "Setting initial timer for #{timeout} seconds"
67
+ @initial_timer = after timeout do
68
+ pb_logger.trace "Initial timer expired."
69
+ complete Punchblock::Component::Input::Complete::NoInput.new
70
+ end
71
+ end
72
+
73
+ def cancel_initial_timer
74
+ return unless @initial_timer
75
+ @initial_timer.cancel
76
+ @initial_timer = nil
77
+ end
78
+
79
+ def reset_inter_digit_timer
80
+ return if @inter_digit_timeout == -1
81
+ @inter_digit_timer ||= begin
82
+ pb_logger.trace "Setting inter-digit timer for #{@inter_digit_timeout/1000} seconds"
83
+ after @inter_digit_timeout/1000 do
84
+ pb_logger.trace "Inter digit-timer expired."
85
+ complete Punchblock::Component::Input::Complete::NoMatch.new
86
+ end
87
+ end
88
+ pb_logger.trace "Resetting inter-digit timer"
89
+ @inter_digit_timer.reset
90
+ end
91
+
92
+ def cancel_inter_digit_timer
93
+ return unless @inter_digit_timer
94
+ @inter_digit_timer.cancel
95
+ @inter_digit_timer = nil
96
+ end
97
+
98
+ def success_reason(match)
99
+ Punchblock::Component::Input::Complete::Success.new :mode => match.mode,
100
+ :confidence => match.confidence,
101
+ :utterance => match.utterance,
102
+ :interpretation => match.interpretation
103
+ end
104
+
105
+ def complete(reason)
106
+ @active = false
107
+ cancel_initial_timer
108
+ cancel_inter_digit_timer
109
+ send_event complete_event(reason)
110
+ end
111
+ end
112
+ end
113
+ end
114
+ end
115
+ end
116
+ end
@@ -7,6 +7,7 @@ module Punchblock
7
7
 
8
8
  autoload :AGICommand
9
9
  autoload :AMIAction
10
+ autoload :Input
10
11
  autoload :Output
11
12
  end
12
13
  end
@@ -9,7 +9,7 @@ module Punchblock
9
9
  class Component
10
10
  include Celluloid
11
11
 
12
- attr_reader :id
12
+ attr_reader :id, :call
13
13
 
14
14
  def initialize(component_node, call = nil)
15
15
  @component_node, @call = component_node, call
@@ -1,3 +1,3 @@
1
1
  module Punchblock
2
- VERSION = "0.8.0"
2
+ VERSION = "0.8.1"
3
3
  end
data/punchblock.gemspec CHANGED
@@ -30,10 +30,10 @@ Gem::Specification.new do |s|
30
30
  s.add_runtime_dependency %q<has-guarded-handlers>, [">= 0.1.0"]
31
31
  s.add_runtime_dependency %q<celluloid>, [">= 0.6.0"]
32
32
  s.add_runtime_dependency %q<ruby_ami>, [">= 0.1.3"]
33
- s.add_runtime_dependency %q<ruby_speech>, [">= 0.3.4"]
33
+ s.add_runtime_dependency %q<ruby_speech>, [">= 0.5.1"]
34
34
 
35
35
  s.add_development_dependency %q<bundler>, ["~> 1.0.0"]
36
- s.add_development_dependency %q<rspec>, [">= 2.5.0"]
36
+ s.add_development_dependency %q<rspec>, ["~> 2.7.0"]
37
37
  s.add_development_dependency %q<ci_reporter>, [">= 1.6.3"]
38
38
  s.add_development_dependency %q<yard>, ["~> 0.6.0"]
39
39
  s.add_development_dependency %q<rcov>, [">= 0"]
@@ -192,6 +192,21 @@ module Punchblock
192
192
  its(:confidence) { should == 0.45 }
193
193
  its(:interpretation) { should == '1234' }
194
194
  its(:utterance) { should == 'one two three four' }
195
+
196
+ describe "when setting options in initializer" do
197
+ subject do
198
+ Input::Complete::Success.new :mode => :dtmf,
199
+ :confidence => 1,
200
+ :utterance => '123',
201
+ :interpretation => 'dtmf-1 dtmf-2 dtmf-3'
202
+ end
203
+
204
+
205
+ its(:mode) { should == :dtmf }
206
+ its(:confidence) { should == 1 }
207
+ its(:utterance) { should == '123' }
208
+ its(:interpretation) { should == 'dtmf-1 dtmf-2 dtmf-3' }
209
+ end
195
210
  end
196
211
 
197
212
  describe Input::Complete::NoMatch do
@@ -130,6 +130,28 @@ module Punchblock
130
130
  subject.process_ami_event ami_event
131
131
  end
132
132
  end
133
+
134
+ context 'with a handler registered for a matching event' do
135
+ let :ami_event do
136
+ RubyAMI::Event.new('DTMF').tap do |e|
137
+ e['Digit'] = '4'
138
+ e['Start'] = 'Yes'
139
+ e['End'] = 'No'
140
+ e['Uniqueid'] = "1320842458.8"
141
+ e['Channel'] = "SIP/1234-00000000"
142
+ end
143
+ end
144
+
145
+ let(:response) { mock 'Response' }
146
+
147
+ it 'should execute the handler' do
148
+ response.expects(:call).once.with ami_event
149
+ subject.register_handler :ami, :name => 'DTMF' do |event|
150
+ response.call event
151
+ end
152
+ subject.process_ami_event ami_event
153
+ end
154
+ end
133
155
  end
134
156
 
135
157
  describe '#execute_command' do
@@ -211,6 +233,20 @@ module Punchblock
211
233
  end
212
234
  end
213
235
 
236
+ context 'with an Input component' do
237
+ let :command do
238
+ Punchblock::Component::Input.new
239
+ end
240
+
241
+ let(:mock_action) { mock 'Component::Asterisk::Input', :id => 'foo' }
242
+
243
+ it 'should create an AGI command component actor and execute it asynchronously' do
244
+ Component::Asterisk::Input.expects(:new).once.with(command, subject).returns mock_action
245
+ mock_action.expects(:execute!).once
246
+ subject.execute_command command
247
+ end
248
+ end
249
+
214
250
  context 'with a component command' do
215
251
  let(:component_id) { 'foobar' }
216
252
 
@@ -0,0 +1,268 @@
1
+ require 'spec_helper'
2
+
3
+ module Punchblock
4
+ module Translator
5
+ class Asterisk
6
+ module Component
7
+ module Asterisk
8
+ describe Input do
9
+ let(:media_engine) { nil }
10
+ let(:translator) { Punchblock::Translator::Asterisk.new mock('AMI'), mock('Client'), media_engine }
11
+ let(:call) { Punchblock::Translator::Asterisk::Call.new 'foo', translator }
12
+ let(:command_options) { nil }
13
+
14
+ let :command do
15
+ Punchblock::Component::Input.new command_options
16
+ end
17
+
18
+ let :grammar do
19
+ RubySpeech::GRXML.draw :mode => 'dtmf', :root => 'pin' do
20
+ rule id: 'digit' do
21
+ one_of do
22
+ 0.upto(9) { |d| item { d.to_s } }
23
+ end
24
+ end
25
+
26
+ rule id: 'pin', scope: 'public' do
27
+ item repeat: '2' do
28
+ ruleref uri: '#digit'
29
+ end
30
+ end
31
+ end
32
+ end
33
+
34
+ let :command_options do
35
+ {
36
+
37
+ }
38
+ end
39
+
40
+ subject { Input.new command, call }
41
+
42
+ describe '#execute' do
43
+ before { command.request! }
44
+
45
+ context 'with a media engine of :unimrcp' do
46
+ pending
47
+ let(:media_engine) { :unimrcp }
48
+ end
49
+
50
+ context 'with a media engine of :asterisk' do
51
+ let(:media_engine) { :asterisk }
52
+
53
+ let(:command_opts) { {} }
54
+
55
+ let :command_options do
56
+ { :mode => :dtmf, :grammar => { :value => grammar } }.merge(command_opts)
57
+ end
58
+
59
+ def ami_event_for_dtmf(digit, position)
60
+ RubyAMI::Event.new('DTMF').tap do |e|
61
+ e['Digit'] = digit.to_s
62
+ e['Start'] = position == :start ? 'Yes' : 'No'
63
+ e['End'] = position == :end ? 'Yes' : 'No'
64
+ end
65
+ end
66
+
67
+ def send_ami_events_for_dtmf(digit)
68
+ call.process_ami_event ami_event_for_dtmf(digit, :start)
69
+ call.process_ami_event ami_event_for_dtmf(digit, :end)
70
+ end
71
+
72
+ let(:reason) { command.complete_event(5).reason }
73
+
74
+ describe "receiving DTMF events" do
75
+ before { subject.execute }
76
+
77
+ context "when a match is found" do
78
+ before do
79
+ send_ami_events_for_dtmf 1
80
+ send_ami_events_for_dtmf 2
81
+ end
82
+
83
+ it "should send a success complete event with the relevant data" do
84
+ reason.should == Punchblock::Component::Input::Complete::Success.new(:mode => :dtmf, :confidence => 1, :utterance => '12', :interpretation => 'dtmf-1 dtmf-2', :component_id => subject.id)
85
+ end
86
+ end
87
+
88
+ context "when the match is invalid" do
89
+ before do
90
+ send_ami_events_for_dtmf 1
91
+ send_ami_events_for_dtmf '#'
92
+ end
93
+
94
+ it "should send a nomatch complete event" do
95
+ reason.should == Punchblock::Component::Input::Complete::NoMatch.new(:component_id => subject.id)
96
+ end
97
+ end
98
+ end
99
+
100
+ describe 'grammar' do
101
+ context 'unset' do
102
+ let(:command_opts) { { :grammar => nil } }
103
+ it "should return an error and not execute any actions" do
104
+ subject.execute
105
+ error = ProtocolError.new 'option error', 'A grammar document is required.'
106
+ command.response(0.1).should == error
107
+ end
108
+ end
109
+ end
110
+
111
+ describe 'mode' do
112
+ context 'unset' do
113
+ let(:command_opts) { { :mode => nil } }
114
+ it "should return an error and not execute any actions" do
115
+ subject.execute
116
+ error = ProtocolError.new 'option error', 'A mode value other than DTMF is unsupported on Asterisk.'
117
+ command.response(0.1).should == error
118
+ end
119
+ end
120
+
121
+ context 'any' do
122
+ let(:command_opts) { { :mode => :any } }
123
+ it "should return an error and not execute any actions" do
124
+ subject.execute
125
+ error = ProtocolError.new 'option error', 'A mode value other than DTMF is unsupported on Asterisk.'
126
+ command.response(0.1).should == error
127
+ end
128
+ end
129
+
130
+ context 'speech' do
131
+ let(:command_opts) { { :mode => :speech } }
132
+ it "should return an error and not execute any actions" do
133
+ subject.execute
134
+ error = ProtocolError.new 'option error', 'A mode value other than DTMF is unsupported on Asterisk.'
135
+ command.response(0.1).should == error
136
+ end
137
+ end
138
+ end
139
+
140
+ describe 'terminator' do
141
+ pending
142
+ end
143
+
144
+ describe 'recognizer' do
145
+ pending
146
+ end
147
+
148
+ describe 'initial-timeout' do
149
+ context 'a positive number' do
150
+ let(:command_opts) { { :initial_timeout => 1000 } }
151
+
152
+ it "should not cause a NoInput if first input is received in time" do
153
+ subject.execute
154
+ send_ami_events_for_dtmf 1
155
+ sleep 1.5
156
+ send_ami_events_for_dtmf 2
157
+ reason.should be_a Punchblock::Component::Input::Complete::Success
158
+ end
159
+
160
+ it "should cause a NoInput complete event to be sent after the timeout" do
161
+ subject.execute
162
+ sleep 1.5
163
+ send_ami_events_for_dtmf 1
164
+ send_ami_events_for_dtmf 2
165
+ reason.should be_a Punchblock::Component::Input::Complete::NoInput
166
+ end
167
+ end
168
+
169
+ context '-1' do
170
+ let(:command_opts) { { :initial_timeout => -1 } }
171
+
172
+ it "should not start a timer" do
173
+ subject.wrapped_object.expects(:begin_initial_timer).never
174
+ subject.execute
175
+ end
176
+ end
177
+
178
+ context 'unset' do
179
+ let(:command_opts) { { :initial_timeout => nil } }
180
+
181
+ it "should not start a timer" do
182
+ subject.wrapped_object.expects(:begin_initial_timer).never
183
+ subject.execute
184
+ end
185
+ end
186
+
187
+ context 'a negative number other than -1' do
188
+ let(:command_opts) { { :initial_timeout => -1000 } }
189
+
190
+ it "should return an error and not execute any actions" do
191
+ subject.execute
192
+ error = ProtocolError.new 'option error', 'An initial timeout value that is negative (and not -1) is invalid.'
193
+ command.response(0.1).should == error
194
+ end
195
+ end
196
+ end
197
+
198
+ describe 'inter-digit-timeout' do
199
+ context 'a positive number' do
200
+ let(:command_opts) { { :inter_digit_timeout => 1000 } }
201
+
202
+ it "should not prevent a Match if input is received in time" do
203
+ subject.execute
204
+ sleep 1.5
205
+ send_ami_events_for_dtmf 1
206
+ sleep 0.5
207
+ send_ami_events_for_dtmf 2
208
+ reason.should be_a Punchblock::Component::Input::Complete::Success
209
+ end
210
+
211
+ it "should cause a NoMatch complete event to be sent after the timeout" do
212
+ subject.execute
213
+ sleep 1.5
214
+ send_ami_events_for_dtmf 1
215
+ sleep 1.5
216
+ send_ami_events_for_dtmf 2
217
+ reason.should be_a Punchblock::Component::Input::Complete::NoMatch
218
+ end
219
+ end
220
+
221
+ context '-1' do
222
+ let(:command_opts) { { :inter_digit_timeout => -1 } }
223
+
224
+ it "should not start a timer" do
225
+ subject.wrapped_object.expects(:begin_inter_digit_timer).never
226
+ subject.execute
227
+ end
228
+ end
229
+
230
+ context 'unset' do
231
+ let(:command_opts) { { :inter_digit_timeout => nil } }
232
+
233
+ it "should not start a timer" do
234
+ subject.wrapped_object.expects(:begin_inter_digit_timer).never
235
+ subject.execute
236
+ end
237
+ end
238
+
239
+ context 'a negative number other than -1' do
240
+ let(:command_opts) { { :inter_digit_timeout => -1000 } }
241
+
242
+ it "should return an error and not execute any actions" do
243
+ subject.execute
244
+ error = ProtocolError.new 'option error', 'An inter-digit timeout value that is negative (and not -1) is invalid.'
245
+ command.response(0.1).should == error
246
+ end
247
+ end
248
+ end
249
+
250
+ describe 'sensitivity' do
251
+ pending
252
+ end
253
+
254
+ describe 'min-confidence' do
255
+ pending
256
+ end
257
+
258
+ describe 'max-silence' do
259
+ pending
260
+ end
261
+ end
262
+ end
263
+ end
264
+ end
265
+ end
266
+ end
267
+ end
268
+ end
data/spec/spec_helper.rb CHANGED
@@ -10,6 +10,10 @@ RSpec.configure do |config|
10
10
  config.mock_with :mocha
11
11
  config.filter_run :focus => true
12
12
  config.run_all_when_everything_filtered = true
13
+
14
+ config.before :suite do |variable|
15
+ Punchblock.logger = Logger.new(STDOUT)
16
+ end
13
17
  end
14
18
 
15
19
  def parse_stanza(xml)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: punchblock
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.0
4
+ version: 0.8.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -11,11 +11,11 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2012-01-06 00:00:00.000000000 Z
14
+ date: 2012-01-09 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: niceogiri
18
- requirement: &2152641860 !ruby/object:Gem::Requirement
18
+ requirement: &2156232960 !ruby/object:Gem::Requirement
19
19
  none: false
20
20
  requirements:
21
21
  - - ! '>='
@@ -23,10 +23,10 @@ dependencies:
23
23
  version: 0.0.4
24
24
  type: :runtime
25
25
  prerelease: false
26
- version_requirements: *2152641860
26
+ version_requirements: *2156232960
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: blather
29
- requirement: &2152641280 !ruby/object:Gem::Requirement
29
+ requirement: &2156230140 !ruby/object:Gem::Requirement
30
30
  none: false
31
31
  requirements:
32
32
  - - ! '>='
@@ -34,10 +34,10 @@ dependencies:
34
34
  version: 0.5.12
35
35
  type: :runtime
36
36
  prerelease: false
37
- version_requirements: *2152641280
37
+ version_requirements: *2156230140
38
38
  - !ruby/object:Gem::Dependency
39
39
  name: activesupport
40
- requirement: &2152640660 !ruby/object:Gem::Requirement
40
+ requirement: &2156227040 !ruby/object:Gem::Requirement
41
41
  none: false
42
42
  requirements:
43
43
  - - ! '>='
@@ -45,10 +45,10 @@ dependencies:
45
45
  version: 2.1.0
46
46
  type: :runtime
47
47
  prerelease: false
48
- version_requirements: *2152640660
48
+ version_requirements: *2156227040
49
49
  - !ruby/object:Gem::Dependency
50
50
  name: state_machine
51
- requirement: &2152639980 !ruby/object:Gem::Requirement
51
+ requirement: &2156223280 !ruby/object:Gem::Requirement
52
52
  none: false
53
53
  requirements:
54
54
  - - ! '>='
@@ -56,10 +56,10 @@ dependencies:
56
56
  version: 1.0.1
57
57
  type: :runtime
58
58
  prerelease: false
59
- version_requirements: *2152639980
59
+ version_requirements: *2156223280
60
60
  - !ruby/object:Gem::Dependency
61
61
  name: future-resource
62
- requirement: &2152639220 !ruby/object:Gem::Requirement
62
+ requirement: &2156218340 !ruby/object:Gem::Requirement
63
63
  none: false
64
64
  requirements:
65
65
  - - ! '>='
@@ -67,10 +67,10 @@ dependencies:
67
67
  version: 0.0.2
68
68
  type: :runtime
69
69
  prerelease: false
70
- version_requirements: *2152639220
70
+ version_requirements: *2156218340
71
71
  - !ruby/object:Gem::Dependency
72
72
  name: has-guarded-handlers
73
- requirement: &2152638480 !ruby/object:Gem::Requirement
73
+ requirement: &2156212420 !ruby/object:Gem::Requirement
74
74
  none: false
75
75
  requirements:
76
76
  - - ! '>='
@@ -78,10 +78,10 @@ dependencies:
78
78
  version: 0.1.0
79
79
  type: :runtime
80
80
  prerelease: false
81
- version_requirements: *2152638480
81
+ version_requirements: *2156212420
82
82
  - !ruby/object:Gem::Dependency
83
83
  name: celluloid
84
- requirement: &2152637500 !ruby/object:Gem::Requirement
84
+ requirement: &2156209840 !ruby/object:Gem::Requirement
85
85
  none: false
86
86
  requirements:
87
87
  - - ! '>='
@@ -89,10 +89,10 @@ dependencies:
89
89
  version: 0.6.0
90
90
  type: :runtime
91
91
  prerelease: false
92
- version_requirements: *2152637500
92
+ version_requirements: *2156209840
93
93
  - !ruby/object:Gem::Dependency
94
94
  name: ruby_ami
95
- requirement: &2152636520 !ruby/object:Gem::Requirement
95
+ requirement: &2156188760 !ruby/object:Gem::Requirement
96
96
  none: false
97
97
  requirements:
98
98
  - - ! '>='
@@ -100,21 +100,21 @@ dependencies:
100
100
  version: 0.1.3
101
101
  type: :runtime
102
102
  prerelease: false
103
- version_requirements: *2152636520
103
+ version_requirements: *2156188760
104
104
  - !ruby/object:Gem::Dependency
105
105
  name: ruby_speech
106
- requirement: &2152635380 !ruby/object:Gem::Requirement
106
+ requirement: &2156167500 !ruby/object:Gem::Requirement
107
107
  none: false
108
108
  requirements:
109
109
  - - ! '>='
110
110
  - !ruby/object:Gem::Version
111
- version: 0.3.4
111
+ version: 0.5.1
112
112
  type: :runtime
113
113
  prerelease: false
114
- version_requirements: *2152635380
114
+ version_requirements: *2156167500
115
115
  - !ruby/object:Gem::Dependency
116
116
  name: bundler
117
- requirement: &2152634520 !ruby/object:Gem::Requirement
117
+ requirement: &2156166720 !ruby/object:Gem::Requirement
118
118
  none: false
119
119
  requirements:
120
120
  - - ~>
@@ -122,21 +122,21 @@ dependencies:
122
122
  version: 1.0.0
123
123
  type: :development
124
124
  prerelease: false
125
- version_requirements: *2152634520
125
+ version_requirements: *2156166720
126
126
  - !ruby/object:Gem::Dependency
127
127
  name: rspec
128
- requirement: &2152633440 !ruby/object:Gem::Requirement
128
+ requirement: &2156165960 !ruby/object:Gem::Requirement
129
129
  none: false
130
130
  requirements:
131
- - - ! '>='
131
+ - - ~>
132
132
  - !ruby/object:Gem::Version
133
- version: 2.5.0
133
+ version: 2.7.0
134
134
  type: :development
135
135
  prerelease: false
136
- version_requirements: *2152633440
136
+ version_requirements: *2156165960
137
137
  - !ruby/object:Gem::Dependency
138
138
  name: ci_reporter
139
- requirement: &2152632400 !ruby/object:Gem::Requirement
139
+ requirement: &2156164980 !ruby/object:Gem::Requirement
140
140
  none: false
141
141
  requirements:
142
142
  - - ! '>='
@@ -144,10 +144,10 @@ dependencies:
144
144
  version: 1.6.3
145
145
  type: :development
146
146
  prerelease: false
147
- version_requirements: *2152632400
147
+ version_requirements: *2156164980
148
148
  - !ruby/object:Gem::Dependency
149
149
  name: yard
150
- requirement: &2152631620 !ruby/object:Gem::Requirement
150
+ requirement: &2156163300 !ruby/object:Gem::Requirement
151
151
  none: false
152
152
  requirements:
153
153
  - - ~>
@@ -155,10 +155,10 @@ dependencies:
155
155
  version: 0.6.0
156
156
  type: :development
157
157
  prerelease: false
158
- version_requirements: *2152631620
158
+ version_requirements: *2156163300
159
159
  - !ruby/object:Gem::Dependency
160
160
  name: rcov
161
- requirement: &2152630600 !ruby/object:Gem::Requirement
161
+ requirement: &2156162340 !ruby/object:Gem::Requirement
162
162
  none: false
163
163
  requirements:
164
164
  - - ! '>='
@@ -166,10 +166,10 @@ dependencies:
166
166
  version: '0'
167
167
  type: :development
168
168
  prerelease: false
169
- version_requirements: *2152630600
169
+ version_requirements: *2156162340
170
170
  - !ruby/object:Gem::Dependency
171
171
  name: rake
172
- requirement: &2152629520 !ruby/object:Gem::Requirement
172
+ requirement: &2156161440 !ruby/object:Gem::Requirement
173
173
  none: false
174
174
  requirements:
175
175
  - - ! '>='
@@ -177,10 +177,10 @@ dependencies:
177
177
  version: '0'
178
178
  type: :development
179
179
  prerelease: false
180
- version_requirements: *2152629520
180
+ version_requirements: *2156161440
181
181
  - !ruby/object:Gem::Dependency
182
182
  name: mocha
183
- requirement: &2152622020 !ruby/object:Gem::Requirement
183
+ requirement: &2156160560 !ruby/object:Gem::Requirement
184
184
  none: false
185
185
  requirements:
186
186
  - - ! '>='
@@ -188,10 +188,10 @@ dependencies:
188
188
  version: '0'
189
189
  type: :development
190
190
  prerelease: false
191
- version_requirements: *2152622020
191
+ version_requirements: *2156160560
192
192
  - !ruby/object:Gem::Dependency
193
193
  name: i18n
194
- requirement: &2152620880 !ruby/object:Gem::Requirement
194
+ requirement: &2156135140 !ruby/object:Gem::Requirement
195
195
  none: false
196
196
  requirements:
197
197
  - - ! '>='
@@ -199,10 +199,10 @@ dependencies:
199
199
  version: '0'
200
200
  type: :development
201
201
  prerelease: false
202
- version_requirements: *2152620880
202
+ version_requirements: *2156135140
203
203
  - !ruby/object:Gem::Dependency
204
204
  name: countdownlatch
205
- requirement: &2152619940 !ruby/object:Gem::Requirement
205
+ requirement: &2156134060 !ruby/object:Gem::Requirement
206
206
  none: false
207
207
  requirements:
208
208
  - - ! '>='
@@ -210,10 +210,10 @@ dependencies:
210
210
  version: '0'
211
211
  type: :development
212
212
  prerelease: false
213
- version_requirements: *2152619940
213
+ version_requirements: *2156134060
214
214
  - !ruby/object:Gem::Dependency
215
215
  name: guard-rspec
216
- requirement: &2152619460 !ruby/object:Gem::Requirement
216
+ requirement: &2156132700 !ruby/object:Gem::Requirement
217
217
  none: false
218
218
  requirements:
219
219
  - - ! '>='
@@ -221,7 +221,7 @@ dependencies:
221
221
  version: '0'
222
222
  type: :development
223
223
  prerelease: false
224
- version_requirements: *2152619460
224
+ version_requirements: *2156132700
225
225
  description: Like Rack is to Rails and Sinatra, Punchblock provides a consistent API
226
226
  on top of several underlying third-party call control protocols.
227
227
  email: punchblock@adhearsion.com
@@ -302,6 +302,7 @@ files:
302
302
  - lib/punchblock/translator/asterisk/component/asterisk.rb
303
303
  - lib/punchblock/translator/asterisk/component/asterisk/agi_command.rb
304
304
  - lib/punchblock/translator/asterisk/component/asterisk/ami_action.rb
305
+ - lib/punchblock/translator/asterisk/component/asterisk/input.rb
305
306
  - lib/punchblock/translator/asterisk/component/asterisk/output.rb
306
307
  - lib/punchblock/version.rb
307
308
  - punchblock.gemspec
@@ -342,6 +343,7 @@ files:
342
343
  - spec/punchblock/translator/asterisk/call_spec.rb
343
344
  - spec/punchblock/translator/asterisk/component/asterisk/agi_command_spec.rb
344
345
  - spec/punchblock/translator/asterisk/component/asterisk/ami_action_spec.rb
346
+ - spec/punchblock/translator/asterisk/component/asterisk/input_spec.rb
345
347
  - spec/punchblock/translator/asterisk/component/asterisk/output_spec.rb
346
348
  - spec/punchblock/translator/asterisk/component_spec.rb
347
349
  - spec/punchblock/translator/asterisk_spec.rb
@@ -361,7 +363,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
361
363
  version: '0'
362
364
  segments:
363
365
  - 0
364
- hash: -4143902595723607278
366
+ hash: -3178520051176557915
365
367
  required_rubygems_version: !ruby/object:Gem::Requirement
366
368
  none: false
367
369
  requirements:
@@ -412,6 +414,7 @@ test_files:
412
414
  - spec/punchblock/translator/asterisk/call_spec.rb
413
415
  - spec/punchblock/translator/asterisk/component/asterisk/agi_command_spec.rb
414
416
  - spec/punchblock/translator/asterisk/component/asterisk/ami_action_spec.rb
417
+ - spec/punchblock/translator/asterisk/component/asterisk/input_spec.rb
415
418
  - spec/punchblock/translator/asterisk/component/asterisk/output_spec.rb
416
419
  - spec/punchblock/translator/asterisk/component_spec.rb
417
420
  - spec/punchblock/translator/asterisk_spec.rb