ruby_ami 2.4.0 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +3 -6
- data/CHANGELOG.md +6 -0
- data/LICENSE.txt +1 -1
- data/README.md +22 -16
- data/lib/ruby_ami.rb +0 -1
- data/lib/ruby_ami/error.rb +5 -0
- data/lib/ruby_ami/lexer.rb +9 -5
- data/lib/ruby_ami/response.rb +4 -0
- data/lib/ruby_ami/stream.rb +2 -1
- data/lib/ruby_ami/version.rb +1 -1
- data/ruby_ami.gemspec +3 -0
- data/spec/ruby_ami/action_spec.rb +7 -5
- data/spec/ruby_ami/agi_result_parser_spec.rb +20 -20
- data/spec/ruby_ami/async_agi_environment_parser_spec.rb +4 -4
- data/spec/ruby_ami/error_spec.rb +1 -1
- data/spec/ruby_ami/stream_spec.rb +26 -39
- data/spec/spec_helper.rb +1 -0
- metadata +18 -8
- data/lib/ruby_ami/client.rb +0 -77
- data/spec/ruby_ami/client_spec.rb +0 -90
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d8f5b78e09b9c102fe10006cafb1a8fad7af27bd
|
4
|
+
data.tar.gz: d6b02c5e8bc1721cbd0666df628e0f7f5c3d1bef
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5a544ce205149a415df2cbbcf0b51e6c01f4980f6fe711a23bbe3831c4530a494c1846b756b0406a9ea0f365b180325168e759751b7eedbfea3b398276bd2c8e
|
7
|
+
data.tar.gz: f292c56da86738c9a6e71e14db5fe6374b2b9f56d1d290a566107ed424baae4fed3d3017026a2b35ab315dea9fbb2c9e9b74e3c60b0a8e8b036a6d2e840bea76
|
data/.travis.yml
CHANGED
@@ -1,15 +1,12 @@
|
|
1
1
|
language: ruby
|
2
2
|
sudo: false
|
3
3
|
rvm:
|
4
|
-
-
|
5
|
-
- 2.
|
6
|
-
-
|
7
|
-
- jruby
|
8
|
-
- rbx-2.1.1
|
4
|
+
- 2.2.5
|
5
|
+
- 2.3.1
|
6
|
+
- jruby-9.1.2.0
|
9
7
|
- ruby-head
|
10
8
|
matrix:
|
11
9
|
allow_failures:
|
12
10
|
- rvm: ruby-head
|
13
|
-
- rvm: rbx-2.1.1
|
14
11
|
notifications:
|
15
12
|
irc: "irc.freenode.org#adhearsion"
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,11 @@
|
|
1
1
|
# [develop](https://github.com/adhearsion/ruby_ami)
|
2
2
|
|
3
|
+
# [3.0.0](https://github.com/adhearsion/ruby_ami/compare/v2.4.0...v3.0.0) - [2016-07-25](https://rubygems.org/gems/ruby_ami/versions/3.0.0)
|
4
|
+
* Breaking change: Ruby 1.9 is no longer supported. Minimum supported versions are Ruby 2.2.0 and JRuby 9.0.0.0
|
5
|
+
* Breaking change: Removed the deprecated `RubyAMI::Client` because it is no longer relevant.
|
6
|
+
* Breaking change: Start the connection when the `RubyAMI::Stream` starts, permitting supervised restart of the stream on connection failure. To support this, the event callback now passes a second parameter which is the stream itself.
|
7
|
+
* Feature: Optimisation of the protocol lexer
|
8
|
+
|
3
9
|
# [2.4.0](https://github.com/adhearsion/ruby_ami/compare/v2.3.0...v2.4.0) - [2015-12-07](https://rubygems.org/gems/ruby_ami/versions/2.4.0)
|
4
10
|
# Feature: Reveal the AMI version for a `Stream` via `Stream#version`
|
5
11
|
|
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
@@ -7,7 +7,7 @@
|
|
7
7
|
[![Coverage Status](https://coveralls.io/repos/adhearsion/ruby_ami/badge.png?branch=develop)](https://coveralls.io/r/adhearsion/ruby_ami)
|
8
8
|
[![Inline docs](http://inch-ci.org/github/adhearsion/ruby_ami.png?branch=develop)](http://inch-ci.org/github/adhearsion/ruby_ami)
|
9
9
|
|
10
|
-
RubyAMI is an AMI client library in Ruby
|
10
|
+
RubyAMI is an AMI client library in Ruby based on Celluloid with the sole purpose of providing a connection to the Asterisk Manager Interface. RubyAMI does not provide any features beyond connection management and protocol parsing. Actions are sent over the wire, and responses are returned. Events are passed to a callback you define. It's up to you to match these up into something useful. In this regard, RubyAMI is very similar to [Blather](https://github.com/sprsquish/blather) for XMPP or [Punchblock](https://github.com/adhearsion/punchblock), the Ruby 3PCC library. In fact, Punchblock uses RubyAMI under the covers for its Asterisk implementation, including an implementation of AsyncAGI.
|
11
11
|
|
12
12
|
NB: If you're looking to develop an application on Asterisk, you should take a look at the [Adhearsion](http://adhearsion.com) framework first. This library is much lower level.
|
13
13
|
|
@@ -21,10 +21,6 @@ In order to setup a connection to listen for AMI events, one can do:
|
|
21
21
|
```ruby
|
22
22
|
require 'ruby_ami'
|
23
23
|
|
24
|
-
stream = RubyAMI::Stream.new '127.0.0.1', 5038, 'manager', 'password',
|
25
|
-
->(e) { handle_event e },
|
26
|
-
Logger.new(STDOUT), 10
|
27
|
-
|
28
24
|
def handle_event(event)
|
29
25
|
case event.name
|
30
26
|
when 'FullyBooted'
|
@@ -34,7 +30,19 @@ def handle_event(event)
|
|
34
30
|
end
|
35
31
|
end
|
36
32
|
|
37
|
-
|
33
|
+
stream = RubyAMI::Stream.new '127.0.0.1', 5038, 'manager', 'password',
|
34
|
+
->(e, stream) { handle_event e },
|
35
|
+
Logger.new(STDOUT), 10
|
36
|
+
|
37
|
+
Celluloid::Actor.join(stream) # This will block until the actor is terminated elsewhere. Otherwise, the actor will run in its own thread allowing other work to be done here.
|
38
|
+
```
|
39
|
+
|
40
|
+
Note that using `Stream.new`, the actor will shut down when the connection is lost (and in this case the program will exit). If it is necessary to restart the actor on failure, you can start it in a Celluloid supervisor:
|
41
|
+
|
42
|
+
```ruby
|
43
|
+
RubyAMI::Stream.supervise_as :ami_connection, '127.0.0.1', 5038, 'manager', 'password',
|
44
|
+
->(e, stream) { handle_event e },
|
45
|
+
Logger.new(STDOUT), 10
|
38
46
|
```
|
39
47
|
|
40
48
|
It is also possible to execute actions in response to events:
|
@@ -42,26 +50,24 @@ It is also possible to execute actions in response to events:
|
|
42
50
|
```ruby
|
43
51
|
require 'ruby_ami'
|
44
52
|
|
45
|
-
|
46
|
-
->(e) { handle_event e },
|
47
|
-
Logger.new(STDOUT), 10
|
48
|
-
|
49
|
-
def handle_event(event)
|
53
|
+
def handle_event(event, stream)
|
50
54
|
case event.name
|
51
55
|
when 'FullyBooted'
|
52
56
|
puts "The connection was successful. Originating a call."
|
53
|
-
response =
|
57
|
+
response = stream.send_action 'Originate', 'Channel' => 'SIP/foo'
|
54
58
|
puts "The call origination resulted in #{response.inspect}"
|
55
59
|
end
|
56
60
|
end
|
57
61
|
|
58
|
-
|
62
|
+
stream = RubyAMI::Stream.new '127.0.0.1', 5038, 'manager', 'password',
|
63
|
+
->(e, stream) { handle_event e, stream },
|
64
|
+
Logger.new(STDOUT), 10
|
65
|
+
|
66
|
+
Celluloid::Actor.join(stream) # This will block until the actor is terminated elsewhere. Otherwise, the actor will run in its own thread allowing other work to be done here.
|
59
67
|
```
|
60
68
|
|
61
69
|
Executing actions does not strictly have to be done within the event handler, but it is not valid to send AMI events before receiving a `FullyBooted` event. If you attempt to execute an action prior to this, it may fail, and `RubyAMI::Stream` will not help you recover or queue the action until the connection is `FullyBooted`; you must manage this timing yourself. That said, assuming you take care of this, you may invoke `RubyAMI::Stream#send_action` from anywhere in your code and it will return the response of the action.
|
62
70
|
|
63
|
-
RubyAMI also has a class called `RubyAMI::Client` which used to be the main usage method. The purpose of this class was to tie together two AMI connections and separate events and action execution between the two in order to avoid some issues present in Asterisk < 1.8 with regards to separating overlapping events and executing multiple actions simultaneously. These issues are no longer present, and so **`RubyAMI::Client` is now deprecated and will be removed in RubyAMI 3.0**.
|
64
|
-
|
65
71
|
## Links:
|
66
72
|
* [Source](https://github.com/adhearsion/ruby_ami)
|
67
73
|
* [Documentation](http://rdoc.info/github/adhearsion/ruby_ami/master/frames)
|
@@ -78,4 +84,4 @@ RubyAMI also has a class called `RubyAMI::Client` which used to be the main usag
|
|
78
84
|
|
79
85
|
## Copyright
|
80
86
|
|
81
|
-
Copyright (c)
|
87
|
+
Copyright (c) 2011-2016 Ben Langfeld, Jay Phillips. MIT licence (see LICENSE for details).
|
data/lib/ruby_ami.rb
CHANGED
data/lib/ruby_ami/error.rb
CHANGED
data/lib/ruby_ami/lexer.rb
CHANGED
@@ -88,9 +88,9 @@ module RubyAMI
|
|
88
88
|
|
89
89
|
# Strip off the header line
|
90
90
|
raw.slice! HEADER_SLICE
|
91
|
-
populate_message_body msg, raw
|
91
|
+
raw_index = populate_message_body msg, raw
|
92
92
|
|
93
|
-
return msg if response_follows && !handle_response_follows(msg, raw)
|
93
|
+
return msg if response_follows && !handle_response_follows(msg, raw[raw_index..-1])
|
94
94
|
|
95
95
|
case msg
|
96
96
|
when Error
|
@@ -129,11 +129,15 @@ module RubyAMI
|
|
129
129
|
@delegate.syntax_error_encountered ignored_chunk
|
130
130
|
end
|
131
131
|
|
132
|
+
# returns first char index after last match
|
132
133
|
def populate_message_body(obj, raw)
|
133
|
-
|
134
|
-
|
134
|
+
headers = raw.scan(KEYVALUEPAIR)
|
135
|
+
if match = $~
|
136
|
+
obj.merge_headers!(Hash[headers])
|
137
|
+
match.end(match.size - 1) + 2
|
138
|
+
else
|
139
|
+
0
|
135
140
|
end
|
136
|
-
obj
|
137
141
|
end
|
138
142
|
|
139
143
|
def handle_response_follows(obj, raw)
|
data/lib/ruby_ami/response.rb
CHANGED
data/lib/ruby_ami/stream.rb
CHANGED
@@ -29,6 +29,7 @@ module RubyAMI
|
|
29
29
|
@lexer = Lexer.new self
|
30
30
|
@sent_actions = {}
|
31
31
|
@causal_actions = {}
|
32
|
+
async.run
|
32
33
|
end
|
33
34
|
|
34
35
|
[:started, :stopped, :ready].each do |state|
|
@@ -125,7 +126,7 @@ module RubyAMI
|
|
125
126
|
end
|
126
127
|
|
127
128
|
def fire_event(event)
|
128
|
-
@event_callback.call event
|
129
|
+
@event_callback.call event, current_actor
|
129
130
|
end
|
130
131
|
|
131
132
|
def register_sent_action(action)
|
data/lib/ruby_ami/version.rb
CHANGED
data/ruby_ami.gemspec
CHANGED
@@ -13,12 +13,15 @@ Gem::Specification.new do |s|
|
|
13
13
|
|
14
14
|
s.rubyforge_project = "ruby_ami"
|
15
15
|
|
16
|
+
s.required_ruby_version = '>= 2.2.0'
|
17
|
+
|
16
18
|
s.files = `git ls-files`.split("\n") << 'lib/ruby_ami/lexer.rb'
|
17
19
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
20
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
21
|
s.require_paths = ["lib"]
|
20
22
|
|
21
23
|
s.add_runtime_dependency %q<celluloid-io>, ["~> 0.13"]
|
24
|
+
s.add_runtime_dependency %q<celluloid>, ["~> 0.16.0"]
|
22
25
|
|
23
26
|
s.add_development_dependency %q<bundler>, ["~> 1.0"]
|
24
27
|
s.add_development_dependency %q<rspec>, ["~> 2.5"]
|
@@ -15,13 +15,15 @@ module RubyAMI
|
|
15
15
|
it { should_not be_complete }
|
16
16
|
|
17
17
|
describe "SIPPeers actions" do
|
18
|
-
|
19
|
-
|
18
|
+
it "has causal events" do
|
19
|
+
Action.new('SIPPeers').has_causal_events?.should be true
|
20
|
+
end
|
20
21
|
end
|
21
22
|
|
22
23
|
describe "the ParkedCalls terminator event" do
|
23
|
-
|
24
|
-
|
24
|
+
it "knows its causal event terminator name" do
|
25
|
+
Action.new('ParkedCalls').causal_event_terminator_name.should == "parkedcallscomplete"
|
26
|
+
end
|
25
27
|
end
|
26
28
|
|
27
29
|
it "should properly convert itself into a String when additional headers are given" do
|
@@ -108,7 +110,7 @@ module RubyAMI
|
|
108
110
|
|
109
111
|
it { should be_complete }
|
110
112
|
|
111
|
-
|
113
|
+
it { subject.response.should be response }
|
112
114
|
end
|
113
115
|
end
|
114
116
|
end
|
@@ -16,46 +16,46 @@ module RubyAMI
|
|
16
16
|
context 'with a simple result with no data' do
|
17
17
|
let(:result_string) { "200%20result=123%0A" }
|
18
18
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
19
|
+
it { subject.code.should == 200 }
|
20
|
+
it { subject.result.should == 123 }
|
21
|
+
it { subject.data.should == '' }
|
22
|
+
it { subject.data_hash.should == nil }
|
23
23
|
end
|
24
24
|
|
25
25
|
context 'with a simple unescaped result with no data' do
|
26
26
|
let(:result_string) { "200 result=123" }
|
27
27
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
28
|
+
it { subject.code.should == 200 }
|
29
|
+
it { subject.result.should == 123 }
|
30
|
+
it { subject.data.should == '' }
|
31
|
+
it { subject.data_hash.should == nil }
|
32
32
|
end
|
33
33
|
|
34
34
|
context 'with a result and data in parens' do
|
35
35
|
let(:result_string) { "200%20result=-123%20(timeout)%0A" }
|
36
36
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
37
|
+
it { subject.code.should == 200 }
|
38
|
+
it { subject.result.should == -123 }
|
39
|
+
it { subject.data.should == 'timeout' }
|
40
|
+
it { subject.data_hash.should == nil }
|
41
41
|
end
|
42
42
|
|
43
43
|
context 'with a result and key-value data' do
|
44
44
|
let(:result_string) { "200%20result=123%20foo=bar%0A" }
|
45
45
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
46
|
+
it { subject.code.should == 200 }
|
47
|
+
it { subject.result.should == 123 }
|
48
|
+
it { subject.data.should == 'foo=bar' }
|
49
|
+
it { subject.data_hash.should == {'foo' => 'bar'} }
|
50
50
|
end
|
51
51
|
|
52
52
|
context 'with a 5xx error' do
|
53
53
|
let(:result_string) { "510%20Invalid%20or%20unknown%20command%0A" }
|
54
54
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
55
|
+
it { subject.code.should == 510 }
|
56
|
+
it { subject.result.should be_nil }
|
57
|
+
it { subject.data.should == 'Invalid or unknown command' }
|
58
|
+
it { subject.data_hash.should be_nil }
|
59
59
|
end
|
60
60
|
end
|
61
61
|
end
|
@@ -9,12 +9,12 @@ module RubyAMI
|
|
9
9
|
|
10
10
|
subject { described_class.new environment_string }
|
11
11
|
|
12
|
-
|
13
|
-
|
12
|
+
it { subject.to_s.should == environment_string }
|
13
|
+
it { subject.to_s.should_not be environment_string }
|
14
14
|
|
15
15
|
describe 'retrieving a hash representation' do
|
16
|
-
|
17
|
-
should == {
|
16
|
+
it "should return a hash of attributes" do
|
17
|
+
subject.to_hash.should == {
|
18
18
|
:agi_request => 'async',
|
19
19
|
:agi_channel => 'SIP/1234-00000000',
|
20
20
|
:agi_language => 'en',
|
data/spec/ruby_ami/error_spec.rb
CHANGED
@@ -6,13 +6,13 @@ module RubyAMI
|
|
6
6
|
let(:server_port) { 50000 - rand(1000) }
|
7
7
|
|
8
8
|
def client
|
9
|
-
@client ||=
|
9
|
+
@client ||= double('Client')
|
10
10
|
end
|
11
11
|
|
12
12
|
before do
|
13
|
-
def client.message_received(message)
|
13
|
+
def client.message_received(message, stream)
|
14
14
|
@messages ||= Queue.new
|
15
|
-
@messages << message
|
15
|
+
@messages << [message, stream]
|
16
16
|
end
|
17
17
|
|
18
18
|
def client.messages
|
@@ -31,37 +31,29 @@ module RubyAMI
|
|
31
31
|
|
32
32
|
def mocked_server(times = nil, fake_client = nil, &block)
|
33
33
|
mock_target = MockServer.new
|
34
|
-
mock_target.should_receive(:receive_data).send(*(times ? [:exactly, times] : [:at_least, 1])
|
34
|
+
mock_target.should_receive(:receive_data).send(*(times ? [:exactly, times] : [:at_least, 1]), &block)
|
35
35
|
s = ServerMock.new '127.0.0.1', server_port, mock_target
|
36
|
-
@stream = Stream.new '127.0.0.1', server_port, username, password, lambda { |m| client.message_received m }
|
37
|
-
@stream.async.run
|
36
|
+
@stream = Stream.new '127.0.0.1', server_port, username, password, lambda { |m, stream| client.message_received m, stream }
|
38
37
|
fake_client.call if fake_client.respond_to? :call
|
39
|
-
Celluloid::Actor.join s
|
40
38
|
Timeout.timeout 5 do
|
39
|
+
Celluloid::Actor.join s
|
41
40
|
Celluloid::Actor.join @stream
|
42
41
|
end
|
43
|
-
|
44
|
-
|
45
|
-
def expect_connected_event
|
46
|
-
client.should_receive(:message_received).with Stream::Connected.new
|
47
|
-
end
|
48
|
-
|
49
|
-
def expect_disconnected_event
|
50
|
-
client.should_receive(:message_received).with Stream::Disconnected.new
|
42
|
+
rescue Timeout::Error
|
51
43
|
end
|
52
44
|
|
53
45
|
before { @sequence = 1 }
|
54
46
|
|
55
47
|
describe "after connection" do
|
56
48
|
it "should be started" do
|
57
|
-
|
58
|
-
|
59
|
-
|
49
|
+
mocked_server 0, -> { @stream.started?.should be true }
|
50
|
+
client_messages.should be == [
|
51
|
+
[Stream::Connected.new, @stream],
|
52
|
+
[Stream::Disconnected.new, @stream],
|
53
|
+
]
|
60
54
|
end
|
61
55
|
|
62
56
|
it "stores the reported AMI version" do
|
63
|
-
expect_connected_event
|
64
|
-
expect_disconnected_event
|
65
57
|
mocked_server(1, lambda {
|
66
58
|
@stream.send_action('Command') # Just to get the server kicked in to replying using the below block
|
67
59
|
expect(@stream.version).to eq('2.8.0')
|
@@ -79,8 +71,6 @@ Message: Recording started
|
|
79
71
|
end
|
80
72
|
|
81
73
|
it "can send an action" do
|
82
|
-
expect_connected_event
|
83
|
-
expect_disconnected_event
|
84
74
|
mocked_server(1, lambda { @stream.send_action('Command') }) do |val, server|
|
85
75
|
val.should == <<-ACTION
|
86
76
|
Action: command\r
|
@@ -98,8 +88,6 @@ Message: Recording started
|
|
98
88
|
end
|
99
89
|
|
100
90
|
it "can send an action with headers" do
|
101
|
-
expect_connected_event
|
102
|
-
expect_disconnected_event
|
103
91
|
mocked_server(1, lambda { @stream.send_action('Command', 'Command' => 'RECORD FILE evil') }) do |val, server|
|
104
92
|
val.should == <<-ACTION
|
105
93
|
Action: command\r
|
@@ -148,8 +136,6 @@ Extension '1,1,AGI(agi:async)' added into 'adhearsion-redirect' context
|
|
148
136
|
let(:password) { 'jones' }
|
149
137
|
|
150
138
|
it "should log itself in" do
|
151
|
-
expect_connected_event
|
152
|
-
expect_disconnected_event
|
153
139
|
mocked_server(1, lambda { }) do |val, server|
|
154
140
|
val.should == <<-ACTION
|
155
141
|
Action: login\r
|
@@ -183,18 +169,13 @@ Cause: 0
|
|
183
169
|
end
|
184
170
|
|
185
171
|
client_messages.should be == [
|
186
|
-
Stream::Connected.new,
|
187
|
-
Event.new('Hangup', 'Channel' => 'SIP/101-3f3f', 'Uniqueid' => '1094154427.10', 'Cause' => '0'),
|
188
|
-
Stream::Disconnected.new
|
172
|
+
[Stream::Connected.new, @stream],
|
173
|
+
[Event.new('Hangup', 'Channel' => 'SIP/101-3f3f', 'Uniqueid' => '1094154427.10', 'Cause' => '0'), @stream],
|
174
|
+
[Stream::Disconnected.new, @stream],
|
189
175
|
]
|
190
176
|
end
|
191
177
|
|
192
178
|
describe 'when a response is received' do
|
193
|
-
before do
|
194
|
-
expect_connected_event
|
195
|
-
expect_disconnected_event
|
196
|
-
end
|
197
|
-
|
198
179
|
it 'should be returned from #send_action' do
|
199
180
|
response = nil
|
200
181
|
mocked_server(1, lambda { response = @stream.send_action 'Command', 'Command' => 'RECORD FILE evil' }) do |val, server|
|
@@ -219,7 +200,7 @@ Message: Thanks for all the fish.
|
|
219
200
|
|
220
201
|
EVENT
|
221
202
|
end
|
222
|
-
|
203
|
+
|
223
204
|
response.should == Response.new('ActionID' => RubyAMI.new_uuid, 'Message' => 'Thanks for all the fish.')
|
224
205
|
end
|
225
206
|
|
@@ -304,20 +285,26 @@ ActionID: #{RubyAMI.new_uuid}
|
|
304
285
|
end
|
305
286
|
|
306
287
|
it 'puts itself in the stopped state and fires a disconnected event when unbound' do
|
307
|
-
expect_connected_event
|
308
|
-
expect_disconnected_event
|
309
288
|
mocked_server(1, lambda { @stream.send_data 'Foo' }) do |val, server|
|
310
289
|
@stream.stopped?.should be false
|
311
290
|
end
|
312
291
|
@stream.alive?.should be false
|
292
|
+
client_messages.should be == [
|
293
|
+
[Stream::Connected.new, @stream],
|
294
|
+
[Stream::Disconnected.new, @stream],
|
295
|
+
]
|
313
296
|
end
|
314
297
|
end
|
315
298
|
|
316
299
|
describe Stream::Connected do
|
317
|
-
|
300
|
+
it "has a name matching the class" do
|
301
|
+
subject.name.should == 'RubyAMI::Stream::Connected'
|
302
|
+
end
|
318
303
|
end
|
319
304
|
|
320
305
|
describe Stream::Disconnected do
|
321
|
-
|
306
|
+
it "has a name matching the class" do
|
307
|
+
subject.name.should == 'RubyAMI::Stream::Disconnected'
|
308
|
+
end
|
322
309
|
end
|
323
310
|
end
|
data/spec/spec_helper.rb
CHANGED
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:
|
4
|
+
version: 3.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ben Langfeld
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2016-07-25 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: celluloid-io
|
@@ -25,6 +25,20 @@ dependencies:
|
|
25
25
|
- - "~>"
|
26
26
|
- !ruby/object:Gem::Version
|
27
27
|
version: '0.13'
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: celluloid
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - "~>"
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: 0.16.0
|
35
|
+
type: :runtime
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - "~>"
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: 0.16.0
|
28
42
|
- !ruby/object:Gem::Dependency
|
29
43
|
name: bundler
|
30
44
|
requirement: !ruby/object:Gem::Requirement
|
@@ -196,7 +210,6 @@ files:
|
|
196
210
|
- lib/ruby_ami/action.rb
|
197
211
|
- lib/ruby_ami/agi_result_parser.rb
|
198
212
|
- lib/ruby_ami/async_agi_environment_parser.rb
|
199
|
-
- lib/ruby_ami/client.rb
|
200
213
|
- lib/ruby_ami/core_ext/celluloid.rb
|
201
214
|
- lib/ruby_ami/error.rb
|
202
215
|
- lib/ruby_ami/event.rb
|
@@ -208,7 +221,6 @@ files:
|
|
208
221
|
- spec/ruby_ami/action_spec.rb
|
209
222
|
- spec/ruby_ami/agi_result_parser_spec.rb
|
210
223
|
- spec/ruby_ami/async_agi_environment_parser_spec.rb
|
211
|
-
- spec/ruby_ami/client_spec.rb
|
212
224
|
- spec/ruby_ami/error_spec.rb
|
213
225
|
- spec/ruby_ami/event_spec.rb
|
214
226
|
- spec/ruby_ami/response_spec.rb
|
@@ -226,7 +238,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
226
238
|
requirements:
|
227
239
|
- - ">="
|
228
240
|
- !ruby/object:Gem::Version
|
229
|
-
version:
|
241
|
+
version: 2.2.0
|
230
242
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
231
243
|
requirements:
|
232
244
|
- - ">="
|
@@ -234,7 +246,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
234
246
|
version: '0'
|
235
247
|
requirements: []
|
236
248
|
rubyforge_project: ruby_ami
|
237
|
-
rubygems_version: 2.
|
249
|
+
rubygems_version: 2.5.1
|
238
250
|
signing_key:
|
239
251
|
specification_version: 4
|
240
252
|
summary: Futzing with AMI so you don't have to
|
@@ -248,11 +260,9 @@ test_files:
|
|
248
260
|
- spec/ruby_ami/action_spec.rb
|
249
261
|
- spec/ruby_ami/agi_result_parser_spec.rb
|
250
262
|
- spec/ruby_ami/async_agi_environment_parser_spec.rb
|
251
|
-
- spec/ruby_ami/client_spec.rb
|
252
263
|
- spec/ruby_ami/error_spec.rb
|
253
264
|
- spec/ruby_ami/event_spec.rb
|
254
265
|
- spec/ruby_ami/response_spec.rb
|
255
266
|
- spec/ruby_ami/stream_spec.rb
|
256
267
|
- spec/spec_helper.rb
|
257
268
|
- spec/support/mock_server.rb
|
258
|
-
has_rdoc:
|
data/lib/ruby_ami/client.rb
DELETED
@@ -1,77 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
module RubyAMI
|
3
|
-
class Client
|
4
|
-
include Celluloid
|
5
|
-
|
6
|
-
trap_exit :stream_died
|
7
|
-
|
8
|
-
attr_reader :events_stream, :actions_stream
|
9
|
-
|
10
|
-
def initialize(options)
|
11
|
-
@options = options
|
12
|
-
@event_handler = @options[:event_handler]
|
13
|
-
@state = :stopped
|
14
|
-
client = current_actor
|
15
|
-
@events_stream = new_stream ->(event) { client.async.handle_event event }
|
16
|
-
@actions_stream = new_stream ->(message) { client.async.handle_message message }
|
17
|
-
end
|
18
|
-
|
19
|
-
[:started, :stopped, :ready].each do |state|
|
20
|
-
define_method("#{state}?") { @state == state }
|
21
|
-
end
|
22
|
-
|
23
|
-
def start
|
24
|
-
@events_stream.async.run
|
25
|
-
@actions_stream.async.run
|
26
|
-
@state = :started
|
27
|
-
end
|
28
|
-
|
29
|
-
def send_action(*args)
|
30
|
-
actions_stream.send_action *args
|
31
|
-
end
|
32
|
-
|
33
|
-
def handle_message(message)
|
34
|
-
logger.trace "[RECV-ACTIONS]: #{message.inspect}"
|
35
|
-
case message
|
36
|
-
when Stream::Connected
|
37
|
-
send_action 'Events', 'EventMask' => 'Off'
|
38
|
-
when Stream::Disconnected
|
39
|
-
when Event
|
40
|
-
pass_event message
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
def handle_event(event)
|
45
|
-
logger.trace "[RECV-EVENTS]: #{event.inspect}"
|
46
|
-
case event
|
47
|
-
when Stream::Connected, Stream::Disconnected
|
48
|
-
else
|
49
|
-
pass_event event
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
private
|
54
|
-
|
55
|
-
def pass_event(event)
|
56
|
-
@event_handler.call event if @event_handler.respond_to? :call
|
57
|
-
end
|
58
|
-
|
59
|
-
def new_stream(callback)
|
60
|
-
Stream.new_link @options[:host], @options[:port], @options[:username], @options[:password], callback, logger, @options[:timeout]
|
61
|
-
end
|
62
|
-
|
63
|
-
def stream_died(stream, reason = nil)
|
64
|
-
terminate
|
65
|
-
end
|
66
|
-
|
67
|
-
def logger
|
68
|
-
super
|
69
|
-
rescue
|
70
|
-
@logger ||= begin
|
71
|
-
logger = Logger
|
72
|
-
logger.define_singleton_method :trace, logger.method(:debug)
|
73
|
-
logger
|
74
|
-
end
|
75
|
-
end
|
76
|
-
end
|
77
|
-
end
|
@@ -1,90 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
require 'spec_helper'
|
3
|
-
|
4
|
-
module RubyAMI
|
5
|
-
describe Client do
|
6
|
-
let(:event_handler) { [] }
|
7
|
-
|
8
|
-
let(:options) do
|
9
|
-
{
|
10
|
-
:host => '127.0.0.1',
|
11
|
-
:port => 50000 - rand(1000),
|
12
|
-
:username => 'username',
|
13
|
-
:password => 'password',
|
14
|
-
:event_handler => lambda { |event| event_handler << event }
|
15
|
-
}
|
16
|
-
end
|
17
|
-
|
18
|
-
subject { Client.new options }
|
19
|
-
|
20
|
-
it { should be_stopped }
|
21
|
-
|
22
|
-
its(:events_stream) { should be_a Stream }
|
23
|
-
its(:actions_stream) { should be_a Stream }
|
24
|
-
|
25
|
-
it 'should return when the timeout option is specified and reached' do
|
26
|
-
pending
|
27
|
-
options[:timeout] = 2
|
28
|
-
options[:host] = '192.0.2.1' # unreachable IP that will generally cause a timeout (RFC 5737)
|
29
|
-
|
30
|
-
start_time = Time.now
|
31
|
-
subject.start
|
32
|
-
duration = Time.now - start_time
|
33
|
-
|
34
|
-
duration.should be_between(options[:timeout], options[:timeout] + 1)
|
35
|
-
end
|
36
|
-
|
37
|
-
describe 'starting up' do
|
38
|
-
before do
|
39
|
-
ms = MockServer.new
|
40
|
-
ms.should_receive(:receive_data).at_least :once
|
41
|
-
s = ServerMock.new options[:host], options[:port], ms
|
42
|
-
subject.async.start
|
43
|
-
sleep 0.2
|
44
|
-
end
|
45
|
-
|
46
|
-
it { should be_started }
|
47
|
-
end
|
48
|
-
|
49
|
-
describe 'logging in streams' do
|
50
|
-
context 'when the actions stream connects' do
|
51
|
-
let(:mock_actions_stream) { mock 'Actions Stream' }
|
52
|
-
|
53
|
-
before do
|
54
|
-
subject.wrapped_object.stub(:actions_stream).and_return mock_actions_stream
|
55
|
-
end
|
56
|
-
|
57
|
-
it 'should disable events' do
|
58
|
-
mock_actions_stream.should_receive(:send_action).with 'Events', 'EventMask' => 'Off'
|
59
|
-
|
60
|
-
subject.handle_message Stream::Connected.new
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
describe 'when the events stream disconnects' do
|
66
|
-
it 'should shut down the client' do
|
67
|
-
subject.events_stream.terminate
|
68
|
-
sleep 0.2
|
69
|
-
subject.alive?.should be_false
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
describe 'when the actions stream disconnects' do
|
74
|
-
it 'should shut down the client' do
|
75
|
-
subject.actions_stream.terminate
|
76
|
-
sleep 0.2
|
77
|
-
subject.alive?.should be_false
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
describe 'when an event is received' do
|
82
|
-
let(:event) { Event.new 'foobar' }
|
83
|
-
|
84
|
-
it 'should call the event handler' do
|
85
|
-
subject.handle_event event
|
86
|
-
event_handler.should == [event]
|
87
|
-
end
|
88
|
-
end
|
89
|
-
end
|
90
|
-
end
|