Slide 1

Slide 1 text

FULL STACK RUBY Realtime web apps with RethinkDB & Opal

Slide 2

Slide 2 text

Ryan Paul RethinkDB Evangelist @segphault

Slide 3

Slide 3 text

The Stack RethinkDB Thin Sinatra Faye WS Opal WS Client React.rb Ruby Client STORAGE BACKEND FRONTEND

Slide 4

Slide 4 text

Track live updates with RethinkDB changefeeds

Slide 5

Slide 5 text

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

Slide 10

Slide 10 text

r.table("users") .filter({name: "Bob"}).delete() Changefeed output: { new_val: null, old_val: { id: '362ae837-2e29-4695-adef-4fa415138f90', name: 'Bob', ... } } Changefeeds

Slide 11

Slide 11 text

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

Slide 30

Slide 30 text

The Stack RethinkDB Thin Sinatra Faye WS Opal WS Client React.rb Ruby Client STORAGE BACKEND FRONTEND

Slide 31

Slide 31 text

Additional Resources • RethinkDB website:
 http://rethinkdb.com • Opal website:
 http://opalrb.org • React.rb project:
 https://github.com/zetachang/ react.rb