ruby_fs 0.1.0 → 0.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/CHANGELOG.md +7 -0
- data/README.md +6 -2
- data/lib/ruby_fs/event.rb +2 -0
- data/lib/ruby_fs/stream.rb +99 -18
- data/lib/ruby_fs/version.rb +1 -1
- data/spec/ruby_fs/stream_spec.rb +99 -1
- data/spec/support/mock_server.rb +2 -0
- metadata +4 -4
data/CHANGELOG.md
CHANGED
@@ -1,4 +1,11 @@
|
|
1
1
|
# develop
|
2
2
|
|
3
|
+
# 0.2.0
|
4
|
+
* Feature: Common command helper methods
|
5
|
+
* Feature: Trace level logging & easy logging override
|
6
|
+
* Feature: Clean shutdown
|
7
|
+
* Feature: Separate stream creation and connection/execution
|
8
|
+
* API Documentation
|
9
|
+
|
3
10
|
# 0.1.0
|
4
11
|
* Initial release
|
data/README.md
CHANGED
@@ -8,9 +8,13 @@ RubyFS is a FreeSWITCH EventSocket client library in Ruby and based on Celluloid
|
|
8
8
|
```ruby
|
9
9
|
require 'ruby_fs'
|
10
10
|
|
11
|
-
|
11
|
+
stream = RubyFS::Stream.new '127.0.0.1', 8021, 'ClueCon', lambda { |e| p e }
|
12
12
|
|
13
|
-
|
13
|
+
stream.run
|
14
|
+
|
15
|
+
stream.api 'originate sofia/mydomain.com/ext@yourvsp.com 1000' do |response|
|
16
|
+
puts "Originate response was #{response.inspect}"
|
17
|
+
end
|
14
18
|
```
|
15
19
|
|
16
20
|
## Links
|
data/lib/ruby_fs/event.rb
CHANGED
data/lib/ruby_fs/stream.rb
CHANGED
@@ -19,63 +19,139 @@ module RubyFS
|
|
19
19
|
|
20
20
|
def initialize(host, port, secret, event_callback, events = true)
|
21
21
|
super()
|
22
|
-
@secret, @event_callback, @events = secret, event_callback, events
|
22
|
+
@host, @port, @secret, @event_callback, @events = host, port, secret, event_callback, events
|
23
23
|
@command_callbacks = []
|
24
|
-
logger.debug "Starting up..."
|
25
24
|
@lexer = Lexer.new method(:receive_request)
|
26
|
-
@socket = TCPSocket.from_ruby_socket ::TCPSocket.new(host, port)
|
27
|
-
post_init
|
28
25
|
end
|
29
26
|
|
30
27
|
[:started, :stopped, :ready].each do |state|
|
31
28
|
define_method("#{state}?") { @state == state }
|
32
29
|
end
|
33
30
|
|
31
|
+
#
|
32
|
+
# Connect to the server and begin handling data
|
34
33
|
def run
|
34
|
+
logger.debug "Starting up..."
|
35
|
+
@socket = TCPSocket.from_ruby_socket ::TCPSocket.new(@host, @port)
|
36
|
+
post_init
|
35
37
|
loop { receive_data @socket.readpartial(4096) }
|
36
|
-
rescue EOFError
|
38
|
+
rescue EOFError, IOError
|
37
39
|
logger.info "Client socket closed!"
|
38
|
-
|
39
|
-
end
|
40
|
-
|
41
|
-
def post_init
|
42
|
-
@state = :started
|
43
|
-
fire_event Connected.new
|
40
|
+
terminate
|
44
41
|
end
|
45
42
|
|
43
|
+
#
|
44
|
+
# Send raw string data to the FS server
|
45
|
+
#
|
46
|
+
# @param [#to_s] data the data to send over the socket
|
46
47
|
def send_data(data)
|
47
|
-
logger.
|
48
|
+
logger.trace "[SEND] #{data.to_s}"
|
48
49
|
@socket.write data.to_s
|
49
50
|
end
|
50
51
|
|
52
|
+
#
|
53
|
+
# Send a FreeSWITCH command with options and a callback for the response
|
54
|
+
#
|
55
|
+
# @param [#to_s] command the command to run
|
56
|
+
# @param [optional, Hash] options the command's options, where keys have _ substituted for -
|
57
|
+
#
|
58
|
+
# @yield [response] Handle the command's response
|
59
|
+
# @yieldparam [RubyFS::Response] response the command's response object
|
51
60
|
def command(command, options = {}, &block)
|
52
61
|
@command_callbacks << (block || lambda { |reply| logger.debug "Reply to a command (#{command}) without a callback: #{reply.inspect}" })
|
53
62
|
string = "#{command}\n"
|
54
63
|
options.each_pair do |key, value|
|
55
|
-
string << "#{key.to_s.gsub '_', '-'}: #{value}\n"
|
64
|
+
string << "#{key.to_s.gsub '_', '-'}: #{value}\n" if value
|
56
65
|
end
|
57
66
|
string << "\n"
|
58
67
|
send_data string
|
59
68
|
end
|
60
69
|
|
61
|
-
|
62
|
-
|
63
|
-
|
70
|
+
#
|
71
|
+
# Send an API action
|
72
|
+
#
|
73
|
+
# @param [#to_s] action the API action to execute
|
74
|
+
#
|
75
|
+
# @yield [response] Handle the command's response
|
76
|
+
# @yieldparam [RubyFS::Response] response the command's response object
|
77
|
+
def api(action, &block)
|
78
|
+
command "api #{action}", &block
|
79
|
+
end
|
80
|
+
|
81
|
+
#
|
82
|
+
# Send an API action in the background
|
83
|
+
#
|
84
|
+
# @param [#to_s] action the API action to execute
|
85
|
+
#
|
86
|
+
# @yield [response] Handle the command's response
|
87
|
+
# @yieldparam [RubyFS::Response] response the command's response object
|
88
|
+
def bgapi(action, &block)
|
89
|
+
command "bgapi #{action}", &block
|
90
|
+
end
|
91
|
+
|
92
|
+
#
|
93
|
+
# Send a message to a particular call
|
94
|
+
#
|
95
|
+
# @param [#to_s] call the call ID to send the message to
|
96
|
+
# @param [optional, Hash] options the message options
|
97
|
+
#
|
98
|
+
# @yield [response] Handle the message's response
|
99
|
+
# @yieldparam [RubyFS::Response] response the message's response object
|
100
|
+
def sendmsg(call, options = {}, &block)
|
101
|
+
command "SendMsg #{call}", options, &block
|
102
|
+
end
|
103
|
+
|
104
|
+
#
|
105
|
+
# Execute an application on a particular call
|
106
|
+
#
|
107
|
+
# @param [#to_s] call the call ID on which to execute the application
|
108
|
+
# @param [#to_s] appname the app to execute
|
109
|
+
# @param [optional, String] options the application options
|
110
|
+
#
|
111
|
+
# @yield [response] Handle the application's response
|
112
|
+
# @yieldparam [RubyFS::Response] response the application's response object
|
113
|
+
def application(call, appname, options = nil, &block)
|
114
|
+
sendmsg call, :call_command => 'execute', :execute_app_name => appname, :execute_app_arg => options, &block
|
115
|
+
end
|
116
|
+
|
117
|
+
#
|
118
|
+
# Shutdown the stream and disconnect from the socket
|
119
|
+
def shutdown
|
120
|
+
@socket.close if @socket
|
64
121
|
end
|
65
122
|
|
123
|
+
# @private
|
66
124
|
def finalize
|
67
125
|
logger.debug "Finalizing stream"
|
68
|
-
@socket.close if @socket
|
69
126
|
@state = :stopped
|
70
127
|
fire_event Disconnected.new
|
71
128
|
end
|
72
129
|
|
130
|
+
#
|
131
|
+
# Fire an event to the specified callback
|
132
|
+
#
|
133
|
+
# @param [Object] event the event to fire
|
73
134
|
def fire_event(event)
|
74
135
|
@event_callback.call event
|
75
136
|
end
|
76
137
|
|
138
|
+
#
|
139
|
+
# The stream's logger object
|
77
140
|
def logger
|
78
|
-
|
141
|
+
super
|
142
|
+
rescue
|
143
|
+
@logger ||= begin
|
144
|
+
logger = Logger
|
145
|
+
logger.define_singleton_method :trace, logger.method(:debug)
|
146
|
+
logger
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
private
|
151
|
+
|
152
|
+
def receive_data(data)
|
153
|
+
logger.trace "[RECV] #{data}"
|
154
|
+
@lexer << data
|
79
155
|
end
|
80
156
|
|
81
157
|
def receive_request(headers, content)
|
@@ -101,5 +177,10 @@ module RubyFS
|
|
101
177
|
end
|
102
178
|
end
|
103
179
|
end
|
180
|
+
|
181
|
+
def post_init
|
182
|
+
@state = :started
|
183
|
+
fire_event Connected.new
|
184
|
+
end
|
104
185
|
end
|
105
186
|
end
|
data/lib/ruby_fs/version.rb
CHANGED
data/spec/ruby_fs/stream_spec.rb
CHANGED
@@ -30,6 +30,7 @@ module RubyFS
|
|
30
30
|
s = ServerMock.new '127.0.0.1', server_port, mock_target
|
31
31
|
@stream = Stream.new '127.0.0.1', server_port, secret, lambda { |m| client.message_received m }, events
|
32
32
|
@stream.run!
|
33
|
+
sleep 0.1
|
33
34
|
fake_client.call s if fake_client.respond_to? :call
|
34
35
|
s.join
|
35
36
|
@stream.join
|
@@ -50,7 +51,7 @@ module RubyFS
|
|
50
51
|
expect_connected_event
|
51
52
|
expect_disconnected_event
|
52
53
|
mocked_server(0) do |val, server|
|
53
|
-
@stream.
|
54
|
+
@stream.should be_started
|
54
55
|
end
|
55
56
|
end
|
56
57
|
|
@@ -61,6 +62,15 @@ module RubyFS
|
|
61
62
|
val.should == "foo"
|
62
63
|
end
|
63
64
|
end
|
65
|
+
|
66
|
+
it "can be shut down" do
|
67
|
+
expect_connected_event
|
68
|
+
expect_disconnected_event
|
69
|
+
mocked_server(0, lambda { |server| @stream.shutdown }) do |val, server|
|
70
|
+
@stream.should be_started
|
71
|
+
end
|
72
|
+
@stream.should_not be_alive
|
73
|
+
end
|
64
74
|
end
|
65
75
|
|
66
76
|
it 'sends events to the client when the stream is ready' do
|
@@ -158,6 +168,94 @@ Reply-Text: +OK accepted
|
|
158
168
|
one: 1
|
159
169
|
foo-bar: doo_dah
|
160
170
|
|
171
|
+
)
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
it "can send API commands with response callbacks" do
|
176
|
+
expect_connected_event
|
177
|
+
expect_disconnected_event
|
178
|
+
handler = mock
|
179
|
+
handler.expects(:call).once.with CommandReply.new(:content_type => 'command/reply', :reply_text => '+OK accepted')
|
180
|
+
mocked_server(1, lambda { |server| @stream.api('foo') { |reply| handler.call reply } }) do |val, server|
|
181
|
+
val.should == "api foo\n\n"
|
182
|
+
server.send_data %Q(
|
183
|
+
Content-Type: command/reply
|
184
|
+
Reply-Text: +OK accepted
|
185
|
+
|
186
|
+
)
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
it "can send background API commands with response callbacks" do
|
191
|
+
expect_connected_event
|
192
|
+
expect_disconnected_event
|
193
|
+
handler = mock
|
194
|
+
handler.expects(:call).once.with CommandReply.new(:content_type => 'command/reply', :reply_text => '+OK Job-UUID: 4e8344be-c1fe-11e1-a7bf-cf9911a69d1e', :job_uuid => '4e8344be-c1fe-11e1-a7bf-cf9911a69d1e')
|
195
|
+
mocked_server(1, lambda { |server| @stream.bgapi('foo') { |reply| handler.call reply } }) do |val, server|
|
196
|
+
val.should == "bgapi foo\n\n"
|
197
|
+
server.send_data %Q(
|
198
|
+
Content-Type: command/reply
|
199
|
+
Reply-Text: +OK Job-UUID: 4e8344be-c1fe-11e1-a7bf-cf9911a69d1e
|
200
|
+
Job-UUID: 4e8344be-c1fe-11e1-a7bf-cf9911a69d1e
|
201
|
+
|
202
|
+
)
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
it "can send messages to calls with options and response callbacks" do
|
207
|
+
expect_connected_event
|
208
|
+
expect_disconnected_event
|
209
|
+
handler = mock
|
210
|
+
handler.expects(:call).once.with CommandReply.new(:content_type => 'command/reply', :reply_text => '+OK accepted')
|
211
|
+
mocked_server(1, lambda { |server| @stream.sendmsg('aUUID', :call_command => 'execute') { |reply| handler.call reply } }) do |val, server|
|
212
|
+
val.should == %Q(SendMsg aUUID
|
213
|
+
call-command: execute
|
214
|
+
|
215
|
+
)
|
216
|
+
server.send_data %Q(
|
217
|
+
Content-Type: command/reply
|
218
|
+
Reply-Text: +OK accepted
|
219
|
+
|
220
|
+
)
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
it "can execute applications on calls without options but with response callbacks" do
|
225
|
+
expect_connected_event
|
226
|
+
expect_disconnected_event
|
227
|
+
handler = mock
|
228
|
+
handler.expects(:call).once.with CommandReply.new(:content_type => 'command/reply', :reply_text => '+OK accepted')
|
229
|
+
mocked_server(1, lambda { |server| @stream.application('aUUID', 'answer') { |reply| handler.call reply } }) do |val, server|
|
230
|
+
val.should == %Q(SendMsg aUUID
|
231
|
+
call-command: execute
|
232
|
+
execute-app-name: answer
|
233
|
+
|
234
|
+
)
|
235
|
+
server.send_data %Q(
|
236
|
+
Content-Type: command/reply
|
237
|
+
Reply-Text: +OK accepted
|
238
|
+
|
239
|
+
)
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
it "can execute applications on calls with options and response callbacks" do
|
244
|
+
expect_connected_event
|
245
|
+
expect_disconnected_event
|
246
|
+
handler = mock
|
247
|
+
handler.expects(:call).once.with CommandReply.new(:content_type => 'command/reply', :reply_text => '+OK accepted')
|
248
|
+
mocked_server(1, lambda { |server| @stream.application('aUUID', 'playback', '/tmp/test.wav') { |reply| handler.call reply } }) do |val, server|
|
249
|
+
val.should == %Q(SendMsg aUUID
|
250
|
+
call-command: execute
|
251
|
+
execute-app-name: playback
|
252
|
+
execute-app-arg: /tmp/test.wav
|
253
|
+
|
254
|
+
)
|
255
|
+
server.send_data %Q(
|
256
|
+
Content-Type: command/reply
|
257
|
+
Reply-Text: +OK accepted
|
258
|
+
|
161
259
|
)
|
162
260
|
end
|
163
261
|
end
|
data/spec/support/mock_server.rb
CHANGED
@@ -26,6 +26,8 @@ class ServerMock
|
|
26
26
|
_, port, host = socket.peeraddr
|
27
27
|
Logger.debug "MockServer Received connection from #{host}:#{port}"
|
28
28
|
loop { receive_data socket.readpartial(4096) }
|
29
|
+
rescue EOFError
|
30
|
+
Logger.debug "Connection from #{host}:#{port} closed"
|
29
31
|
end
|
30
32
|
|
31
33
|
def receive_data(data)
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby_fs
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.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-
|
12
|
+
date: 2012-07-02 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: uuidtools
|
@@ -295,7 +295,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
295
295
|
version: '0'
|
296
296
|
segments:
|
297
297
|
- 0
|
298
|
-
hash:
|
298
|
+
hash: 3148609615352772925
|
299
299
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
300
300
|
none: false
|
301
301
|
requirements:
|
@@ -304,7 +304,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
304
304
|
version: '0'
|
305
305
|
segments:
|
306
306
|
- 0
|
307
|
-
hash:
|
307
|
+
hash: 3148609615352772925
|
308
308
|
requirements: []
|
309
309
|
rubyforge_project: ruby_fs
|
310
310
|
rubygems_version: 1.8.24
|