ruby_fs 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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
- client = RubyFS::Stream.new '127.0.0.1', 8021, 'ClueCon', lambda { |e| p e }
11
+ stream = RubyFS::Stream.new '127.0.0.1', 8021, 'ClueCon', lambda { |e| p e }
12
12
 
13
- client.start
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
@@ -2,6 +2,8 @@ require 'ruby_fs/response'
2
2
 
3
3
  module RubyFS
4
4
  class Event < Response
5
+ #
6
+ # @return [String[ The name of the event
5
7
  def event_name
6
8
  content[:event_name]
7
9
  end
@@ -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
- current_actor.terminate!
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.debug "[SEND] #{data.to_s}"
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
- def receive_data(data)
62
- logger.debug "[RECV] #{data}"
63
- @lexer << data
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
- Logger
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
@@ -1,3 +1,3 @@
1
1
  module RubyFS
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -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.started?.should be_true
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
@@ -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.1.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-06-29 00:00:00.000000000 Z
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: 2481817772884138362
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: 2481817772884138362
307
+ hash: 3148609615352772925
308
308
  requirements: []
309
309
  rubyforge_project: ruby_fs
310
310
  rubygems_version: 1.8.24