Skip to content
This repository was archived by the owner on Nov 9, 2017. It is now read-only.

Commit fef9e48

Browse files
author
Ryan Paul
committed
Initial Commit
0 parents  commit fef9e48

File tree

5 files changed

+208
-0
lines changed

5 files changed

+208
-0
lines changed

Gemfile

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
source 'https://rubygems.org'
2+
3+
gem 'sinatra'
4+
gem 'faye-websocket'
5+
gem 'rethinkdb'
6+
gem 'thin'
7+
gem 'haml'
8+
gem 'opal', :github => 'opal/opal'
9+
gem 'opal-browser', :github => 'opal/opal-browser'
10+
gem 'opal-jquery', :github => 'opal/opal-jquery'
11+
gem 'opal-haml', :github => 'opal/opal-haml'
12+
gem 'react.rb'
13+
gem 'react-source'

app.rb

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
require "sinatra"
2+
require "tilt/haml"
3+
require "faye/websocket"
4+
require "rethinkdb"
5+
require "opal"
6+
require "json"
7+
8+
include RethinkDB::Shortcuts
9+
10+
DBHOST = ENV["RETHINKDB_PORT_28015_TCP_ADDR"]
11+
DBPORT = ENV["RETHINKDB_PORT_28015_TCP_PORT"]
12+
13+
def query rql
14+
conn = r.connect host: DBHOST, port: DBPORT
15+
rql.run(conn).to_json
16+
ensure
17+
conn.close
18+
end
19+
20+
class App < Sinatra::Base
21+
def initialize
22+
super
23+
@clients = []
24+
25+
EM.next_tick do
26+
conn = r.connect host: DBHOST, port: DBPORT
27+
r.table("todo").changes.em_run(conn) do |err, change|
28+
@clients.each {|c| c.send change.to_json }
29+
end
30+
end
31+
end
32+
33+
def setup_websocket ws
34+
ws.on(:close) { @clients.delete ws }
35+
ws.on(:open) { @clients << ws }
36+
37+
ws.on :message do |msg|
38+
data = JSON.parse msg.data
39+
case data["command"]
40+
when "add"
41+
query r.table("todo").insert text: data["text"], status: false
42+
when "update"
43+
query r.table("todo").get(data["id"]).update status: data["status"]
44+
when "delete"
45+
query r.table("todo").get(data["id"]).delete()
46+
end
47+
end
48+
end
49+
50+
get "/" do
51+
if Faye::WebSocket.websocket? request.env
52+
ws = Faye::WebSocket.new request.env
53+
setup_websocket ws
54+
ws.rack_response
55+
else
56+
haml :index
57+
end
58+
end
59+
60+
get "/api/items" do
61+
query r.table("todo").coerce_to("array")
62+
end
63+
end

client/main.rb

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
require "opal"
2+
require "react"
3+
require "json"
4+
require "browser"
5+
require "browser/http"
6+
require "browser/socket"
7+
8+
puts "Running client-side Opal code"
9+
10+
class TodoList
11+
include React::Component
12+
13+
def render
14+
ul do
15+
params[:items].each do |item|
16+
li do
17+
label do
18+
input(type: "checkbox", checked: item["status"]).on(:click) do |e|
19+
self.emit :toggle, id: item["id"], status: e.current_target.checked
20+
end
21+
span { item["text"] }
22+
end
23+
end
24+
end
25+
end
26+
end
27+
end
28+
29+
class TodoAdd
30+
include React::Component
31+
32+
def render
33+
div do
34+
input(type: "text", placeholder: "Input task name", ref: "text")
35+
button {"Add"}.on(:click) do
36+
text = self.refs[:text].dom_node.value
37+
self.emit :add, text: text
38+
end
39+
end
40+
end
41+
end
42+
43+
class App
44+
include React::Component
45+
46+
define_state(:items) { [] }
47+
after_mount :setup
48+
49+
def setup
50+
Browser::HTTP.get("/api/items").then do |res|
51+
self.items = res.json
52+
setup_websocket
53+
end
54+
end
55+
56+
def setup_websocket
57+
@ws = Browser::Socket.new "ws://drydock.local:32792"
58+
59+
@ws.on(:open) { p "Connection opened" }
60+
@ws.on(:close) { p "Socket closed" }
61+
62+
@ws.on(:message) do |e|
63+
data = JSON.parse e.data
64+
puts "Received:", data
65+
66+
# Add new item
67+
if data[:new_val] && !data[:old_val]
68+
self.items = self.items << data[:new_val]
69+
# Update existing item
70+
elsif data[:new_val] && data[:old_val]
71+
self.items = self.items.map do |i|
72+
i["id"] == data[:new_val]["id"] ? data[:new_val] : i
73+
end
74+
# Remove deleted item
75+
elsif !data[:new_val] && data[:old_val]
76+
self.items = self.items - [data[:old_val]]
77+
end
78+
79+
end
80+
end
81+
82+
def transmit data
83+
@ws.puts data.to_json
84+
end
85+
86+
def render
87+
div do
88+
present(TodoList, items: self.items).on :toggle do |data|
89+
transmit command: "update", id: data["id"], status: data["status"]
90+
end
91+
92+
present(TodoAdd).on :add do |data|
93+
transmit command: "add", text: data["text"]
94+
end
95+
end
96+
end
97+
end
98+
99+
$document.ready do
100+
React.render(React.create_element(App), `document.body`)
101+
end

config.ru

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
require 'bundler'
2+
Bundler.require
3+
4+
require "./app"
5+
6+
Faye::WebSocket.load_adapter("thin")
7+
8+
react_path = ::React::Source.bundled_path_for("react-with-addons.js")
9+
10+
$opal = Opal::Server.new do |s|
11+
s.append_path File.dirname react_path
12+
s.append_path "client"
13+
s.main = "main"
14+
end
15+
16+
map "/assets" do
17+
run $opal.sprockets
18+
end
19+
20+
$opalinit = Opal::Processor.load_asset_code($opal.sprockets, 'main')
21+
22+
run App

views/index.haml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
!!!
2+
%html
3+
%head
4+
%title Test
5+
%script(src="/assets/react-with-addons.js")
6+
%script(src="/assets/main.js")
7+
%script(src="https://code.jquery.com/jquery-1.7.2.min.js")
8+
%script= $opalinit
9+
%body

0 commit comments

Comments
 (0)