elrpc 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b19cb4f1857c727202dfee06bce97cad08cf54ad
4
- data.tar.gz: 1038805533721cde69d0a9e1a9b9a7991cd3c61d
3
+ metadata.gz: 1ff7cff0814489d4e855848dd5f6c59ebbcffcf0
4
+ data.tar.gz: 246d1edd90a7f3ecf6f039b23a8b9c13f7fc053b
5
5
  SHA512:
6
- metadata.gz: 53444d2a46a9fc54d3eed9004b6e23df4553d6b57aa39c42e46f9c8088ac5a523bce1e2f7b54e40e51bc9f50a35f24ead175c042dec6aa7329da8f2febe5c63a
7
- data.tar.gz: 01a5342c11b311b802d895d88e694e3b5fb848c0d5b1200d1be4f265ff6fefaba4ab1d7c94f4f26f33be0bc5f869c521ce55df44c92cb4f2e64b51630d0b1d56
6
+ metadata.gz: cf75699c742ea27c6c49073a45e5c01c50f4b6411946738236906459dcaf54263e6c6d765681b34d3ad62d884691f38b2ac5d031da070601276b836078e38d88
7
+ data.tar.gz: 502a9ac563cc7886a70dd4c66b0f0a5b99448478d3faa5656f872bbe104ccad86c269b70ad4022f2f533eedd1f975fa0e0e3613715f84b5393872d5fc7821e65
data/README.md CHANGED
@@ -3,14 +3,15 @@
3
3
  EPC is an RPC stack for Emacs Lisp and Elrpc is an implementation of EPC in Ruby.
4
4
  Using elrpc, you can develop an emacs extension with Ruby code.
5
5
 
6
- - https://github.com/kiwanami/emacs-epc
6
+ - [EPC at github](https://github.com/kiwanami/emacs-epc)
7
7
 
8
8
  ## Sample Code
9
9
 
10
- ### Ruby code (child process)
10
+ ### Ruby code (server process)
11
11
 
12
- This code will be started by the parent process.
12
+ This code is started by the client process, such as Emacs Lisp.
13
13
 
14
+ `echo.rb`
14
15
  ```ruby
15
16
  require 'elrpc'
16
17
 
@@ -27,16 +28,17 @@ end
27
28
  server.wait
28
29
  ```
29
30
 
30
- ### Emacs Lisp code (parent process)
31
+ ### Emacs Lisp code (client process)
31
32
 
32
- This elisp code calls the child process.
33
+ This elisp code calls the server process.
33
34
  The package `epc` is required.
34
35
 
36
+ `echo-client.el`
35
37
  ```el
36
38
  (require 'epc)
37
39
 
38
40
  (let (epc)
39
- ;; start a child process (using bundle exec)
41
+ ;; start a server process (using bundle exec)
40
42
  (setq epc (epc:start-epc "bundle" '("exec" "ruby" "echo.rb")))
41
43
 
42
44
  (deferred:$
@@ -49,14 +51,15 @@ The package `epc` is required.
49
51
  (epc:stop-epc epc)) ; just `eval-last-sexp' here
50
52
  ```
51
53
 
52
- ### Ruby code (parent process)
54
+ ### Ruby code (client process)
53
55
 
54
- You can also write the parent process code in Ruby.
56
+ You can also write the client process code in Ruby.
55
57
 
58
+ `echo-client.rb`
56
59
  ```ruby
57
60
  require 'elrpc'
58
61
 
59
- # start a child process
62
+ # start a server process
60
63
  cl = Elrpc.start_process(["ruby","echo.rb"])
61
64
 
62
65
  # synchronous calling
@@ -71,7 +74,7 @@ puts "2 wait"
71
74
  sleep 0.2
72
75
 
73
76
  puts "4 ok"
74
- # kill the child process
77
+ # kill the server process
75
78
  cl.stop
76
79
  ```
77
80
 
@@ -103,6 +106,16 @@ Or install it yourself as:
103
106
 
104
107
  ## API Document
105
108
 
109
+ ### EPC Overview
110
+
111
+ The EPC uses a peer-to-peer-architecture. After the connection is established, both peers can define remote methods and call the methods at the other side.
112
+
113
+ Let we define the words *server* and *client*. *Server* is a process which opens a TCP port and waiting for the connection. *Client* is a process which connects to the *server*. In most cases, a *client* process starts a *server* process. Then, the *server* process provides some services to the *client* process.
114
+
115
+ This diagram shows the API usage and the relation of processes.
116
+
117
+ ![EPC Overview](https://cacoo.com/diagrams/cFQgb5d5W9MphN8z-B5059.png)
118
+
106
119
  Please see the EPC document for the overview of EPC stack and protocol details.
107
120
 
108
121
  - [EPC Readme](https://github.com/kiwanami/emacs-epc)
@@ -111,21 +124,181 @@ Please see the `elparser` document for object serialization.
111
124
 
112
125
  - [elparser Readme](https://github.com/kiwanami/ruby-elparser)
113
126
 
114
- ### Start EPC
127
+ ### Building Server-Process
128
+
129
+ - Module method : `Elrpc::start_server(methods = [], port = 0)`
130
+ - Arguments
131
+ - `methods` : Array of `Elrpc::Method` instances.
132
+ - `port` : TCP Port number. 0 means that the number is decided by OS.
133
+ - Return
134
+ - A `Elrpc::RPCService` instance
135
+
136
+
137
+ *Sample Code*
138
+ ```ruby
139
+ server = Elrpc::start_server
140
+ ```
141
+
142
+
143
+ ### Defining Remote Method
144
+
145
+ - `Elrpc::RPCService#def_method(name, argdoc=nil, docstring=nil, &block)`
146
+ - Arguments
147
+ - `name` : String. Method name which is referred by the peer process.
148
+ - `argdoc` : String[optional]. Argument information for human.
149
+ - `docstring` : String[optional]. Method information for human.
150
+ - `&block` : Block. Code block which is called by the peer process. The return value is serialized and sent to the peer.
151
+
152
+ The return value of the code block is serialized and sent to the peer process. So, if the return value includes wrong values which can't be serialized by `elparser`, the runtime exception `EPCStackError` is thrown to the method calling of the peer process.
153
+
154
+ *Sample Code*
155
+ ```ruby
156
+ server.def_method("echo") do |arg|
157
+ arg
158
+ end
159
+
160
+ server.def_method("add") do |a, b|
161
+ a + b
162
+ end
115
163
 
116
- ### Stop EPC
164
+ server.def_method("inject",
165
+ "init(initial value), op(operator symbol), list",
166
+ "Apply Enumerable#inject method.") do |init, op, list|
167
+ list.inject(init, op)
168
+ end
169
+ ```
117
170
 
118
- ### Define Remote Method
171
+ ### Calling Remote Method
172
+
173
+ If the peer process defines some methods, the instance of `Elrpc::RPCService` can call the peer's method, regardless of the server process or the client one. (See the EPC document.)
174
+
175
+ - `Elrpc::RPCService#call_method(name, *args)`
176
+ - Synchronous method calling. The current thread is blocked until the calling result is returned.
177
+ - Arguments
178
+ - `name` : String. Method name to call.
179
+ - `args` : Array(Variable length arguments) Argument values.
180
+ - Return
181
+ - The return value which is returned by the peer process.
182
+ - Exception
183
+ - `EPCRuntimeError` : An exception which is thrown by the peer's method.
184
+ - `EPCStackError` : An exception which is thrown by the EPC protocol stack.
185
+
186
+ - `Elrpc::RPCService#call_method_async(name, *args, &block)`
187
+ - Asynchronous method calling. The current thread is not blocked. The calling result is passed to the code block.
188
+ - Arguments
189
+ - `name` : String. Method name to call.
190
+ - `args` : Array(Variable length arguments) Argument values.
191
+ - `block` : Block.
192
+ - Block Arguments
193
+ - `err` : If `nil`, the method calling succeed and the return value is bounded by `value`. If not `nil`, an exception is thrown within the method calling.
194
+ - `EPCRuntimeError` : An exception which is thrown by the peer's method.
195
+ - `EPCStackError` : An exception which is thrown by the EPC protocol stack.
196
+ - `value` : The return value which is returned by the peer process.
197
+
198
+ *Sample Code*
199
+ ```ruby
200
+ puts server.call_method("echo", "hello")
119
201
 
120
- ### Call Remote Method
202
+ server.call_method_async("echo", "hello") do |err, value|
203
+ puts value
204
+ end
121
205
 
122
- ### Error Handling
206
+ puts server.call_method("add", 1, 2)
207
+
208
+ server.call_method("add", 1, 2) do |err, value|
209
+ puts value
210
+ end
211
+
212
+ puts server.call_method("inject", 0, :+, [1,2,3,4])
213
+ puts server.call_method("inject", 1, :*, [1,2,3,4])
214
+ ```
123
215
 
124
216
  ### Utilities
125
217
 
126
- ### Define Server
218
+ - `Elrpc::RPCService#query_methods`
219
+ - `Elrpc::RPCService#query_methods_async`
220
+ - Return
221
+ - Array of method specs of the peer process.
222
+
223
+ *Sample Code*
224
+ ```ruby
225
+ server.query_methods
226
+ # => [[:echo, nil, nil], [:add, nil, nil], [:inject, "init, op, list", "Enumerable#inject"]]
227
+ ```
228
+
229
+ ### EPC Process
230
+
231
+ Elrpc can implement the client process which starts a server process. The server process can be implemented in Ruby and the other language, such as Perl, Python and Emacs Lisp.
232
+
233
+ - Module method `Elrpc::start_process(cmd)`
234
+ - Argument
235
+ - `cmd` : Array. Command line elements for the server process.
236
+ - Return
237
+ - An instance of `Elrpc::Service`.
238
+
239
+ *Sample Code*
240
+ ```ruby
241
+ cl = Elrpc.start_process(["ruby","echo.rb"])
242
+
243
+ puts cl.call_method("echo", "1 hello")
244
+
245
+ cl.stop
246
+ ```
247
+
248
+ ## Development
249
+
250
+ In most cases, the client process is Emacs and the server one is implemented by Elrpc to extend Emacs functions in Ruby.
251
+ However, it may be difficult to develop the programs which belong to the different environment.
252
+ So, at first, it is better to implement both sides in Ruby and write tests.
253
+
254
+ If you want to watch the STDOUT and STDERR of the server process, start the process from command line and connect to the process with `irb` or `pry`, like following:
255
+
256
+ *Starting the server process*
257
+ ```
258
+ $ bundle exec ruby echo.rb
259
+ 12345
260
+ ```
261
+
262
+ `12345` is port number to connect from the client process. The number changes each time.
263
+ Then, start `irb` in the another terminal.
264
+
265
+ *Connecting to the process from irb*
266
+ ```
267
+ $ bundle exec irb
268
+ > require 'elrpc'
269
+ > cl = Elrpc.start_client(12345)
270
+ > cl.call_method("echo", "hello")
271
+ ```
272
+
273
+ When you invoke `call_method`, the first terminal in which the server process runs, may show some output.
274
+
275
+ ## Performance
276
+
277
+ EPC is designed for fast communication between Emacs and other processes.
278
+ Employing S-exp serialization and keeping TCP connection, EPC is faster than HTTP-based RPC, such as JSON-RPC.
279
+
280
+ Executing the benchmark program `test/echo-bench.rb`, You can check the performance in your environment. The program measures following aspects:
281
+
282
+ - round trip time of method invocation
283
+ - synchronous/asynchronous calling
284
+ - string transportation
285
+ - array/list serialization and transportation
286
+ - hash/alist serialization and transportation
287
+
288
+ Here is the result on Lenovo X240 with Intel Core i7-4600U CPU 2.10GHz, 8GB RAM, ruby 2.1.4p265 x86_64-linux.
289
+
290
+ ```
291
+ $ bundle exec ruby test/echo-bench.rb
292
+ user system total real
293
+ int 0.180000 0.040000 0.220000 ( 0.402582)
294
+ int_a 0.180000 0.010000 0.190000 ( 0.205672)
295
+ string 0.390000 0.040000 0.430000 ( 0.753418)
296
+ array 1.410000 0.070000 1.480000 ( 2.687667)
297
+ hash 3.930000 0.070000 4.000000 ( 7.632865)
298
+ (call/sec) int: 9090.9 int_async: 10526.3 string: 4651.2 array: 1351.4 hash: 500.0
299
+ ```
127
300
 
128
- ### Debug
301
+ In the condition Ruby to Ruby, `elrpc` can perform around 10000 call/sec.
129
302
 
130
303
  ## License
131
304
 
data/elrpc.gemspec CHANGED
@@ -20,6 +20,6 @@ Gem::Specification.new do |spec|
20
20
 
21
21
  spec.add_runtime_dependency "elparser", "~> 0.0"
22
22
  spec.add_development_dependency "bundler", "~> 1.7"
23
- spec.add_development_dependency "rake", "~> 10.0"
24
- spec.add_development_dependency "test-unit", "~> 3.0"
23
+ spec.add_development_dependency "rake", "~> 10"
24
+ spec.add_development_dependency "test-unit", "~> 3"
25
25
  end
data/lib/elrpc/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Elrpc
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
data/lib/elrpc.rb CHANGED
@@ -5,6 +5,7 @@ require "socket"
5
5
  require "thread"
6
6
  require "monitor"
7
7
  require "logger"
8
+ require "open3"
8
9
 
9
10
  require "elparser"
10
11
 
@@ -72,17 +73,24 @@ module Elrpc
72
73
 
73
74
  def _start_logger
74
75
  return Thread.start do
76
+ stdout = @io3[1]
77
+ stderr = @io3[2]
75
78
  loop do
76
- ret = @io.readline
77
- break if ret.nil?
78
- @logger.puts(ret) if @logger
79
+ IO.select([stdout, stderr]).flatten.compact.each do |io|
80
+ io.each do |line|
81
+ next if line.nil? || line.empty?
82
+ @logger.puts(line) if @logger
83
+ end
84
+ end
85
+ break if stdout.eof? && stderr.eof?
79
86
  end
80
87
  end
81
88
  end
82
89
 
83
90
  def start
84
- @io = IO.popen(@cmd)
85
- @port = @io.readline.to_i
91
+ @io3 = Open3.popen3(@cmd) # stdin, stdout, stderr, wait_thr
92
+ @port = @io3[2].readline.to_i
93
+ @io3[0].close
86
94
  @output = nil
87
95
  @thread = _start_logger
88
96
  @client = Elrpc.start_client(@port)
@@ -115,6 +123,8 @@ module Elrpc
115
123
 
116
124
  def stop
117
125
  @client.stop
126
+ @io3[1].close
127
+ @io3[2].close
118
128
  end
119
129
 
120
130
  end
@@ -149,6 +159,9 @@ module Elrpc
149
159
  def message
150
160
  "#{@_classname} : #{@_message}"
151
161
  end
162
+ def to_s
163
+ message
164
+ end
152
165
 
153
166
  def remote_classname
154
167
  @_classname
@@ -159,6 +172,7 @@ module Elrpc
159
172
  def remote_backtrace
160
173
  @_backtrace
161
174
  end
175
+
162
176
  end
163
177
 
164
178
  class EPCStackError < StandardError
@@ -171,6 +185,9 @@ module Elrpc
171
185
  def message
172
186
  "#{@_classname} : #{@_message}"
173
187
  end
188
+ def to_s
189
+ message
190
+ end
174
191
 
175
192
  def remote_classname
176
193
  @_classname
@@ -1,7 +1,7 @@
1
1
  (require 'epc)
2
2
 
3
3
  (let (epc)
4
- ;; start a child process
4
+ ;; start a server process
5
5
  ;; bundle exec
6
6
  (setq epc (epc:start-epc "bundle" '("exec" "ruby" "echo.rb")))
7
7
 
@@ -21,7 +21,7 @@
21
21
  ;; synchronous calling (debug purpose)
22
22
  (message "%S" (epc:call-sync epc 'echo '(world)))
23
23
 
24
- ;;
24
+ ;; kill the server process
25
25
  (epc:stop-epc epc)
26
26
  )
27
27
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: elrpc
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - SAKURAI Masashi
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-02-17 00:00:00.000000000 Z
11
+ date: 2015-12-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: elparser
@@ -44,28 +44,28 @@ dependencies:
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '10.0'
47
+ version: '10'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '10.0'
54
+ version: '10'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: test-unit
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '3.0'
61
+ version: '3'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: '3.0'
68
+ version: '3'
69
69
  description: EPC (RPC stack for the Emacs Lisp) for Ruby.
70
70
  email:
71
71
  - m.sakurai@kiwanami.net
@@ -112,7 +112,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
112
112
  version: '0'
113
113
  requirements: []
114
114
  rubyforge_project:
115
- rubygems_version: 2.4.3
115
+ rubygems_version: 2.4.8
116
116
  signing_key:
117
117
  specification_version: 4
118
118
  summary: EPC (RPC stack for the Emacs Lisp) for Ruby.