Skip to content

Commit 4acdb9a

Browse files
author
Ari Russo
committed
first
0 parents  commit 4acdb9a

File tree

10 files changed

+291
-0
lines changed

10 files changed

+291
-0
lines changed

LICENSE

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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.

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# browser-repl
2+
3+
4+
5+
Licensed under Apache 2.0, See the file LICENSE
6+
Copyright (c) 2014 [Ari Russo](http://arirusso.com)

Rakefile

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
require 'rake'
2+
require 'rake/testtask'
3+
4+
Rake::TestTask.new(:test) do |t|
5+
t.libs << "test"
6+
t.test_files = FileList["test/**/*_test.rb"]
7+
t.verbose = true
8+
end
9+
10+
task :default => [:test]

bin/repl

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#!/usr/bin/env ruby
2+
$LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', 'lib')
3+
4+
require "browser-repl"
5+
6+
EM.run { BrowserRepl::REPL.new(:host => "localhost", :port => 9007) }

gemfile

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
source "https://rubygems.org"
2+
3+
group :test do
4+
gem "mocha"
5+
gem "shoulda-context"
6+
end
7+
8+
gem "colorize"
9+
gem "em-websocket"

lib/browser-repl.rb

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# libs
2+
require "colorize"
3+
require "em-websocket"
4+
require "json"
5+
require "readline"
6+
require "socket"
7+
8+
# classes
9+
require "browser-repl/messager"
10+
require "browser-repl/repl"
11+
12+
module BrowserRepl
13+
14+
VERSION = "0.1"
15+
16+
end

lib/browser-repl/messager.rb

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
module BrowserRepl
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

lib/browser-repl/repl.rb

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
# Patch EventMachine::WebSocket so that we can initialize EM on demand
2+
module EventMachine
3+
module WebSocket
4+
def self.start(options, &blk)
5+
EM.epoll
6+
#EM.run {
7+
trap("TERM") { stop }
8+
trap("INT") { stop }
9+
10+
run(options, &blk)
11+
#}
12+
end
13+
14+
def self.run(options)
15+
host, port = options.values_at(:host, :port)
16+
EM.start_server(host, port, Connection, options) do |c|
17+
yield c
18+
end
19+
end
20+
end
21+
end
22+
23+
module BrowserRepl
24+
25+
class REPL
26+
27+
attr_reader :messager
28+
29+
def initialize(config)
30+
@config = config
31+
@socket = nil
32+
@messager = nil
33+
start
34+
end
35+
36+
private
37+
38+
def get_input
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+
if statement == "exit"
46+
exit
47+
else
48+
@messager.out({ :statement => statement })
49+
end
50+
end
51+
52+
def start
53+
EM::WebSocket.run(@config) do |ws|
54+
@socket = ws
55+
@messager = Messager.new(@socket)
56+
configure
57+
end
58+
end
59+
60+
def configure
61+
@socket.onopen do |handshake|
62+
puts "b-r: Connection open"
63+
@active = true
64+
get_input
65+
end
66+
67+
@socket.onclose do
68+
puts "b-r: Connection closed"
69+
@active = false
70+
end
71+
72+
@socket.onmessage do |raw_message|
73+
@messager.in(raw_message) do |message|
74+
output = if !message[:value].nil?
75+
message[:value]
76+
elsif !message[:error].nil?
77+
message[:error].red
78+
end
79+
puts(output)
80+
end
81+
get_input
82+
end
83+
84+
end
85+
86+
end
87+
end

test/helper.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
dir = File.dirname(File.expand_path(__FILE__))
2+
$LOAD_PATH.unshift dir + '/../lib'
3+
4+
require 'test/unit'
5+
require "mocha/test_unit"
6+
require "shoulda-context"
7+
8+
require "browser-repl"

test/messager_test.rb

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
require "helper"
2+
3+
class BrowserRepl::MessagerTest < Test::Unit::TestCase
4+
5+
include BrowserRepl
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

0 commit comments

Comments
 (0)