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

Goliath & async Ruby

Goliath & async Ruby

Avatar for Jacques Fuentes

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