matrioska 0.2.1 → 0.3.0

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.
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