web-repl 0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +13 -0
- data/README.md +66 -0
- data/bin/web-repl +16 -0
- data/js/replConnection-0.1.min.js +1 -0
- data/js/replConnection.js +117 -0
- data/lib/web-repl.rb +22 -0
- data/lib/web-repl/messager.rb +48 -0
- data/lib/web-repl/patch.rb +39 -0
- data/lib/web-repl/repl.rb +128 -0
- data/test/helper.rb +8 -0
- data/test/messager_test.rb +88 -0
- data/test/repl_test.rb +14 -0
- metadata +140 -0
data/LICENSE
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
Copyright 2014 Ari Russo
|
2
|
+
|
3
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
you may not use this file except in compliance with the License.
|
5
|
+
You may obtain a copy of the License at
|
6
|
+
|
7
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
|
9
|
+
Unless required by applicable law or agreed to in writing, software
|
10
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
See the License for the specific language governing permissions and
|
13
|
+
limitations under the License.
|
data/README.md
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
# web-repl
|
2
|
+
|
3
|
+
This is a Javascript [REPL](http://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop) that runs in Ruby. Evaluation is done by a web browser instance.
|
4
|
+
|
5
|
+
One use of this is to control the Chrome Developer Console remotely.
|
6
|
+
|
7
|
+
#### Background
|
8
|
+
|
9
|
+
I was working on a toy program recently that needed the browser to be in full screen mode, which made using the regular Chrome console very difficult to use. I came up with this program as an alternative.
|
10
|
+
|
11
|
+
There are similar tools that run in nodejs for example but since my program uses a Ruby backend anyway, this is convenient for me.
|
12
|
+
|
13
|
+
It communicates over websocket.
|
14
|
+
|
15
|
+
There is basically no attention to security here, so please use at your own discretion.
|
16
|
+
|
17
|
+
#### Usage
|
18
|
+
|
19
|
+
###### Browser
|
20
|
+
|
21
|
+
To enable the browser side of this, include something like this in the head of your webpage:
|
22
|
+
|
23
|
+
```html
|
24
|
+
<script src="js/replConnection.js"></script>
|
25
|
+
<script type="text/javascript">
|
26
|
+
window.onload = function() {
|
27
|
+
var repl = new ReplConnection("localhost", 9007, { debug: true, reconnect: true });
|
28
|
+
repl.start();
|
29
|
+
}
|
30
|
+
</script>
|
31
|
+
```
|
32
|
+
|
33
|
+
The javascript assets for this project are located in the [/js directory](https://github.com/arirusso/web-repl/tree/master/js).
|
34
|
+
|
35
|
+
There is also a full example of a webpage (with [rack](http://rack.github.io/) configuration) in the [/examples/page directory](https://github.com/arirusso/web-repl/tree/master/examples/page)
|
36
|
+
|
37
|
+
###### REPL
|
38
|
+
|
39
|
+
The REPL can be used either in a Ruby program/console or there is a "binary" Ruby script.
|
40
|
+
|
41
|
+
In Ruby the usage looks like this:
|
42
|
+
|
43
|
+
```ruby
|
44
|
+
require "web-repl"
|
45
|
+
|
46
|
+
WebRepl.start(:host => "localhost", :port => 9007)
|
47
|
+
```
|
48
|
+
|
49
|
+
You can see an explanation of background usage here.
|
50
|
+
|
51
|
+
To use this as a script, run this from the command line. (The script should install with the gem)
|
52
|
+
|
53
|
+
web-repl localhost:9007
|
54
|
+
|
55
|
+
#### Installation
|
56
|
+
|
57
|
+
gem install web-repl
|
58
|
+
|
59
|
+
or with Bundler
|
60
|
+
|
61
|
+
gem "web-repl"
|
62
|
+
|
63
|
+
#### License
|
64
|
+
|
65
|
+
Licensed under Apache 2.0, See the file LICENSE
|
66
|
+
Copyright (c) 2014 [Ari Russo](http://arirusso.com)
|
data/bin/web-repl
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
3
|
+
|
4
|
+
require "optparse"
|
5
|
+
require "web-repl"
|
6
|
+
|
7
|
+
ARGV[0]
|
8
|
+
|
9
|
+
raise OptionParser::MissingArgument if ARGV[0].nil?
|
10
|
+
|
11
|
+
host, port = *ARGV[0].scan(/(\w+):(\d{4,})/)[0]
|
12
|
+
|
13
|
+
raise OptionParser::MissingArgument if host.nil? || port.nil?
|
14
|
+
|
15
|
+
WebRepl.start(:host => host, :port => port)
|
16
|
+
exit 0
|
@@ -0,0 +1 @@
|
|
1
|
+
function ReplConnection(e,t,n){if(n===null){n={}}this.debug=!!n.debug;this.reconnect=!!n.reconnect;this.retryTime=n.retryTime||1e3;this.onReceive=n.onReceive;this.host=e;this.port=t;this.active=false;this.socket;this.supported="WebSocket"in window;if(this.supported){console.log("REPL: socket ok")}else{console.log("REPL: socket not supported")}}ReplConnection.prototype.eval=function(statement){response={};try{response.value=eval(statement)}catch(err){response.error=err.message}return response};ReplConnection.prototype.initSocket=function(e){if(this.socket!==undefined&&this.socket!==null){this.socket.close()}var t="ws://"+this.host+":"+this.port+"/echo";this.socket=new WebSocket(t);e()};ReplConnection.prototype.handleSocketOpen=function(){this.active=true;console.log("REPL: socket ready")};ReplConnection.prototype.tryConnection=function(){console.log("REPL: waiting for connection");if(this.reconnect){var e=this;window.setTimeout(function(){e.start()},this.retryTime)}};ReplConnection.prototype.handleSocketClose=function(e){if(!this.active){this.tryConnection()}else{console.log("REPL: socket closed");this.active=false;if(this.reconnect){this.tryConnection()}}};ReplConnection.prototype.handleMessageReceived=function(e){if(this.debug){console.log("REPL: message received");console.log(e)}var t=JSON.parse(e.data);var n=t.timestamp;t.timestamp=new Date(n);if(this.onReceive!==undefined){this.onReceive(t)}var r=this.eval(t.statement);r.timestamp=(new Date).getTime();var i=JSON.stringify(r);if(this.debug){console.log("REPL: replying ");console.log(r)}this.socket.send(i)};ReplConnection.prototype.initEventHandling=function(){var e=this;this.socket.onopen=function(){e.handleSocketOpen()};this.socket.onclose=function(t){e.handleSocketClose(t)};this.socket.onmessage=function(t){e.handleMessageReceived(t)}};ReplConnection.prototype.start=function(e){if(this.supported){var t=this;this.initSocket(function(){t.initEventHandling();if(e!==undefined){e(t)}})}}
|
@@ -0,0 +1,117 @@
|
|
1
|
+
// A connection to the REPL server using Websocket
|
2
|
+
function ReplConnection(host, port, options) {
|
3
|
+
if (options === null) {
|
4
|
+
options = {};
|
5
|
+
}
|
6
|
+
this.debug = !!options.debug;
|
7
|
+
this.reconnect = !!options.reconnect;
|
8
|
+
this.retryTime = options.retryTime || 1000;
|
9
|
+
this.onReceive = options.onReceive;
|
10
|
+
this.host = host;
|
11
|
+
this.port = port;
|
12
|
+
this.active = false;
|
13
|
+
this.socket;
|
14
|
+
this.supported = ("WebSocket" in window);
|
15
|
+
if (this.supported) {
|
16
|
+
console.log("REPL: socket ok");
|
17
|
+
} else {
|
18
|
+
console.log("REPL: socket not supported");
|
19
|
+
}
|
20
|
+
}
|
21
|
+
|
22
|
+
// This is the "eval" for the REPL
|
23
|
+
ReplConnection.prototype.eval = function(statement) {
|
24
|
+
response = {}
|
25
|
+
try {
|
26
|
+
response.value = eval(statement);
|
27
|
+
} catch(err) {
|
28
|
+
response.error = err.message;
|
29
|
+
}
|
30
|
+
return response;
|
31
|
+
}
|
32
|
+
|
33
|
+
// Initialize the Websocket connection
|
34
|
+
ReplConnection.prototype.initSocket = function(successCallback) {
|
35
|
+
if (this.socket !== undefined && this.socket !== null) {
|
36
|
+
this.socket.close();
|
37
|
+
}
|
38
|
+
var address = "ws://" + this.host + ":" + this.port + "/echo";
|
39
|
+
this.socket = new WebSocket(address);
|
40
|
+
successCallback();
|
41
|
+
}
|
42
|
+
|
43
|
+
// To be run when the Websocket registers as being open
|
44
|
+
ReplConnection.prototype.handleSocketOpen = function() {
|
45
|
+
this.active = true;
|
46
|
+
console.log("REPL: socket ready");
|
47
|
+
}
|
48
|
+
|
49
|
+
// Try to create a Websocket connection
|
50
|
+
ReplConnection.prototype.tryConnection = function() {
|
51
|
+
console.log("REPL: waiting for connection");
|
52
|
+
if (this.reconnect) {
|
53
|
+
var connection = this;
|
54
|
+
window.setTimeout(function() {
|
55
|
+
connection.start();
|
56
|
+
}, this.retryTime);
|
57
|
+
}
|
58
|
+
}
|
59
|
+
|
60
|
+
// To be run when the Websocket registers as being closed. This includes when it's waiting for a connection.
|
61
|
+
ReplConnection.prototype.handleSocketClose = function(event) {
|
62
|
+
if (!this.active) {
|
63
|
+
this.tryConnection();
|
64
|
+
} else {
|
65
|
+
console.log("REPL: socket closed");
|
66
|
+
this.active = false;
|
67
|
+
if (this.reconnect) {
|
68
|
+
this.tryConnection();
|
69
|
+
}
|
70
|
+
}
|
71
|
+
}
|
72
|
+
|
73
|
+
// To be run when the Websocket registers an event over the connection.
|
74
|
+
ReplConnection.prototype.handleMessageReceived = function(event) {
|
75
|
+
if (this.debug) {
|
76
|
+
console.log("REPL: message received");
|
77
|
+
console.log(event);
|
78
|
+
}
|
79
|
+
var message = JSON.parse(event.data);
|
80
|
+
// turn the timestamp from the rec'd message into a real date
|
81
|
+
var timestamp = message.timestamp;
|
82
|
+
message.timestamp = new Date(timestamp);
|
83
|
+
//
|
84
|
+
if (this.onReceive !== undefined) {
|
85
|
+
this.onReceive(message); // fire the custom callback
|
86
|
+
}
|
87
|
+
// prepare the response
|
88
|
+
var response = this.eval(message.statement); // evaluate the statement
|
89
|
+
response.timestamp = new Date().getTime(); // timestamp for the returned message
|
90
|
+
var json = JSON.stringify(response);
|
91
|
+
if (this.debug) {
|
92
|
+
console.log("REPL: replying ");
|
93
|
+
console.log(response);
|
94
|
+
}
|
95
|
+
this.socket.send(json);
|
96
|
+
}
|
97
|
+
|
98
|
+
// Initialize the Websocket event handling actions
|
99
|
+
ReplConnection.prototype.initEventHandling = function() {
|
100
|
+
var connection = this;
|
101
|
+
this.socket.onopen = function() { connection.handleSocketOpen() };
|
102
|
+
this.socket.onclose = function(event) { connection.handleSocketClose(event); };
|
103
|
+
this.socket.onmessage = function(event) { connection.handleMessageReceived(event); };
|
104
|
+
}
|
105
|
+
|
106
|
+
// Initialize the Websocket and start waiting for a REPL connection
|
107
|
+
ReplConnection.prototype.start = function(successCallback) {
|
108
|
+
if (this.supported) {
|
109
|
+
var connection = this;
|
110
|
+
this.initSocket(function() {
|
111
|
+
connection.initEventHandling();
|
112
|
+
if (successCallback !== undefined) {
|
113
|
+
successCallback(connection);
|
114
|
+
}
|
115
|
+
});
|
116
|
+
}
|
117
|
+
}
|
data/lib/web-repl.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# libs
|
2
|
+
require "colorize"
|
3
|
+
require "em-websocket"
|
4
|
+
require "json"
|
5
|
+
require "readline"
|
6
|
+
require "socket"
|
7
|
+
|
8
|
+
# classes
|
9
|
+
require "web-repl/messager"
|
10
|
+
require "web-repl/patch"
|
11
|
+
require "web-repl/repl"
|
12
|
+
|
13
|
+
module WebRepl
|
14
|
+
|
15
|
+
VERSION = "0.1"
|
16
|
+
|
17
|
+
# Shortcut to REPL.start
|
18
|
+
def self.start(*a)
|
19
|
+
REPL.start(*a)
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module WebRepl
|
2
|
+
|
3
|
+
# Handles sending and receiving messages to/from the socket
|
4
|
+
class Messager
|
5
|
+
|
6
|
+
# @param [EventMachine::WebSocket] socket
|
7
|
+
# @param [Hash] options
|
8
|
+
# @option options [Boolean] :debug
|
9
|
+
def initialize(socket, options = {})
|
10
|
+
@socket = socket
|
11
|
+
@debug = options[:debug]
|
12
|
+
end
|
13
|
+
|
14
|
+
# Handle an inputted message
|
15
|
+
# @param [String] raw_message A raw inputted JSON message
|
16
|
+
# @return [Hash]
|
17
|
+
def in(raw_message, &block)
|
18
|
+
hash = JSON.parse(raw_message, :symbolize_names => true)
|
19
|
+
hash[:timestamp] = Time.at(hash[:timestamp].to_i / 1000) if !hash[:timestamp].nil?
|
20
|
+
yield(hash) if block_given?
|
21
|
+
hash
|
22
|
+
end
|
23
|
+
|
24
|
+
# Generate a new timestamp in js format
|
25
|
+
# @return [Fixnum]
|
26
|
+
def new_timestamp
|
27
|
+
Time.now.to_i * 1000 # javascript time int format
|
28
|
+
end
|
29
|
+
|
30
|
+
# Send a message over the socket
|
31
|
+
# @param [Hash] message A message to send
|
32
|
+
# @return [String, nil] If a message was sent, its JSON string; otherwise nil
|
33
|
+
def out(message)
|
34
|
+
if !@socket.nil?
|
35
|
+
message[:timestamp] ||= new_timestamp
|
36
|
+
json = message.to_json
|
37
|
+
@debug.puts "Sending message: #{json}" if @debug
|
38
|
+
@socket.send(json)
|
39
|
+
json
|
40
|
+
else
|
41
|
+
@debug.puts "Warning: No connection" if @debug
|
42
|
+
nil
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# Patch EventMachine::WebSocket so that we can initialize EM on demand. This is useful when
|
2
|
+
# having multiple EMs working. It won't error out by calling EM.run repeatedly.
|
3
|
+
#
|
4
|
+
module EventMachine
|
5
|
+
module WebSocket
|
6
|
+
def self.start(options = {}, &block)
|
7
|
+
EM.epoll
|
8
|
+
if EM.reactor_running?
|
9
|
+
handle_start(options, &block)
|
10
|
+
else
|
11
|
+
EM.run { handle_start(options, &block) }
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.run(options = {}, &block)
|
16
|
+
if EM.reactor_running?
|
17
|
+
handle_run(options, &block)
|
18
|
+
else
|
19
|
+
EM.run { handle_run(options, &block) }
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def self.handle_run(options = {}, &block)
|
26
|
+
host, port = options.values_at(:host, :port)
|
27
|
+
EM.start_server(host, port, Connection, options) do |c|
|
28
|
+
yield c
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.handle_start(options = {}, &block)
|
33
|
+
trap("TERM") { stop }
|
34
|
+
trap("INT") { stop }
|
35
|
+
run(options, &block)
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
module WebRepl
|
2
|
+
|
3
|
+
# The main REPL object
|
4
|
+
class REPL
|
5
|
+
|
6
|
+
attr_reader :thread
|
7
|
+
|
8
|
+
# Start a repl connection
|
9
|
+
# @param [Hash] config A hash of config options to be passed to EM::WebSocket.run directly
|
10
|
+
# @param [Hash] options
|
11
|
+
# @option options [IO, nil] :debug A debug logger or nil if debug is not needed (default: nil)
|
12
|
+
# @option options [Boolean] :background Do not wait for input, just run in the bg
|
13
|
+
# @return [WebRepl::REPL]
|
14
|
+
def self.start(config, options = {})
|
15
|
+
new(config, options).tap { |repl| repl.start(options) }
|
16
|
+
end
|
17
|
+
|
18
|
+
# @param [Hash] config A hash of config options to be passed to EM::WebSocket.run directly
|
19
|
+
# @param [Hash] options
|
20
|
+
# @option options [IO, nil] :debug A debug logger or nil if debug is not needed (default: nil)
|
21
|
+
def initialize(config, options = {})
|
22
|
+
@config = config
|
23
|
+
@socket = nil
|
24
|
+
@messager = nil
|
25
|
+
@buffer = []
|
26
|
+
@debug = options[:debug]
|
27
|
+
end
|
28
|
+
|
29
|
+
# Send a statement to the browser for evaluation
|
30
|
+
# @param [Fixnum, String] statement A Javascript statement to be evaluated
|
31
|
+
# @return [String, nil] The data that was sent to the browser, or nil if sending could not be completed.
|
32
|
+
def evaluate(statement)
|
33
|
+
@messager.out({ :statement => statement }) unless @messager.nil?
|
34
|
+
end
|
35
|
+
|
36
|
+
# Prompt the Ruby user for input and send that input to the browser for evaluation (blocking)
|
37
|
+
# @return [String, nil] The data that was sent to the browser, or nil if sending could not be completed
|
38
|
+
def gets
|
39
|
+
line = Readline.readline('> ', true)
|
40
|
+
return nil if line.nil?
|
41
|
+
if line =~ /^\s*$/ or Readline::HISTORY.to_a[-2] == line
|
42
|
+
Readline::HISTORY.pop
|
43
|
+
end
|
44
|
+
statement = line.strip
|
45
|
+
case statement
|
46
|
+
when "exit" then exit
|
47
|
+
else
|
48
|
+
evaluate(statement)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# Wait for a response from the browser
|
53
|
+
def wait_for_response
|
54
|
+
loop until !@buffer.empty?
|
55
|
+
end
|
56
|
+
|
57
|
+
# Start the Websocket connection (blocking)
|
58
|
+
# @param [Hash] options
|
59
|
+
# @option options [Boolean] :background Do not wait for input, just run in the bg
|
60
|
+
def start(options = {}, &block)
|
61
|
+
@thread = Thread.new do
|
62
|
+
EM::WebSocket.run(@config) do |ws|
|
63
|
+
if @socket.nil?
|
64
|
+
@socket = ws
|
65
|
+
@messager = Messager.new(@socket)
|
66
|
+
configure_event_handling(:background => options[:background], &block)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
acknowledge_handshake do
|
71
|
+
yield if block_given?
|
72
|
+
gets unless !!options[:background]
|
73
|
+
end
|
74
|
+
@thread.join unless !!options[:background]
|
75
|
+
end
|
76
|
+
|
77
|
+
# Execute a block when a connection is made
|
78
|
+
# @return [TrueClass]
|
79
|
+
def acknowledge_handshake(&block)
|
80
|
+
Thread.new do
|
81
|
+
loop until !@handshake.nil?
|
82
|
+
yield
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# Close the REPL
|
87
|
+
def close
|
88
|
+
@socket.close unless @socket.nil?
|
89
|
+
@thread.kill unless @thread.nil?
|
90
|
+
end
|
91
|
+
|
92
|
+
private
|
93
|
+
|
94
|
+
def handle_open(handshake, options = {})
|
95
|
+
puts "web-repl: Connection open"
|
96
|
+
@handshake = handshake
|
97
|
+
end
|
98
|
+
|
99
|
+
def handle_close
|
100
|
+
puts "web-repl: Connection closed"
|
101
|
+
@handshake = nil
|
102
|
+
end
|
103
|
+
|
104
|
+
def handle_message_received(raw_message, options = {})
|
105
|
+
@messager.in(raw_message) do |message|
|
106
|
+
@buffer.clear
|
107
|
+
@buffer << message
|
108
|
+
keys = { :error => :red, :value => :white }
|
109
|
+
text = nil
|
110
|
+
keys.each do |k,v|
|
111
|
+
text ||= message[k].to_s.send(v) unless message[k].nil?
|
112
|
+
end
|
113
|
+
puts(text)
|
114
|
+
end
|
115
|
+
gets unless !!options[:background]
|
116
|
+
end
|
117
|
+
|
118
|
+
# Configure the Websocket event handling
|
119
|
+
# @param [Hash] options
|
120
|
+
# @option options [Boolean] :background Do not wait for input, just run in the bg
|
121
|
+
def configure_event_handling(options = {})
|
122
|
+
@socket.onopen { |handshake| handle_open(handshake) }
|
123
|
+
@socket.onclose { handle_close }
|
124
|
+
@socket.onmessage { |raw_message| handle_message_received(raw_message) }
|
125
|
+
end
|
126
|
+
|
127
|
+
end
|
128
|
+
end
|
data/test/helper.rb
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
require "helper"
|
2
|
+
|
3
|
+
class WebRepl::MessagerTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
include WebRepl
|
6
|
+
|
7
|
+
context "Messager" do
|
8
|
+
|
9
|
+
setup do
|
10
|
+
@socket = Object.new
|
11
|
+
@messager = Messager.new(@socket)
|
12
|
+
end
|
13
|
+
|
14
|
+
context "#in" do
|
15
|
+
|
16
|
+
setup do
|
17
|
+
@message = { :value => "blah", :timestamp => 1396406728702 }.to_json
|
18
|
+
@result = @messager.in(@message)
|
19
|
+
end
|
20
|
+
|
21
|
+
should "convert from String to Hash" do
|
22
|
+
assert_not_nil @result
|
23
|
+
assert_equal Hash, @result.class
|
24
|
+
assert_equal "blah", @result[:value]
|
25
|
+
end
|
26
|
+
|
27
|
+
should "convert timestamp from js time to ruby" do
|
28
|
+
timestamp = @result[:timestamp]
|
29
|
+
assert_not_nil timestamp
|
30
|
+
assert_equal Time, timestamp.class
|
31
|
+
assert_equal 2014, timestamp.year
|
32
|
+
assert_equal 4, timestamp.month
|
33
|
+
assert_equal 22, timestamp.hour
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
context "#new_timestamp" do
|
39
|
+
|
40
|
+
should "be js int time format" do
|
41
|
+
result = @messager.new_timestamp
|
42
|
+
assert_not_nil result
|
43
|
+
assert_equal Fixnum, result.class
|
44
|
+
assert result.to_s.size > Time.new.to_i.to_s.size
|
45
|
+
assert_equal (result / 1000).to_s.size, Time.new.to_i.to_s.size
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
context "#out" do
|
51
|
+
|
52
|
+
setup do
|
53
|
+
@message = { :statement => "something" }
|
54
|
+
end
|
55
|
+
|
56
|
+
should "not overwrite timestamp" do
|
57
|
+
@socket.expects(:send).once
|
58
|
+
ts = Time.now.to_i / 1000
|
59
|
+
@message[:timestamp] = ts
|
60
|
+
@messager.out(@message)
|
61
|
+
assert_equal ts, @message[:timestamp]
|
62
|
+
end
|
63
|
+
|
64
|
+
should "generate new timestamp" do
|
65
|
+
@socket.expects(:send).once
|
66
|
+
@messager.out(@message)
|
67
|
+
assert_not_nil @message[:timestamp]
|
68
|
+
assert_equal Fixnum, @message[:timestamp].class
|
69
|
+
end
|
70
|
+
|
71
|
+
should "return nil if fails" do
|
72
|
+
messager = Messager.new(nil)
|
73
|
+
result = messager.out(@message)
|
74
|
+
assert_nil result
|
75
|
+
end
|
76
|
+
|
77
|
+
should "return json string if success" do
|
78
|
+
@socket.expects(:send).once
|
79
|
+
result = @messager.out(@message)
|
80
|
+
assert_not_nil result
|
81
|
+
assert_equal String, result.class
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
data/test/repl_test.rb
ADDED
metadata
ADDED
@@ -0,0 +1,140 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: web-repl
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '0.1'
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Ari Russo
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2014-04-03 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: colorize
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: em-websocket
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: mocha
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: rake
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: shoulda-context
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
description: Javascript/Web REPL in Ruby
|
95
|
+
email:
|
96
|
+
- ari.russo@gmail.com
|
97
|
+
executables:
|
98
|
+
- web-repl
|
99
|
+
extensions: []
|
100
|
+
extra_rdoc_files: []
|
101
|
+
files:
|
102
|
+
- bin/web-repl
|
103
|
+
- js/replConnection-0.1.min.js
|
104
|
+
- js/replConnection.js
|
105
|
+
- lib/web-repl/messager.rb
|
106
|
+
- lib/web-repl/patch.rb
|
107
|
+
- lib/web-repl/repl.rb
|
108
|
+
- lib/web-repl.rb
|
109
|
+
- test/helper.rb
|
110
|
+
- test/messager_test.rb
|
111
|
+
- test/repl_test.rb
|
112
|
+
- LICENSE
|
113
|
+
- README.md
|
114
|
+
homepage: http://github.com/arirusso/web-repl
|
115
|
+
licenses: []
|
116
|
+
post_install_message:
|
117
|
+
rdoc_options: []
|
118
|
+
require_paths:
|
119
|
+
- lib
|
120
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ! '>='
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
126
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
127
|
+
none: false
|
128
|
+
requirements:
|
129
|
+
- - ! '>='
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: 1.3.6
|
132
|
+
requirements: []
|
133
|
+
rubyforge_project: web-repl
|
134
|
+
rubygems_version: 1.8.25
|
135
|
+
signing_key:
|
136
|
+
specification_version: 3
|
137
|
+
summary: Javascript/Web REPL in Ruby
|
138
|
+
test_files:
|
139
|
+
- test/messager_test.rb
|
140
|
+
- test/repl_test.rb
|