matrioska 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 733fe495bf17c9860210d440b027d00ee86cb79a
4
- data.tar.gz: 9f6945be056f682b47edcedde4d1d78f17adce03
3
+ metadata.gz: 6ecbdde8b3c29cb0fab98b0d5119057ffea090f2
4
+ data.tar.gz: 04c8aa768b6407c3e863aee68c7b1a12dbb533cb
5
5
  SHA512:
6
- metadata.gz: 6ec8e9cf5a602512e90fbfe0be8c4bfb36f885948ac77cbc960c0838c9b062f04db6fe0c0279e7c75bcfcc4a58d99441316d7650d469466620b67c17ade38180
7
- data.tar.gz: 6ed1d738cab01ccadff21619a3578ae5c8915351128b702dc6081090707f124bc4b54d5c0993c8a90f94f0eff8b797b8ad8cdc72d26121e1d4a171dbdc9d5b3e
6
+ metadata.gz: 45f4f78f0ff041f95ffae346f8c52dbba8ab9f4e29f538cf2ee8dfe4cbd8d5ee87a09c3783d31d5ea3adcb7d729a16f4acb8e6d3eb933889f44d37c49f2d62f8
7
+ data.tar.gz: f24ba4a1137df7f0695e4c87ddbb5159967e868dde1b347339b47220b794dab7e3184b7e257aa03d7207cd709a90a6ca9db23275b63e56c94f7742cec353fc3e
@@ -3,6 +3,8 @@ rvm:
3
3
  - 1.9.2
4
4
  - 1.9.3
5
5
  - 2.0.0
6
+ - 2.1.0
7
+ - 2.2.0
6
8
  - jruby-19mode
7
9
  - rbx-19mode
8
10
  - ruby-head
@@ -1,4 +1,6 @@
1
- # develop
1
+ # Version 0.3.0
2
+ * Feature: added multi-digit patterns
3
+ * Feature: added inter-digit timeout to support multi-digit patterns
2
4
 
3
5
  # Version 0.2.1
4
6
  * Don't require manual loading of `DialWithApps`
@@ -3,3 +3,4 @@ require "matrioska/version"
3
3
  require "matrioska/plugin"
4
4
  require "matrioska/app_runner"
5
5
  require "matrioska/dial_with_apps"
6
+ require "matrioska/core_ext/adhearsion_call"
@@ -2,28 +2,36 @@ module Matrioska
2
2
  class AppRunner
3
3
  include Adhearsion::CallController::Utility
4
4
 
5
+ VALID_DIGITS = /[^0-9*#]/
6
+
5
7
  def initialize(call)
6
8
  @call = call
9
+ register_runner_with_call
7
10
  @app_map = {}
8
- @running = false
9
11
  end
10
12
 
11
13
  def start
14
+ if started? && running?
15
+ logger.warn "Already-active runner #{self} received start event."
16
+ return
17
+ end
18
+
12
19
  @state = :started
13
- logger.debug "MATRIOSKA START CALLED"
14
- unless @running
15
- @component = Punchblock::Component::Input.new mode: :dtmf, grammar: { value: grammar_accept }
16
- logger.debug "MATRIOSKA STARTING LISTENER"
17
- @component.register_event_handler Punchblock::Event::Complete do |event|
18
- handle_input_complete event
19
- end
20
- @call.write_and_await_response @component if @call.active?
20
+ logger.debug "MATRIOSKA STARTING LISTENER"
21
+ @component = Punchblock::Component::Input.new mode: :dtmf, inter_digit_timeout: Adhearsion.config[:matrioska].timeout * 1_000, grammar: { value: build_grammar }
22
+ @component.register_event_handler Punchblock::Event::Complete do |event|
23
+ handle_input_complete event
21
24
  end
25
+ @call.write_and_await_response @component if @call.alive? && @call.active?
22
26
  end
23
27
 
24
28
  def stop!
25
29
  @state = :stopped
26
- @component.stop! if @component && @component.executing?
30
+ @component.stop! if running?
31
+ end
32
+
33
+ def running?
34
+ !!(@component && @component.executing?)
27
35
  end
28
36
 
29
37
  def status
@@ -38,12 +46,12 @@ module Matrioska
38
46
  @state == :stopped
39
47
  end
40
48
 
41
- def map_app(digit, controller = nil, &block)
42
- digit = digit.to_s
49
+ def map_app(pattern, controller = nil, &block)
50
+ pattern = pattern.to_s
43
51
  range = "1234567890*#"
44
52
 
45
- unless range.include?(digit) && digit.size == 1
46
- raise ArgumentError, "The first argument should be a single digit String or number in the range 1234567890*#"
53
+ if VALID_DIGITS.match(pattern)
54
+ raise ArgumentError, "The first argument should be a String or number containing only 1234567890*#"
47
55
  end
48
56
 
49
57
  payload = if block_given?
@@ -54,18 +62,18 @@ module Matrioska
54
62
  controller
55
63
  end
56
64
 
57
- @app_map[digit] = payload
65
+ @app_map[pattern] = payload
58
66
  end
59
67
 
60
68
  def handle_input_complete(event)
61
69
  if @state == :stopped
62
- logger.warn "Stopped runner #{self} received event."
70
+ logger.warn "Stopped runner #{self} received stop event."
63
71
  return
64
72
  end
65
73
  logger.debug "MATRIOSKA HANDLING INPUT"
66
74
  result = event.reason.respond_to?(:utterance) ? event.reason.utterance : nil
67
75
  digit = parse_dtmf result
68
- match_and_run digit unless @running
76
+ match_and_run digit
69
77
  end
70
78
 
71
79
  private
@@ -77,12 +85,11 @@ module Matrioska
77
85
  def match_and_run(digit)
78
86
  if match = @app_map[digit]
79
87
  logger.debug "MATRIOSKA #match_and_run called with #{digit}"
80
- @running = true
81
88
  callback = lambda do |call|
82
89
  @running = false
83
- logger.debug "MATRIOSKA CALLBACK RESTARTING LISTENER"
84
- if call.active?
85
- start unless stopped?
90
+ if call.alive? && call.active? && started?
91
+ logger.debug "MATRIOSKA CALLBACK RESTARTING LISTENER"
92
+ start
86
93
  else
87
94
  logger.debug "MATRIOSKA CALLBACK NOT DOING ANYTHING BECAUSE CALL IS DEAD"
88
95
  end
@@ -104,5 +111,26 @@ module Matrioska
104
111
  rescue Adhearsion::Call::Hangup
105
112
  logger.debug "Matrioska terminated because the call was disconnected"
106
113
  end
114
+
115
+ def build_grammar
116
+ current_app_map = app_map
117
+ RubySpeech::GRXML.draw mode: :dtmf, root: 'options' do
118
+ rule id: 'options', scope: 'public' do
119
+ one_of do
120
+ current_app_map.keys.each do |index|
121
+ item do
122
+ index
123
+ end
124
+ end
125
+ end
126
+ end
127
+ end
128
+ end
129
+
130
+ def register_runner_with_call
131
+ @call[:matrioska_runners] ||= []
132
+ @call[:matrioska_runners] << self
133
+ end
134
+
107
135
  end
108
136
  end
@@ -0,0 +1,15 @@
1
+ require 'adhearsion'
2
+
3
+ class Adhearsion::Call
4
+ def stop_all_runners!
5
+ return unless variables[:matrioska_runners]
6
+
7
+ variables[:matrioska_runners].each do |runner|
8
+ runner.stop!
9
+ end
10
+ end
11
+
12
+ def runners
13
+ Array(variables[:matrioska_runners])
14
+ end
15
+ end
@@ -38,15 +38,19 @@ module Matrioska
38
38
  dial = Adhearsion::CallController::Dial::ParallelConfirmationDial.new to, options, call
39
39
  yield dial
40
40
 
41
- local_runner = Matrioska::AppRunner.new call
42
- @local_runner_block.call local_runner
43
- local_runner.start
41
+ if @local_runner_block
42
+ local_runner = Matrioska::AppRunner.new call
43
+ @local_runner_block.call local_runner
44
+ local_runner.start
45
+ end
44
46
 
45
47
  dial.prep_calls do |new_call|
46
48
  new_call.on_joined call do
47
- remote_runner = Matrioska::AppRunner.new new_call
48
- @remote_runner_block.call remote_runner
49
- remote_runner.start
49
+ if @remote_runner_block
50
+ remote_runner = Matrioska::AppRunner.new new_call
51
+ @remote_runner_block.call remote_runner
52
+ remote_runner.start
53
+ end
50
54
  end
51
55
  end
52
56
 
@@ -8,5 +8,9 @@ module Matrioska
8
8
  end
9
9
  end
10
10
  end
11
+
12
+ config :matrioska do
13
+ timeout 2, desc: "Time (in seconds) to wait between each digit before trying to resolve match", transform: Proc.new { |v| v.to_i }
14
+ end
11
15
  end
12
16
  end
@@ -1,3 +1,3 @@
1
1
  module Matrioska
2
- VERSION = "0.2.1"
2
+ VERSION = "0.3.0"
3
3
  end
@@ -7,7 +7,7 @@ Gem::Specification.new do |s|
7
7
  s.version = Matrioska::VERSION
8
8
  s.authors = ["Luca Pradovera"]
9
9
  s.email = ["lpradovera@mojolingo.com"]
10
- s.homepage = "https://github.com/polysics/matrioska"
10
+ s.homepage = "https://github.com/adhearsion/matrioska"
11
11
  s.summary = %q{Adhearsion plugin for in-call apps}
12
12
  s.description = %q{Adhearsion plugin for in-call apps. Provides a features-style interface to run applications in calls.}
13
13
  s.license = 'MIT'
@@ -18,20 +18,24 @@ module Matrioska
18
18
  end
19
19
 
20
20
  describe "#start" do
21
+ before do
22
+ subject.map_app("34*") { call.do_stuff_from_a_block }
23
+ subject.map_app(5, MockController)
24
+ end
25
+
21
26
  let(:grxml) {
22
- RubySpeech::GRXML.draw mode: 'dtmf', root: 'inputdigits' do
23
- rule id: 'inputdigits', scope: 'public' do
27
+ RubySpeech::GRXML.draw mode: :dtmf, root: 'options' do
28
+ rule id: 'options', scope: 'public' do
24
29
  one_of do
25
- 0.upto(9) { |d| item { d.to_s } }
26
- item { "#" }
27
- item { "*" }
30
+ item { "34*" }
31
+ item { "5" }
28
32
  end
29
33
  end
30
34
  end
31
35
  }
32
36
 
33
37
  let(:input_component) {
34
- Punchblock::Component::Input.new mode: :dtmf, grammar: { value: grxml }
38
+ Punchblock::Component::Input.new mode: :dtmf, inter_digit_timeout: Adhearsion.config[:matrioska].timeout.to_i * 1_000, grammar: { value: grxml }
35
39
  }
36
40
 
37
41
  it "should start the appropriate component" do
@@ -39,6 +43,14 @@ module Matrioska
39
43
  subject.start
40
44
  subject.status.should == :started
41
45
  end
46
+
47
+ it "should do nothing if #start is called twice" do
48
+ call.should_receive(:write_and_await_response).once.with(input_component)
49
+ subject.should_receive(:running?).once.and_return(true)
50
+ subject.start
51
+ subject.start
52
+ end
53
+
42
54
  end
43
55
 
44
56
  describe "#stop!" do
@@ -60,12 +72,15 @@ module Matrioska
60
72
 
61
73
  describe "#map_app" do
62
74
  context "with invalid input" do
63
- let(:too_long) { "ab" }
64
- let(:wrong) { "a" }
75
+ let(:long_pattern) { "*99" }
76
+ let(:wrong) { "a12" }
77
+
78
+ it "should raise if the first argument has invalid characters" do
79
+ expect { subject.map_app(wrong) {} }.to raise_error ArgumentError, "The first argument should be a String or number containing only 1234567890*#"
80
+ end
65
81
 
66
- it "should raise if the first argument is not a single digit string in the range" do
67
- expect { subject.map_app(too_long) {} }.to raise_error ArgumentError, "The first argument should be a single digit String or number in the range 1234567890*#"
68
- expect { subject.map_app(wrong) {} }.to raise_error ArgumentError, "The first argument should be a single digit String or number in the range 1234567890*#"
82
+ it "should not raise if the first argument has multiple valid digits" do
83
+ expect { subject.map_app(long_pattern) {} }.to_not raise_error
69
84
  end
70
85
 
71
86
  it "raises if called without either a class or a block" do
@@ -84,7 +99,7 @@ module Matrioska
84
99
  end
85
100
 
86
101
  before do
87
- subject.map_app(3) { call.do_stuff_from_a_block }
102
+ subject.map_app("34*") { call.do_stuff_from_a_block }
88
103
  subject.map_app(5, MockController)
89
104
  end
90
105
 
@@ -106,13 +121,15 @@ module Matrioska
106
121
 
107
122
  it "executes the block if the payload is a Proc" do
108
123
  call.should_receive(:do_stuff_from_a_block).once
124
+ subject.should_receive(:started?).and_return true
109
125
  subject.should_receive(:start).once
110
- subject.handle_input_complete mock_event("3")
126
+ subject.handle_input_complete mock_event("34*")
111
127
  sleep 0.1 # Give the controller time to finish and the callback to fire
112
128
  end
113
129
 
114
130
  it "executes the controller if the payload is a Class" do
115
131
  call.should_receive(:do_stuff_from_a_class).once
132
+ subject.should_receive(:started?).and_return true
116
133
  subject.should_receive(:start).once
117
134
  subject.handle_input_complete mock_event("5")
118
135
  sleep 0.1 # Give the controller time to finish and the callback to fire
@@ -0,0 +1,26 @@
1
+ require 'adhearsion'
2
+ require 'matrioska'
3
+
4
+ describe Adhearsion::Call do
5
+ let(:call) { Adhearsion::Call.new }
6
+
7
+
8
+ it 'should track all runners launched with it' do
9
+ runner = Matrioska::AppRunner.new call
10
+ call.runners.should include(runner)
11
+ end
12
+
13
+ it 'should be possible to stop all runners on a call' do
14
+ r1 = Matrioska::AppRunner.new call
15
+ r2 = Matrioska::AppRunner.new call
16
+ r3 = Matrioska::AppRunner.new call
17
+
18
+ r1.should_receive(:stop!).once
19
+ r2.should_receive(:stop!).once
20
+ r3.should_receive(:stop!).once
21
+
22
+ call.stop_all_runners!
23
+ end
24
+
25
+
26
+ end
@@ -143,5 +143,75 @@ describe Matrioska::DialWithApps do
143
143
  second_other_mock_call << mock_end
144
144
  dial_thread.join.should be_true
145
145
  end
146
+
147
+ it "allows specifying only a local listener" do
148
+ call.should_receive(:answer).once
149
+
150
+ mock_local_runner.should_receive(:bar).once
151
+ mock_local_runner.should_receive(:start).once
152
+
153
+ mock_remote_runner = nil
154
+
155
+ other_mock_call.should_receive(:dial).with(to, from: 'foo').once
156
+ other_mock_call.should_receive(:hangup).once.and_return do
157
+ other_mock_call << mock_end
158
+ end
159
+
160
+ second_other_mock_call.should_receive(:dial).with(second_to, from: 'foo').once
161
+ second_other_mock_call.should_receive(:join).once.and_return do
162
+ second_other_mock_call << Punchblock::Event::Joined.new(call_uri: call_id)
163
+ end
164
+
165
+ dial_thread = Thread.new do
166
+ controller.instance_exec(to,second_to) do |to, second_to|
167
+ dial_with_apps([to, second_to], from: 'foo') do |dial|
168
+
169
+ local do |runner|
170
+ runner.bar
171
+ end
172
+ end
173
+ end
174
+ end
175
+
176
+ sleep 0.1
177
+ second_other_mock_call << mock_answered
178
+ second_other_mock_call << mock_end
179
+ dial_thread.join.should be_true
180
+ end
181
+
182
+ it "allows specifying only a remote listener" do
183
+ call.should_receive(:answer).once
184
+
185
+ mock_local_runner = nil
186
+
187
+ mock_remote_runner.should_receive(:bar).once
188
+ mock_remote_runner.should_receive(:start).once
189
+
190
+ other_mock_call.should_receive(:dial).with(to, from: 'foo').once
191
+ other_mock_call.should_receive(:hangup).once.and_return do
192
+ other_mock_call << mock_end
193
+ end
194
+
195
+ second_other_mock_call.should_receive(:dial).with(second_to, from: 'foo').once
196
+ second_other_mock_call.should_receive(:join).once.and_return do
197
+ second_other_mock_call << Punchblock::Event::Joined.new(call_uri: call_id)
198
+ end
199
+
200
+ dial_thread = Thread.new do
201
+ controller.instance_exec(to,second_to) do |to, second_to|
202
+ dial_with_apps([to, second_to], from: 'foo') do |dial|
203
+
204
+ remote do |runner|
205
+ runner.bar
206
+ end
207
+ end
208
+ end
209
+ end
210
+
211
+ sleep 0.1
212
+ second_other_mock_call << mock_answered
213
+ second_other_mock_call << mock_end
214
+ dial_thread.join.should be_true
215
+ end
146
216
  end
147
217
  end
@@ -11,4 +11,9 @@ RSpec.configure do |config|
11
11
  config.before :suite do
12
12
  Adhearsion::Logging.start Adhearsion::Logging.default_appenders, :trace, Adhearsion.config.platform.logging.formatter
13
13
  end
14
+
15
+ config.before do
16
+ @uuid = SecureRandom.uuid
17
+ Punchblock.stub new_request_id: @uuid
18
+ end
14
19
  end
metadata CHANGED
@@ -1,83 +1,83 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: matrioska
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Luca Pradovera
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-11-05 00:00:00.000000000 Z
11
+ date: 2015-05-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: adhearsion
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ~>
17
+ - - "~>"
18
18
  - !ruby/object:Gem::Version
19
19
  version: '2.4'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ~>
24
+ - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '2.4'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: bundler
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - ~>
31
+ - - "~>"
32
32
  - !ruby/object:Gem::Version
33
33
  version: '1.0'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - ~>
38
+ - - "~>"
39
39
  - !ruby/object:Gem::Version
40
40
  version: '1.0'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rspec
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - ~>
45
+ - - "~>"
46
46
  - !ruby/object:Gem::Version
47
47
  version: '2.5'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - ~>
52
+ - - "~>"
53
53
  - !ruby/object:Gem::Version
54
54
  version: '2.5'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: rake
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - '>='
59
+ - - ">="
60
60
  - !ruby/object:Gem::Version
61
61
  version: '0'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - '>='
66
+ - - ">="
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: guard-rspec
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
- - - '>='
73
+ - - ">="
74
74
  - !ruby/object:Gem::Version
75
75
  version: '0'
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
- - - '>='
80
+ - - ">="
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
83
  description: Adhearsion plugin for in-call apps. Provides a features-style interface
@@ -88,9 +88,9 @@ executables: []
88
88
  extensions: []
89
89
  extra_rdoc_files: []
90
90
  files:
91
- - .gitignore
92
- - .rspec
93
- - .travis.yml
91
+ - ".gitignore"
92
+ - ".rspec"
93
+ - ".travis.yml"
94
94
  - CHANGELOG.md
95
95
  - Gemfile
96
96
  - Guardfile
@@ -99,14 +99,16 @@ files:
99
99
  - Rakefile
100
100
  - lib/matrioska.rb
101
101
  - lib/matrioska/app_runner.rb
102
+ - lib/matrioska/core_ext/adhearsion_call.rb
102
103
  - lib/matrioska/dial_with_apps.rb
103
104
  - lib/matrioska/plugin.rb
104
105
  - lib/matrioska/version.rb
105
106
  - matrioska.gemspec
106
107
  - spec/matrioska/app_runner_spec.rb
108
+ - spec/matrioska/core_ext/adhearsion_call_spec.rb
107
109
  - spec/matrioska/dial_with_apps_spec.rb
108
110
  - spec/spec_helper.rb
109
- homepage: https://github.com/polysics/matrioska
111
+ homepage: https://github.com/adhearsion/matrioska
110
112
  licenses:
111
113
  - MIT
112
114
  metadata: {}
@@ -116,21 +118,22 @@ require_paths:
116
118
  - lib
117
119
  required_ruby_version: !ruby/object:Gem::Requirement
118
120
  requirements:
119
- - - '>='
121
+ - - ">="
120
122
  - !ruby/object:Gem::Version
121
123
  version: '0'
122
124
  required_rubygems_version: !ruby/object:Gem::Requirement
123
125
  requirements:
124
- - - '>='
126
+ - - ">="
125
127
  - !ruby/object:Gem::Version
126
128
  version: '0'
127
129
  requirements: []
128
130
  rubyforge_project: matrioska
129
- rubygems_version: 2.0.3
131
+ rubygems_version: 2.4.6
130
132
  signing_key:
131
133
  specification_version: 4
132
134
  summary: Adhearsion plugin for in-call apps
133
135
  test_files:
134
136
  - spec/matrioska/app_runner_spec.rb
137
+ - spec/matrioska/core_ext/adhearsion_call_spec.rb
135
138
  - spec/matrioska/dial_with_apps_spec.rb
136
139
  - spec/spec_helper.rb