easy-serve 0.3 → 0.4
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.
- checksums.yaml +4 -4
- data/examples/remote-drb.rb +55 -0
- data/examples/remote-eval.rb +56 -0
- data/examples/{remote.rb → remote-manual.rb} +2 -1
- data/examples/remote-run-script.rb +19 -0
- data/examples/remote-run.rb +53 -0
- data/lib/easy-serve.rb +33 -12
- data/lib/easy-serve/remote-drb.rb +94 -0
- data/lib/easy-serve/remote-eval.rb +63 -0
- data/lib/easy-serve/remote-run.rb +61 -0
- data/lib/easy-serve/remote.rb +25 -0
- metadata +11 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fa7e317b0835fcccb71074755f7c440a36eb97c5
|
4
|
+
data.tar.gz: f1473351a5081398d0c1a8dc4fe424555cbe7a8b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3f5ee396c21115e4d691c15409aa47f07ca9947a82753e33db9663f21e58ba0930f53541a193095b9705a44d14d96018ad6c03c38a187c407379d90196c7c1ac
|
7
|
+
data.tar.gz: b4027e51407e2070ec4039f70fda510cc5b8a22e5aae5396de86c360e2c2366840f9436bd84d313fc0b2ead5df016c4ff7cd4c83276d719512da868a82a64123
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'easy-serve/remote'
|
2
|
+
|
3
|
+
addr_there = ARGV.shift
|
4
|
+
|
5
|
+
unless addr_there
|
6
|
+
abort <<-END
|
7
|
+
|
8
|
+
Usage: #$0 addr_there
|
9
|
+
|
10
|
+
The 'addr_there' is the remote address on which client code will run.
|
11
|
+
It must be a destination accepted by ssh, optionally including a user name:
|
12
|
+
|
13
|
+
[user@]hostname
|
14
|
+
|
15
|
+
The 'hostname' must be a valid hostname (not just an ssh alias), since it
|
16
|
+
will be used for the drb connection as well.
|
17
|
+
|
18
|
+
END
|
19
|
+
end
|
20
|
+
|
21
|
+
EasyServe.start do |ez|
|
22
|
+
log = ez.log
|
23
|
+
log.level = Logger::INFO
|
24
|
+
log.formatter = nil if $VERBOSE
|
25
|
+
|
26
|
+
ez.start_servers do
|
27
|
+
ez.server "simple-server", :tcp, nil, 0 do |svr|
|
28
|
+
Thread.new do
|
29
|
+
loop do
|
30
|
+
Thread.new(svr.accept) do |conn|
|
31
|
+
log.info "accepted connection from #{conn.inspect}"
|
32
|
+
conn.write "hello from #{log.progname}"
|
33
|
+
log.info "wrote greeting"
|
34
|
+
conn.close_write
|
35
|
+
log.info "trying to read from #{conn.inspect}"
|
36
|
+
log.info "received: #{conn.read}"
|
37
|
+
conn.close
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
ez.remote "simple-server", host: addr_there do |conn|
|
45
|
+
# this block runs locally, but calls methods on the remote using drb
|
46
|
+
log.progname = "druby remote on #{addr_there}"
|
47
|
+
log.info "trying to read from #{conn.inspect}"
|
48
|
+
log.info "received: #{conn.read}"
|
49
|
+
# note: conn is drb proxy to real conn on remote host, so after the
|
50
|
+
# string is read from the socket in the remote, it is then serialized
|
51
|
+
# by drb back to this (local) process. Don't do this in production!
|
52
|
+
conn.write "hello from #{log.progname}"
|
53
|
+
conn.close
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'easy-serve/remote'
|
2
|
+
|
3
|
+
addr_there = ARGV.shift
|
4
|
+
|
5
|
+
unless addr_there
|
6
|
+
abort <<-END
|
7
|
+
|
8
|
+
Usage: #$0 addr_there
|
9
|
+
|
10
|
+
The 'addr_there' is the remote address on which client code will run.
|
11
|
+
It must be a destination accepted by ssh, optionally including a user name:
|
12
|
+
|
13
|
+
[user@]hostname
|
14
|
+
|
15
|
+
The 'hostname' may by any valid hostname or ssh alias.
|
16
|
+
|
17
|
+
END
|
18
|
+
end
|
19
|
+
|
20
|
+
EasyServe.start do |ez|
|
21
|
+
log = ez.log
|
22
|
+
log.level = Logger::INFO
|
23
|
+
log.formatter = nil if $VERBOSE
|
24
|
+
|
25
|
+
ez.start_servers do
|
26
|
+
ez.server "simple-server", :tcp, nil, 0 do |svr|
|
27
|
+
Thread.new do
|
28
|
+
loop do
|
29
|
+
Thread.new(svr.accept) do |conn|
|
30
|
+
log.info "accepted connection from #{conn.inspect}"
|
31
|
+
conn.write "hello from #{log.progname}"
|
32
|
+
log.info "wrote greeting"
|
33
|
+
conn.close_write
|
34
|
+
log.info "trying to read from #{conn.inspect}"
|
35
|
+
log.info "received: #{conn.read}"
|
36
|
+
conn.close
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
ez.remote "simple-server", host: addr_there, eval: %{
|
44
|
+
conn = conns[0]
|
45
|
+
# this code is executed on the remote host, connected by conn, not drb
|
46
|
+
log.progname = "eval remote on \#{host}"
|
47
|
+
log.info "trying to read from \#{conn.inspect}"
|
48
|
+
log.info "received: \#{conn.read}"
|
49
|
+
conn.write "hello from \#{log.progname}"
|
50
|
+
conn.close
|
51
|
+
}
|
52
|
+
# Note use of \#{} to interpolate variables that are only available
|
53
|
+
# in the binding where the code is eval-ed. Alternately, use
|
54
|
+
# eval: %Q{...}
|
55
|
+
# but then interpolation from this script is not posssible.
|
56
|
+
end
|
@@ -4,7 +4,8 @@ servers_file = ARGV.shift
|
|
4
4
|
unless servers_file
|
5
5
|
abort <<-END
|
6
6
|
Usage: #$0 servers_file
|
7
|
-
For the client, copy the generated servers_file to the client host
|
7
|
+
For the client, copy the generated servers_file to the client host, and
|
8
|
+
run with the same command.
|
8
9
|
END
|
9
10
|
end
|
10
11
|
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class RemoteRunScript
|
2
|
+
attr_reader :conns, :host, :log, :args
|
3
|
+
|
4
|
+
def initialize conns, host, log, *args
|
5
|
+
@conns = conns
|
6
|
+
@host = host
|
7
|
+
@log = log
|
8
|
+
@args = args
|
9
|
+
end
|
10
|
+
|
11
|
+
def run
|
12
|
+
conn = conns[0]
|
13
|
+
log.progname = "run remote on #{host}"
|
14
|
+
log.info "trying to read from #{conn.inspect}"
|
15
|
+
log.info "received: #{conn.read}"
|
16
|
+
conn.write "hello from #{log.progname}"
|
17
|
+
conn.close
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'easy-serve/remote'
|
2
|
+
|
3
|
+
addr_there = ARGV.shift
|
4
|
+
|
5
|
+
unless addr_there
|
6
|
+
abort <<-END
|
7
|
+
|
8
|
+
Usage: #$0 addr_there
|
9
|
+
|
10
|
+
The 'addr_there' is the remote address on which client code will run.
|
11
|
+
It must be a destination accepted by ssh, optionally including a user name:
|
12
|
+
|
13
|
+
[user@]hostname
|
14
|
+
|
15
|
+
The 'hostname' may by any valid hostname or ssh alias.
|
16
|
+
|
17
|
+
Note: you must set up the remote by doing
|
18
|
+
|
19
|
+
scp examples/remote-run-script.rb addr_there:/tmp/
|
20
|
+
|
21
|
+
END
|
22
|
+
end
|
23
|
+
|
24
|
+
EasyServe.start do |ez|
|
25
|
+
log = ez.log
|
26
|
+
log.level = Logger::INFO
|
27
|
+
log.formatter = nil if $VERBOSE
|
28
|
+
|
29
|
+
ez.start_servers do
|
30
|
+
ez.server "simple-server", :tcp, nil, 0 do |svr|
|
31
|
+
Thread.new do
|
32
|
+
loop do
|
33
|
+
Thread.new(svr.accept) do |conn|
|
34
|
+
log.info "accepted connection from #{conn.inspect}"
|
35
|
+
conn.write "hello from #{log.progname}"
|
36
|
+
log.info "wrote greeting"
|
37
|
+
conn.close_write
|
38
|
+
log.info "trying to read from #{conn.inspect}"
|
39
|
+
log.info "received: #{conn.read}"
|
40
|
+
conn.close
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
ez.remote "simple-server", host: addr_there,
|
48
|
+
dir: "/tmp",
|
49
|
+
file: "remote-run-script.rb",
|
50
|
+
# 'file' passed to load, so can be rel to dir or ruby's $LOAD_PATH
|
51
|
+
class_name: "RemoteRunScript",
|
52
|
+
args: []
|
53
|
+
end
|
data/lib/easy-serve.rb
CHANGED
@@ -4,7 +4,7 @@ require 'yaml'
|
|
4
4
|
require 'fileutils'
|
5
5
|
|
6
6
|
class EasyServe
|
7
|
-
VERSION = "0.
|
7
|
+
VERSION = "0.4"
|
8
8
|
|
9
9
|
class Server
|
10
10
|
attr_reader :name, :pid, :addr
|
@@ -58,17 +58,19 @@ class EasyServe
|
|
58
58
|
@clients = [] # pid
|
59
59
|
@passive_clients = [] # pid
|
60
60
|
@owner = false
|
61
|
-
@servers =
|
61
|
+
@servers = opts[:servers] # name => Server
|
62
62
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
63
|
+
unless servers
|
64
|
+
if servers_file
|
65
|
+
@servers =
|
66
|
+
begin
|
67
|
+
load_server_table
|
68
|
+
rescue Errno::ENOENT
|
69
|
+
init_server_table
|
70
|
+
end
|
71
|
+
else
|
72
|
+
init_server_table
|
73
|
+
end
|
72
74
|
end
|
73
75
|
end
|
74
76
|
|
@@ -167,11 +169,27 @@ class EasyServe
|
|
167
169
|
name =~ /-\d+\z/ ? name.succ : name + "-0"
|
168
170
|
end
|
169
171
|
|
172
|
+
def host_name
|
173
|
+
@host_name ||= begin
|
174
|
+
hn = Socket.gethostname
|
175
|
+
begin
|
176
|
+
official_hostname = Socket.gethostbyname(hn)[0]
|
177
|
+
if /\./ =~ official_hostname
|
178
|
+
official_hostname
|
179
|
+
else
|
180
|
+
official_hostname + ".local"
|
181
|
+
end
|
182
|
+
rescue
|
183
|
+
'localhost'
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
170
188
|
def server name, proto = :unix, host = nil, port = nil
|
171
189
|
server_class, *server_addr =
|
172
190
|
case proto
|
173
191
|
when /unix/i; [UNIXServer, choose_socket_filename(name, base: host)]
|
174
|
-
when /tcp/i; [TCPServer, host ||
|
192
|
+
when /tcp/i; [TCPServer, host || host_name, port || 0]
|
175
193
|
else raise ArgumentError, "Unknown socket protocol: #{proto.inspect}"
|
176
194
|
end
|
177
195
|
|
@@ -264,6 +282,9 @@ class EasyServe
|
|
264
282
|
else TCPSocket
|
265
283
|
end
|
266
284
|
socket_class.new(*addr)
|
285
|
+
rescue => ex
|
286
|
+
ex.message << " addr=#{addr.inspect}"
|
287
|
+
raise
|
267
288
|
end
|
268
289
|
|
269
290
|
# ^C in the irb session (parent process) should not kill the
|
@@ -0,0 +1,94 @@
|
|
1
|
+
require 'drb'
|
2
|
+
|
3
|
+
class EasyServe
|
4
|
+
# useful for testing only -- use _eval or _run for production
|
5
|
+
def remote_drb *server_names, host: nil
|
6
|
+
## passive option?
|
7
|
+
## remote logfile option?
|
8
|
+
|
9
|
+
DRb.start_service("druby://#{host_name}:0", nil)
|
10
|
+
|
11
|
+
hostname = host.sub(/.*@/,"")
|
12
|
+
host_uri = "druby://#{hostname}:0"
|
13
|
+
|
14
|
+
log.progname = "remote_drb #{host}"
|
15
|
+
|
16
|
+
IO.popen ["ssh", host, "ruby"], "w+" do |ssh|
|
17
|
+
ssh.puts %Q{
|
18
|
+
$stdout.sync = true
|
19
|
+
begin
|
20
|
+
require 'drb'
|
21
|
+
require 'yaml'
|
22
|
+
require 'easy-serve'
|
23
|
+
|
24
|
+
server_names = #{server_names.inspect}
|
25
|
+
servers = YAML.load(#{YAML.dump(servers).inspect})
|
26
|
+
log_level = #{log.level}
|
27
|
+
host_uri = #{host_uri.inspect}
|
28
|
+
|
29
|
+
EasyServe.start servers: servers do |ez|
|
30
|
+
log = ez.log
|
31
|
+
log.level = log_level
|
32
|
+
log.formatter = nil if $VERBOSE
|
33
|
+
|
34
|
+
ez.local *server_names do |*conns|
|
35
|
+
begin
|
36
|
+
DRb.start_service(host_uri, {conns: conns})
|
37
|
+
puts DRb.uri
|
38
|
+
|
39
|
+
Thread.new do
|
40
|
+
loop do
|
41
|
+
sleep 1
|
42
|
+
begin
|
43
|
+
puts "."
|
44
|
+
rescue
|
45
|
+
exit
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
DRb.thread.join
|
51
|
+
|
52
|
+
rescue => ex
|
53
|
+
puts "ez error", ex, ex.backtrace
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
rescue => ex
|
58
|
+
puts "ez error", ex, ex.backtrace
|
59
|
+
end
|
60
|
+
}
|
61
|
+
|
62
|
+
ssh.close_write
|
63
|
+
result = ssh.gets
|
64
|
+
|
65
|
+
if !result
|
66
|
+
raise RemoteError, "problem with ssh connection to remote"
|
67
|
+
else
|
68
|
+
error = result[/ez error/]
|
69
|
+
if error
|
70
|
+
raise RemoteError, "error raised in remote: #{ssh.read}"
|
71
|
+
else
|
72
|
+
uri = result[/druby:\/\/\S+/]
|
73
|
+
if uri
|
74
|
+
Thread.new do
|
75
|
+
loop do
|
76
|
+
ssh.gets # consume the "."
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
log.debug "remote is at #{uri}"
|
81
|
+
ro = DRbObject.new_with_uri(uri)
|
82
|
+
conns = ro[:conns]
|
83
|
+
conns_ary = []
|
84
|
+
conns.each {|c| conns_ary << c} # needed because it's a DRbObject
|
85
|
+
yield(*conns_ary) if block_given?
|
86
|
+
else
|
87
|
+
raise RemoteError,
|
88
|
+
"no druby uri in string from remote: #{result.inspect}"
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
class EasyServe
|
2
|
+
# useful simple cases in testing and in production, but long eval strings
|
3
|
+
# can be hard to debug -- use _run instead
|
4
|
+
def remote_eval *server_names, host: nil, **opts
|
5
|
+
## passive option?
|
6
|
+
## remote logfile option?
|
7
|
+
|
8
|
+
log.progname = "remote_eval #{host}"
|
9
|
+
|
10
|
+
IO.popen ["ssh", host, "ruby"], "w+" do |ssh|
|
11
|
+
ssh.puts %Q{
|
12
|
+
$stdout.sync = true
|
13
|
+
begin
|
14
|
+
require 'yaml'
|
15
|
+
require 'easy-serve'
|
16
|
+
|
17
|
+
class EasyServe
|
18
|
+
def binding_for_remote_eval conns, host, log
|
19
|
+
binding
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
server_names = #{server_names.inspect}
|
24
|
+
servers = YAML.load(#{YAML.dump(servers).inspect})
|
25
|
+
log_level = #{log.level}
|
26
|
+
eval_string = #{opts[:eval].inspect}
|
27
|
+
host = #{host.inspect}
|
28
|
+
|
29
|
+
EasyServe.start servers: servers do |ez|
|
30
|
+
log = ez.log
|
31
|
+
log.level = log_level
|
32
|
+
log.formatter = nil if $VERBOSE
|
33
|
+
|
34
|
+
ez.local *server_names do |*conns|
|
35
|
+
begin
|
36
|
+
eval eval_string, ez.binding_for_remote_eval(conns, host, log)
|
37
|
+
rescue => ex
|
38
|
+
puts "ez error", ex, ex.backtrace
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
rescue => ex
|
43
|
+
puts "ez error", ex, ex.backtrace
|
44
|
+
end
|
45
|
+
}
|
46
|
+
|
47
|
+
ssh.close_write
|
48
|
+
result = ssh.gets
|
49
|
+
|
50
|
+
if result
|
51
|
+
error = result[/ez error/]
|
52
|
+
if error
|
53
|
+
raise RemoteError, "error raised in remote: #{ssh.read}"
|
54
|
+
else
|
55
|
+
puts result
|
56
|
+
while s = ssh.gets
|
57
|
+
puts s
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
class EasyServe
|
2
|
+
# useful in production, though it requires remote lib files to be set up
|
3
|
+
def remote_run *server_names, host: nil, **opts
|
4
|
+
## passive option?
|
5
|
+
## remote logfile option?
|
6
|
+
|
7
|
+
log.progname = "remote_run #{host}"
|
8
|
+
|
9
|
+
IO.popen ["ssh", host, "ruby"], "w+" do |ssh|
|
10
|
+
ssh.puts %Q{
|
11
|
+
$stdout.sync = true
|
12
|
+
begin
|
13
|
+
require 'yaml'
|
14
|
+
require 'easy-serve'
|
15
|
+
|
16
|
+
server_names = #{server_names.inspect}
|
17
|
+
servers = YAML.load(#{YAML.dump(servers).inspect})
|
18
|
+
log_level = #{log.level}
|
19
|
+
host = #{host.inspect}
|
20
|
+
args = YAML.load(#{YAML.dump(opts[:args]).inspect})
|
21
|
+
|
22
|
+
#{opts[:dir] && "Dir.chdir #{opts[:dir].inspect}"}
|
23
|
+
load #{opts[:file].inspect}
|
24
|
+
|
25
|
+
EasyServe.start servers: servers do |ez|
|
26
|
+
log = ez.log
|
27
|
+
log.level = log_level
|
28
|
+
log.formatter = nil if $VERBOSE
|
29
|
+
|
30
|
+
ez.local *server_names do |*conns|
|
31
|
+
begin
|
32
|
+
cl = Object.const_get(#{opts[:class_name].inspect})
|
33
|
+
ro = cl.new(conns, host, log, *args)
|
34
|
+
ro.run
|
35
|
+
rescue => ex
|
36
|
+
puts "ez error", ex, ex.backtrace
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
rescue => ex
|
41
|
+
puts "ez error", ex, ex.backtrace
|
42
|
+
end
|
43
|
+
}
|
44
|
+
|
45
|
+
ssh.close_write
|
46
|
+
result = ssh.gets
|
47
|
+
|
48
|
+
if result
|
49
|
+
error = result[/ez error/]
|
50
|
+
if error
|
51
|
+
raise RemoteError, "error raised in remote: #{ssh.read}"
|
52
|
+
else
|
53
|
+
puts result
|
54
|
+
while s = ssh.gets
|
55
|
+
puts s
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'easy-serve'
|
2
|
+
|
3
|
+
class EasyServe
|
4
|
+
class RemoteError < RuntimeError; end
|
5
|
+
|
6
|
+
def remote *server_names, host: nil, **opts
|
7
|
+
raise ArgumentError, "no host specified" unless host
|
8
|
+
|
9
|
+
if opts[:eval]
|
10
|
+
require 'easy-serve/remote-eval'
|
11
|
+
remote_eval *server_names, host: host, **opts
|
12
|
+
|
13
|
+
elsif opts[:file]
|
14
|
+
require 'easy-serve/remote-run'
|
15
|
+
remote_run *server_names, host: host, **opts
|
16
|
+
|
17
|
+
elsif block_given?
|
18
|
+
require 'easy-serve/remote-drb'
|
19
|
+
remote_drb *server_names, host: host, **opts, &Proc.new
|
20
|
+
|
21
|
+
else
|
22
|
+
raise ArgumentError, "cannot select remote mode based on arguments"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: easy-serve
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '0.
|
4
|
+
version: '0.4'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Joel VanderWerf
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-
|
11
|
+
date: 2013-08-26 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Framework for starting tcp/unix servers and connected clients under one
|
14
14
|
parent process.
|
@@ -22,9 +22,17 @@ files:
|
|
22
22
|
- README.md
|
23
23
|
- COPYING
|
24
24
|
- lib/easy-serve.rb
|
25
|
+
- lib/easy-serve/remote-run.rb
|
26
|
+
- lib/easy-serve/remote-drb.rb
|
27
|
+
- lib/easy-serve/remote.rb
|
28
|
+
- lib/easy-serve/remote-eval.rb
|
25
29
|
- examples/simple.rb
|
30
|
+
- examples/remote-manual.rb
|
26
31
|
- examples/multi.rb
|
27
|
-
- examples/remote.rb
|
32
|
+
- examples/remote-run.rb
|
33
|
+
- examples/remote-drb.rb
|
34
|
+
- examples/remote-run-script.rb
|
35
|
+
- examples/remote-eval.rb
|
28
36
|
- examples/passive.rb
|
29
37
|
homepage: https://github.com/vjoel/easy-serve
|
30
38
|
licenses:
|