What is RethinkDB?
• Open source database for building
realtime web applications
• NoSQL database that
stores schemaless JSON documents
• Distributed database that is easy to
scale
Slide 6
Slide 6 text
Built for Realtime Apps
• Subscribe to change notifications
from database queries
• No more polling — the database
pushes changes to your app
• Reduce the amount of plumbing
needed to stream live updates
Slide 7
Slide 7 text
Subscribe to change notifications
on database queries
Changefeeds
Slide 8
Slide 8 text
r.table("users").changes()
Changefeeds
Track changes on the users table
Slide 9
Slide 9 text
Changefeeds
• The changes command returns a
cursor that receives updates
• Each update includes the new and
old value of the modified record
r.table("players")
.orderBy({index: r.desc("score")})
.limit(3).changes()
Track top three players by score
Chain the changes command
to an actual ReQL query:
Changefeeds
Slide 12
Slide 12 text
Building the backend with
RethinkDB and Sinatra
Slide 13
Slide 13 text
Backend
• Running on Thin for
EventMachine support
• Sinatra for API endpoints and
serving resources
• Faye WebSockets for realtime
messaging
Slide 14
Slide 14 text
Changefeed
EM.next_tick do
conn = r.connect()
r.table("todo").changes.em_run(conn) do |err, change|
@clients.each {|c| c.send change.to_json }
end
end
Send updates to all connected users
Slide 15
Slide 15 text
WebSockets
def setup_websocket ws
ws.on(:close) { @clients.delete ws }
ws.on(:open) { @clients << ws }
ws.on :message do |msg|
data = JSON.parse msg.data
case data["command"]
when "add"
query r.table("todo")
.insert text: data["text"], status: false
when "update"
query r.table("todo")
.get(data["id"])
.update status: data["status"]
end
end
end
Slide 16
Slide 16 text
WebSockets
Serve WebSockets on port 80
get "/" do
if Faye::WebSocket.websocket? request.env
ws = Faye::WebSocket.new request.env
setup_websocket ws
ws.rack_response
else
haml :index
end
end
Slide 17
Slide 17 text
Ruby to JavaScript with the
Opal transpiler project
Slide 18
Slide 18 text
What is Opal?
• Transpiler that converts Ruby to
JavaScript
• Based largely on Ruby 1.9 language spec
• Includes runtime code that mimics Ruby
environment
• Supports some interop between Ruby
and JavaScript
Slide 19
Slide 19 text
Why Opal?
• Use one language consistently
throughout your project
• Reuse (some) existing code
• Ruby is powerful and expressive,
well-suited for many tasks
Slide 20
Slide 20 text
But Why?
BECAUSE I CAN
Slide 21
Slide 21 text
Why Not Opal?
• Extra layer of abstraction
introduces more complexity
• Custom runtime code introduces
some performance overhead
• Under-documented moving target
not conducive to maintainability
Slide 22
Slide 22 text
Why Not Opal?
seriously crazy tracebacks
Slide 23
Slide 23 text
Asset setup in config.ru
Dynamically serve transpiled assets
$opal = Opal::Server.new do |s|
s.append_path "client"
s.main = "main"
end
map "/assets" do
run $opal.sprockets
end
$opalinit = Opal::Processor.load_asset_code(
$opal.sprockets, "main")
Slide 24
Slide 24 text
Assets
Drop $opalinit in a tag
!!!
%html
%head
%title Test
%script(src="/assets/main.js")
%script= $opalinit
%body
Slide 25
Slide 25 text
Building the frontend with
React.rb components
Slide 26
Slide 26 text
React.rb
• Opal-based wrapper for React
• Lets you build component-based
web frontend entirely in Ruby
• Provides an intuitive DSL for
generating HTML
• Can be used on frontend or backend
Slide 27
Slide 27 text
React.rb
class App
include React::Component
def render
h1 { "Hello, world!" }
end
end
Defining a React.rb component
Slide 28
Slide 28 text
React.rb
class TodoAdd
include React::Component
def render
div do
input(type: "text", ref: "text")
button {"Add"}.on(:click) do
text = self.refs[:text].dom_node.value
self.emit :add, text: text
end
end
end
end
Slide 29
Slide 29 text
React.rb
def transmit data
@ws.puts data.to_json
end
def render
div do
present(TodoAdd).on :add do |data|
transmit command: "add", text: data["text"]
end
end
end