Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Realtime web apps with RethinkDB and full-stack...

Ryan Paul
September 30, 2015

Realtime web apps with RethinkDB and full-stack Ruby

Learn how to build a full-stack realtime web application with Ruby on the frontend and the backend. Get a hands-on look at how to use Opal, the Ruby-to-JavaScript transpiler, to build a Ruby-based web frontend that communicates with a Ruby backend via WebSockets.

Ryan Paul

September 30, 2015
Tweet

More Decks by Ryan Paul

Other Decks in Programming

Transcript

  1. The Stack RethinkDB Thin Sinatra Faye WS Opal WS Client

    React.rb Ruby Client STORAGE BACKEND FRONTEND
  2. 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
  3. 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
  4. Changefeeds • The changes command returns a cursor that receives

    updates • Each update includes the new and old value of the modified record
  5. r.table("users") .filter({name: "Bob"}).delete() Changefeed output: { new_val: null, old_val: {

    id: '362ae837-2e29-4695-adef-4fa415138f90', name: 'Bob', ... } } Changefeeds
  6. Backend • Running on Thin for EventMachine support • Sinatra

    for API endpoints and serving resources • Faye WebSockets for realtime messaging
  7. 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
  8. 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
  9. 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
  10. 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
  11. Why Opal? • Use one language consistently throughout your project

    • Reuse (some) existing code • Ruby is powerful and expressive, well-suited for many tasks
  12. 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
  13. 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")
  14. Assets Drop $opalinit in a <script> tag !!! %html %head

    %title Test %script(src="/assets/main.js") %script= $opalinit %body
  15. 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
  16. React.rb class App include React::Component def render h1 { "Hello,

    world!" } end end Defining a React.rb component
  17. 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
  18. 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
  19. The Stack RethinkDB Thin Sinatra Faye WS Opal WS Client

    React.rb Ruby Client STORAGE BACKEND FRONTEND