ruby_ami 1.1.2 → 1.2.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.
- data/.travis.yml +1 -1
- data/CHANGELOG.md +5 -0
- data/lib/ruby_ami/agi_result_parser.rb +42 -0
- data/lib/ruby_ami/async_agi_environment_parser.rb +26 -0
- data/lib/ruby_ami/client.rb +4 -3
- data/lib/ruby_ami/stream.rb +6 -4
- data/lib/ruby_ami/version.rb +1 -1
- data/lib/ruby_ami.rb +2 -0
- data/ruby_ami.gemspec +3 -3
- data/spec/ruby_ami/agi_result_parser_spec.rb +51 -0
- data/spec/ruby_ami/async_agi_environment_parser_spec.rb +45 -0
- data/spec/ruby_ami/client_spec.rb +2 -2
- data/spec/ruby_ami/stream_spec.rb +1 -0
- data/spec/spec_helper.rb +16 -11
- metadata +21 -14
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,10 @@
|
|
1
1
|
# [develop](https://github.com/adhearsion/ruby_ami)
|
2
2
|
|
3
|
+
# [1.2.0](https://github.com/adhearsion/ruby_ami/compare/v1.1.2...v1.2.0) - [2012-07-18](https://rubygems.org/gems/ruby_ami/versions/1.2.0)
|
4
|
+
* Feature: Added parsers for (Async)AGI environment and result strings
|
5
|
+
* Bugfix: Avoid a race condition in stream establishment and event receipt
|
6
|
+
* Bugfix: If socket creation fails, log an appropriate error
|
7
|
+
|
3
8
|
# [1.1.2](https://github.com/adhearsion/ruby_ami/compare/v1.1.1...v1.1.2) - [2012-07-04](https://rubygems.org/gems/ruby_ami/versions/1.1.2)
|
4
9
|
* Bugfix: Avoid recursive stream stopping
|
5
10
|
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'cgi'
|
2
|
+
|
3
|
+
module RubyAMI
|
4
|
+
class AGIResultParser
|
5
|
+
attr_reader :code, :result, :data
|
6
|
+
|
7
|
+
FORMAT = /^(\d{3}) result=(-?\d*) ?(\(?.*\)?)?$/.freeze
|
8
|
+
DATA_KV_FORMAT = /([\w\d]+)=([\w\d]*)/.freeze
|
9
|
+
DATA_CLEANER = /(^\()|(\)$)/.freeze
|
10
|
+
|
11
|
+
def initialize(result_string)
|
12
|
+
@result_string = result_string.dup
|
13
|
+
raise ArgumentError, "The result string did not match the required format." unless match
|
14
|
+
parse
|
15
|
+
end
|
16
|
+
|
17
|
+
def data_hash
|
18
|
+
return unless data_kv_match
|
19
|
+
{data_kv_match[1] => data_kv_match[2]}
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def unescape
|
25
|
+
CGI.unescape @result_string
|
26
|
+
end
|
27
|
+
|
28
|
+
def match
|
29
|
+
@match ||= unescape.chomp.match(FORMAT)
|
30
|
+
end
|
31
|
+
|
32
|
+
def parse
|
33
|
+
@code = match[1].to_i
|
34
|
+
@result = match[2].to_i
|
35
|
+
@data = match[3] ? match[3].gsub(DATA_CLEANER, '').freeze : nil
|
36
|
+
end
|
37
|
+
|
38
|
+
def data_kv_match
|
39
|
+
@data_kv_match ||= data.match(DATA_KV_FORMAT)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'cgi'
|
2
|
+
|
3
|
+
module RubyAMI
|
4
|
+
class AsyncAGIEnvironmentParser
|
5
|
+
def initialize(environment_string)
|
6
|
+
@environment_string = environment_string.dup
|
7
|
+
end
|
8
|
+
|
9
|
+
def to_hash
|
10
|
+
to_array.inject({}) do |accumulator, element|
|
11
|
+
accumulator[element[0].to_sym] = element[1] || ''
|
12
|
+
accumulator
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_s
|
17
|
+
@environment_string.dup
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def to_array
|
23
|
+
CGI.unescape(@environment_string).split("\n").map { |p| p.split ': ' }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/lib/ruby_ami/client.rb
CHANGED
@@ -41,8 +41,9 @@ module RubyAMI
|
|
41
41
|
end
|
42
42
|
|
43
43
|
def start
|
44
|
-
@events_stream =
|
45
|
-
@actions_stream =
|
44
|
+
@events_stream = new_stream lambda { |event| @event_processor << event }
|
45
|
+
@actions_stream = new_stream lambda { |message| @message_processor << message }
|
46
|
+
streams.each(&:run!)
|
46
47
|
@state = :started
|
47
48
|
streams.each(&:join)
|
48
49
|
end
|
@@ -178,7 +179,7 @@ module RubyAMI
|
|
178
179
|
&block
|
179
180
|
end
|
180
181
|
|
181
|
-
def
|
182
|
+
def new_stream(callback)
|
182
183
|
Stream.new @options[:host], @options[:port], callback
|
183
184
|
end
|
184
185
|
|
data/lib/ruby_ami/stream.rb
CHANGED
@@ -15,12 +15,9 @@ module RubyAMI
|
|
15
15
|
|
16
16
|
def initialize(host, port, event_callback)
|
17
17
|
super()
|
18
|
-
@event_callback = event_callback
|
18
|
+
@host, @port, @event_callback = host, port, event_callback
|
19
19
|
logger.debug "Starting up..."
|
20
20
|
@lexer = Lexer.new self
|
21
|
-
@socket = TCPSocket.from_ruby_socket ::TCPSocket.new(host, port)
|
22
|
-
post_init
|
23
|
-
run!
|
24
21
|
end
|
25
22
|
|
26
23
|
[:started, :stopped, :ready].each do |state|
|
@@ -28,7 +25,12 @@ module RubyAMI
|
|
28
25
|
end
|
29
26
|
|
30
27
|
def run
|
28
|
+
@socket = TCPSocket.from_ruby_socket ::TCPSocket.new(@host, @port)
|
29
|
+
post_init
|
31
30
|
loop { receive_data @socket.readpartial(4096) }
|
31
|
+
rescue Errno::ECONNREFUSED, SocketError => e
|
32
|
+
logger.error "Connection failed due to #{e.class}. Check your config and the server."
|
33
|
+
current_actor.terminate!
|
32
34
|
rescue EOFError
|
33
35
|
logger.info "Client socket closed!"
|
34
36
|
current_actor.terminate!
|
data/lib/ruby_ami/version.rb
CHANGED
data/lib/ruby_ami.rb
CHANGED
data/ruby_ami.gemspec
CHANGED
@@ -25,10 +25,10 @@ Gem::Specification.new do |s|
|
|
25
25
|
s.add_runtime_dependency %q<countdownlatch>, ["~> 1.0"]
|
26
26
|
|
27
27
|
s.add_development_dependency %q<bundler>, ["~> 1.0"]
|
28
|
-
s.add_development_dependency %q<rspec>, ["
|
28
|
+
s.add_development_dependency %q<rspec>, ["~> 2.5"]
|
29
29
|
s.add_development_dependency %q<cucumber>, [">= 0"]
|
30
|
-
s.add_development_dependency %q<ci_reporter>, ["
|
31
|
-
s.add_development_dependency %q<yard>, ["~> 0.6
|
30
|
+
s.add_development_dependency %q<ci_reporter>, ["~> 1.6"]
|
31
|
+
s.add_development_dependency %q<yard>, ["~> 0.6"]
|
32
32
|
s.add_development_dependency %q<rake>, [">= 0"]
|
33
33
|
s.add_development_dependency %q<mocha>, [">= 0"]
|
34
34
|
s.add_development_dependency %q<simplecov>, [">= 0"]
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module RubyAMI
|
4
|
+
describe AGIResultParser do
|
5
|
+
subject { described_class.new result_string }
|
6
|
+
|
7
|
+
context 'with something that does not match the valid format' do
|
8
|
+
let(:result_string) { 'foobar' }
|
9
|
+
|
10
|
+
it 'should raise ArgumentError on creation' do
|
11
|
+
expect { subject }.to raise_error(ArgumentError, /format/)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
context 'with a simple result with no data' do
|
16
|
+
let(:result_string) { "200%20result=123%0A" }
|
17
|
+
|
18
|
+
its(:code) { should == 200 }
|
19
|
+
its(:result) { should == 123 }
|
20
|
+
its(:data) { should == '' }
|
21
|
+
its(:data_hash) { should == nil }
|
22
|
+
end
|
23
|
+
|
24
|
+
context 'with a simple unescaped result with no data' do
|
25
|
+
let(:result_string) { "200 result=123" }
|
26
|
+
|
27
|
+
its(:code) { should == 200 }
|
28
|
+
its(:result) { should == 123 }
|
29
|
+
its(:data) { should == '' }
|
30
|
+
its(:data_hash) { should == nil }
|
31
|
+
end
|
32
|
+
|
33
|
+
context 'with a result and data in parens' do
|
34
|
+
let(:result_string) { "200%20result=-123%20(timeout)%0A" }
|
35
|
+
|
36
|
+
its(:code) { should == 200 }
|
37
|
+
its(:result) { should == -123 }
|
38
|
+
its(:data) { should == 'timeout' }
|
39
|
+
its(:data_hash) { should == nil }
|
40
|
+
end
|
41
|
+
|
42
|
+
context 'with a result and key-value data' do
|
43
|
+
let(:result_string) { "200%20result=123%20foo=bar%0A" }
|
44
|
+
|
45
|
+
its(:code) { should == 200 }
|
46
|
+
its(:result) { should == 123 }
|
47
|
+
its(:data) { should == 'foo=bar' }
|
48
|
+
its(:data_hash) { should == {'foo' => 'bar'} }
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module RubyAMI
|
4
|
+
describe AsyncAGIEnvironmentParser do
|
5
|
+
let :environment_string do
|
6
|
+
"agi_request%3A%20async%0Aagi_channel%3A%20SIP%2F1234-00000000%0Aagi_language%3A%20en%0Aagi_type%3A%20SIP%0Aagi_uniqueid%3A%201320835995.0%0Aagi_version%3A%201.8.4.1%0Aagi_callerid%3A%205678%0Aagi_calleridname%3A%20Jane%20Smith%0Aagi_callingpres%3A%200%0Aagi_callingani2%3A%200%0Aagi_callington%3A%200%0Aagi_callingtns%3A%200%0Aagi_dnid%3A%201000%0Aagi_rdnis%3A%20unknown%0Aagi_context%3A%20default%0Aagi_extension%3A%201000%0Aagi_priority%3A%201%0Aagi_enhanced%3A%200.0%0Aagi_accountcode%3A%20%0Aagi_threadid%3A%204366221312%0A%0A"
|
7
|
+
end
|
8
|
+
|
9
|
+
subject { described_class.new environment_string }
|
10
|
+
|
11
|
+
its(:to_s) { should == environment_string }
|
12
|
+
its(:to_s) { should_not be environment_string }
|
13
|
+
|
14
|
+
describe 'retrieving a hash representation' do
|
15
|
+
its(:to_hash) do
|
16
|
+
should == {
|
17
|
+
:agi_request => 'async',
|
18
|
+
:agi_channel => 'SIP/1234-00000000',
|
19
|
+
:agi_language => 'en',
|
20
|
+
:agi_type => 'SIP',
|
21
|
+
:agi_uniqueid => '1320835995.0',
|
22
|
+
:agi_version => '1.8.4.1',
|
23
|
+
:agi_callerid => '5678',
|
24
|
+
:agi_calleridname => 'Jane Smith',
|
25
|
+
:agi_callingpres => '0',
|
26
|
+
:agi_callingani2 => '0',
|
27
|
+
:agi_callington => '0',
|
28
|
+
:agi_callingtns => '0',
|
29
|
+
:agi_dnid => '1000',
|
30
|
+
:agi_rdnis => 'unknown',
|
31
|
+
:agi_context => 'default',
|
32
|
+
:agi_extension => '1000',
|
33
|
+
:agi_priority => '1',
|
34
|
+
:agi_enhanced => '0.0',
|
35
|
+
:agi_accountcode => '',
|
36
|
+
:agi_threadid => '4366221312'
|
37
|
+
}
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should not return the same hash object every time" do
|
41
|
+
subject.to_hash.should_not be subject.to_hash
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -329,8 +329,8 @@ module RubyAMI
|
|
329
329
|
end
|
330
330
|
|
331
331
|
describe '#stop' do
|
332
|
-
let(:mock_actions_stream) { mock 'Actions Stream' }
|
333
|
-
let(:mock_events_stream) { mock 'Events Stream' }
|
332
|
+
let(:mock_actions_stream) { mock 'Actions Stream', :alive? => true }
|
333
|
+
let(:mock_events_stream) { mock 'Events Stream', :alive? => true }
|
334
334
|
|
335
335
|
let(:streams) { [mock_actions_stream, mock_events_stream] }
|
336
336
|
|
@@ -26,6 +26,7 @@ module RubyAMI
|
|
26
26
|
mock_target.expects(:receive_data).send(*(times ? [:times, times] : [:at_least, 1])).with &block
|
27
27
|
s = ServerMock.new '127.0.0.1', server_port, mock_target
|
28
28
|
@stream = Stream.new '127.0.0.1', server_port, lambda { |m| client.message_received m }
|
29
|
+
@stream.run!
|
29
30
|
fake_client.call if fake_client.respond_to? :call
|
30
31
|
s.join
|
31
32
|
@stream.join
|
data/spec/spec_helper.rb
CHANGED
@@ -1,18 +1,23 @@
|
|
1
|
-
|
2
|
-
require 'simplecov
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
1
|
+
begin
|
2
|
+
require 'simplecov'
|
3
|
+
require 'simplecov-rcov'
|
4
|
+
class SimpleCov::Formatter::MergedFormatter
|
5
|
+
def format(result)
|
6
|
+
SimpleCov::Formatter::HTMLFormatter.new.format(result)
|
7
|
+
SimpleCov::Formatter::RcovFormatter.new.format(result)
|
8
|
+
end
|
7
9
|
end
|
8
|
-
|
9
|
-
SimpleCov.
|
10
|
-
|
11
|
-
|
10
|
+
SimpleCov.formatter = SimpleCov::Formatter::MergedFormatter
|
11
|
+
SimpleCov.start do
|
12
|
+
add_filter "/vendor/"
|
13
|
+
end
|
14
|
+
rescue Exception => e
|
15
|
+
puts "Couldn't load simplecov"
|
16
|
+
puts e.message
|
17
|
+
puts e.backtrace.join("\n")
|
12
18
|
end
|
13
19
|
|
14
20
|
require 'ruby_ami'
|
15
|
-
require 'mocha'
|
16
21
|
require 'countdownlatch'
|
17
22
|
|
18
23
|
Dir[File.dirname(__FILE__) + "/support/**/*.rb"].each {|f| require f}
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby_ami
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-07-
|
12
|
+
date: 2012-07-18 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: uuidtools
|
@@ -112,17 +112,17 @@ dependencies:
|
|
112
112
|
requirement: !ruby/object:Gem::Requirement
|
113
113
|
none: false
|
114
114
|
requirements:
|
115
|
-
- -
|
115
|
+
- - ~>
|
116
116
|
- !ruby/object:Gem::Version
|
117
|
-
version: 2.5
|
117
|
+
version: '2.5'
|
118
118
|
type: :development
|
119
119
|
prerelease: false
|
120
120
|
version_requirements: !ruby/object:Gem::Requirement
|
121
121
|
none: false
|
122
122
|
requirements:
|
123
|
-
- -
|
123
|
+
- - ~>
|
124
124
|
- !ruby/object:Gem::Version
|
125
|
-
version: 2.5
|
125
|
+
version: '2.5'
|
126
126
|
- !ruby/object:Gem::Dependency
|
127
127
|
name: cucumber
|
128
128
|
requirement: !ruby/object:Gem::Requirement
|
@@ -144,17 +144,17 @@ dependencies:
|
|
144
144
|
requirement: !ruby/object:Gem::Requirement
|
145
145
|
none: false
|
146
146
|
requirements:
|
147
|
-
- -
|
147
|
+
- - ~>
|
148
148
|
- !ruby/object:Gem::Version
|
149
|
-
version: 1.6
|
149
|
+
version: '1.6'
|
150
150
|
type: :development
|
151
151
|
prerelease: false
|
152
152
|
version_requirements: !ruby/object:Gem::Requirement
|
153
153
|
none: false
|
154
154
|
requirements:
|
155
|
-
- -
|
155
|
+
- - ~>
|
156
156
|
- !ruby/object:Gem::Version
|
157
|
-
version: 1.6
|
157
|
+
version: '1.6'
|
158
158
|
- !ruby/object:Gem::Dependency
|
159
159
|
name: yard
|
160
160
|
requirement: !ruby/object:Gem::Requirement
|
@@ -162,7 +162,7 @@ dependencies:
|
|
162
162
|
requirements:
|
163
163
|
- - ~>
|
164
164
|
- !ruby/object:Gem::Version
|
165
|
-
version: 0.6
|
165
|
+
version: '0.6'
|
166
166
|
type: :development
|
167
167
|
prerelease: false
|
168
168
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -170,7 +170,7 @@ dependencies:
|
|
170
170
|
requirements:
|
171
171
|
- - ~>
|
172
172
|
- !ruby/object:Gem::Version
|
173
|
-
version: 0.6
|
173
|
+
version: '0.6'
|
174
174
|
- !ruby/object:Gem::Dependency
|
175
175
|
name: rake
|
176
176
|
requirement: !ruby/object:Gem::Requirement
|
@@ -293,6 +293,8 @@ files:
|
|
293
293
|
- features/support/lexer_helper.rb
|
294
294
|
- lib/ruby_ami.rb
|
295
295
|
- lib/ruby_ami/action.rb
|
296
|
+
- lib/ruby_ami/agi_result_parser.rb
|
297
|
+
- lib/ruby_ami/async_agi_environment_parser.rb
|
296
298
|
- lib/ruby_ami/client.rb
|
297
299
|
- lib/ruby_ami/error.rb
|
298
300
|
- lib/ruby_ami/event.rb
|
@@ -304,6 +306,8 @@ files:
|
|
304
306
|
- lib/ruby_ami/version.rb
|
305
307
|
- ruby_ami.gemspec
|
306
308
|
- spec/ruby_ami/action_spec.rb
|
309
|
+
- spec/ruby_ami/agi_result_parser_spec.rb
|
310
|
+
- spec/ruby_ami/async_agi_environment_parser_spec.rb
|
307
311
|
- spec/ruby_ami/client_spec.rb
|
308
312
|
- spec/ruby_ami/error_spec.rb
|
309
313
|
- spec/ruby_ami/event_spec.rb
|
@@ -326,7 +330,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
326
330
|
version: '0'
|
327
331
|
segments:
|
328
332
|
- 0
|
329
|
-
hash:
|
333
|
+
hash: 1172578337856307051
|
330
334
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
331
335
|
none: false
|
332
336
|
requirements:
|
@@ -335,7 +339,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
335
339
|
version: '0'
|
336
340
|
segments:
|
337
341
|
- 0
|
338
|
-
hash:
|
342
|
+
hash: 1172578337856307051
|
339
343
|
requirements: []
|
340
344
|
rubyforge_project: ruby_ami
|
341
345
|
rubygems_version: 1.8.24
|
@@ -350,6 +354,8 @@ test_files:
|
|
350
354
|
- features/support/introspective_lexer.rb
|
351
355
|
- features/support/lexer_helper.rb
|
352
356
|
- spec/ruby_ami/action_spec.rb
|
357
|
+
- spec/ruby_ami/agi_result_parser_spec.rb
|
358
|
+
- spec/ruby_ami/async_agi_environment_parser_spec.rb
|
353
359
|
- spec/ruby_ami/client_spec.rb
|
354
360
|
- spec/ruby_ami/error_spec.rb
|
355
361
|
- spec/ruby_ami/event_spec.rb
|
@@ -357,3 +363,4 @@ test_files:
|
|
357
363
|
- spec/ruby_ami/stream_spec.rb
|
358
364
|
- spec/spec_helper.rb
|
359
365
|
- spec/support/mock_server.rb
|
366
|
+
has_rdoc:
|