ruby_ami 2.2.1 → 2.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +6 -0
- data/README.md +12 -6
- data/lib/ruby_ami/lexer.rb +3 -2
- data/lib/ruby_ami/stream.rb +9 -3
- data/lib/ruby_ami/version.rb +1 -1
- data/spec/ruby_ami/client_spec.rb +2 -2
- data/spec/ruby_ami/stream_spec.rb +51 -7
- metadata +32 -56
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 5953bb5e6fa27b0deeee4847c2a858ac569fcc0a
|
4
|
+
data.tar.gz: 55cf2a66637e697c1395e89da1ae2c6d79dd05cf
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a98f86436f743270e3d0950a3056ac3ed673df03648379726a86a28717cc059688f94aded13b090c98cb25e7456a2c61af60179d2702dcacd53dd2ed5d4e298f
|
7
|
+
data.tar.gz: d5a21a2b1bd5e35813a38d724415a3acd519a4461e9690ef0e2bd177a1d74037c3bc2cf62bc34748b4b8c447866130ac996fb7b87763f4901e081c31a8a2a8cb
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,11 @@
|
|
1
1
|
# [develop](https://github.com/adhearsion/ruby_ami)
|
2
2
|
|
3
|
+
# [2.3.0](https://github.com/adhearsion/ruby_ami/compare/v2.2.1...v2.3.0) - [2015-06-01](https://rubygems.org/gems/ruby_ami/versions/2.3.0)
|
4
|
+
* Feature: Allow optional error handler when calling `send_action`
|
5
|
+
* Bugfix: Catch for Errno::HOSTUNREACH error when connecting to AMI
|
6
|
+
* Bugfix: Add support for Goodbye responses instead of processing them as syntax errors
|
7
|
+
* Bugfix: Allow simple example of event handling (switching on `event.name` only) to work by giving connection status events a name to match their class names
|
8
|
+
|
3
9
|
# [2.2.1](https://github.com/adhearsion/ruby_ami/compare/v2.2.0...v2.2.1) - [2014-05-22](https://rubygems.org/gems/ruby_ami/versions/2.2.1)
|
4
10
|
* Bugfix: Ensure DateTime is present when consumer code doesn't `require 'time'` ([#24](https://github.com/adhearsion/ruby_ami/issues/24))
|
5
11
|
|
data/README.md
CHANGED
@@ -1,4 +1,12 @@
|
|
1
|
-
# RubyAMI
|
1
|
+
# RubyAMI
|
2
|
+
|
3
|
+
[![Gem Version](https://badge.fury.io/rb/ruby_ami.png)](https://rubygems.org/gems/ruby_ami)
|
4
|
+
[![Build Status](https://secure.travis-ci.org/adhearsion/ruby_ami.png?branch=develop)](http://travis-ci.org/adhearsion/ruby_ami)
|
5
|
+
[![Dependency Status](https://gemnasium.com/adhearsion/ruby_ami.png?travis)](https://gemnasium.com/adhearsion/ruby_ami)
|
6
|
+
[![Code Climate](https://codeclimate.com/github/adhearsion/ruby_ami.png)](https://codeclimate.com/github/adhearsion/ruby_ami)
|
7
|
+
[![Coverage Status](https://coveralls.io/repos/adhearsion/ruby_ami/badge.png?branch=develop)](https://coveralls.io/r/adhearsion/ruby_ami)
|
8
|
+
[![Inline docs](http://inch-ci.org/github/adhearsion/ruby_ami.png?branch=develop)](http://inch-ci.org/github/adhearsion/ruby_ami)
|
9
|
+
|
2
10
|
RubyAMI is an AMI client library in Ruby and based on EventMachine 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.
|
3
11
|
|
4
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.
|
@@ -10,20 +18,18 @@ NB: If you're looking to develop an application on Asterisk, you should take a l
|
|
10
18
|
```ruby
|
11
19
|
require 'ruby_ami'
|
12
20
|
|
13
|
-
stream = RubyAMI::Stream.new '127.0.0.1', 5038, 'manager', 'password',
|
21
|
+
$stream = RubyAMI::Stream.new '127.0.0.1', 5038, 'manager', 'password',
|
14
22
|
->(e) { handle_event e },
|
15
23
|
Logger.new(STDOUT), 10
|
16
24
|
|
17
25
|
def handle_event(event)
|
18
26
|
case event.name
|
19
27
|
when 'FullyBooted'
|
20
|
-
stream.async.send_action 'Originate', 'Channel' => 'SIP/foo'
|
28
|
+
$stream.async.send_action 'Originate', 'Channel' => 'SIP/foo'
|
21
29
|
end
|
22
30
|
end
|
23
31
|
|
24
|
-
stream.
|
25
|
-
|
26
|
-
Celluloid::Actor.join stream
|
32
|
+
$stream.run # This will block until the actor is terminated elsewhere. $stream.async.run is also available if you need to do other things in the main thread.
|
27
33
|
```
|
28
34
|
|
29
35
|
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**.
|
data/lib/ruby_ami/lexer.rb
CHANGED
@@ -11,10 +11,11 @@ module RubyAMI
|
|
11
11
|
EVENT = /event: *(?<event_name>.*)?/i
|
12
12
|
ERROR = /response: *error/i
|
13
13
|
FOLLOWS = /response: *follows/i
|
14
|
+
GOODBYE = /response: *goodbye/i
|
14
15
|
SCANNER = /.*?#{STANZA_BREAK}/m
|
15
16
|
HEADER_SLICE = /.*\r\n/
|
16
17
|
IMMEDIATE_RESP = /.*/
|
17
|
-
CLASSIFIER = /((?<event>#{EVENT})|(?<success>#{SUCCESS})|(?<pong>#{PONG})|(?<follows>#{FOLLOWS})|(?<error>#{ERROR})|(?<immediate>#{IMMEDIATE_RESP})\r\n)\r\n/i
|
18
|
+
CLASSIFIER = /((?<event>#{EVENT})|(?<success>#{SUCCESS})|(?<pong>#{PONG})|(?<follows>#{FOLLOWS})|(?<error>#{ERROR})|(?<goodbye>#{GOODBYE})|(?<immediate>#{IMMEDIATE_RESP})\r\n)\r\n/i
|
18
19
|
|
19
20
|
attr_accessor :ami_version
|
20
21
|
|
@@ -69,7 +70,7 @@ module RubyAMI
|
|
69
70
|
|
70
71
|
msg = if match[:event]
|
71
72
|
Event.new match[:event_name]
|
72
|
-
elsif match[:success] || match[:pong]
|
73
|
+
elsif match[:success] || match[:pong] || match[:goodbye]
|
73
74
|
Response.new
|
74
75
|
elsif match[:follows]
|
75
76
|
response_follows = true
|
data/lib/ruby_ami/stream.rb
CHANGED
@@ -2,6 +2,10 @@
|
|
2
2
|
module RubyAMI
|
3
3
|
class Stream
|
4
4
|
class ConnectionStatus
|
5
|
+
def name
|
6
|
+
self.class.to_s
|
7
|
+
end
|
8
|
+
|
5
9
|
def eql?(other)
|
6
10
|
other.is_a? self.class
|
7
11
|
end
|
@@ -37,7 +41,7 @@ module RubyAMI
|
|
37
41
|
end
|
38
42
|
post_init
|
39
43
|
loop { receive_data @socket.readpartial(4096) }
|
40
|
-
rescue Errno::ECONNREFUSED, SocketError => e
|
44
|
+
rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH, SocketError => e
|
41
45
|
logger.error "Connection failed due to #{e.class}. Check your config and the server."
|
42
46
|
rescue EOFError
|
43
47
|
logger.info "Client socket closed!"
|
@@ -57,14 +61,16 @@ module RubyAMI
|
|
57
61
|
@socket.write data
|
58
62
|
end
|
59
63
|
|
60
|
-
def send_action(name, headers = {})
|
64
|
+
def send_action(name, headers = {}, error_handler = self.method(:abort))
|
61
65
|
condition = Celluloid::Condition.new
|
62
66
|
action = dispatch_action name, headers do |response|
|
63
67
|
condition.signal response
|
64
68
|
end
|
65
69
|
condition.wait
|
66
70
|
action.response.tap do |resp|
|
67
|
-
|
71
|
+
if resp.is_a? Exception
|
72
|
+
error_handler.call(resp)
|
73
|
+
end
|
68
74
|
end
|
69
75
|
end
|
70
76
|
|
data/lib/ruby_ami/version.rb
CHANGED
@@ -66,7 +66,7 @@ module RubyAMI
|
|
66
66
|
it 'should shut down the client' do
|
67
67
|
subject.events_stream.terminate
|
68
68
|
sleep 0.2
|
69
|
-
subject.
|
69
|
+
subject.alive?.should be_false
|
70
70
|
end
|
71
71
|
end
|
72
72
|
|
@@ -74,7 +74,7 @@ module RubyAMI
|
|
74
74
|
it 'should shut down the client' do
|
75
75
|
subject.actions_stream.terminate
|
76
76
|
sleep 0.2
|
77
|
-
subject.
|
77
|
+
subject.alive?.should be_false
|
78
78
|
end
|
79
79
|
end
|
80
80
|
|
@@ -190,20 +190,56 @@ Message: Recording started
|
|
190
190
|
response.should == Response.new('ActionID' => RubyAMI.new_uuid, 'Message' => 'Recording started')
|
191
191
|
end
|
192
192
|
|
193
|
+
it 'should handle disconnect as a Response' do
|
194
|
+
response = nil
|
195
|
+
mocked_server(1, lambda { response = @stream.send_action 'Logoff' }) do |val, server|
|
196
|
+
server.send_data <<-EVENT
|
197
|
+
Response: Goodbye
|
198
|
+
ActionID: #{RubyAMI.new_uuid}
|
199
|
+
Message: Thanks for all the fish.
|
200
|
+
|
201
|
+
EVENT
|
202
|
+
end
|
203
|
+
|
204
|
+
response.should == Response.new('ActionID' => RubyAMI.new_uuid, 'Message' => 'Thanks for all the fish.')
|
205
|
+
end
|
206
|
+
|
193
207
|
describe 'when it is an error' do
|
194
|
-
|
195
|
-
send_action
|
196
|
-
|
197
|
-
|
208
|
+
describe 'when there is no error handler' do
|
209
|
+
it 'should be raised by #send_action, but not kill the stream' do
|
210
|
+
send_action = lambda do
|
211
|
+
expect { @stream.send_action 'status' }.to raise_error(RubyAMI::Error, 'Action failed')
|
212
|
+
@stream.should be_alive
|
213
|
+
end
|
214
|
+
|
215
|
+
mocked_server(1, send_action) do |val, server|
|
216
|
+
server.send_data <<-EVENT
|
217
|
+
Response: Error
|
218
|
+
ActionID: #{RubyAMI.new_uuid}
|
219
|
+
Message: Action failed
|
220
|
+
|
221
|
+
EVENT
|
222
|
+
end
|
198
223
|
end
|
224
|
+
end
|
199
225
|
|
200
|
-
|
201
|
-
|
226
|
+
describe 'when there is an error handler' do
|
227
|
+
it 'should call the error handler' do
|
228
|
+
error_handler = lambda { |resp| resp.should be_a_kind_of RubyAMI::Error }
|
229
|
+
|
230
|
+
send_action = lambda do
|
231
|
+
expect { @stream.send_action 'status', {}, error_handler }.to_not raise_error
|
232
|
+
@stream.should be_alive
|
233
|
+
end
|
234
|
+
|
235
|
+
mocked_server(1, send_action) do |val, server|
|
236
|
+
server.send_data <<-EVENT
|
202
237
|
Response: Error
|
203
238
|
ActionID: #{RubyAMI.new_uuid}
|
204
239
|
Message: Action failed
|
205
240
|
|
206
|
-
|
241
|
+
EVENT
|
242
|
+
end
|
207
243
|
end
|
208
244
|
end
|
209
245
|
end
|
@@ -257,4 +293,12 @@ ActionID: #{RubyAMI.new_uuid}
|
|
257
293
|
@stream.alive?.should be false
|
258
294
|
end
|
259
295
|
end
|
296
|
+
|
297
|
+
describe Stream::Connected do
|
298
|
+
its(:name) { should == 'RubyAMI::Stream::Connected' }
|
299
|
+
end
|
300
|
+
|
301
|
+
describe Stream::Disconnected do
|
302
|
+
its(:name) { should == 'RubyAMI::Stream::Disconnected' }
|
303
|
+
end
|
260
304
|
end
|
metadata
CHANGED
@@ -1,8 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby_ami
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
5
|
-
prerelease:
|
4
|
+
version: 2.3.0
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Ben Langfeld
|
@@ -10,182 +9,160 @@ authors:
|
|
10
9
|
autorequire:
|
11
10
|
bindir: bin
|
12
11
|
cert_chain: []
|
13
|
-
date:
|
12
|
+
date: 2015-06-03 00:00:00.000000000 Z
|
14
13
|
dependencies:
|
15
14
|
- !ruby/object:Gem::Dependency
|
16
15
|
name: celluloid-io
|
17
16
|
requirement: !ruby/object:Gem::Requirement
|
18
|
-
none: false
|
19
17
|
requirements:
|
20
|
-
- - ~>
|
18
|
+
- - "~>"
|
21
19
|
- !ruby/object:Gem::Version
|
22
20
|
version: '0.13'
|
23
21
|
type: :runtime
|
24
22
|
prerelease: false
|
25
23
|
version_requirements: !ruby/object:Gem::Requirement
|
26
|
-
none: false
|
27
24
|
requirements:
|
28
|
-
- - ~>
|
25
|
+
- - "~>"
|
29
26
|
- !ruby/object:Gem::Version
|
30
27
|
version: '0.13'
|
31
28
|
- !ruby/object:Gem::Dependency
|
32
29
|
name: bundler
|
33
30
|
requirement: !ruby/object:Gem::Requirement
|
34
|
-
none: false
|
35
31
|
requirements:
|
36
|
-
- - ~>
|
32
|
+
- - "~>"
|
37
33
|
- !ruby/object:Gem::Version
|
38
34
|
version: '1.0'
|
39
35
|
type: :development
|
40
36
|
prerelease: false
|
41
37
|
version_requirements: !ruby/object:Gem::Requirement
|
42
|
-
none: false
|
43
38
|
requirements:
|
44
|
-
- - ~>
|
39
|
+
- - "~>"
|
45
40
|
- !ruby/object:Gem::Version
|
46
41
|
version: '1.0'
|
47
42
|
- !ruby/object:Gem::Dependency
|
48
43
|
name: rspec
|
49
44
|
requirement: !ruby/object:Gem::Requirement
|
50
|
-
none: false
|
51
45
|
requirements:
|
52
|
-
- - ~>
|
46
|
+
- - "~>"
|
53
47
|
- !ruby/object:Gem::Version
|
54
48
|
version: '2.5'
|
55
49
|
type: :development
|
56
50
|
prerelease: false
|
57
51
|
version_requirements: !ruby/object:Gem::Requirement
|
58
|
-
none: false
|
59
52
|
requirements:
|
60
|
-
- - ~>
|
53
|
+
- - "~>"
|
61
54
|
- !ruby/object:Gem::Version
|
62
55
|
version: '2.5'
|
63
56
|
- !ruby/object:Gem::Dependency
|
64
57
|
name: cucumber
|
65
58
|
requirement: !ruby/object:Gem::Requirement
|
66
|
-
none: false
|
67
59
|
requirements:
|
68
|
-
- -
|
60
|
+
- - ">="
|
69
61
|
- !ruby/object:Gem::Version
|
70
62
|
version: '0'
|
71
63
|
type: :development
|
72
64
|
prerelease: false
|
73
65
|
version_requirements: !ruby/object:Gem::Requirement
|
74
|
-
none: false
|
75
66
|
requirements:
|
76
|
-
- -
|
67
|
+
- - ">="
|
77
68
|
- !ruby/object:Gem::Version
|
78
69
|
version: '0'
|
79
70
|
- !ruby/object:Gem::Dependency
|
80
71
|
name: yard
|
81
72
|
requirement: !ruby/object:Gem::Requirement
|
82
|
-
none: false
|
83
73
|
requirements:
|
84
|
-
- - ~>
|
74
|
+
- - "~>"
|
85
75
|
- !ruby/object:Gem::Version
|
86
76
|
version: '0.6'
|
87
77
|
type: :development
|
88
78
|
prerelease: false
|
89
79
|
version_requirements: !ruby/object:Gem::Requirement
|
90
|
-
none: false
|
91
80
|
requirements:
|
92
|
-
- - ~>
|
81
|
+
- - "~>"
|
93
82
|
- !ruby/object:Gem::Version
|
94
83
|
version: '0.6'
|
95
84
|
- !ruby/object:Gem::Dependency
|
96
85
|
name: rake
|
97
86
|
requirement: !ruby/object:Gem::Requirement
|
98
|
-
none: false
|
99
87
|
requirements:
|
100
|
-
- -
|
88
|
+
- - ">="
|
101
89
|
- !ruby/object:Gem::Version
|
102
90
|
version: '0'
|
103
91
|
type: :development
|
104
92
|
prerelease: false
|
105
93
|
version_requirements: !ruby/object:Gem::Requirement
|
106
|
-
none: false
|
107
94
|
requirements:
|
108
|
-
- -
|
95
|
+
- - ">="
|
109
96
|
- !ruby/object:Gem::Version
|
110
97
|
version: '0'
|
111
98
|
- !ruby/object:Gem::Dependency
|
112
99
|
name: guard-rspec
|
113
100
|
requirement: !ruby/object:Gem::Requirement
|
114
|
-
none: false
|
115
101
|
requirements:
|
116
|
-
- -
|
102
|
+
- - ">="
|
117
103
|
- !ruby/object:Gem::Version
|
118
104
|
version: '0'
|
119
105
|
type: :development
|
120
106
|
prerelease: false
|
121
107
|
version_requirements: !ruby/object:Gem::Requirement
|
122
|
-
none: false
|
123
108
|
requirements:
|
124
|
-
- -
|
109
|
+
- - ">="
|
125
110
|
- !ruby/object:Gem::Version
|
126
111
|
version: '0'
|
127
112
|
- !ruby/object:Gem::Dependency
|
128
113
|
name: guard-shell
|
129
114
|
requirement: !ruby/object:Gem::Requirement
|
130
|
-
none: false
|
131
115
|
requirements:
|
132
|
-
- -
|
116
|
+
- - ">="
|
133
117
|
- !ruby/object:Gem::Version
|
134
118
|
version: '0'
|
135
119
|
type: :development
|
136
120
|
prerelease: false
|
137
121
|
version_requirements: !ruby/object:Gem::Requirement
|
138
|
-
none: false
|
139
122
|
requirements:
|
140
|
-
- -
|
123
|
+
- - ">="
|
141
124
|
- !ruby/object:Gem::Version
|
142
125
|
version: '0'
|
143
126
|
- !ruby/object:Gem::Dependency
|
144
127
|
name: guard-cucumber
|
145
128
|
requirement: !ruby/object:Gem::Requirement
|
146
|
-
none: false
|
147
129
|
requirements:
|
148
|
-
- -
|
130
|
+
- - ">="
|
149
131
|
- !ruby/object:Gem::Version
|
150
132
|
version: '0'
|
151
133
|
type: :development
|
152
134
|
prerelease: false
|
153
135
|
version_requirements: !ruby/object:Gem::Requirement
|
154
|
-
none: false
|
155
136
|
requirements:
|
156
|
-
- -
|
137
|
+
- - ">="
|
157
138
|
- !ruby/object:Gem::Version
|
158
139
|
version: '0'
|
159
140
|
- !ruby/object:Gem::Dependency
|
160
141
|
name: guard-rake
|
161
142
|
requirement: !ruby/object:Gem::Requirement
|
162
|
-
none: false
|
163
143
|
requirements:
|
164
|
-
- -
|
144
|
+
- - ">="
|
165
145
|
- !ruby/object:Gem::Version
|
166
146
|
version: '0'
|
167
147
|
type: :development
|
168
148
|
prerelease: false
|
169
149
|
version_requirements: !ruby/object:Gem::Requirement
|
170
|
-
none: false
|
171
150
|
requirements:
|
172
|
-
- -
|
151
|
+
- - ">="
|
173
152
|
- !ruby/object:Gem::Version
|
174
153
|
version: '0'
|
175
154
|
- !ruby/object:Gem::Dependency
|
176
155
|
name: benchmark_suite
|
177
156
|
requirement: !ruby/object:Gem::Requirement
|
178
|
-
none: false
|
179
157
|
requirements:
|
180
|
-
- -
|
158
|
+
- - ">="
|
181
159
|
- !ruby/object:Gem::Version
|
182
160
|
version: '0'
|
183
161
|
type: :development
|
184
162
|
prerelease: false
|
185
163
|
version_requirements: !ruby/object:Gem::Requirement
|
186
|
-
none: false
|
187
164
|
requirements:
|
188
|
-
- -
|
165
|
+
- - ">="
|
189
166
|
- !ruby/object:Gem::Version
|
190
167
|
version: '0'
|
191
168
|
description: A Ruby client library for the Asterisk Management Interface built on
|
@@ -197,9 +174,9 @@ executables: []
|
|
197
174
|
extensions: []
|
198
175
|
extra_rdoc_files: []
|
199
176
|
files:
|
200
|
-
- .gitignore
|
201
|
-
- .rspec
|
202
|
-
- .travis.yml
|
177
|
+
- ".gitignore"
|
178
|
+
- ".rspec"
|
179
|
+
- ".travis.yml"
|
203
180
|
- CHANGELOG.md
|
204
181
|
- Gemfile
|
205
182
|
- Guardfile
|
@@ -240,27 +217,26 @@ files:
|
|
240
217
|
- spec/support/mock_server.rb
|
241
218
|
homepage: ''
|
242
219
|
licenses: []
|
220
|
+
metadata: {}
|
243
221
|
post_install_message:
|
244
222
|
rdoc_options: []
|
245
223
|
require_paths:
|
246
224
|
- lib
|
247
225
|
required_ruby_version: !ruby/object:Gem::Requirement
|
248
|
-
none: false
|
249
226
|
requirements:
|
250
|
-
- -
|
227
|
+
- - ">="
|
251
228
|
- !ruby/object:Gem::Version
|
252
229
|
version: '0'
|
253
230
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
254
|
-
none: false
|
255
231
|
requirements:
|
256
|
-
- -
|
232
|
+
- - ">="
|
257
233
|
- !ruby/object:Gem::Version
|
258
234
|
version: '0'
|
259
235
|
requirements: []
|
260
236
|
rubyforge_project: ruby_ami
|
261
|
-
rubygems_version:
|
237
|
+
rubygems_version: 2.4.5
|
262
238
|
signing_key:
|
263
|
-
specification_version:
|
239
|
+
specification_version: 4
|
264
240
|
summary: Futzing with AMI so you don't have to
|
265
241
|
test_files:
|
266
242
|
- features/lexer.feature
|