Search code examples
rubysinatraunicorneventmachinefibers

Sinatra + Fibers + EventMachine


I would like to know to to pause a Root Fiber in ruby (if possible).

I have this Sinatra app, and I am making async calls to an external API with EventMachine. I don't want to respond to the client until the api responds me.

For example, sleeping the Root Fiber in Sinatra until the EventMachine callback wake it up.

Thanks.


Solution

  • get '/some/route/' do
      fib = Fiber.current
      req = EM::SomeNonBlokingLib.request
      req.callback do |response|
        fib.resume(response)
      end
      req.errback do |err|
        fib.resume(err)
      end
      Fiber.yield
    end
    

    EDIT

    In your case you should spawn a Fiber for each request. So. Firstly create Rack config file and add some magick:

    # config.ru
    BOOT_PATH = File.expand_path('../http.rb',  __FILE__)
    require BOOT_PATH
    
    class FiberSpawn
      def initialize(app)
        @app = app
      end
    
      def call(env)
        fib = Fiber.new do
          res = @app.call(env)
          env['async.callback'].call(res)
        end
        EM.next_tick{ fib.resume }
        throw :async
      end
    end
    
    use FiberSpawn
    run Http
    

    Then your http Sinatra application:

    # http.rb
    require 'sinatra'
    require 'fiber'
    
    class Http < Sinatra::Base
      get '/' do
        f = Fiber.current
        EM.add_timer(1) do
          f.resume("Hello World")
        end
        Fiber.yield
      end
    end
    

    Now you could run it under thin for example:

    > thin start -R config.ru
    

    Then if you will visit locakhost:3000 you'll see your Hello World message