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

Goliath & async Ruby

Goliath & async Ruby

Jacques Fuentes

May 23, 2012
Tweet

More Decks by Jacques Fuentes

Other Decks in Programming

Transcript

  1. Draw Something by OMGPOP downloads on release day downloads w/in

    50 days drawings per second 30K 50M 3K OMGSCALE
  2. class Handler < EM::Connection include EM::HttpServer def initialize(mysql) @mysql =

    mysql end def process_http_request http = EM::HttpRequest.new('http://some-awesome-api').get http.callback do query = @mysql.query "insert awesome data" query.callback do |result| send_response "saved data!" end end end def send_response(content) r = EM::DelegatedHttpResponse.new(self) r.status = 200 r.content = content r.send_response end end EM.run do client = Mysql2::EM::Client.new EM::start_server("0.0.0.0", 8085, Handler, client) end
  3. require 'eventmachine' require 'em-http' require 'fiber' def async_fetch(url) f =

    Fiber.current http = EventMachine::HttpRequest.new(url).get :timeout => 10 http.callback { f.resume(http) } http.errback { f.resume(http) } return Fiber.yield end EventMachine.run do Fiber.new { puts "Setting up HTTP request #1" data = async_fetch('http://www.google.com/') puts "Fetched page #1: #{data.response_header.status}" EventMachine.stop }.resume end
  4. em-synchrony by Ilya Grigorik Fiber “aware” library & wrappers makes

    async look sync Redis/Memcache ActiveRecord MySQL
  5. require 'goliath' class Hello < Goliath::API # default to JSON

    output, allow Yaml as secondary use Goliath::Rack::Render, ['json', 'yaml'] def response(env) [200, {}, "Hello World"] end end # > ruby hello.rb -sv # > [97570:INFO] 2011-02-15 00:33:51 :: Starting server on 0.0.0.0:9000 in development mode. Watch out for stones.
  6. require 'config/setup' Configuration::Server.setup! class Server < Goliath::API use Goliath::Rack::Render, 'json'

    use Goliath::Rack::Heartbeat use Goliath::Rack::Params map "/sticky-notes" do run StickyNotesAPI.new end map "/todos" do run TodosAPI.new end map "/research-items" do run ResearchItemsAPI.new end map "/at-a-glance" do run AtAGlanceAPI.new end map "/tickets-graphs" do run TicketGraphsAPI.new end end
  7. class TodosAPI < Goliath::API include Helper def get Todo.for_user(current_user).chronological.active end

    def put (@todo = Todo.find(params['id'])).update_attributes put_params publish @todo end def post @todo = Todo.create! post_params publish @todo end private def put_params params.slice('complete_date', 'text', 'due_date', 'assignee_id') end def post_params params.merge("creator_id" => current_user[:id]) end end
  8. module Helper include Dashboard::RedisHelper def response(env) http_method = env['REQUEST_METHOD'].downcase return

    cors_options if http_method == 'options' invoke_api_using http_method end def publish(resource) event = { app_name: resource.class.app_name, ignore: current_user[:id], payload: resource } redis.publish 'dashboard', event.to_json end private def invoke_api_using(http_method) response = send(http_method) [200, cors_headers, response] end def cors_headers { 'Access-Control-Allow-Origin' => env['HTTP_ORIGIN'], 'Access-Control-Allow-Methods' => 'GET, PUT, POST, DELETE, OPTIONS', 'Access-Control-Allow-Headers' => 'Content-Type, Depth, User-Agent, X- Requested-With, Cache-Control', 'Access-Control-Allow-Credentials' => 'true' } end end
  9. module Pusher def self.config YAML.load(File.read('config/pusher.yml'))[ENV['PUSHER_ENV']] end def self.start EM.run do

    @subscriber = EM::Hiredis.connect @subscriber.subscribe('dashboard') @sockets = Pusher::Sockets.new EventMachine::WebSocket.start(config) do |ws| ws.onopen { @sockets << ws } ws.onclose { @sockets.delete(ws) } end @subscriber.on(:message) do |channel, msg| event = Pusher::Event.new msg @sockets.send event end end end end
  10. class Dashboard.Websocket constructor: (@options) -> _.extend @, Backbone.Events @start() start:

    -> @socket = new WebSocket @options.url @socket.onmessage = @onmessage @socket.onclose = @onclose @trigger "start", @ onmessage: (e) => data = JSON.parse e.data @trigger "message", data onclose: (e) => @reconnect() @trigger "closed", data reconnect: -> clearTimeout @_reconnect @_reconnect = setTimeout => @start() , 2000 class Dashboard.Application load: -> @socket() socket: -> @websocket = new Dashboard.Websocket url: "ws://0.0.0.0:2092" @websocket.start() @websocket.bind "message", @_receive, @ _receive: (event) -> app = @getApp event.app_name app.update event.payload
  11. require 'goliath' require 'em-synchrony/activerecord' require 'yajl' require 'grape' class User

    < ActiveRecord::Base; end; class MyAPI < Grape::API version 'v1', :using => :path format :json resource 'users' do get "/" do User.all end get "/:id" do User.find(params['id']) end post "/create" do User.create(params['user']) end end end class APIServer < Goliath::API def response(env) MyAPI.call(env) end end