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 CHANGED
@@ -4,7 +4,7 @@ rvm:
4
4
  - 1.9.3
5
5
  - jruby-19mode
6
6
  # - rbx-19mode
7
- - ruby-head
7
+ # - ruby-head
8
8
  before_install:
9
9
  - wget http://ftp.us.debian.org/debian/pool/main/r/ragel/ragel_6.7-1.1_i386.deb
10
10
  - sudo dpkg -i ragel_6.7-1.1_i386.deb
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
@@ -41,8 +41,9 @@ module RubyAMI
41
41
  end
42
42
 
43
43
  def start
44
- @events_stream = start_stream lambda { |event| @event_processor << event }
45
- @actions_stream = start_stream lambda { |message| @message_processor << message }
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 start_stream(callback)
182
+ def new_stream(callback)
182
183
  Stream.new @options[:host], @options[:port], callback
183
184
  end
184
185
 
@@ -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!
@@ -1,3 +1,3 @@
1
1
  module RubyAMI
2
- VERSION = "1.1.2"
2
+ VERSION = "1.2.0"
3
3
  end
data/lib/ruby_ami.rb CHANGED
@@ -16,6 +16,8 @@ end
16
16
 
17
17
  %w{
18
18
  action
19
+ agi_result_parser
20
+ async_agi_environment_parser
19
21
  client
20
22
  error
21
23
  event
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>, [">= 2.5.0"]
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>, [">= 1.6.3"]
31
- s.add_development_dependency %q<yard>, ["~> 0.6.0"]
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
- require 'simplecov'
2
- require 'simplecov-rcov'
3
- class SimpleCov::Formatter::MergedFormatter
4
- def format(result)
5
- SimpleCov::Formatter::HTMLFormatter.new.format(result)
6
- SimpleCov::Formatter::RcovFormatter.new.format(result)
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
- end
9
- SimpleCov.formatter = SimpleCov::Formatter::MergedFormatter
10
- SimpleCov.start do
11
- add_filter "/vendor/"
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.1.2
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-04 00:00:00.000000000 Z
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.0
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.0
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.3
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.3
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.0
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.0
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: 726303340289195819
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: 726303340289195819
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: