farcall 0.1.2 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +38 -7
- data/farcall.gemspec +8 -5
- data/lib/farcall/boss_transport.rb +3 -1
- data/lib/farcall/em_farcall.rb +248 -0
- data/lib/farcall/em_wsserver_endpoint.rb +73 -0
- data/lib/farcall/endpoint.rb +61 -17
- data/lib/farcall/monitor_lock.rb +74 -0
- data/lib/farcall/transport.rb +3 -1
- data/lib/farcall/version.rb +1 -1
- data/lib/farcall/wsclient_transport.rb +55 -0
- data/lib/farcall.rb +3 -0
- data/spec/endpoint_spec.rb +41 -4
- data/spec/websock_spec.rb +233 -0
- metadata +54 -8
- data/spec/rmi_spec.rb +0 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7ccf7c600e32b64b16b3ee5e278064bef7214274
|
4
|
+
data.tar.gz: 2e7fc842321b53483a09b02caf9cc9c8fc8af85f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 611fea3359ae86917d1625f8b1daf79e78c4cf20421a8ee64d3e679a00fbe0007a8bd79a551fb9e24bf6d38e3eea47dc9e77ac92927cb05c7c66f51d0ae8749e
|
7
|
+
data.tar.gz: 4010ab7cf9dbe52c2e4ca519c3df823a5ea104ff7783118340e576ebd2551c0866b20967c55b82e0a8a23e00a7314a73362efb433db6da9f092a8386c1388afe
|
data/README.md
CHANGED
@@ -1,11 +1,23 @@
|
|
1
1
|
# Farcall
|
2
2
|
|
3
|
+
## News
|
4
|
+
|
5
|
+
Since 0.3.0 farcall gem provides websocket client and server out of the box.
|
6
|
+
|
3
7
|
## Description
|
4
8
|
|
5
9
|
The simple and elegant cross-platform RPC protocol that uses any formatter/transport capable of
|
6
10
|
transmitting dictionary-like objects, for example, JSON,
|
7
11
|
[BOSS](https://github.com/sergeych/boss_protocol), XML, BSON and many others. This gem
|
8
|
-
supports out of the box JSON and [BOSS](https://github.com/sergeych/boss_protocol) protocols
|
12
|
+
supports out of the box JSON and [BOSS](https://github.com/sergeych/boss_protocol) protocols and
|
13
|
+
streams and sockets as the media.
|
14
|
+
|
15
|
+
There is also optional support for eventmachine based wbesocket server and regular client websocket
|
16
|
+
connection. All you need is to include gem 'em-websocket' and/or gem 'websocket-client-simple'.
|
17
|
+
All websocket implementations use JSON encoding to vbe interoperable with most web allications.
|
18
|
+
|
19
|
+
We do not include them in the dependencies because eventmachine is big and does not work with jruby,
|
20
|
+
and websocket client is not always needed and we are fond of minimizing dependencies.
|
9
21
|
|
10
22
|
RPC is made asynchronously, each call can have any return values. While one call is waiting,
|
11
23
|
other calls can be executed. The protocol is bidirectional Call parameters could be
|
@@ -15,7 +27,7 @@ dictionary, wahtever.
|
|
15
27
|
Exception/errors transmitting is also supported. The interface is very simple and rubyish. The
|
16
28
|
protocol is very easy to implement if there is no implementation, see
|
17
29
|
[Farcall protocol specification](https://github.com/sergeych/farcall/wiki). Java library for
|
18
|
-
Android and desktop is
|
30
|
+
Android and desktop is ready upon request (leave me a task or a message in th github).
|
19
31
|
|
20
32
|
## Installation
|
21
33
|
|
@@ -23,8 +35,15 @@ Add this line to your application's Gemfile:
|
|
23
35
|
|
24
36
|
```ruby
|
25
37
|
gem 'farcall'
|
26
|
-
# If you want to use binary-effective boss encoding:
|
27
|
-
# gem '
|
38
|
+
# If you want to use binary-effective boss encoding, uncomment:
|
39
|
+
# gem 'boss-protocol', '>= 1.4.1'
|
40
|
+
#
|
41
|
+
# if you want to use eventmachine and server websocket, uncomment:
|
42
|
+
# gem 'em-websocket'
|
43
|
+
#
|
44
|
+
# To use websocket client, uncomment
|
45
|
+
# gem 'websocket-client-simple'
|
46
|
+
|
28
47
|
```
|
29
48
|
|
30
49
|
And then execute:
|
@@ -59,8 +78,16 @@ Or install it yourself as:
|
|
59
78
|
TestProvider.new socket: connected_socket, format: :boss
|
60
79
|
```
|
61
80
|
|
62
|
-
|
63
|
-
|
81
|
+
`Farcall::Provider` provides easy constructors to use it with the transport or the endpoint.
|
82
|
+
If you need to implement farcall over somw other media, just extend `TestTransport` and provide
|
83
|
+
`send_data` and call `on_received_data` when need. It's very simple and straightforward.
|
84
|
+
|
85
|
+
Consult [online documentation for Transport](http://www.rubydoc.info/gems/farcall/Farcall/Transport)
|
86
|
+
and [Provider](http://www.rubydoc.info/gems/farcall/Farcall/Provider) for more.
|
87
|
+
|
88
|
+
In the most common case you just have to connect two sockets, in which case everythng works right
|
89
|
+
out of the box. Suppose whe have some socket connected to one above, then TestProvider methods are
|
90
|
+
available via this connection:
|
64
91
|
|
65
92
|
```ruby
|
66
93
|
|
@@ -120,11 +147,15 @@ So, I would recommend:
|
|
120
147
|
|
121
148
|
- if you need BOSS but can't find it on your platform, use both and contact me :)
|
122
149
|
|
150
|
+
## Usage with eventmacine
|
151
|
+
|
152
|
+
You can use `EmFarcall::Endpoint` widely as it uses a pair of EM::Channel as a trasnport, e.g.
|
153
|
+
it could be easily connected to any evented data source.
|
154
|
+
|
123
155
|
## Documentation
|
124
156
|
|
125
157
|
* [Farcall protocol](https://github.com/sergeych/farcall/wiki)
|
126
158
|
|
127
|
-
|
128
159
|
* Gem [online docs](http://www.rubydoc.info/gems/farcall)
|
129
160
|
|
130
161
|
## Contributing
|
data/farcall.gemspec
CHANGED
@@ -14,7 +14,7 @@ Gem::Specification.new do |spec|
|
|
14
14
|
pass structures (dictionaries, hashes, whatever you name it). Out of the box provides
|
15
15
|
JSON and BOSS formats over streams and sockets.
|
16
16
|
End
|
17
|
-
spec.homepage = ""
|
17
|
+
spec.homepage = "https://github.com/sergeych/farcall"
|
18
18
|
spec.license = "MIT"
|
19
19
|
|
20
20
|
spec.files = `git ls-files -z`.split("\x0")
|
@@ -22,8 +22,11 @@ Gem::Specification.new do |spec|
|
|
22
22
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
23
23
|
spec.require_paths = ["lib"]
|
24
24
|
|
25
|
-
spec.
|
26
|
-
spec.add_development_dependency
|
27
|
-
spec.add_development_dependency
|
28
|
-
spec.add_development_dependency
|
25
|
+
spec.add_dependency 'hashie'
|
26
|
+
spec.add_development_dependency 'bundler', '~> 1.7'
|
27
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
28
|
+
spec.add_development_dependency 'rspec'
|
29
|
+
spec.add_development_dependency 'em-websocket'
|
30
|
+
spec.add_development_dependency 'websocket-client-simple'
|
31
|
+
spec.add_development_dependency 'boss-protocol', '>= 1.4.3'
|
29
32
|
end
|
@@ -9,6 +9,8 @@ module Farcall
|
|
9
9
|
# Create json transport, see Farcall::Transpor#create for parameters
|
10
10
|
def initialize **params
|
11
11
|
setup_streams **params
|
12
|
+
@formatter = Boss::Formatter.new(@output)
|
13
|
+
@formatter.set_stream_mode
|
12
14
|
end
|
13
15
|
|
14
16
|
def on_data_received= block
|
@@ -21,7 +23,7 @@ module Farcall
|
|
21
23
|
end
|
22
24
|
|
23
25
|
def send_data hash
|
24
|
-
@
|
26
|
+
@formatter << hash
|
25
27
|
end
|
26
28
|
|
27
29
|
def close
|
@@ -0,0 +1,248 @@
|
|
1
|
+
begin
|
2
|
+
require 'hashie'
|
3
|
+
require 'eventmachine'
|
4
|
+
|
5
|
+
# As the eventmachine callback paradigm is completely different from the threaded paradigm
|
6
|
+
# of the Farcall, that runs pretty well under JRuby and in multithreaded MRI, we provide
|
7
|
+
# compatible but different implementations: {EmFarcall::Endpoint}, {EmFarcall::Interface}
|
8
|
+
# and {EmFarcall::Provider}. Changes to adapt these are minimal except of the callback
|
9
|
+
# paradigm. The rest is the same.
|
10
|
+
#
|
11
|
+
module EmFarcall
|
12
|
+
|
13
|
+
# Endpoint that run in the reactor thread of the EM. Eventmachine should rin by the time of
|
14
|
+
# creation of the endpoint. All the methods can be called from any thread, not only
|
15
|
+
# EM's reactor thread.
|
16
|
+
#
|
17
|
+
# As the eventmachine callback paradigm is completely different from the threaded paradigm
|
18
|
+
# of the Farcall, that runs pretty well under
|
19
|
+
# JRuby and in multithreaded MRI, we provide
|
20
|
+
# compatible but different endpoint to run under EM.
|
21
|
+
#
|
22
|
+
# Its main difference is that there is no sync_call, instead, calling remote commands
|
23
|
+
# from the endpoint and/ot interface can provide blocks that are called when the remote
|
24
|
+
# is executed.
|
25
|
+
#
|
26
|
+
# The EM version of the endpoint works with any 2 EM:C:Channels.
|
27
|
+
#
|
28
|
+
class Endpoint
|
29
|
+
|
30
|
+
# Create new endpoint to work with input and output channels
|
31
|
+
#
|
32
|
+
# @param [EM::Channel] input_channel
|
33
|
+
# @param [EM::Channel] output_channel
|
34
|
+
def initialize(input_channel, output_channel, errback=nil, provider: nil)
|
35
|
+
EM.schedule {
|
36
|
+
@input, @output, @errback = input_channel, output_channel, errback
|
37
|
+
@trace = false
|
38
|
+
@in_serial = @out_serial = 0
|
39
|
+
@callbacks = {}
|
40
|
+
@handlers = {}
|
41
|
+
@unnamed_handler = -> (name, *args, **kwargs) {
|
42
|
+
raise NoMethodError, "method does not exist: #{name}"
|
43
|
+
}
|
44
|
+
@input.subscribe { |data|
|
45
|
+
process_input(data)
|
46
|
+
}
|
47
|
+
if provider
|
48
|
+
@provider = provider
|
49
|
+
provider.endpoint = self
|
50
|
+
end
|
51
|
+
}
|
52
|
+
end
|
53
|
+
|
54
|
+
# Set or get provider instance. When provider is set, its public methods are called by the remote
|
55
|
+
# and any possible exception are passed back to caller party. You can use any ruby class instance
|
56
|
+
# everything will work, operators, indexes[] and like.
|
57
|
+
attr_accessor :provider
|
58
|
+
|
59
|
+
# Call remote with specified name and arguments calling block when done.
|
60
|
+
# if block is provided, it will be called when the remote will be called and possibly return
|
61
|
+
# some data.
|
62
|
+
#
|
63
|
+
# Block receives single object paramter with two fields: `result.error` and `result.result`.
|
64
|
+
#
|
65
|
+
# `result.error` is not nil when the remote raised error, then `error[:class]` and
|
66
|
+
# `error.text` are set accordingly.
|
67
|
+
#
|
68
|
+
# if error is nil then result.result receives any return data from the remote method.
|
69
|
+
#
|
70
|
+
# for example:
|
71
|
+
#
|
72
|
+
# endpoint.call( 'some_func', 10, 20) { |done|
|
73
|
+
# if done.error
|
74
|
+
# puts "Remote error class: #{done.error[:class]}: #{done.error.text}"
|
75
|
+
# else
|
76
|
+
# puts "Remote returned #{done.result}"
|
77
|
+
# }
|
78
|
+
#
|
79
|
+
# @param [String] name command name
|
80
|
+
# @return [Endpoint] self
|
81
|
+
def call(name, *args, **kwargs, &block)
|
82
|
+
EM.schedule {
|
83
|
+
if block
|
84
|
+
@callbacks[@in_serial] = block
|
85
|
+
end
|
86
|
+
send_block cmd: name, args: args, kwargs: kwargs
|
87
|
+
}
|
88
|
+
self
|
89
|
+
end
|
90
|
+
|
91
|
+
# Close the endpoint
|
92
|
+
def close
|
93
|
+
super
|
94
|
+
end
|
95
|
+
|
96
|
+
# Report error via errback and the endpoint
|
97
|
+
def error text
|
98
|
+
STDERR.puts "farcall ws server error #{text}"
|
99
|
+
EM.schedule {
|
100
|
+
@errback.call(text) if @errback
|
101
|
+
close
|
102
|
+
}
|
103
|
+
end
|
104
|
+
|
105
|
+
# Set handler to perform the named command. Block will be called when the remote party calls
|
106
|
+
# with parameters passed from the remote. The block returned value will be passed back to
|
107
|
+
# the caller.
|
108
|
+
#
|
109
|
+
# If the block raises the exception it will be reported to the caller as an error (depending
|
110
|
+
# on it's platofrm, will raise exception on its end or report error)
|
111
|
+
def on(name, &block)
|
112
|
+
@handlers[name.to_s] = block
|
113
|
+
end
|
114
|
+
|
115
|
+
# Process remote command. First parameter passed to the block is the method name, the rest
|
116
|
+
# are optional arguments of the call:
|
117
|
+
#
|
118
|
+
# endpoint.on_command { |name, *args, **kwargs|
|
119
|
+
# if name == 'echo'
|
120
|
+
# { args: args, keyword_args: kwargs }
|
121
|
+
# else
|
122
|
+
# raise "unknown command"
|
123
|
+
# end
|
124
|
+
# }
|
125
|
+
#
|
126
|
+
# raising exceptions from the block cause farcall error to be returned back th the caller.
|
127
|
+
def on_command &block
|
128
|
+
raise "unnamed handler should be present" unless block
|
129
|
+
@unnamed_handler = block
|
130
|
+
end
|
131
|
+
|
132
|
+
# Same as #on_command (compatibilty method)
|
133
|
+
def on_remote_call &block
|
134
|
+
on_command block
|
135
|
+
end
|
136
|
+
|
137
|
+
# Get the Farcall::RemoteInterface connnected to this endpoint. Any subsequent calls with
|
138
|
+
# return the same instance.
|
139
|
+
def remote
|
140
|
+
@remote ||= EmFarcall::Interface.new endpoint: self
|
141
|
+
end
|
142
|
+
|
143
|
+
private
|
144
|
+
|
145
|
+
# :nodoc: sends block with correct framing
|
146
|
+
def send_block **data
|
147
|
+
data[:serial] = @out_serial
|
148
|
+
@out_serial += 1
|
149
|
+
@output << data
|
150
|
+
end
|
151
|
+
|
152
|
+
# :nodoc:
|
153
|
+
def execute_command cmd, ref, args, kwargs
|
154
|
+
kwargs = (kwargs || {}).inject({}) {
|
155
|
+
|all, kv|
|
156
|
+
all[kv[0].to_sym] = kv[1]
|
157
|
+
all
|
158
|
+
}
|
159
|
+
args << kwargs if kwargs && !kwargs.empty?
|
160
|
+
result = if proc = @handlers[cmd.to_s]
|
161
|
+
proc.call(*args)
|
162
|
+
elsif @provider
|
163
|
+
provider.send :remote_call, cmd.to_sym, args
|
164
|
+
else
|
165
|
+
@unnamed_handler.call(cmd, args)
|
166
|
+
end
|
167
|
+
send_block ref: ref, result: result
|
168
|
+
|
169
|
+
rescue
|
170
|
+
if @trace
|
171
|
+
puts $!
|
172
|
+
puts $!.backtrace.join("\n")
|
173
|
+
end
|
174
|
+
send_block ref: ref, error: { class: $!.class.name, text: $!.to_s }
|
175
|
+
end
|
176
|
+
|
177
|
+
# :nodoc: important that this method is called from reactor thread only
|
178
|
+
def process_input data
|
179
|
+
# To be free from :keys and 'keys'
|
180
|
+
data = Hashie::Mash.new(data) unless data.is_a?(Hashie::Mash)
|
181
|
+
if data.serial != @in_serial
|
182
|
+
error "framing error (wrong serial:)"
|
183
|
+
else
|
184
|
+
@in_serial += 1
|
185
|
+
if (cmd = data.cmd) != nil
|
186
|
+
execute_command(cmd, data.serial, data.args || [], data.kwargs || {})
|
187
|
+
else
|
188
|
+
ref = data.ref
|
189
|
+
if ref
|
190
|
+
if (block = @callbacks.delete(ref)) != nil
|
191
|
+
block.call(Hashie::Mash.new(result: data.result, error: data.error))
|
192
|
+
end
|
193
|
+
else
|
194
|
+
error "framing error: no ref in block #{data.inspect}"
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
# Interface to the remote provider via Farcall protocols. Works the same as if the object
|
202
|
+
# is local and yields block in return, unlike Farcall::Interface that blocks
|
203
|
+
#
|
204
|
+
# RemoteInterface transparently creates methods as you call them to speedup subsequent
|
205
|
+
# calls.
|
206
|
+
#
|
207
|
+
class Interface
|
208
|
+
|
209
|
+
# Create interface connected to some endpoint ar transpost.
|
210
|
+
#
|
211
|
+
# Please remember that Farcall::Transport instance could be used with only
|
212
|
+
# one connected object, unlike Farcall::Endpoint, which could be connected to several
|
213
|
+
# consumers.
|
214
|
+
#
|
215
|
+
# @param [Farcall::Endpoint|Farcall::Transport] arg either endpoint or a transport
|
216
|
+
# to connect interface to
|
217
|
+
def initialize(endpoint)
|
218
|
+
@endpoint = endpoint
|
219
|
+
end
|
220
|
+
|
221
|
+
def method_missing(method_name, *arguments, **kw_arguments, &block)
|
222
|
+
instance_eval <<-End
|
223
|
+
def #{method_name} *arguments, **kw_arguments, &block
|
224
|
+
@endpoint.call '#{method_name}', *arguments, **kw_arguments, &block
|
225
|
+
end
|
226
|
+
End
|
227
|
+
@endpoint.call method_name, *arguments, **kw_arguments, &block
|
228
|
+
end
|
229
|
+
|
230
|
+
def respond_to_missing?(method_name, include_private = false)
|
231
|
+
true
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
class Provider < Farcall::Provider
|
236
|
+
|
237
|
+
attr_accessor :endpoint
|
238
|
+
|
239
|
+
def far_interface
|
240
|
+
endpoint.remote
|
241
|
+
end
|
242
|
+
|
243
|
+
end
|
244
|
+
end
|
245
|
+
rescue LoadError
|
246
|
+
$!.to_s =~ /eventmachine/ or raise
|
247
|
+
end
|
248
|
+
|
@@ -0,0 +1,73 @@
|
|
1
|
+
begin
|
2
|
+
require 'em-websocket'
|
3
|
+
require 'eventmachine'
|
4
|
+
require_relative './em_farcall'
|
5
|
+
|
6
|
+
module EmFarcall
|
7
|
+
|
8
|
+
# Farcall websocket client. To use it you must add to your Gemfile:
|
9
|
+
#
|
10
|
+
# gem 'websocket-client-simple'
|
11
|
+
#
|
12
|
+
# then you can use it with EM:
|
13
|
+
#
|
14
|
+
# endpoint = nil
|
15
|
+
#
|
16
|
+
# EM::WebSocket.run(params) do |ws|
|
17
|
+
# ws.onopen { |handshake|
|
18
|
+
# # Check handshake.path, handshake query
|
19
|
+
# # for example to select the provider, then connect Farcall to websocket:
|
20
|
+
# endpoint = EmFarcall::WsServerEndpoint.new ws, provider: WsProvider.new
|
21
|
+
# }
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# now we can use it as usual: remote can call provder method, and we can call remote:
|
25
|
+
#
|
26
|
+
# endpoint.remote.do_something( times: 4) { |result|
|
27
|
+
# }
|
28
|
+
#
|
29
|
+
#
|
30
|
+
# We do not include it into gem dependencies as it uses EventMachine
|
31
|
+
# which is not needed under JRuby and weight alot (the resto of Farcall plays well with jruby
|
32
|
+
# and MRI threads)
|
33
|
+
#
|
34
|
+
# Due to event-driven nature of eventmachine, WsServerEndpoint uses special version of
|
35
|
+
# {EmFarcall::Endpoint} and {EmFarcall::WsProvider} which are code compatible with regular
|
36
|
+
# farcall classes except for the callback-style calls where appropriate.
|
37
|
+
class WsServerEndpoint < EmFarcall::Endpoint
|
38
|
+
|
39
|
+
# Create endpoint with the already opened websocket instance. Note that all the handshake
|
40
|
+
# should be done prior to construct endpoint (e.g. you may want to have different endpoints
|
41
|
+
# for different paths and arguments)
|
42
|
+
#
|
43
|
+
# See {EmFarcall::Endpoint} for methods to call remote interface and process remote requests.
|
44
|
+
#
|
45
|
+
# @param [EM::WebSocket] websocket socket in open state (handshake should be passed)
|
46
|
+
def initialize websocket, **kwargs
|
47
|
+
@input = EM::Channel.new
|
48
|
+
@output = EM::Channel.new
|
49
|
+
super(@input, @output, **kwargs)
|
50
|
+
|
51
|
+
websocket.onmessage { |data|
|
52
|
+
@input << unpack(data)
|
53
|
+
}
|
54
|
+
@output.subscribe { |data|
|
55
|
+
websocket.send(pack data)
|
56
|
+
}
|
57
|
+
end
|
58
|
+
|
59
|
+
def unpack data
|
60
|
+
JSON.parse data
|
61
|
+
end
|
62
|
+
|
63
|
+
def pack data
|
64
|
+
JSON[data]
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
rescue LoadError
|
71
|
+
$!.to_s =~ /em-websocket/ or raise
|
72
|
+
end
|
73
|
+
|
data/lib/farcall/endpoint.rb
CHANGED
@@ -1,13 +1,19 @@
|
|
1
|
+
require 'hashie'
|
2
|
+
|
1
3
|
module Farcall
|
2
4
|
|
3
5
|
# The protocol endpoint. Takes some transport and implements Farcall protocol over
|
4
6
|
# it. You can use it direcly or with Farcall::RemoteInterface and Farcall::LocalProvider helper
|
5
7
|
# classes.
|
6
8
|
#
|
9
|
+
# Note that the returned data is converted to Hashie::Mash primarily for the sake of :key vs.
|
10
|
+
# 'key' ambigity that otherwise might appear depending on the transport encoding protocol. Anyway
|
11
|
+
# it is better than ruby hash ;)
|
12
|
+
#
|
7
13
|
# Endpoint class is thread-safe.
|
8
14
|
class Endpoint
|
9
15
|
|
10
|
-
# Set or get provider instance. When provider is set, its methods are called by the remote
|
16
|
+
# Set or get provider instance. When provider is set, its public methods are called by the remote
|
11
17
|
# and any possible exception are passed back to caller party. You can use any ruby class instance
|
12
18
|
# everything will work, operators, indexes[] and like.
|
13
19
|
attr_accessor :provider
|
@@ -59,8 +65,9 @@ module Farcall
|
|
59
65
|
end
|
60
66
|
|
61
67
|
# Call remote party. Retruns immediately. When remote party answers, calls the specified block
|
62
|
-
# if present. The block should take |error, result| parameters.
|
63
|
-
#
|
68
|
+
# if present. The block should take |error, result| parameters. If result's content hashes
|
69
|
+
# or result itself are instances of th Hashie::Mash. Error could be nil or
|
70
|
+
# {'class' =>, 'text' => } Hashie::Mash hash. result is always nil if error is presented.
|
64
71
|
#
|
65
72
|
# It is desirable to use Farcall::Endpoint#interface or
|
66
73
|
# Farcall::RemoteInterface rather than this low-level method.
|
@@ -84,7 +91,8 @@ module Farcall
|
|
84
91
|
# Farcall::RemoteInterface rather than this low-level method.
|
85
92
|
#
|
86
93
|
# @param [String] name of the remote command
|
87
|
-
# @return [Object] any data that remote party retruns
|
94
|
+
# @return [Object] any data that remote party retruns. If it is a hash, it is a Hashie::Mash
|
95
|
+
# instance.
|
88
96
|
# @raise [Farcall::RemoteError]
|
89
97
|
#
|
90
98
|
def sync_call(name, *args, **kwargs)
|
@@ -143,12 +151,14 @@ module Farcall
|
|
143
151
|
|
144
152
|
def _received(data)
|
145
153
|
# p [:r, data]
|
154
|
+
data = Hashie::Mash.new data
|
155
|
+
|
146
156
|
cmd, serial, args, kwargs, ref, result, error =
|
147
157
|
%w{cmd serial args kwargs ref result error}.map { |k| data[k] || data[k.to_sym] }
|
148
158
|
!serial || serial < 0 and abort 'missing or bad serial'
|
149
159
|
|
150
160
|
@receive_lock.synchronize {
|
151
|
-
serial == @in_serial or abort "
|
161
|
+
serial == @in_serial or abort "framing error (wrong serial)"
|
152
162
|
@in_serial += 1
|
153
163
|
}
|
154
164
|
|
@@ -161,15 +171,18 @@ module Farcall
|
|
161
171
|
if kwargs && !kwargs.empty?
|
162
172
|
# ruby thing: keyqord args must be symbols, not strings:
|
163
173
|
fixed = {}
|
164
|
-
kwargs.each { |k,v| fixed[k.to_sym] = v}
|
174
|
+
kwargs.each { |k, v| fixed[k.to_sym] = v }
|
165
175
|
args << fixed
|
166
176
|
end
|
167
|
-
@provider.send cmd.to_sym,
|
177
|
+
@provider.send :remote_call, cmd.to_sym, args
|
168
178
|
elsif @on_remote_call
|
169
179
|
@on_remote_call.call cmd, args, kwargs
|
170
180
|
end
|
171
181
|
_send ref: serial, result: result
|
172
182
|
rescue Exception => e
|
183
|
+
# puts e
|
184
|
+
# puts e.backtrace.join("\n")
|
185
|
+
|
173
186
|
_send ref: serial, error: { 'class' => e.class.name, 'text' => e.to_s }
|
174
187
|
end
|
175
188
|
|
@@ -195,23 +208,54 @@ module Farcall
|
|
195
208
|
# suites you better.
|
196
209
|
#
|
197
210
|
# Please remember that Farcall::Transport instance could be used with only
|
198
|
-
# one
|
211
|
+
# one connected object, unlike Farcall::Endpoint, which could be connected to several
|
199
212
|
# consumers.
|
213
|
+
#
|
214
|
+
# @param [Farcall::Endpoint] endpoint to connect to (no transport should be provided).
|
215
|
+
# note that if endpoint is specified, transport would be ignored eeven if used
|
216
|
+
# @param [Farcall::Transport] transport to use (don't use endpoint then)
|
200
217
|
def initialize endpoint: nil, transport: nil, **params
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
218
|
+
if endpoint || transport || params.size > 0
|
219
|
+
@endpoint = if endpoint
|
220
|
+
endpoint
|
221
|
+
else
|
222
|
+
transport ||= Farcall::Transport.create **params
|
223
|
+
Farcall::Endpoint.new transport
|
224
|
+
end
|
225
|
+
@endpoint.provider = self
|
226
|
+
end
|
208
227
|
end
|
209
228
|
|
210
229
|
# Get remote interface
|
211
|
-
# @return [Farcall::Interface] to call methods on the other end
|
230
|
+
# @return [Farcall::Interface] to call methods on the other end, e.g. if this provider would
|
231
|
+
# like to call other party's methiod, it can do it cimply by:
|
232
|
+
#
|
233
|
+
# far_interface.some_method('hello')
|
234
|
+
#
|
212
235
|
def far_interface
|
213
236
|
@endpoint.remote
|
214
237
|
end
|
238
|
+
|
239
|
+
# close connection if need
|
240
|
+
def close_connection
|
241
|
+
@endpoint.close
|
242
|
+
end
|
243
|
+
|
244
|
+
protected
|
245
|
+
|
246
|
+
# Override it to repond to remote calls. Base implementation let invoke only public method
|
247
|
+
# only owned by this class. Be careful to not to expose more than intended!
|
248
|
+
def remote_call name, args
|
249
|
+
m = public_method(name)
|
250
|
+
if m && m.owner == self.class
|
251
|
+
m.call(*args)
|
252
|
+
else
|
253
|
+
raise NoMethodError, "method #{name} is not found"
|
254
|
+
end
|
255
|
+
rescue NameError
|
256
|
+
raise NoMethodError, "method #{name} is not found"
|
257
|
+
end
|
258
|
+
|
215
259
|
end
|
216
260
|
|
217
261
|
# Intervace to the remote provider via Farcall protocols. Works the same as if the object
|
@@ -228,7 +272,7 @@ module Farcall
|
|
228
272
|
# Create interface connected to some endpoint ar transpost.
|
229
273
|
#
|
230
274
|
# Please remember that Farcall::Transport instance could be used with only
|
231
|
-
# one
|
275
|
+
# one connected object, unlike Farcall::Endpoint, which could be connected to several
|
232
276
|
# consumers.
|
233
277
|
#
|
234
278
|
# @param [Farcall::Endpoint|Farcall::Transport] arg either endpoint or a transport
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'thread'
|
2
|
+
|
3
|
+
# :nodoc:
|
4
|
+
class MontorLock
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@condition = ConditionVariable.new
|
8
|
+
@mutex = Mutex.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def notify
|
12
|
+
@mutex.synchronize {
|
13
|
+
@condition.signal
|
14
|
+
}
|
15
|
+
end
|
16
|
+
|
17
|
+
def wait
|
18
|
+
@mutex.synchronize {
|
19
|
+
@condition.wait(@mutex)
|
20
|
+
yield if block_given?
|
21
|
+
}
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
# :nodoc:
|
27
|
+
class Semaphore
|
28
|
+
|
29
|
+
def initialize state_set=false
|
30
|
+
@monitor = MontorLock.new
|
31
|
+
@state_set = state_set
|
32
|
+
end
|
33
|
+
|
34
|
+
def set?
|
35
|
+
@state_set
|
36
|
+
end
|
37
|
+
|
38
|
+
def clear?
|
39
|
+
!set?
|
40
|
+
end
|
41
|
+
|
42
|
+
def wait state
|
43
|
+
while @state_set != state do
|
44
|
+
@monitor.wait
|
45
|
+
end
|
46
|
+
@state_set
|
47
|
+
end
|
48
|
+
|
49
|
+
def wait_set
|
50
|
+
wait true
|
51
|
+
end
|
52
|
+
|
53
|
+
def wait_clear
|
54
|
+
wait false
|
55
|
+
end
|
56
|
+
|
57
|
+
def wait_change &block
|
58
|
+
@monitor.wait {
|
59
|
+
block.call(@state_set) if block
|
60
|
+
}
|
61
|
+
@state_set
|
62
|
+
end
|
63
|
+
|
64
|
+
def set new_state=true
|
65
|
+
if @state_set != new_state
|
66
|
+
@state_set = new_state
|
67
|
+
@monitor.notify
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def clear
|
72
|
+
set false
|
73
|
+
end
|
74
|
+
end
|
data/lib/farcall/transport.rb
CHANGED
@@ -46,7 +46,9 @@ module Farcall
|
|
46
46
|
end
|
47
47
|
|
48
48
|
# Tansport must call this process on each incoming hash
|
49
|
-
# passing it as the only parameter, e.g. self.on_data_received(hash)
|
49
|
+
# passing it as the only parameter, e.g. self.on_data_received.call(hash)
|
50
|
+
# Common trick is to start inner event loop on on_data_recieved=, don't forget
|
51
|
+
# to call super first.
|
50
52
|
attr_accessor :on_data_received, :on_abort, :on_close
|
51
53
|
|
52
54
|
# Utility function. Calls the provided block on data reception. Resets the
|
data/lib/farcall/version.rb
CHANGED
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'farcall'
|
2
|
+
require 'websocket-client-simple'
|
3
|
+
require_relative './monitor_lock'
|
4
|
+
require 'json'
|
5
|
+
|
6
|
+
module Farcall
|
7
|
+
# Websocket client transport using JSON encodeing. Works with ruby threads, pure ruby, runs
|
8
|
+
# everywhere. Use if like any thoer Farcall::Transport, for example:
|
9
|
+
#
|
10
|
+
# in your Gemfile
|
11
|
+
#
|
12
|
+
# gem 'websocket-client-simple'
|
13
|
+
#
|
14
|
+
# in the code
|
15
|
+
#
|
16
|
+
# wst = Farcall::WebsocketJsonClientTransport.new 'ws://icodici.com:8080/test'
|
17
|
+
# i = Farcall::Interface.new transport: wst
|
18
|
+
# result = i.authenticate(login, password) # remote call via interface...
|
19
|
+
#
|
20
|
+
class WebsocketJsonClientTransport < Farcall::Transport
|
21
|
+
|
22
|
+
# Create transport connected to the specified websocket url. Constructor blocks
|
23
|
+
# until connected, or raise error if connection can't be established. Transport uses
|
24
|
+
# JSON encodgin over standard websocket protocol.
|
25
|
+
def initialize ws_url
|
26
|
+
# The stranges bug around in the WebSocket::Client (actually in his eventemitter)
|
27
|
+
me = self
|
28
|
+
|
29
|
+
is_open = Semaphore.new
|
30
|
+
@ws = WebSocket::Client::Simple.connect(ws_url)
|
31
|
+
|
32
|
+
@ws.on(:open) {
|
33
|
+
# if me != self
|
34
|
+
# puts "\n\n\nSelf is set to wrong in the callback in #{RUBY_VERSION}\n\n\n"
|
35
|
+
# end
|
36
|
+
# puts "client is open"
|
37
|
+
is_open.set
|
38
|
+
}
|
39
|
+
|
40
|
+
@ws.on(:message) { |m|
|
41
|
+
# puts "ws client received #{JSON.parse m.data}"
|
42
|
+
me.on_data_received and me.on_data_received.call(JSON.parse m.data)
|
43
|
+
# puts "and sent"
|
44
|
+
}
|
45
|
+
@ws.on(:close) { close }
|
46
|
+
is_open.wait_set
|
47
|
+
end
|
48
|
+
|
49
|
+
# :nodoc:
|
50
|
+
def send_data data
|
51
|
+
@ws.send JSON[data]
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
data/lib/farcall.rb
CHANGED
data/spec/endpoint_spec.rb
CHANGED
@@ -11,8 +11,35 @@ class TestProvider < Farcall::Provider
|
|
11
11
|
@a, @b = a, b
|
12
12
|
return "Foo: #{a+b}, #{optional}"
|
13
13
|
end
|
14
|
+
|
15
|
+
def self.doncallpublic
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
def get_hash
|
20
|
+
{ 'foo' => 'bar', 'bardd' => 'buzz', 'last' => 'item', 'bar' => 'test'}
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def dontcall
|
26
|
+
|
27
|
+
end
|
14
28
|
end
|
15
29
|
|
30
|
+
class StringProvider < Farcall::Provider
|
31
|
+
def initialize(str)
|
32
|
+
@str = str
|
33
|
+
end
|
34
|
+
|
35
|
+
def provide_hash
|
36
|
+
{ 'bar' => 'test', 'foo' => 'bar'}
|
37
|
+
end
|
38
|
+
|
39
|
+
def value
|
40
|
+
@str
|
41
|
+
end
|
42
|
+
end
|
16
43
|
|
17
44
|
describe 'endpoint' do
|
18
45
|
include Farcall
|
@@ -38,14 +65,20 @@ describe 'endpoint' do
|
|
38
65
|
i.a.should == 5
|
39
66
|
i.b.should == 6
|
40
67
|
|
41
|
-
ib.split.should == ['Hello', 'world']
|
68
|
+
# ib.split.should == ['Hello', 'world']
|
69
|
+
|
70
|
+
expect(-> { i.dontcall() }).to raise_error Farcall::RemoteError, /NoMethodError/
|
71
|
+
expect(-> { i.sleep() }).to raise_error Farcall::RemoteError, /NoMethodError/
|
72
|
+
expect(-> { i.abort() }).to raise_error Farcall::RemoteError, /NoMethodError/
|
73
|
+
expect(-> { i.doncallpublic() }).to raise_error Farcall::RemoteError, /NoMethodError/
|
74
|
+
expect(-> { i.initialize(1) }).to raise_error Farcall::RemoteError, /NoMethodError/
|
42
75
|
end
|
43
76
|
|
44
77
|
def check_protocol format
|
45
78
|
s1, s2 = Socket.pair(:UNIX, :STREAM, 0)
|
46
79
|
|
47
80
|
tp = TestProvider.new socket: s1, format: format
|
48
|
-
i = Farcall::Interface.new socket: s2, format: format, provider: "
|
81
|
+
i = Farcall::Interface.new socket: s2, format: format, provider: StringProvider.new("bar")
|
49
82
|
|
50
83
|
expect(-> { i.foo() }).to raise_error Farcall::RemoteError
|
51
84
|
|
@@ -55,14 +88,18 @@ describe 'endpoint' do
|
|
55
88
|
i.a.should == 5
|
56
89
|
i.b.should == 6
|
57
90
|
|
58
|
-
|
91
|
+
i.get_hash.foo.should == 'bar'
|
92
|
+
i.get_hash.bar.should == 'test'
|
93
|
+
tp.far_interface.value.should == 'bar'
|
94
|
+
tp.far_interface.provide_hash.bar.should == 'test'
|
95
|
+
tp.far_interface.provide_hash.foo.should == 'bar'
|
59
96
|
end
|
60
97
|
|
61
98
|
it 'should connect json via shortcut' do
|
62
99
|
check_protocol :json
|
63
100
|
end
|
64
101
|
|
65
|
-
it '
|
102
|
+
it 'boss connect boss via shortcut' do
|
66
103
|
check_protocol :boss
|
67
104
|
end
|
68
105
|
|
@@ -0,0 +1,233 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'eventmachine'
|
3
|
+
|
4
|
+
def standard_check_wsclient(cnt1, r1, r2, r3)
|
5
|
+
r1 = Hashie::Mash.new(r1)
|
6
|
+
r2 = Hashie::Mash.new(r2)
|
7
|
+
|
8
|
+
r1.kwargs.hello.should == 'world'
|
9
|
+
r1.superpong.should == [1, 2, 3]
|
10
|
+
r2.kwargs.hello.should == 'world'
|
11
|
+
r2.superpong.should == [1, 2, 10]
|
12
|
+
|
13
|
+
cnt1.should == 2
|
14
|
+
end
|
15
|
+
|
16
|
+
def standard_check(cnt1, r1, r2, r3)
|
17
|
+
r1.error.should == nil
|
18
|
+
r1.result.kwargs.hello.should == 'world'
|
19
|
+
r1.result.superpong.should == [1, 2, 3]
|
20
|
+
r2.error.should == nil
|
21
|
+
r2.result.kwargs.hello.should == 'world'
|
22
|
+
r2.result.superpong.should == [1, 2, 10]
|
23
|
+
r3.error[:class].should == 'RuntimeError'
|
24
|
+
r3.error.text.should == 'test error'
|
25
|
+
cnt1.should == 3
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
def setup_endpoints
|
30
|
+
c1, c2, c3, c4 = 4.times.map { EM::Channel.new }
|
31
|
+
|
32
|
+
@e1 = EmFarcall::Endpoint.new c1, c2
|
33
|
+
@e2 = EmFarcall::Endpoint.new c3, c4
|
34
|
+
|
35
|
+
c2.subscribe { |x| c3 << x }
|
36
|
+
c4.subscribe { |x| c1 << x }
|
37
|
+
|
38
|
+
@e2.on :superping do |*args, **kwargs|
|
39
|
+
if kwargs[:need_error]
|
40
|
+
raise 'test error'
|
41
|
+
end
|
42
|
+
{ superpong: args, kwargs: kwargs }
|
43
|
+
end
|
44
|
+
[@e1, @e2]
|
45
|
+
end
|
46
|
+
|
47
|
+
describe 'em_farcall' do
|
48
|
+
|
49
|
+
it 'exchange messages' do
|
50
|
+
r1 = nil
|
51
|
+
r2 = nil
|
52
|
+
r3 = nil
|
53
|
+
cnt1 = 0
|
54
|
+
|
55
|
+
EM.run {
|
56
|
+
e1, e2 = setup_endpoints
|
57
|
+
e1.call 'superping', 1, 2, 3, hello: 'world' do |r|
|
58
|
+
r1 = r
|
59
|
+
cnt1 += 1
|
60
|
+
end
|
61
|
+
|
62
|
+
e1.call 'superping', 1, 2, 10, hello: 'world' do |r|
|
63
|
+
r2 = r
|
64
|
+
cnt1 += 1
|
65
|
+
end
|
66
|
+
|
67
|
+
e1.call 'superping', 1, 2, 10, need_error: true, hello: 'world' do |r|
|
68
|
+
r3 = r
|
69
|
+
cnt1 += 1
|
70
|
+
EM.stop
|
71
|
+
end
|
72
|
+
|
73
|
+
EM.add_timer(4) {
|
74
|
+
EM.stop
|
75
|
+
}
|
76
|
+
}
|
77
|
+
standard_check(cnt1, r1, r2, r3)
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'uses remote interface' do
|
81
|
+
r1 = nil
|
82
|
+
r2 = nil
|
83
|
+
r3 = nil
|
84
|
+
cnt1 = 0
|
85
|
+
|
86
|
+
EM.run {
|
87
|
+
e1, e2 = setup_endpoints
|
88
|
+
i = EmFarcall::Interface.new e1
|
89
|
+
|
90
|
+
i.superping(1, 2, 3, hello: 'world') { |r|
|
91
|
+
r1 = r
|
92
|
+
cnt1 += 1
|
93
|
+
}
|
94
|
+
|
95
|
+
i.superping 1, 2, 10, hello: 'world' do |r|
|
96
|
+
r2 = r
|
97
|
+
cnt1 += 1
|
98
|
+
end
|
99
|
+
|
100
|
+
i.superping 1, 2, 10, need_error: true, hello: 'world' do |r|
|
101
|
+
r3 = r
|
102
|
+
cnt1 += 1
|
103
|
+
EM.stop
|
104
|
+
end
|
105
|
+
|
106
|
+
i.test_not_existing
|
107
|
+
|
108
|
+
EM.add_timer(4) {
|
109
|
+
EM.stop
|
110
|
+
}
|
111
|
+
}
|
112
|
+
standard_check(cnt1, r1, r2, r3)
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'runs via websockets' do
|
116
|
+
r1 = nil
|
117
|
+
r2 = nil
|
118
|
+
r3 = nil
|
119
|
+
cnt1 = 0
|
120
|
+
|
121
|
+
EM.run {
|
122
|
+
params = {
|
123
|
+
:host => 'localhost',
|
124
|
+
:port => 8088
|
125
|
+
}
|
126
|
+
e1 = nil
|
127
|
+
e2 = nil
|
128
|
+
|
129
|
+
EM::WebSocket.run(params) do |ws|
|
130
|
+
ws.onopen { |handshake|
|
131
|
+
e2 = EmFarcall::WsServerEndpoint.new ws
|
132
|
+
|
133
|
+
e2.on :superping do |*args, **kwargs|
|
134
|
+
if kwargs[:need_error]
|
135
|
+
raise 'test error'
|
136
|
+
end
|
137
|
+
{ superpong: args, kwargs: kwargs }
|
138
|
+
end
|
139
|
+
}
|
140
|
+
end
|
141
|
+
|
142
|
+
|
143
|
+
EM.defer {
|
144
|
+
t1 = Farcall::WebsocketJsonClientTransport.new 'ws://localhost:8088/test'
|
145
|
+
i = Farcall::Interface.new transport: t1
|
146
|
+
|
147
|
+
r1 = i.superping(1, 2, 3, hello: 'world')
|
148
|
+
cnt1 += 1
|
149
|
+
|
150
|
+
r2 = i.superping 1, 2, 10, hello: 'world'
|
151
|
+
cnt1 += 1
|
152
|
+
|
153
|
+
expect {
|
154
|
+
r3 = i.superping 1, 2, 10, need_error: true, hello: 'world'
|
155
|
+
}.to raise_error(Farcall::RemoteError, "RuntimeError: test error")
|
156
|
+
|
157
|
+
expect {
|
158
|
+
r3 = i.superping_bad 13, 2, 10, hello: 'world'
|
159
|
+
}.to raise_error(Farcall::RemoteError, /NoMethodError/)
|
160
|
+
|
161
|
+
|
162
|
+
EM.stop
|
163
|
+
}
|
164
|
+
EM.add_timer(4) {
|
165
|
+
EM.stop
|
166
|
+
}
|
167
|
+
}
|
168
|
+
|
169
|
+
standard_check_wsclient(cnt1, r1, r2, r3)
|
170
|
+
end
|
171
|
+
|
172
|
+
|
173
|
+
class WsProvider < EmFarcall::Provider
|
174
|
+
def superping *args, **kwargs
|
175
|
+
if kwargs[:need_error]
|
176
|
+
raise 'test error'
|
177
|
+
end
|
178
|
+
{ superpong: args, kwargs: kwargs }
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
it 'runs via websockets with provider' do
|
183
|
+
r1 = nil
|
184
|
+
r2 = nil
|
185
|
+
r3 = nil
|
186
|
+
cnt1 = 0
|
187
|
+
|
188
|
+
EM.run {
|
189
|
+
params = {
|
190
|
+
:host => 'localhost',
|
191
|
+
:port => 8088
|
192
|
+
}
|
193
|
+
e1 = nil
|
194
|
+
e2 = nil
|
195
|
+
|
196
|
+
EM::WebSocket.run(params) do |ws|
|
197
|
+
ws.onopen { |handshake|
|
198
|
+
e2 = EmFarcall::WsServerEndpoint.new ws, provider: WsProvider.new
|
199
|
+
}
|
200
|
+
end
|
201
|
+
|
202
|
+
|
203
|
+
EM.defer {
|
204
|
+
t1 = Farcall::WebsocketJsonClientTransport.new 'ws://localhost:8088/test'
|
205
|
+
i = Farcall::Interface.new transport: t1
|
206
|
+
|
207
|
+
r1 = i.superping(1, 2, 3, hello: 'world')
|
208
|
+
cnt1 += 1
|
209
|
+
|
210
|
+
r2 = i.superping 1, 2, 10, hello: 'world'
|
211
|
+
cnt1 += 1
|
212
|
+
|
213
|
+
expect {
|
214
|
+
r3 = i.superping 1, 2, 11, need_error: true, hello: 'world'
|
215
|
+
}.to raise_error(Farcall::RemoteError, "RuntimeError: test error")
|
216
|
+
|
217
|
+
expect {
|
218
|
+
r3 = i.superping_bad 13, 2, 10, need_error: true, hello: 'world'
|
219
|
+
}.to raise_error(Farcall::RemoteError, /NoMethodError/)
|
220
|
+
|
221
|
+
|
222
|
+
EM.stop
|
223
|
+
}
|
224
|
+
EM.add_timer(4) {
|
225
|
+
EM.stop
|
226
|
+
}
|
227
|
+
}
|
228
|
+
|
229
|
+
standard_check_wsclient(cnt1, r1, r2, r3)
|
230
|
+
end
|
231
|
+
|
232
|
+
|
233
|
+
end
|
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: farcall
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- sergeych
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-06-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: hashie
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: bundler
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -52,20 +66,48 @@ dependencies:
|
|
52
66
|
- - ">="
|
53
67
|
- !ruby/object:Gem::Version
|
54
68
|
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: em-websocket
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: websocket-client-simple
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
55
97
|
- !ruby/object:Gem::Dependency
|
56
98
|
name: boss-protocol
|
57
99
|
requirement: !ruby/object:Gem::Requirement
|
58
100
|
requirements:
|
59
101
|
- - ">="
|
60
102
|
- !ruby/object:Gem::Version
|
61
|
-
version: 1.4.
|
103
|
+
version: 1.4.3
|
62
104
|
type: :development
|
63
105
|
prerelease: false
|
64
106
|
version_requirements: !ruby/object:Gem::Requirement
|
65
107
|
requirements:
|
66
108
|
- - ">="
|
67
109
|
- !ruby/object:Gem::Version
|
68
|
-
version: 1.4.
|
110
|
+
version: 1.4.3
|
69
111
|
description: |2
|
70
112
|
Simple and effective cross-platform RPC protocol. Can work with any transport capable to
|
71
113
|
pass structures (dictionaries, hashes, whatever you name it). Out of the box provides
|
@@ -85,15 +127,19 @@ files:
|
|
85
127
|
- farcall.gemspec
|
86
128
|
- lib/farcall.rb
|
87
129
|
- lib/farcall/boss_transport.rb
|
130
|
+
- lib/farcall/em_farcall.rb
|
131
|
+
- lib/farcall/em_wsserver_endpoint.rb
|
88
132
|
- lib/farcall/endpoint.rb
|
89
133
|
- lib/farcall/json_transport.rb
|
134
|
+
- lib/farcall/monitor_lock.rb
|
90
135
|
- lib/farcall/transport.rb
|
91
136
|
- lib/farcall/version.rb
|
137
|
+
- lib/farcall/wsclient_transport.rb
|
92
138
|
- spec/endpoint_spec.rb
|
93
|
-
- spec/rmi_spec.rb
|
94
139
|
- spec/spec_helper.rb
|
95
140
|
- spec/transports_spec.rb
|
96
|
-
|
141
|
+
- spec/websock_spec.rb
|
142
|
+
homepage: https://github.com/sergeych/farcall
|
97
143
|
licenses:
|
98
144
|
- MIT
|
99
145
|
metadata: {}
|
@@ -113,12 +159,12 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
113
159
|
version: '0'
|
114
160
|
requirements: []
|
115
161
|
rubyforge_project:
|
116
|
-
rubygems_version: 2.
|
162
|
+
rubygems_version: 2.5.1
|
117
163
|
signing_key:
|
118
164
|
specification_version: 4
|
119
165
|
summary: Simple, elegant and cross-platofrm RPC protocol
|
120
166
|
test_files:
|
121
167
|
- spec/endpoint_spec.rb
|
122
|
-
- spec/rmi_spec.rb
|
123
168
|
- spec/spec_helper.rb
|
124
169
|
- spec/transports_spec.rb
|
170
|
+
- spec/websock_spec.rb
|