rapel 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +3 -0
- data/README.md +2 -5
- data/bin/console +3 -2
- data/bin/runtime.rb +65 -0
- data/lib/rapel.rb +83 -1
- data/lib/rapel/expression.rb +30 -0
- data/lib/rapel/replserver.rb +72 -0
- data/lib/rapel/runtime.rb +42 -0
- data/lib/rapel/version.rb +1 -1
- data/rapel.gemspec +1 -1
- metadata +9 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b92563a1ed6631d218e18f28bdae877f2fc9d871
|
4
|
+
data.tar.gz: a6a5260a12414bf9741af4119225c2aa6670d881
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6b67829358e692e0d1d10869537aca3b8292cd73733e7721464067c4c744198be3ea4d50b62b5345767b8e00c6a84bf547e2f1d338674752b469ad99f7629186
|
7
|
+
data.tar.gz: 4d27177c6ded616ace9ac0e74a2f06ad7d27f4d5374d48eb6b168332ee85190a2cd7a92625400cd41e17ca7f7fdf9cc0c8b19633aa1b892a12be55608f3c0b76
|
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -1,8 +1,6 @@
|
|
1
1
|
# Rapel
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
TODO: Delete this and the text above, and describe your gem
|
3
|
+
Rapel (ruh-PELL) provides a multi-client, multi-runtime REPL server which accepts incoming expressions, e.g. 2+2, evaluates them in a runtime, and returns the result, e.g. 4.
|
6
4
|
|
7
5
|
## Installation
|
8
6
|
|
@@ -32,8 +30,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
|
|
32
30
|
|
33
31
|
## Contributing
|
34
32
|
|
35
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/
|
36
|
-
|
33
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/domgetter/rapel.
|
37
34
|
|
38
35
|
## License
|
39
36
|
|
data/bin/console
CHANGED
data/bin/runtime.rb
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'socket'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
logger = File.open("#{File.dirname(__FILE__)}/runtime.log", "a")
|
5
|
+
logger.puts("my pid is #{$$}")
|
6
|
+
logger.flush
|
7
|
+
args = ARGV.select {|arg| arg.start_with? "--" }.map {|arg| arg[2..-1].split "="}.to_h
|
8
|
+
logger.puts("Starting runtime at port #{args["port"]}")
|
9
|
+
logger.flush
|
10
|
+
begin
|
11
|
+
server = TCPServer.new(args["port"])
|
12
|
+
rescue
|
13
|
+
logger.puts $!.inspect
|
14
|
+
logger.flush
|
15
|
+
end
|
16
|
+
begin
|
17
|
+
socket = TCPSocket.new('localhost', args["callback-port"])
|
18
|
+
socket.puts({pid: $$, session_id: args["uuid"]}.to_json)
|
19
|
+
rescue
|
20
|
+
logger.puts $!.inspect
|
21
|
+
logger.flush
|
22
|
+
end
|
23
|
+
|
24
|
+
def escape_newlines(string)
|
25
|
+
string.gsub("\\", "\\\\").gsub("\n", " \\n")
|
26
|
+
end
|
27
|
+
|
28
|
+
def unescape_newlines(string)
|
29
|
+
string.gsub(" \\n", "\n").gsub("\\\\", "\\")
|
30
|
+
end
|
31
|
+
|
32
|
+
loop do
|
33
|
+
begin
|
34
|
+
Thread.new(server.accept) do |client|
|
35
|
+
logger.puts("incoming connection from #{client.inspect}")
|
36
|
+
b389483479436 = binding
|
37
|
+
loop {
|
38
|
+
message = client.gets
|
39
|
+
logger.puts("processing message #{message.inspect} from #{client.inspect}")
|
40
|
+
if message.match(/shutdown#{args["port"]}/)
|
41
|
+
logger.puts("shutting down runtime on port #{args["port"]}")
|
42
|
+
exit
|
43
|
+
end
|
44
|
+
begin
|
45
|
+
code = unescape_newlines(message)
|
46
|
+
value = eval(code, b389483479436)
|
47
|
+
rescue Exception => e
|
48
|
+
value = $!
|
49
|
+
end
|
50
|
+
begin
|
51
|
+
logger.puts("returning #{value.inspect} to #{client.inspect}")
|
52
|
+
logger.flush
|
53
|
+
client.puts(escape_newlines(value.inspect))
|
54
|
+
rescue Exception => e
|
55
|
+
logger.puts e.inspect
|
56
|
+
client.puts("")
|
57
|
+
end
|
58
|
+
}
|
59
|
+
client.close
|
60
|
+
end
|
61
|
+
rescue
|
62
|
+
break
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
data/lib/rapel.rb
CHANGED
@@ -1,5 +1,87 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'socket'
|
3
|
+
require 'securerandom'
|
4
|
+
|
5
|
+
Signal.trap("INT") do
|
6
|
+
Rapel.shutdown
|
7
|
+
exit
|
8
|
+
end
|
9
|
+
|
10
|
+
class Hash
|
11
|
+
def slice(*keys)
|
12
|
+
keys.reduce({}) {|h, k| h[k] = self[k]}
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class TCPSocket
|
17
|
+
|
18
|
+
def accept_loop
|
19
|
+
loop do
|
20
|
+
message = JSON.parse(self.gets.chomp, symbolize_names: true)
|
21
|
+
$stdout.puts("Message received: #{message} from: #{self}")
|
22
|
+
case message[:op]
|
23
|
+
when "eval"
|
24
|
+
$stdout.puts("Eval message recieved: #{message[:code].inspect}")
|
25
|
+
begin
|
26
|
+
Rapel::REPLServer::Input.new("qer")
|
27
|
+
rescue
|
28
|
+
puts $!
|
29
|
+
end
|
30
|
+
yield Rapel::REPLServer::Input.new(message)
|
31
|
+
else
|
32
|
+
break
|
33
|
+
end
|
34
|
+
end
|
35
|
+
close
|
36
|
+
end
|
37
|
+
|
38
|
+
def repl
|
39
|
+
accept_loop do |input|
|
40
|
+
input.read do |expression, context|
|
41
|
+
expression.evaluate(context) do |result|
|
42
|
+
respond_with(result)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def respond_with(result)
|
49
|
+
response = result.to_json
|
50
|
+
self.puts response
|
51
|
+
$stdout.puts "returned #{response} to #{self}"
|
52
|
+
rescue Exception => e
|
53
|
+
$stdout.puts e.inspect
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
1
57
|
require "rapel/version"
|
58
|
+
require "rapel/replserver"
|
59
|
+
require "rapel/expression"
|
60
|
+
require "rapel/runtime"
|
2
61
|
|
3
62
|
module Rapel
|
4
|
-
|
63
|
+
def self.runtimes
|
64
|
+
@@server.runtimes
|
65
|
+
end
|
66
|
+
|
67
|
+
def self.escape_newlines(string)
|
68
|
+
string.gsub("\\", "\\\\").gsub("\n", " \\n")
|
69
|
+
end
|
70
|
+
|
71
|
+
def self.unescape_newlines(string)
|
72
|
+
string.gsub(" \\n", "\n").gsub("\\\\", "\\")
|
73
|
+
end
|
74
|
+
|
75
|
+
def self.shutdown_runtimes
|
76
|
+
@@server.shutdown
|
77
|
+
end
|
78
|
+
|
79
|
+
def self.start
|
80
|
+
@@server = REPLServer.new
|
81
|
+
@@server.start
|
82
|
+
end
|
83
|
+
|
84
|
+
def self.shutdown
|
85
|
+
@@server.shutdown
|
86
|
+
end
|
5
87
|
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Rapel
|
2
|
+
|
3
|
+
class Expression
|
4
|
+
def initialize(exp)
|
5
|
+
@expression = exp
|
6
|
+
end
|
7
|
+
|
8
|
+
def evaluate(context)
|
9
|
+
$stdout.puts("Evaluating #{self.inspect} within #{context.inspect}")
|
10
|
+
result = {session_id: context.session_id}
|
11
|
+
begin
|
12
|
+
context.socket.puts(Rapel.escape_newlines(@expression))
|
13
|
+
value = context.socket.gets.chomp
|
14
|
+
result[:result] = Rapel.unescape_newlines(value)
|
15
|
+
rescue Exception => e
|
16
|
+
$stdout.puts e
|
17
|
+
result[:result] = ""
|
18
|
+
result[:error] = e.message
|
19
|
+
end
|
20
|
+
$stdout.puts "Received #{value.inspect} from #{context.inspect}"
|
21
|
+
|
22
|
+
yield result
|
23
|
+
end
|
24
|
+
|
25
|
+
def inspect
|
26
|
+
@expression.inspect
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module Rapel
|
2
|
+
|
3
|
+
class REPLServer
|
4
|
+
|
5
|
+
attr_reader :runtimes
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@runtimes = {}
|
9
|
+
server_port = 8091
|
10
|
+
$stdout.puts "Starting Rapel server on port #{server_port}"
|
11
|
+
@server = TCPServer.new(server_port)
|
12
|
+
callback_server_port = 8092
|
13
|
+
callback_server = TCPServer.new(callback_server_port)
|
14
|
+
|
15
|
+
session_id = SecureRandom.uuid
|
16
|
+
runtime = Runtime.new(callback_server_port, session_id)
|
17
|
+
@runtimes[session_id] = runtime
|
18
|
+
|
19
|
+
async_listen_for_runtimes(callback_server)
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
def async_new_repl_connections
|
24
|
+
Thread.new(@server.accept) do |conn|
|
25
|
+
$stdout.puts("Client connected on port: #{conn.addr[1]}")
|
26
|
+
conn.repl
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def async_listen_for_runtimes(callback_server)
|
31
|
+
Thread.new do
|
32
|
+
Thread.new(callback_server.accept) do |callback_conn|
|
33
|
+
message = JSON.parse(callback_conn.gets, symbolize_names: true)
|
34
|
+
runtime = @runtimes[message[:session_id]]
|
35
|
+
runtime.start(message[:pid])
|
36
|
+
$stdout.puts "Runtime ready at port #{runtime.port.to_s}"
|
37
|
+
callback_conn.close
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def start
|
43
|
+
loop { async_new_repl_connections }
|
44
|
+
end
|
45
|
+
|
46
|
+
def shutdown
|
47
|
+
@runtimes.each do |id, runtime|
|
48
|
+
$stdout.puts runtime.inspect
|
49
|
+
$stdout.puts ("shutdown"+runtime.port.to_s).inspect
|
50
|
+
runtime.shutdown
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
class Input
|
55
|
+
def initialize(input)
|
56
|
+
@input = input
|
57
|
+
end
|
58
|
+
def read
|
59
|
+
begin
|
60
|
+
context = Rapel.runtimes.first[1]
|
61
|
+
expression = Rapel::Expression.new(@input.slice(:code))
|
62
|
+
expression
|
63
|
+
rescue Exception => e
|
64
|
+
$stdout.puts(e.inspect)
|
65
|
+
end
|
66
|
+
$stdout.puts("Input read: #{expression.inspect}")
|
67
|
+
yield expression, context
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Rapel
|
2
|
+
|
3
|
+
class Runtime
|
4
|
+
|
5
|
+
attr_reader :session_id, :port
|
6
|
+
attr_accessor :socket
|
7
|
+
|
8
|
+
def initialize(callback_port, session_id)
|
9
|
+
runtime_port = 9220
|
10
|
+
@port = runtime_port
|
11
|
+
@session_id = session_id
|
12
|
+
system(ruby_runtime_string(callback_port))
|
13
|
+
end
|
14
|
+
|
15
|
+
def ruby_runtime_string(callback_port)
|
16
|
+
["ruby #{File.dirname(__FILE__)}/../../bin/runtime.rb",
|
17
|
+
"--port=#{port}",
|
18
|
+
"--callback-port=#{callback_port}",
|
19
|
+
"--uuid=#{session_id}",
|
20
|
+
"&"].join(" ")
|
21
|
+
end
|
22
|
+
|
23
|
+
def shutdown
|
24
|
+
puts "killing at port #{port}"
|
25
|
+
socket.puts("shutdown"+port.to_s)
|
26
|
+
rescue
|
27
|
+
puts "killing pid #{@pid}"
|
28
|
+
`kill -9 #{@pid}`
|
29
|
+
end
|
30
|
+
|
31
|
+
def start(pid)
|
32
|
+
@pid = pid
|
33
|
+
@ready = true
|
34
|
+
@socket = TCPSocket.new('localhost', port)
|
35
|
+
end
|
36
|
+
|
37
|
+
def inspect
|
38
|
+
"\#<Runtime @session=\"#@session_id\" @ready=#@ready>"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
data/lib/rapel/version.rb
CHANGED
data/rapel.gemspec
CHANGED
@@ -10,7 +10,7 @@ Gem::Specification.new do |spec|
|
|
10
10
|
spec.email = ["nicklink483@gmail.com"]
|
11
11
|
|
12
12
|
spec.summary = %q{Multi-client, multi-runtime REPL Server}
|
13
|
-
spec.description = %q{Rapel provides a multi-client, multi-runtime REPL server which accepts incoming expressions, e.g. 2+2, evaluates them in a runtime, and returns the result, e.g. 4.}
|
13
|
+
spec.description = %q{Rapel (ruh-PELL) provides a multi-client, multi-runtime REPL server which accepts incoming expressions, e.g. 2+2, evaluates them in a runtime, and returns the result, e.g. 4.}
|
14
14
|
spec.homepage = "https://github.com/domgetter/rapel"
|
15
15
|
spec.license = "MIT"
|
16
16
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rapel
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dominic Muller
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-05-
|
11
|
+
date: 2016-05-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -52,9 +52,9 @@ dependencies:
|
|
52
52
|
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '0'
|
55
|
-
description: Rapel provides a multi-client, multi-runtime REPL server which
|
56
|
-
incoming expressions, e.g. 2+2, evaluates them in a runtime, and returns
|
57
|
-
e.g. 4.
|
55
|
+
description: Rapel (ruh-PELL) provides a multi-client, multi-runtime REPL server which
|
56
|
+
accepts incoming expressions, e.g. 2+2, evaluates them in a runtime, and returns
|
57
|
+
the result, e.g. 4.
|
58
58
|
email:
|
59
59
|
- nicklink483@gmail.com
|
60
60
|
executables: []
|
@@ -69,8 +69,12 @@ files:
|
|
69
69
|
- README.md
|
70
70
|
- Rakefile
|
71
71
|
- bin/console
|
72
|
+
- bin/runtime.rb
|
72
73
|
- bin/setup
|
73
74
|
- lib/rapel.rb
|
75
|
+
- lib/rapel/expression.rb
|
76
|
+
- lib/rapel/replserver.rb
|
77
|
+
- lib/rapel/runtime.rb
|
74
78
|
- lib/rapel/version.rb
|
75
79
|
- rapel.gemspec
|
76
80
|
homepage: https://github.com/domgetter/rapel
|