rapel 0.1.0 → 0.2.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 87d7d40d72f64667ac13d584c0047f8d4aa511c3
4
- data.tar.gz: de2243f93bdf36fa4e79547b3b828f6208c84e19
3
+ metadata.gz: b92563a1ed6631d218e18f28bdae877f2fc9d871
4
+ data.tar.gz: a6a5260a12414bf9741af4119225c2aa6670d881
5
5
  SHA512:
6
- metadata.gz: b0fd44fdef4b60ea1cc04789a152baec11e0c26a9aae2fc7236b8ac97a209ebc6394b1dad22b3ab11f7ea4b2815f1402b4569256af90a6c6c14aeb9911e9f916
7
- data.tar.gz: de1d3a0be60e1344776edaca50ba39db6d13a37dafe61e0a42e6f380e84292130f4d1be42aa3e1606ff7dd1abba6cae247ddaa020cd8d0570a776ce0f63d52ff
6
+ metadata.gz: 6b67829358e692e0d1d10869537aca3b8292cd73733e7721464067c4c744198be3ea4d50b62b5345767b8e00c6a84bf547e2f1d338674752b469ad99f7629186
7
+ data.tar.gz: 4d27177c6ded616ace9ac0e74a2f06ad7d27f4d5374d48eb6b168332ee85190a2cd7a92625400cd41e17ca7f7fdf9cc0c8b19633aa1b892a12be55608f3c0b76
data/.gitignore CHANGED
@@ -7,3 +7,6 @@
7
7
  /pkg/
8
8
  /spec/reports/
9
9
  /tmp/
10
+ *.gem
11
+ bin/runtime.log
12
+ tags
data/README.md CHANGED
@@ -1,8 +1,6 @@
1
1
  # Rapel
2
2
 
3
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/rapel`. To experiment with that code, run `bin/console` for an interactive prompt.
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/[USERNAME]/rapel.
36
-
33
+ Bug reports and pull requests are welcome on GitHub at https://github.com/domgetter/rapel.
37
34
 
38
35
  ## License
39
36
 
@@ -10,5 +10,6 @@ require "rapel"
10
10
  # require "pry"
11
11
  # Pry.start
12
12
 
13
- require "irb"
14
- IRB.start
13
+ Rapel.start
14
+ # require "irb"
15
+ # IRB.start
@@ -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
+
@@ -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
- # Your code goes here...
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
@@ -1,3 +1,3 @@
1
1
  module Rapel
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -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.1.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-14 00:00:00.000000000 Z
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 accepts
56
- incoming expressions, e.g. 2+2, evaluates them in a runtime, and returns the result,
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