Skip to content

Instantly share code, notes, and snippets.

@vhodges
Created October 26, 2011 14:20

Revisions

  1. vhodges revised this gist Oct 26, 2011. 1 changed file with 18 additions and 38 deletions.
    56 changes: 18 additions & 38 deletions wizard.rb
    Original file line number Diff line number Diff line change
    @@ -1,34 +1,31 @@
    #
    # Here is my code and it works as far as it goes, but on_close
    # is never being called, so I don't get a chance to clean up the
    # old subscriptions
    # This is now a working example!
    #

    require 'rubygems'

    #require 'redis/connection/synchrony'
    #require 'hiredis'

    require 'em-hiredis'

    require 'goliath'

    # The 'Man behind the curtain' (making magic or some such nonesense)
    class Wizard < Goliath::API

    use Rack::Static, :urls => ["/index.html"], :root => Goliath::Application.app_path("public")
    use Goliath::Rack::Params

    attr_accessor :redis

    def response(env)
    path = env[Goliath::Request::REQUEST_PATH]
    return [404, {}, "Not found"] unless path == "/events"

    channel = env.params[:channel]
    return [404, {}, "Channel required"] if channel.nil? || channel == ""

    env.logger.info "Connecting to channel '#{channel}' for updates"

    @redis ||= EM::Hiredis.connect

    @redis.subscribe(channel)
    @redis.on(:message) do |chn, msg|
    env.logger.info "Message received from channel #{chn}: #{msg}"
    env['redis'] = EM::Hiredis.connect
    env['redis'].subscribe(channel)
    env['redis'].on(:message) do |chn, msg|
    env.logger.info "Message received from channel #{chn}: #{msg} (We're looking for #{channel})"
    if chn === channel
    res = env.stream_send("data:#{msg}\n\n")
    end
    @@ -37,33 +34,16 @@ def response(env)
    streaming_response(200, {'Content-Type' => 'text/event-stream'})
    end

    # This code is untested as it's never called!
    def on_close(env)
    channel = env.params[:channel]
    @redis ||= EM::Hiredis.connect
    @redis.unsubscribe(channel)
    path = env[Goliath::Request::REQUEST_PATH]
    return unless path == "/events"

    env.logger.info "Connection closed."
    end

    end

    class FourOhFour < Goliath::API
    def response(env)
    [404, {}, "Not found"]
    end
    end

    class SSE < Goliath::API
    use Rack::Static, :urls => ["/index.html"], :root => Goliath::Application.app_path("public")
    use Goliath::Rack::Params

    get "/events" do
    run Wizard.new
    end
    channel = env.params[:channel]
    return if channel.nil? || channel == ""

    get "/favicon.ico" do
    run FourOhFour.new
    env.logger.info "Connection closed, Unsubscribing."
    env['redis'].unsubscribe(channel)
    env['redis'].close
    end

    end
  2. vhodges created this gist Oct 26, 2011.
    10 changes: 10 additions & 0 deletions Gemfile
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,10 @@
    source 'http://rubygems.org'

    gem "hiredis", "~> 0.3.1"
    gem "em-synchrony"

    gem 'em-hiredis'
    #gem "redis", "~> 2.2.0", :require => ["redis/connection/synchrony", "redis"]

    gem "goliath"
    #gem 'em-synchrony', :git => 'git://github.com/igrigorik/em-synchrony.git'
    44 changes: 44 additions & 0 deletions public_slash_index.html
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,44 @@
    <!DOCTYPE html>
    <html>
    <body>
    <h3>Hello SSE!</h3>
    <script>

    function getQuerystring(key, default_)
    {
    if (default_==null) default_="";
    key = key.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]");
    var regex = new RegExp("[\\?&]"+key+"=([^&#]*)");
    var qs = regex.exec(window.location.href);
    if(qs == null)
    return default_;
    else
    return qs[1];
    }

    var channel = getQuerystring('channel') || "foo";

    var source = new EventSource('/events?channel=' + channel);

    // new connection opened callback
    source.addEventListener('open', function(e) {
    console.log('connection opened');
    }, false);

    // subscribe to unnamed messages
    source.onmessage = function(e) {
    console.log(e);
    document.body.innerHTML += e.data + '<br />';
    alert(e.data);
    };

    // connection closed callback
    source.addEventListener('error', function(e) {
    if (e.eventPhase == EventSource.CLOSED) {
    console.log('connection closed');
    }
    }, false);

    </script>
    </body>
    </html>
    69 changes: 69 additions & 0 deletions wizard.rb
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,69 @@
    #
    # Here is my code and it works as far as it goes, but on_close
    # is never being called, so I don't get a chance to clean up the
    # old subscriptions
    #

    require 'rubygems'

    #require 'redis/connection/synchrony'
    #require 'hiredis'

    require 'em-hiredis'

    require 'goliath'

    # The 'Man behind the curtain' (making magic or some such nonesense)
    class Wizard < Goliath::API

    attr_accessor :redis

    def response(env)

    channel = env.params[:channel]

    env.logger.info "Connecting to channel '#{channel}' for updates"

    @redis ||= EM::Hiredis.connect

    @redis.subscribe(channel)
    @redis.on(:message) do |chn, msg|
    env.logger.info "Message received from channel #{chn}: #{msg}"
    if chn === channel
    res = env.stream_send("data:#{msg}\n\n")
    end
    end

    streaming_response(200, {'Content-Type' => 'text/event-stream'})
    end

    # This code is untested as it's never called!
    def on_close(env)
    channel = env.params[:channel]
    @redis ||= EM::Hiredis.connect
    @redis.unsubscribe(channel)

    env.logger.info "Connection closed."
    end

    end

    class FourOhFour < Goliath::API
    def response(env)
    [404, {}, "Not found"]
    end
    end

    class SSE < Goliath::API
    use Rack::Static, :urls => ["/index.html"], :root => Goliath::Application.app_path("public")
    use Goliath::Rack::Params

    get "/events" do
    run Wizard.new
    end

    get "/favicon.ico" do
    run FourOhFour.new
    end

    end