firehose 0.0.16 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.rspec +1 -1
 - data/README.md +6 -7
 - data/firehose.gemspec +1 -1
 - data/lib/firehose.rb +1 -16
 - data/lib/firehose/cli.rb +4 -16
 - data/lib/firehose/publisher.rb +8 -8
 - data/lib/firehose/rack.rb +32 -31
 - data/lib/firehose/subscription.rb +56 -46
 - data/lib/firehose/version.rb +2 -2
 - data/spec/integrations/thin_spec.rb +6 -6
 - data/spec/lib/broker_spec.rb +23 -23
 - data/spec/lib/consumer_spec.rb +56 -56
 - data/spec/spec_helper.rb +1 -4
 - metadata +30 -34
 - data/lib/firehose/broker.rb +0 -34
 - data/lib/firehose/consumer.rb +0 -49
 - data/spec/integrations/amqp_resources_spec.rb +0 -51
 
    
        data/.rspec
    CHANGED
    
    | 
         @@ -1 +1 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            --colour
         
     | 
| 
      
 1 
     | 
    
         
            +
            --colour --backtrace
         
     | 
    
        data/README.md
    CHANGED
    
    | 
         @@ -13,11 +13,11 @@ Firehose is both a Rack application and JavasScript library that makes building 
     | 
|
| 
       13 
13 
     | 
    
         | 
| 
       14 
14 
     | 
    
         
             
            # Getting Started
         
     | 
| 
       15 
15 
     | 
    
         | 
| 
       16 
     | 
    
         
            -
            First, you'll need to install and run  
     | 
| 
      
 16 
     | 
    
         
            +
            First, you'll need to install and run Redis.
         
     | 
| 
       17 
17 
     | 
    
         | 
| 
       18 
18 
     | 
    
         
             
            ```sh
         
     | 
| 
       19 
     | 
    
         
            -
            $ apt-get install  
     | 
| 
       20 
     | 
    
         
            -
            $ brew install  
     | 
| 
      
 19 
     | 
    
         
            +
            $ apt-get install redis       # Install on Ubuntu
         
     | 
| 
      
 20 
     | 
    
         
            +
            $ brew install redis          # Install on Mac Homebrew
         
     | 
| 
       21 
21 
     | 
    
         
             
            ```
         
     | 
| 
       22 
22 
     | 
    
         | 
| 
       23 
23 
     | 
    
         
             
            Then install the gem.
         
     | 
| 
         @@ -34,7 +34,7 @@ Now fire up the server. 
     | 
|
| 
       34 
34 
     | 
    
         
             
            $ firehose server
         
     | 
| 
       35 
35 
     | 
    
         
             
            >> Thin web server (v1.3.1 codename Triple Espresso)
         
     | 
| 
       36 
36 
     | 
    
         
             
            >> Maximum connections set to 1024
         
     | 
| 
       37 
     | 
    
         
            -
            >> Listening on 127.0.0.1: 
     | 
| 
      
 37 
     | 
    
         
            +
            >> Listening on 127.0.0.1:7474, CTRL+C to stop
         
     | 
| 
       38 
38 
     | 
    
         
             
            ```
         
     | 
| 
       39 
39 
     | 
    
         | 
| 
       40 
40 
     | 
    
         
             
            In case you're wondering, the Firehose application server runs the Rack app `Firehose::Rack::App.new` inside of Thin.
         
     | 
| 
         @@ -87,15 +87,14 @@ new Firehose.Consumer({ 
     | 
|
| 
       87 
87 
     | 
    
         
             
              },
         
     | 
| 
       88 
88 
     | 
    
         
             
              // Note that we do NOT specify a protocol here because we don't
         
     | 
| 
       89 
89 
     | 
    
         
             
              // know that yet.
         
     | 
| 
       90 
     | 
    
         
            -
              uri: '//localhost: 
     | 
| 
      
 90 
     | 
    
         
            +
              uri: '//localhost:7474/hello'
         
     | 
| 
       91 
91 
     | 
    
         
             
            }).connect();
         
     | 
| 
       92 
92 
     | 
    
         
             
            ```
         
     | 
| 
       93 
93 
     | 
    
         | 
| 
       94 
94 
     | 
    
         
             
            Then publish another message.
         
     | 
| 
       95 
95 
     | 
    
         | 
| 
       96 
     | 
    
         
            -
             
     | 
| 
       97 
96 
     | 
    
         
             
            ```sh
         
     | 
| 
       98 
     | 
    
         
            -
            $ curl -X PUT -d "This is almost magical" "http://localhost: 
     | 
| 
      
 97 
     | 
    
         
            +
            $ curl -X PUT -d "\"This is almost magical\"" "http://localhost:7474/hello"
         
     | 
| 
       99 
98 
     | 
    
         
             
            ```
         
     | 
| 
       100 
99 
     | 
    
         | 
| 
       101 
100 
     | 
    
         
             
            # How is it different from socket.io?
         
     | 
    
        data/firehose.gemspec
    CHANGED
    
    | 
         @@ -20,7 +20,7 @@ Gem::Specification.new do |s| 
     | 
|
| 
       20 
20 
     | 
    
         | 
| 
       21 
21 
     | 
    
         
             
              # specify any dependencies here; for example:
         
     | 
| 
       22 
22 
     | 
    
         
             
              s.add_runtime_dependency "eventmachine", ">= 1.0.0.beta"
         
     | 
| 
       23 
     | 
    
         
            -
              s.add_runtime_dependency " 
     | 
| 
      
 23 
     | 
    
         
            +
              s.add_runtime_dependency "em-hiredis"
         
     | 
| 
       24 
24 
     | 
    
         
             
              s.add_runtime_dependency "thin"
         
     | 
| 
       25 
25 
     | 
    
         
             
              s.add_runtime_dependency "thor"
         
     | 
| 
       26 
26 
     | 
    
         
             
              s.add_runtime_dependency "faraday"
         
     | 
    
        data/lib/firehose.rb
    CHANGED
    
    | 
         @@ -1,31 +1,16 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            require 'firehose/version'
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
       3 
     | 
    
         
            -
            require ' 
     | 
| 
      
 3 
     | 
    
         
            +
            require 'em-hiredis'
         
     | 
| 
       4 
4 
     | 
    
         
             
            require 'logger'
         
     | 
| 
       5 
5 
     | 
    
         | 
| 
       6 
6 
     | 
    
         
             
            module Firehose
         
     | 
| 
       7 
7 
     | 
    
         
             
              autoload :Subscription, 'firehose/subscription'
         
     | 
| 
       8 
8 
     | 
    
         
             
              autoload :Publisher,    'firehose/publisher'
         
     | 
| 
       9 
9 
     | 
    
         
             
              autoload :Producer,     'firehose/producer'
         
     | 
| 
       10 
     | 
    
         
            -
              autoload :Consumer,     'firehose/consumer'
         
     | 
| 
       11 
10 
     | 
    
         
             
              autoload :Default,      'firehose/default'
         
     | 
| 
       12 
     | 
    
         
            -
              autoload :Broker,       'firehose/broker'
         
     | 
| 
       13 
11 
     | 
    
         
             
              autoload :Rack,         'firehose/rack'
         
     | 
| 
       14 
12 
     | 
    
         
             
              autoload :CLI,          'firehose/cli'
         
     | 
| 
       15 
13 
     | 
    
         | 
| 
       16 
     | 
    
         
            -
              # TODO move this into a configuration or session class.
         
     | 
| 
       17 
     | 
    
         
            -
              # Hang on to AMQP configuration settings.
         
     | 
| 
       18 
     | 
    
         
            -
              def self.amqp
         
     | 
| 
       19 
     | 
    
         
            -
                @amqp ||= Struct.new(:connection).new(AMQP.connect)
         
     | 
| 
       20 
     | 
    
         
            -
              end
         
     | 
| 
       21 
     | 
    
         
            -
             
     | 
| 
       22 
     | 
    
         
            -
              # TODO figure out a better way to memoize AMQP connection for production runtimes, and 
         
     | 
| 
       23 
     | 
    
         
            -
              # make it resetable for testing environment. Some sort of Firehose::Session object is probably
         
     | 
| 
       24 
     | 
    
         
            -
              # in order
         
     | 
| 
       25 
     | 
    
         
            -
              def self.reset!
         
     | 
| 
       26 
     | 
    
         
            -
                @amqp = nil
         
     | 
| 
       27 
     | 
    
         
            -
              end
         
     | 
| 
       28 
     | 
    
         
            -
             
     | 
| 
       29 
14 
     | 
    
         
             
              # Logging
         
     | 
| 
       30 
15 
     | 
    
         
             
              def self.logger
         
     | 
| 
       31 
16 
     | 
    
         
             
                @logger ||= Logger.new($stdout)
         
     | 
    
        data/lib/firehose/cli.rb
    CHANGED
    
    | 
         @@ -8,27 +8,15 @@ module Firehose 
     | 
|
| 
       8 
8 
     | 
    
         
             
                method_option :host, :type => :string, :default => '0.0.0.0', :required => true, :aliases => '-h'
         
     | 
| 
       9 
9 
     | 
    
         | 
| 
       10 
10 
     | 
    
         
             
                def server
         
     | 
| 
       11 
     | 
    
         
            -
                  broker = Firehose::Broker.new
         
     | 
| 
       12 
     | 
    
         
            -
             
     | 
| 
       13 
11 
     | 
    
         
             
                  server = Thin::Server.new(options[:host], options[:port]) do
         
     | 
| 
       14 
     | 
    
         
            -
                     
     | 
| 
       15 
     | 
    
         
            -
                    # connection reference issues. I'd like to have this ancillary stuff accessiable via
         
     | 
| 
       16 
     | 
    
         
            -
                    # a different port or even a socket.
         
     | 
| 
       17 
     | 
    
         
            -
                    map '/_firehose/stats' do
         
     | 
| 
       18 
     | 
    
         
            -
                      run Proc.new {
         
     | 
| 
       19 
     | 
    
         
            -
                        [200, {'Content-Type' => 'text/plain'}, [broker.stats.inspect]]
         
     | 
| 
       20 
     | 
    
         
            -
                      }
         
     | 
| 
       21 
     | 
    
         
            -
                    end
         
     | 
| 
       22 
     | 
    
         
            -
             
     | 
| 
       23 
     | 
    
         
            -
                    map '/' do
         
     | 
| 
       24 
     | 
    
         
            -
                      run Firehose::Rack::App.new(broker)
         
     | 
| 
       25 
     | 
    
         
            -
                    end
         
     | 
| 
      
 12 
     | 
    
         
            +
                    run Firehose::Rack::App.new
         
     | 
| 
       26 
13 
     | 
    
         
             
                  end
         
     | 
| 
       27 
14 
     | 
    
         | 
| 
       28 
15 
     | 
    
         
             
                  begin
         
     | 
| 
       29 
16 
     | 
    
         
             
                    server.start!
         
     | 
| 
       30 
     | 
    
         
            -
                  rescue  
     | 
| 
       31 
     | 
    
         
            -
                    Firehose.logger.error "Unable to connect to  
     | 
| 
      
 17 
     | 
    
         
            +
                  rescue RuntimeError
         
     | 
| 
      
 18 
     | 
    
         
            +
                    Firehose.logger.error "Unable to connect to Redis, are you sure it's running?"
         
     | 
| 
      
 19 
     | 
    
         
            +
                    raise
         
     | 
| 
       32 
20 
     | 
    
         
             
                  end
         
     | 
| 
       33 
21 
     | 
    
         
             
                end
         
     | 
| 
       34 
22 
     | 
    
         
             
              end
         
     | 
    
        data/lib/firehose/publisher.rb
    CHANGED
    
    | 
         @@ -1,13 +1,13 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            module Firehose
         
     | 
| 
       2 
2 
     | 
    
         
             
              class Publisher
         
     | 
| 
       3 
     | 
    
         
            -
                def publish( 
     | 
| 
       4 
     | 
    
         
            -
                   
     | 
| 
       5 
     | 
    
         
            -
                   
     | 
| 
       6 
     | 
    
         
            -
             
     | 
| 
       7 
     | 
    
         
            -
             
     | 
| 
       8 
     | 
    
         
            -
             
     | 
| 
       9 
     | 
    
         
            -
             
     | 
| 
       10 
     | 
    
         
            -
                   
     | 
| 
      
 3 
     | 
    
         
            +
                def publish(channel, message)
         
     | 
| 
      
 4 
     | 
    
         
            +
                  Firehose.logger.debug "Redis publishing `#{message}` to `#{channel}`"
         
     | 
| 
      
 5 
     | 
    
         
            +
                  redis.publish(channel, message).errback { raise 'Error publishing' }
         
     | 
| 
      
 6 
     | 
    
         
            +
                end
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
              private
         
     | 
| 
      
 9 
     | 
    
         
            +
                def redis
         
     | 
| 
      
 10 
     | 
    
         
            +
                  @redis ||= EM::Hiredis.connect
         
     | 
| 
       11 
11 
     | 
    
         
             
                end
         
     | 
| 
       12 
12 
     | 
    
         
             
              end
         
     | 
| 
       13 
13 
     | 
    
         
             
            end
         
     | 
    
        data/lib/firehose/rack.rb
    CHANGED
    
    | 
         @@ -5,16 +5,14 @@ module Firehose 
     | 
|
| 
       5 
5 
     | 
    
         
             
                AsyncResponse = [-1, {}, []]
         
     | 
| 
       6 
6 
     | 
    
         | 
| 
       7 
7 
     | 
    
         
             
                class HttpLongPoll
         
     | 
| 
       8 
     | 
    
         
            -
                  def initialize(broker)
         
     | 
| 
       9 
     | 
    
         
            -
                    @broker = broker
         
     | 
| 
       10 
     | 
    
         
            -
                  end
         
     | 
| 
       11 
     | 
    
         
            -
             
     | 
| 
       12 
8 
     | 
    
         
             
                  def call(env)
         
     | 
| 
       13 
9 
     | 
    
         
             
                    req     = ::Rack::Request.new(env)
         
     | 
| 
       14 
10 
     | 
    
         
             
                    cid     = req.params['cid']
         
     | 
| 
       15 
11 
     | 
    
         
             
                    path    = req.path
         
     | 
| 
       16 
12 
     | 
    
         
             
                    method  = req.request_method
         
     | 
| 
       17 
13 
     | 
    
         
             
                    timeout = 30
         
     | 
| 
      
 14 
     | 
    
         
            +
                    queue_name = "#{cid}@#{path}"
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
       18 
16 
     | 
    
         
             
                    # TODO seperate out CORS logic as an async middleware with a Goliath web server.
         
     | 
| 
       19 
17 
     | 
    
         
             
                    cors_origin = env['HTTP_ORIGIN']
         
     | 
| 
       20 
18 
     | 
    
         
             
                    cors_headers  = {
         
     | 
| 
         @@ -33,20 +31,23 @@ module Firehose 
     | 
|
| 
       33 
31 
     | 
    
         
             
                        response_headers = cors_origin ? cors_headers : {}
         
     | 
| 
       34 
32 
     | 
    
         | 
| 
       35 
33 
     | 
    
         
             
                        # Setup a subscription with a client id. We haven't subscribed yet here.
         
     | 
| 
       36 
     | 
    
         
            -
                         
     | 
| 
      
 34 
     | 
    
         
            +
                        if queue = queues[queue_name]
         
     | 
| 
      
 35 
     | 
    
         
            +
                          queue.live
         
     | 
| 
      
 36 
     | 
    
         
            +
                        else
         
     | 
| 
      
 37 
     | 
    
         
            +
                          queue = queues[queue_name] = Firehose::Subscription::Queue.new(cid, path)
         
     | 
| 
      
 38 
     | 
    
         
            +
                        end
         
     | 
| 
       37 
39 
     | 
    
         | 
| 
       38 
40 
     | 
    
         
             
                        # Setup a timeout timer to tell clients that time out that everything is OK
         
     | 
| 
       39 
41 
     | 
    
         
             
                        # and they should come back for more
         
     | 
| 
       40 
     | 
    
         
            -
                         
     | 
| 
      
 42 
     | 
    
         
            +
                        long_poll_timer = EM::Timer.new(timeout) do
         
     | 
| 
       41 
43 
     | 
    
         
             
                          # We send a 204 OK to tell the client to reconnect.
         
     | 
| 
       42 
44 
     | 
    
         
             
                          env['async.callback'].call [204, response_headers, []]
         
     | 
| 
       43 
45 
     | 
    
         
             
                          Firehose.logger.debug "HTTP wait `#{cid}@#{path}` timed out"
         
     | 
| 
       44 
46 
     | 
    
         
             
                        end
         
     | 
| 
       45 
47 
     | 
    
         | 
| 
       46 
48 
     | 
    
         
             
                        # Ok, now subscribe to the subscription.
         
     | 
| 
       47 
     | 
    
         
            -
                         
     | 
| 
       48 
     | 
    
         
            -
                           
     | 
| 
       49 
     | 
    
         
            -
                          consumer.unsubscribe
         
     | 
| 
      
 49 
     | 
    
         
            +
                        queue.pop do |message, subscription|
         
     | 
| 
      
 50 
     | 
    
         
            +
                          long_poll_timer.cancel # Turn off the heart beat so we don't execute any of that business.
         
     | 
| 
       50 
51 
     | 
    
         
             
                          env['async.callback'].call [200, response_headers, [message]]
         
     | 
| 
       51 
52 
     | 
    
         
             
                          Firehose.logger.debug "HTTP sent `#{message}` to `#{cid}@#{path}`"
         
     | 
| 
       52 
53 
     | 
    
         
             
                        end
         
     | 
| 
         @@ -55,7 +56,11 @@ module Firehose 
     | 
|
| 
       55 
56 
     | 
    
         
             
                        # Unsubscribe from the subscription if its still open and something bad happened
         
     | 
| 
       56 
57 
     | 
    
         
             
                        # or the heart beat triggered before we could finish.
         
     | 
| 
       57 
58 
     | 
    
         
             
                        env['async.close'].callback do
         
     | 
| 
       58 
     | 
    
         
            -
                           
     | 
| 
      
 59 
     | 
    
         
            +
                          # Kill queue if we don't hear back in 30s
         
     | 
| 
      
 60 
     | 
    
         
            +
                          queue.kill timeout do
         
     | 
| 
      
 61 
     | 
    
         
            +
                            Firehose.logger.debug "Deleting queue to `#{queue_name}`"
         
     | 
| 
      
 62 
     | 
    
         
            +
                            queues.delete queue_name
         
     | 
| 
      
 63 
     | 
    
         
            +
                          end
         
     | 
| 
       59 
64 
     | 
    
         
             
                          Firehose.logger.debug "HTTP connection `#{cid}@#{path}` closing"
         
     | 
| 
       60 
65 
     | 
    
         
             
                        end
         
     | 
| 
       61 
66 
     | 
    
         
             
                      end
         
     | 
| 
         @@ -67,7 +72,7 @@ module Firehose 
     | 
|
| 
       67 
72 
     | 
    
         
             
                    when 'PUT'
         
     | 
| 
       68 
73 
     | 
    
         
             
                      body = env['rack.input'].read
         
     | 
| 
       69 
74 
     | 
    
         
             
                      Firehose.logger.debug "HTTP published `#{body}` to `#{path}`"
         
     | 
| 
       70 
     | 
    
         
            -
                       
     | 
| 
      
 75 
     | 
    
         
            +
                      publisher.publish(path, body)
         
     | 
| 
       71 
76 
     | 
    
         | 
| 
       72 
77 
     | 
    
         
             
                      [202, {}, []]
         
     | 
| 
       73 
78 
     | 
    
         
             
                    else
         
     | 
| 
         @@ -75,23 +80,28 @@ module Firehose 
     | 
|
| 
       75 
80 
     | 
    
         
             
                      [501, {'Content-Type' => 'text/plain'}, ["#{method} not supported."]]
         
     | 
| 
       76 
81 
     | 
    
         
             
                    end
         
     | 
| 
       77 
82 
     | 
    
         
             
                  end
         
     | 
| 
      
 83 
     | 
    
         
            +
             
     | 
| 
      
 84 
     | 
    
         
            +
                private 
         
     | 
| 
      
 85 
     | 
    
         
            +
                  def publisher
         
     | 
| 
      
 86 
     | 
    
         
            +
                    @publisher ||= Firehose::Publisher.new
         
     | 
| 
      
 87 
     | 
    
         
            +
                  end
         
     | 
| 
      
 88 
     | 
    
         
            +
             
     | 
| 
      
 89 
     | 
    
         
            +
                  def queues
         
     | 
| 
      
 90 
     | 
    
         
            +
                    @queues ||= {}
         
     | 
| 
      
 91 
     | 
    
         
            +
                  end
         
     | 
| 
       78 
92 
     | 
    
         
             
                end
         
     | 
| 
       79 
93 
     | 
    
         | 
| 
       80 
94 
     | 
    
         
             
                class WebSocket < ::Rack::WebSocket::Application
         
     | 
| 
       81 
     | 
    
         
            -
                  attr_reader :cid, :path
         
     | 
| 
      
 95 
     | 
    
         
            +
                  attr_reader :cid, :path, :subscription
         
     | 
| 
       82 
96 
     | 
    
         | 
| 
       83 
     | 
    
         
            -
                  def initialize(broker)
         
     | 
| 
       84 
     | 
    
         
            -
                    @broker = broker
         
     | 
| 
       85 
     | 
    
         
            -
                  end
         
     | 
| 
       86 
     | 
    
         
            -
             
     | 
| 
       87 
97 
     | 
    
         
             
                  # Subscribe to a path and make some magic happen, mmkmay?
         
     | 
| 
       88 
98 
     | 
    
         
             
                  def on_open(env)
         
     | 
| 
       89 
99 
     | 
    
         
             
                    req   = ::Rack::Request.new(env)
         
     | 
| 
       90 
100 
     | 
    
         
             
                    @cid   = req.params['cid']
         
     | 
| 
       91 
101 
     | 
    
         
             
                    @path  = req.path
         
     | 
| 
      
 102 
     | 
    
         
            +
                    @subscription = Firehose::Subscription.new(cid, path)
         
     | 
| 
       92 
103 
     | 
    
         | 
| 
       93 
     | 
    
         
            -
                     
     | 
| 
       94 
     | 
    
         
            -
                    @consumer.subscribe_to path do |message|
         
     | 
| 
      
 104 
     | 
    
         
            +
                    subscription.subscribe do |message, subscription|
         
     | 
| 
       95 
105 
     | 
    
         
             
                      Firehose.logger.debug "WS sent `#{message}` to `#{cid}@#{path}`"
         
     | 
| 
       96 
106 
     | 
    
         
             
                      send_data message
         
     | 
| 
       97 
107 
     | 
    
         
             
                    end
         
     | 
| 
         @@ -100,37 +110,28 @@ module Firehose 
     | 
|
| 
       100 
110 
     | 
    
         | 
| 
       101 
111 
     | 
    
         
             
                  # Delete the subscription if the thing even happened.
         
     | 
| 
       102 
112 
     | 
    
         
             
                  def on_close(env)
         
     | 
| 
       103 
     | 
    
         
            -
                     
     | 
| 
      
 113 
     | 
    
         
            +
                    subscription.unsubscribe
         
     | 
| 
       104 
114 
     | 
    
         
             
                    Firehose.logger.debug "WS connection `#{cid}@#{path}` closing"
         
     | 
| 
       105 
115 
     | 
    
         
             
                  end
         
     | 
| 
       106 
116 
     | 
    
         | 
| 
       107 
117 
     | 
    
         
             
                  # Log websocket level errors
         
     | 
| 
       108 
118 
     | 
    
         
             
                  def on_error(env, error)
         
     | 
| 
       109 
     | 
    
         
            -
                    Firehose.logger.error "WS connection `#{cid}@#{path}` error `#{error}`: #{ 
     | 
| 
       110 
     | 
    
         
            -
                    @consumer.unsubscribe if @consumer
         
     | 
| 
      
 119 
     | 
    
         
            +
                    Firehose.logger.error "WS connection `#{cid}@#{path}` error `#{error}`: #{error.backtrace}"
         
     | 
| 
       111 
120 
     | 
    
         
             
                  end
         
     | 
| 
       112 
121 
     | 
    
         
             
                end
         
     | 
| 
       113 
122 
     | 
    
         | 
| 
       114 
123 
     | 
    
         
             
                class App
         
     | 
| 
       115 
     | 
    
         
            -
                  # Firehose broker that will be used to pub/sub messages.
         
     | 
| 
       116 
     | 
    
         
            -
                  attr_reader :broker
         
     | 
| 
       117 
     | 
    
         
            -
             
     | 
| 
       118 
     | 
    
         
            -
                  # Fire up a default broker if one is not specified.
         
     | 
| 
       119 
     | 
    
         
            -
                  def initialize(broker = Firehose::Broker.new)
         
     | 
| 
       120 
     | 
    
         
            -
                    @broker = broker
         
     | 
| 
       121 
     | 
    
         
            -
                  end
         
     | 
| 
       122 
     | 
    
         
            -
             
     | 
| 
       123 
124 
     | 
    
         
             
                  def call(env)
         
     | 
| 
       124 
125 
     | 
    
         
             
                    websocket_request?(env) ? websocket.call(env) : http_long_poll.call(env)
         
     | 
| 
       125 
126 
     | 
    
         
             
                  end
         
     | 
| 
       126 
127 
     | 
    
         | 
| 
       127 
128 
     | 
    
         
             
                private
         
     | 
| 
       128 
129 
     | 
    
         
             
                  def websocket
         
     | 
| 
       129 
     | 
    
         
            -
                     
     | 
| 
      
 130 
     | 
    
         
            +
                    WebSocket.new
         
     | 
| 
       130 
131 
     | 
    
         
             
                  end
         
     | 
| 
       131 
132 
     | 
    
         | 
| 
       132 
133 
     | 
    
         
             
                  def http_long_poll
         
     | 
| 
       133 
     | 
    
         
            -
                    @http_long_poll ||= HttpLongPoll.new 
     | 
| 
      
 134 
     | 
    
         
            +
                    @http_long_poll ||= HttpLongPoll.new
         
     | 
| 
       134 
135 
     | 
    
         
             
                  end
         
     | 
| 
       135 
136 
     | 
    
         | 
| 
       136 
137 
     | 
    
         
             
                  def websocket_request?(env)
         
     | 
| 
         @@ -1,65 +1,75 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            module Firehose
         
     | 
| 
       2 
2 
     | 
    
         
             
              class Subscription
         
     | 
| 
       3 
     | 
    
         
            -
                #  
     | 
| 
       4 
     | 
    
         
            -
                 
     | 
| 
       5 
     | 
    
         
            -
                # TODO should the Consumer handle TTL?
         
     | 
| 
       6 
     | 
    
         
            -
                TTL = 15000
         
     | 
| 
       7 
     | 
    
         
            -
             
     | 
| 
       8 
     | 
    
         
            -
                # Time to live for the amqp_queue on the server after the subscription is canceled. This
         
     | 
| 
       9 
     | 
    
         
            -
                # is mostly for flakey connections where the client may reconnect after *ttl* and continue
         
     | 
| 
       10 
     | 
    
         
            -
                # receiving messages.
         
     | 
| 
       11 
     | 
    
         
            -
                attr_accessor :ttl
         
     | 
| 
       12 
     | 
    
         
            -
             
     | 
| 
       13 
     | 
    
         
            -
                # Consumer and channel for the subscription.
         
     | 
| 
       14 
     | 
    
         
            -
                attr_reader :consumer
         
     | 
| 
      
 3 
     | 
    
         
            +
                # consumer_id and channel for the subscription.
         
     | 
| 
      
 4 
     | 
    
         
            +
                attr_reader :consumer_id
         
     | 
| 
       15 
5 
     | 
    
         | 
| 
       16 
6 
     | 
    
         
             
                # Channel that we'll use for the pub-sub activity. This probably maps to an URL
         
     | 
| 
       17 
7 
     | 
    
         
             
                attr_reader :channel
         
     | 
| 
       18 
8 
     | 
    
         | 
| 
       19 
     | 
    
         
            -
                def initialize( 
     | 
| 
       20 
     | 
    
         
            -
                  @ 
     | 
| 
      
 9 
     | 
    
         
            +
                def initialize(consumer_id, channel)
         
     | 
| 
      
 10 
     | 
    
         
            +
                  @consumer_id, @channel = consumer_id, channel
         
     | 
| 
       21 
11 
     | 
    
         
             
                end
         
     | 
| 
       22 
12 
     | 
    
         | 
| 
       23 
     | 
    
         
            -
                #  
     | 
| 
       24 
     | 
    
         
            -
                #  
     | 
| 
      
 13 
     | 
    
         
            +
                # Subscribe to messages on the backend to fill up the subscription queue. consumer_ids of the messages
         
     | 
| 
      
 14 
     | 
    
         
            +
                # will queue up units of "work" to process data from the subscription.
         
     | 
| 
       25 
15 
     | 
    
         
             
                def subscribe(&block)
         
     | 
| 
       26 
     | 
    
         
            -
                   
     | 
| 
       27 
     | 
    
         
            -
                   
     | 
| 
       28 
     | 
    
         
            -
             
     | 
| 
       29 
     | 
    
         
            -
             
     | 
| 
       30 
     | 
    
         
            -
                   
     | 
| 
       31 
     | 
    
         
            -
             
     | 
| 
       32 
     | 
    
         
            -
                  # When we get a message, we want to remove the consumer from the amqp_queue so that the x-expires
         
     | 
| 
       33 
     | 
    
         
            -
                  # ttl starts ticking down. On the reconnect, the consumer connects to the amqp_queue and resets the
         
     | 
| 
       34 
     | 
    
         
            -
                  # timer on x-expires... in theory at least.
         
     | 
| 
       35 
     | 
    
         
            -
                  @amqp_consumer = AMQP::Consumer.new(amqp_channel, amqp_queue, consumer.guid)
         
     | 
| 
       36 
     | 
    
         
            -
                  @amqp_consumer.on_delivery do |metadata, message|
         
     | 
| 
       37 
     | 
    
         
            -
                    Firehose.logger.debug "AMQP delivering `#{message}` to `#{consumer.guid}@#{channel}`"
         
     | 
| 
       38 
     | 
    
         
            -
                    block.call(message, self)
         
     | 
| 
       39 
     | 
    
         
            -
                    # The ack needs to go after the block is called. This makes sure that all processing
         
     | 
| 
       40 
     | 
    
         
            -
                    # happens downstream before we remove it from the amqp_queue entirely.
         
     | 
| 
       41 
     | 
    
         
            -
                    metadata.ack
         
     | 
| 
       42 
     | 
    
         
            -
                  end.consume
         
     | 
| 
       43 
     | 
    
         
            -
                  Firehose.logger.debug "AMQP subscribed to `#{consumer.guid}@#{channel}`"
         
     | 
| 
      
 16 
     | 
    
         
            +
                  redis.subscribe(channel)
         
     | 
| 
      
 17 
     | 
    
         
            +
                  redis.on(:message) do |channel, message|
         
     | 
| 
      
 18 
     | 
    
         
            +
                    Firehose.logger.debug "Redis recieved `#{message}` to `#{consumer_id}@#{channel}`"
         
     | 
| 
      
 19 
     | 
    
         
            +
                    block.call message, self
         
     | 
| 
      
 20 
     | 
    
         
            +
                  end
         
     | 
| 
      
 21 
     | 
    
         
            +
                  Firehose.logger.debug "Redis subscribed to `#{consumer_id}@#{channel}`"
         
     | 
| 
       44 
22 
     | 
    
         
             
                  self # Return the subscription for chaining.
         
     | 
| 
       45 
23 
     | 
    
         
             
                end
         
     | 
| 
       46 
24 
     | 
    
         | 
| 
       47 
     | 
    
         
            -
                def unsubscribe
         
     | 
| 
       48 
     | 
    
         
            -
                  Firehose.logger.debug " 
     | 
| 
       49 
     | 
    
         
            -
                   
     | 
| 
       50 
     | 
    
         
            -
                   
     | 
| 
      
 25 
     | 
    
         
            +
                def unsubscribe(&block)
         
     | 
| 
      
 26 
     | 
    
         
            +
                  Firehose.logger.debug "Redis unsubscribed from `#{consumer_id}@#{channel}`"
         
     | 
| 
      
 27 
     | 
    
         
            +
                  redis.close
         
     | 
| 
      
 28 
     | 
    
         
            +
                  block.call(self) if block
         
     | 
| 
      
 29 
     | 
    
         
            +
                  self
         
     | 
| 
      
 30 
     | 
    
         
            +
                end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
              private
         
     | 
| 
      
 33 
     | 
    
         
            +
                def redis
         
     | 
| 
      
 34 
     | 
    
         
            +
                  @redis ||= EM::Hiredis.connect
         
     | 
| 
      
 35 
     | 
    
         
            +
                end
         
     | 
| 
      
 36 
     | 
    
         
            +
              end
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
              # Queue subscription messages so that we can remember and/or operate on them
         
     | 
| 
      
 39 
     | 
    
         
            +
              class Subscription::Queue
         
     | 
| 
      
 40 
     | 
    
         
            +
                attr_reader :subscription, :channel
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                def initialize(consumer_id, channel)
         
     | 
| 
      
 43 
     | 
    
         
            +
                  @subscription = Subscription.new(consumer_id, channel)
         
     | 
| 
      
 44 
     | 
    
         
            +
                  # Start the subscription and start dropping mesasge onto the queue
         
     | 
| 
      
 45 
     | 
    
         
            +
                  subscription.subscribe do |message|
         
     | 
| 
      
 46 
     | 
    
         
            +
                    queue.push message
         
     | 
| 
      
 47 
     | 
    
         
            +
                  end
         
     | 
| 
      
 48 
     | 
    
         
            +
                end
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                # Pop an item off the subscription queue so we can work on it.
         
     | 
| 
      
 51 
     | 
    
         
            +
                def pop(&block)
         
     | 
| 
      
 52 
     | 
    
         
            +
                  queue.pop do |message|
         
     | 
| 
      
 53 
     | 
    
         
            +
                    block.call message, subscription
         
     | 
| 
      
 54 
     | 
    
         
            +
                  end
         
     | 
| 
      
 55 
     | 
    
         
            +
                end
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
                # Kill the queue in n seconds.
         
     | 
| 
      
 58 
     | 
    
         
            +
                def kill(ttl=0, &block)
         
     | 
| 
      
 59 
     | 
    
         
            +
                  if ttl.zero?
         
     | 
| 
      
 60 
     | 
    
         
            +
                    subscription.unsubscribe &block
         
     | 
| 
      
 61 
     | 
    
         
            +
                  else
         
     | 
| 
      
 62 
     | 
    
         
            +
                    @timer = EM::Timer.new(ttl){ kill 0 }
         
     | 
| 
      
 63 
     | 
    
         
            +
                  end
         
     | 
| 
       51 
64 
     | 
    
         
             
                end
         
     | 
| 
       52 
65 
     | 
    
         | 
| 
       53 
     | 
    
         
            -
                 
     | 
| 
       54 
     | 
    
         
            -
             
     | 
| 
       55 
     | 
    
         
            -
                def on_unsubscribe(&block)
         
     | 
| 
       56 
     | 
    
         
            -
                  @unsubscribe_callback = block
         
     | 
| 
      
 66 
     | 
    
         
            +
                def live
         
     | 
| 
      
 67 
     | 
    
         
            +
                  @timer.cancel if @timer
         
     | 
| 
       57 
68 
     | 
    
         
             
                end
         
     | 
| 
       58 
69 
     | 
    
         | 
| 
       59 
     | 
    
         
            -
             
     | 
| 
       60 
     | 
    
         
            -
                 
     | 
| 
       61 
     | 
    
         
            -
             
     | 
| 
       62 
     | 
    
         
            -
                  @ttl ||= TTL
         
     | 
| 
      
 70 
     | 
    
         
            +
              private
         
     | 
| 
      
 71 
     | 
    
         
            +
                def queue
         
     | 
| 
      
 72 
     | 
    
         
            +
                  @queue ||= EM::Queue.new
         
     | 
| 
       63 
73 
     | 
    
         
             
                end
         
     | 
| 
       64 
74 
     | 
    
         
             
              end
         
     | 
| 
       65 
75 
     | 
    
         
             
            end
         
     | 
    
        data/lib/firehose/version.rb
    CHANGED
    
    
| 
         @@ -12,12 +12,11 @@ describe Firehose::Rack do 
     | 
|
| 
       12 
12 
     | 
    
         
             
              end
         
     | 
| 
       13 
13 
     | 
    
         | 
| 
       14 
14 
     | 
    
         
             
              let(:app)       { Firehose::Rack::App.new }
         
     | 
| 
       15 
     | 
    
         
            -
              let(:messages)  { (1.. 
     | 
| 
      
 15 
     | 
    
         
            +
              let(:messages)  { (1..2000).map(&:to_s) }
         
     | 
| 
       16 
16 
     | 
    
         
             
              let(:channel)   { "/firehose/integration/#{Time.now.to_i}" }
         
     | 
| 
       17 
17 
     | 
    
         
             
              let(:uri)       { Firehose::Default::URI }
         
     | 
| 
       18 
18 
     | 
    
         
             
              let(:http_url)  { "http://#{uri.host}:#{uri.port}#{channel}" }
         
     | 
| 
       19 
19 
     | 
    
         
             
              let(:ws_url)    { "ws://#{uri.host}:#{uri.port}#{channel}" }
         
     | 
| 
       20 
     | 
    
         
            -
              let(:cid)       { "client-#{Time.now.to_i}" }
         
     | 
| 
       21 
20 
     | 
    
         | 
| 
       22 
21 
     | 
    
         
             
              it "should pub-sub http and websockets" do
         
     | 
| 
       23 
22 
     | 
    
         
             
                # Setup variables that we'll use after we turn off EM to validate our
         
     | 
| 
         @@ -39,7 +38,7 @@ describe Firehose::Rack do 
     | 
|
| 
       39 
38 
     | 
    
         | 
| 
       40 
39 
     | 
    
         
             
                # Lets have an HTTP Long poll client
         
     | 
| 
       41 
40 
     | 
    
         
             
                http_long_poll = Proc.new do
         
     | 
| 
       42 
     | 
    
         
            -
                  http = EM::HttpRequest.new(http_url).get(:query => {'cid' =>  
     | 
| 
      
 41 
     | 
    
         
            +
                  http = EM::HttpRequest.new(http_url).get(:query => {'cid' => 'alpha'})
         
     | 
| 
       43 
42 
     | 
    
         
             
                  http.errback { em.stop }
         
     | 
| 
       44 
43 
     | 
    
         
             
                  http.callback do
         
     | 
| 
       45 
44 
     | 
    
         
             
                    received_http << http.response
         
     | 
| 
         @@ -53,7 +52,7 @@ describe Firehose::Rack do 
     | 
|
| 
       53 
52 
     | 
    
         | 
| 
       54 
53 
     | 
    
         
             
                # And test a web socket client too, at the same time.
         
     | 
| 
       55 
54 
     | 
    
         
             
                websocket = Proc.new do
         
     | 
| 
       56 
     | 
    
         
            -
                  ws = EventMachine::WebSocketClient.connect(ws_url)
         
     | 
| 
      
 55 
     | 
    
         
            +
                  ws = EventMachine::WebSocketClient.connect("#{ws_url}?cid=bravo")
         
     | 
| 
       57 
56 
     | 
    
         
             
                  ws.errback  { em.stop }
         
     | 
| 
       58 
57 
     | 
    
         
             
                  ws.stream do |msg|
         
     | 
| 
       59 
58 
     | 
    
         
             
                    received_ws << msg
         
     | 
| 
         @@ -64,7 +63,8 @@ describe Firehose::Rack do 
     | 
|
| 
       64 
63 
     | 
    
         
             
                # Great, we have all the pieces in order, lets run this thing in the reactor.
         
     | 
| 
       65 
64 
     | 
    
         
             
                em do
         
     | 
| 
       66 
65 
     | 
    
         
             
                  # Start the server
         
     | 
| 
       67 
     | 
    
         
            -
                  ::Thin::Server.new('0.0.0.0', uri.port, app) 
     | 
| 
      
 66 
     | 
    
         
            +
                  server = ::Thin::Server.new('0.0.0.0', uri.port, app)
         
     | 
| 
      
 67 
     | 
    
         
            +
                  server.start
         
     | 
| 
       68 
68 
     | 
    
         | 
| 
       69 
69 
     | 
    
         
             
                  # Start the http_long_pollr.
         
     | 
| 
       70 
70 
     | 
    
         
             
                  http_long_poll.call
         
     | 
| 
         @@ -75,7 +75,7 @@ describe Firehose::Rack do 
     | 
|
| 
       75 
75 
     | 
    
         
             
                end
         
     | 
| 
       76 
76 
     | 
    
         | 
| 
       77 
77 
     | 
    
         
             
                # When EM stops, these assertions will be made.
         
     | 
| 
       78 
     | 
    
         
            -
                received_http.should  =~ messages
         
     | 
| 
       79 
78 
     | 
    
         
             
                received_ws.should    =~ messages
         
     | 
| 
      
 79 
     | 
    
         
            +
                received_http.should  =~ messages
         
     | 
| 
       80 
80 
     | 
    
         
             
              end
         
     | 
| 
       81 
81 
     | 
    
         
             
            end
         
     | 
    
        data/spec/lib/broker_spec.rb
    CHANGED
    
    | 
         @@ -1,30 +1,30 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            require 'spec_helper'
         
     | 
| 
      
 1 
     | 
    
         
            +
            # require 'spec_helper'
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
       3 
     | 
    
         
            -
            describe Firehose::Broker do
         
     | 
| 
       4 
     | 
    
         
            -
             
     | 
| 
      
 3 
     | 
    
         
            +
            # describe Firehose::Broker do
         
     | 
| 
      
 4 
     | 
    
         
            +
            #   include EM::TestHelper
         
     | 
| 
       5 
5 
     | 
    
         | 
| 
       6 
     | 
    
         
            -
             
     | 
| 
      
 6 
     | 
    
         
            +
            #   let(:broker)  { Firehose::Broker.new }
         
     | 
| 
       7 
7 
     | 
    
         | 
| 
       8 
     | 
    
         
            -
             
     | 
| 
       9 
     | 
    
         
            -
             
     | 
| 
      
 8 
     | 
    
         
            +
            #   it "should unsubscibe consumers and remove them from the collection" do
         
     | 
| 
      
 9 
     | 
    
         
            +
            #     stats = nil
         
     | 
| 
       10 
10 
     | 
    
         | 
| 
       11 
     | 
    
         
            -
             
     | 
| 
       12 
     | 
    
         
            -
             
     | 
| 
       13 
     | 
    
         
            -
             
     | 
| 
       14 
     | 
    
         
            -
             
     | 
| 
      
 11 
     | 
    
         
            +
            #     em do
         
     | 
| 
      
 12 
     | 
    
         
            +
            #       broker.consumer('1').subscribe_to('/the-channel')
         
     | 
| 
      
 13 
     | 
    
         
            +
            #       broker.consumer('2').subscribe_to('/the-channel')
         
     | 
| 
      
 14 
     | 
    
         
            +
            #       broker.consumer('2').subscribe_to('/a-channel')
         
     | 
| 
       15 
15 
     | 
    
         | 
| 
       16 
     | 
    
         
            -
             
     | 
| 
       17 
     | 
    
         
            -
             
     | 
| 
       18 
     | 
    
         
            -
             
     | 
| 
       19 
     | 
    
         
            -
             
     | 
| 
       20 
     | 
    
         
            -
             
     | 
| 
       21 
     | 
    
         
            -
             
     | 
| 
      
 16 
     | 
    
         
            +
            #       em.add_timer(1) do
         
     | 
| 
      
 17 
     | 
    
         
            +
            #         stats = broker.stats
         
     | 
| 
      
 18 
     | 
    
         
            +
            #         broker.stop
         
     | 
| 
      
 19 
     | 
    
         
            +
            #         em.stop
         
     | 
| 
      
 20 
     | 
    
         
            +
            #       end
         
     | 
| 
      
 21 
     | 
    
         
            +
            #     end
         
     | 
| 
       22 
22 
     | 
    
         | 
| 
       23 
     | 
    
         
            -
             
     | 
| 
       24 
     | 
    
         
            -
             
     | 
| 
       25 
     | 
    
         
            -
             
     | 
| 
       26 
     | 
    
         
            -
             
     | 
| 
      
 23 
     | 
    
         
            +
            #     stats.should == {
         
     | 
| 
      
 24 
     | 
    
         
            +
            #       '1' => {'subscriptions' => ['/the-channel'] },
         
     | 
| 
      
 25 
     | 
    
         
            +
            #       '2' => {'subscriptions' => ['/the-channel', '/a-channel']}
         
     | 
| 
      
 26 
     | 
    
         
            +
            #     }
         
     | 
| 
       27 
27 
     | 
    
         | 
| 
       28 
     | 
    
         
            -
             
     | 
| 
       29 
     | 
    
         
            -
             
     | 
| 
       30 
     | 
    
         
            -
            end
         
     | 
| 
      
 28 
     | 
    
         
            +
            #     broker.stats.should == {}
         
     | 
| 
      
 29 
     | 
    
         
            +
            #   end
         
     | 
| 
      
 30 
     | 
    
         
            +
            # end
         
     | 
    
        data/spec/lib/consumer_spec.rb
    CHANGED
    
    | 
         @@ -1,66 +1,66 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            require 'spec_helper'
         
     | 
| 
      
 1 
     | 
    
         
            +
            # require 'spec_helper'
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
       3 
     | 
    
         
            -
            describe Firehose::Consumer do
         
     | 
| 
       4 
     | 
    
         
            -
             
     | 
| 
      
 3 
     | 
    
         
            +
            # describe Firehose::Consumer do
         
     | 
| 
      
 4 
     | 
    
         
            +
            #   include EM::TestHelper
         
     | 
| 
       5 
5 
     | 
    
         | 
| 
       6 
     | 
    
         
            -
             
     | 
| 
       7 
     | 
    
         
            -
             
     | 
| 
       8 
     | 
    
         
            -
             
     | 
| 
       9 
     | 
    
         
            -
             
     | 
| 
      
 6 
     | 
    
         
            +
            #   let(:consumer)  { Firehose::Consumer.new }
         
     | 
| 
      
 7 
     | 
    
         
            +
            #   let(:publisher) { Firehose::Publisher.new }
         
     | 
| 
      
 8 
     | 
    
         
            +
            #   let(:channel)   { '/papa-smurf' }
         
     | 
| 
      
 9 
     | 
    
         
            +
            #   let(:another_channel)  { '/mama-smurf' }
         
     | 
| 
       10 
10 
     | 
    
         | 
| 
       11 
     | 
    
         
            -
             
     | 
| 
       12 
     | 
    
         
            -
             
     | 
| 
       13 
     | 
    
         
            -
             
     | 
| 
      
 11 
     | 
    
         
            +
            #   describe "subscriptions" do
         
     | 
| 
      
 12 
     | 
    
         
            +
            #     it "should subscribe to channel" do
         
     | 
| 
      
 13 
     | 
    
         
            +
            #       sent, recieved = 'hi', nil
         
     | 
| 
       14 
14 
     | 
    
         | 
| 
       15 
     | 
    
         
            -
             
     | 
| 
       16 
     | 
    
         
            -
             
     | 
| 
       17 
     | 
    
         
            -
             
     | 
| 
       18 
     | 
    
         
            -
             
     | 
| 
       19 
     | 
    
         
            -
             
     | 
| 
       20 
     | 
    
         
            -
             
     | 
| 
       21 
     | 
    
         
            -
             
     | 
| 
       22 
     | 
    
         
            -
             
     | 
| 
       23 
     | 
    
         
            -
             
     | 
| 
      
 15 
     | 
    
         
            +
            #       em do
         
     | 
| 
      
 16 
     | 
    
         
            +
            #         consumer.subscribe_to channel do |msg|
         
     | 
| 
      
 17 
     | 
    
         
            +
            #           recieved = msg
         
     | 
| 
      
 18 
     | 
    
         
            +
            #           em.stop
         
     | 
| 
      
 19 
     | 
    
         
            +
            #         end
         
     | 
| 
      
 20 
     | 
    
         
            +
            #         em.add_timer(1) do
         
     | 
| 
      
 21 
     | 
    
         
            +
            #           publisher.publish(channel, sent)
         
     | 
| 
      
 22 
     | 
    
         
            +
            #         end
         
     | 
| 
      
 23 
     | 
    
         
            +
            #       end
         
     | 
| 
       24 
24 
     | 
    
         | 
| 
       25 
     | 
    
         
            -
             
     | 
| 
       26 
     | 
    
         
            -
             
     | 
| 
      
 25 
     | 
    
         
            +
            #       recieved.should == sent
         
     | 
| 
      
 26 
     | 
    
         
            +
            #     end
         
     | 
| 
       27 
27 
     | 
    
         | 
| 
       28 
     | 
    
         
            -
             
     | 
| 
       29 
     | 
    
         
            -
             
     | 
| 
       30 
     | 
    
         
            -
             
     | 
| 
       31 
     | 
    
         
            -
             
     | 
| 
       32 
     | 
    
         
            -
             
     | 
| 
       33 
     | 
    
         
            -
             
     | 
| 
       34 
     | 
    
         
            -
             
     | 
| 
       35 
     | 
    
         
            -
             
     | 
| 
       36 
     | 
    
         
            -
             
     | 
| 
      
 28 
     | 
    
         
            +
            #     it "should track subscriptions" do
         
     | 
| 
      
 29 
     | 
    
         
            +
            #       lambda{
         
     | 
| 
      
 30 
     | 
    
         
            +
            #         em do
         
     | 
| 
      
 31 
     | 
    
         
            +
            #           consumer.subscribe_to channel
         
     | 
| 
      
 32 
     | 
    
         
            +
            #           consumer.subscribe_to another_channel
         
     | 
| 
      
 33 
     | 
    
         
            +
            #           em.add_timer(1){ em.stop }
         
     | 
| 
      
 34 
     | 
    
         
            +
            #         end
         
     | 
| 
      
 35 
     | 
    
         
            +
            #       }.should change{ consumer.subscriptions.size }.by(2)
         
     | 
| 
      
 36 
     | 
    
         
            +
            #     end
         
     | 
| 
       37 
37 
     | 
    
         | 
| 
       38 
     | 
    
         
            -
             
     | 
| 
       39 
     | 
    
         
            -
             
     | 
| 
       40 
     | 
    
         
            -
             
     | 
| 
       41 
     | 
    
         
            -
             
     | 
| 
       42 
     | 
    
         
            -
             
     | 
| 
       43 
     | 
    
         
            -
             
     | 
| 
       44 
     | 
    
         
            -
             
     | 
| 
       45 
     | 
    
         
            -
             
     | 
| 
      
 38 
     | 
    
         
            +
            #     it "should only allow one subscription per channel" do
         
     | 
| 
      
 39 
     | 
    
         
            +
            #       lambda{
         
     | 
| 
      
 40 
     | 
    
         
            +
            #         em do
         
     | 
| 
      
 41 
     | 
    
         
            +
            #           3.times { consumer.subscribe_to channel }
         
     | 
| 
      
 42 
     | 
    
         
            +
            #           em.add_timer(1){ em.stop }
         
     | 
| 
      
 43 
     | 
    
         
            +
            #         end
         
     | 
| 
      
 44 
     | 
    
         
            +
            #       }.should change{ consumer.subscriptions.size }.by(1)
         
     | 
| 
      
 45 
     | 
    
         
            +
            #     end
         
     | 
| 
       46 
46 
     | 
    
         | 
| 
       47 
     | 
    
         
            -
             
     | 
| 
       48 
     | 
    
         
            -
             
     | 
| 
      
 47 
     | 
    
         
            +
            #     it "should unsubscribe from all channels" do
         
     | 
| 
      
 48 
     | 
    
         
            +
            #       subscribed_count, after_unsubscribe_count = 0, nil
         
     | 
| 
       49 
49 
     | 
    
         | 
| 
       50 
     | 
    
         
            -
             
     | 
| 
       51 
     | 
    
         
            -
             
     | 
| 
       52 
     | 
    
         
            -
             
     | 
| 
       53 
     | 
    
         
            -
             
     | 
| 
       54 
     | 
    
         
            -
             
     | 
| 
       55 
     | 
    
         
            -
             
     | 
| 
       56 
     | 
    
         
            -
             
     | 
| 
       57 
     | 
    
         
            -
             
     | 
| 
       58 
     | 
    
         
            -
             
     | 
| 
       59 
     | 
    
         
            -
             
     | 
| 
       60 
     | 
    
         
            -
             
     | 
| 
      
 50 
     | 
    
         
            +
            #       em do
         
     | 
| 
      
 51 
     | 
    
         
            +
            #         consumer.subscribe_to channel
         
     | 
| 
      
 52 
     | 
    
         
            +
            #         consumer.subscribe_to another_channel
         
     | 
| 
      
 53 
     | 
    
         
            +
            #         subscribed_count = consumer.subscriptions.size
         
     | 
| 
      
 54 
     | 
    
         
            +
            #         em.add_timer(1) do 
         
     | 
| 
      
 55 
     | 
    
         
            +
            #           consumer.unsubscribe
         
     | 
| 
      
 56 
     | 
    
         
            +
            #           em.add_timer(1) do 
         
     | 
| 
      
 57 
     | 
    
         
            +
            #             em.stop
         
     | 
| 
      
 58 
     | 
    
         
            +
            #           end
         
     | 
| 
      
 59 
     | 
    
         
            +
            #         end
         
     | 
| 
      
 60 
     | 
    
         
            +
            #       end
         
     | 
| 
       61 
61 
     | 
    
         | 
| 
       62 
     | 
    
         
            -
             
     | 
| 
       63 
     | 
    
         
            -
             
     | 
| 
       64 
     | 
    
         
            -
             
     | 
| 
       65 
     | 
    
         
            -
             
     | 
| 
       66 
     | 
    
         
            -
            end
         
     | 
| 
      
 62 
     | 
    
         
            +
            #       subscribed_count.should == 2
         
     | 
| 
      
 63 
     | 
    
         
            +
            #       consumer.subscriptions.size.should == 0
         
     | 
| 
      
 64 
     | 
    
         
            +
            #     end
         
     | 
| 
      
 65 
     | 
    
         
            +
            #   end
         
     | 
| 
      
 66 
     | 
    
         
            +
            # end
         
     | 
    
        data/spec/spec_helper.rb
    CHANGED
    
    | 
         @@ -2,6 +2,7 @@ require 'logger' 
     | 
|
| 
       2 
2 
     | 
    
         
             
            require 'em-http'
         
     | 
| 
       3 
3 
     | 
    
         
             
            require 'em-websocket-client'
         
     | 
| 
       4 
4 
     | 
    
         | 
| 
      
 5 
     | 
    
         
            +
            # Skip logging if VERBOSE isn't set to true.
         
     | 
| 
       5 
6 
     | 
    
         
             
            require 'firehose'
         
     | 
| 
       6 
7 
     | 
    
         
             
            Firehose.logger = Logger.new('/dev/null') unless ENV['VERBOSE']
         
     | 
| 
       7 
8 
     | 
    
         | 
| 
         @@ -37,8 +38,4 @@ RSpec.configure do |config| 
     | 
|
| 
       37 
38 
     | 
    
         
             
              config.treat_symbols_as_metadata_keys_with_true_values = true
         
     | 
| 
       38 
39 
     | 
    
         
             
              config.run_all_when_everything_filtered = true
         
     | 
| 
       39 
40 
     | 
    
         
             
              config.filter_run :focus
         
     | 
| 
       40 
     | 
    
         
            -
              config.before(:each) do
         
     | 
| 
       41 
     | 
    
         
            -
                # For now, this resets the AMQP configuration between runs.
         
     | 
| 
       42 
     | 
    
         
            -
                Firehose.reset!
         
     | 
| 
       43 
     | 
    
         
            -
              end
         
     | 
| 
       44 
41 
     | 
    
         
             
            end
         
     | 
    
        metadata
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            --- !ruby/object:Gem::Specification
         
     | 
| 
       2 
2 
     | 
    
         
             
            name: firehose
         
     | 
| 
       3 
3 
     | 
    
         
             
            version: !ruby/object:Gem::Version
         
     | 
| 
       4 
     | 
    
         
            -
              version: 0.0 
     | 
| 
      
 4 
     | 
    
         
            +
              version: 0.1.0
         
     | 
| 
       5 
5 
     | 
    
         
             
              prerelease: 
         
     | 
| 
       6 
6 
     | 
    
         
             
            platform: ruby
         
     | 
| 
       7 
7 
     | 
    
         
             
            authors:
         
     | 
| 
         @@ -10,11 +10,11 @@ authors: 
     | 
|
| 
       10 
10 
     | 
    
         
             
            autorequire: 
         
     | 
| 
       11 
11 
     | 
    
         
             
            bindir: bin
         
     | 
| 
       12 
12 
     | 
    
         
             
            cert_chain: []
         
     | 
| 
       13 
     | 
    
         
            -
            date: 2012-05- 
     | 
| 
      
 13 
     | 
    
         
            +
            date: 2012-05-09 00:00:00.000000000 Z
         
     | 
| 
       14 
14 
     | 
    
         
             
            dependencies:
         
     | 
| 
       15 
15 
     | 
    
         
             
            - !ruby/object:Gem::Dependency
         
     | 
| 
       16 
16 
     | 
    
         
             
              name: eventmachine
         
     | 
| 
       17 
     | 
    
         
            -
              requirement: & 
     | 
| 
      
 17 
     | 
    
         
            +
              requirement: &70097385746880 !ruby/object:Gem::Requirement
         
     | 
| 
       18 
18 
     | 
    
         
             
                none: false
         
     | 
| 
       19 
19 
     | 
    
         
             
                requirements:
         
     | 
| 
       20 
20 
     | 
    
         
             
                - - ! '>='
         
     | 
| 
         @@ -22,21 +22,21 @@ dependencies: 
     | 
|
| 
       22 
22 
     | 
    
         
             
                    version: 1.0.0.beta
         
     | 
| 
       23 
23 
     | 
    
         
             
              type: :runtime
         
     | 
| 
       24 
24 
     | 
    
         
             
              prerelease: false
         
     | 
| 
       25 
     | 
    
         
            -
              version_requirements: * 
     | 
| 
      
 25 
     | 
    
         
            +
              version_requirements: *70097385746880
         
     | 
| 
       26 
26 
     | 
    
         
             
            - !ruby/object:Gem::Dependency
         
     | 
| 
       27 
     | 
    
         
            -
              name:  
     | 
| 
       28 
     | 
    
         
            -
              requirement: & 
     | 
| 
      
 27 
     | 
    
         
            +
              name: em-hiredis
         
     | 
| 
      
 28 
     | 
    
         
            +
              requirement: &70097385746460 !ruby/object:Gem::Requirement
         
     | 
| 
       29 
29 
     | 
    
         
             
                none: false
         
     | 
| 
       30 
30 
     | 
    
         
             
                requirements:
         
     | 
| 
       31 
31 
     | 
    
         
             
                - - ! '>='
         
     | 
| 
       32 
32 
     | 
    
         
             
                  - !ruby/object:Gem::Version
         
     | 
| 
       33 
     | 
    
         
            -
                    version: 0 
     | 
| 
      
 33 
     | 
    
         
            +
                    version: '0'
         
     | 
| 
       34 
34 
     | 
    
         
             
              type: :runtime
         
     | 
| 
       35 
35 
     | 
    
         
             
              prerelease: false
         
     | 
| 
       36 
     | 
    
         
            -
              version_requirements: * 
     | 
| 
      
 36 
     | 
    
         
            +
              version_requirements: *70097385746460
         
     | 
| 
       37 
37 
     | 
    
         
             
            - !ruby/object:Gem::Dependency
         
     | 
| 
       38 
38 
     | 
    
         
             
              name: thin
         
     | 
| 
       39 
     | 
    
         
            -
              requirement: & 
     | 
| 
      
 39 
     | 
    
         
            +
              requirement: &70097385746000 !ruby/object:Gem::Requirement
         
     | 
| 
       40 
40 
     | 
    
         
             
                none: false
         
     | 
| 
       41 
41 
     | 
    
         
             
                requirements:
         
     | 
| 
       42 
42 
     | 
    
         
             
                - - ! '>='
         
     | 
| 
         @@ -44,10 +44,10 @@ dependencies: 
     | 
|
| 
       44 
44 
     | 
    
         
             
                    version: '0'
         
     | 
| 
       45 
45 
     | 
    
         
             
              type: :runtime
         
     | 
| 
       46 
46 
     | 
    
         
             
              prerelease: false
         
     | 
| 
       47 
     | 
    
         
            -
              version_requirements: * 
     | 
| 
      
 47 
     | 
    
         
            +
              version_requirements: *70097385746000
         
     | 
| 
       48 
48 
     | 
    
         
             
            - !ruby/object:Gem::Dependency
         
     | 
| 
       49 
49 
     | 
    
         
             
              name: thor
         
     | 
| 
       50 
     | 
    
         
            -
              requirement: & 
     | 
| 
      
 50 
     | 
    
         
            +
              requirement: &70097385745580 !ruby/object:Gem::Requirement
         
     | 
| 
       51 
51 
     | 
    
         
             
                none: false
         
     | 
| 
       52 
52 
     | 
    
         
             
                requirements:
         
     | 
| 
       53 
53 
     | 
    
         
             
                - - ! '>='
         
     | 
| 
         @@ -55,10 +55,10 @@ dependencies: 
     | 
|
| 
       55 
55 
     | 
    
         
             
                    version: '0'
         
     | 
| 
       56 
56 
     | 
    
         
             
              type: :runtime
         
     | 
| 
       57 
57 
     | 
    
         
             
              prerelease: false
         
     | 
| 
       58 
     | 
    
         
            -
              version_requirements: * 
     | 
| 
      
 58 
     | 
    
         
            +
              version_requirements: *70097385745580
         
     | 
| 
       59 
59 
     | 
    
         
             
            - !ruby/object:Gem::Dependency
         
     | 
| 
       60 
60 
     | 
    
         
             
              name: faraday
         
     | 
| 
       61 
     | 
    
         
            -
              requirement: & 
     | 
| 
      
 61 
     | 
    
         
            +
              requirement: &70097385745160 !ruby/object:Gem::Requirement
         
     | 
| 
       62 
62 
     | 
    
         
             
                none: false
         
     | 
| 
       63 
63 
     | 
    
         
             
                requirements:
         
     | 
| 
       64 
64 
     | 
    
         
             
                - - ! '>='
         
     | 
| 
         @@ -66,10 +66,10 @@ dependencies: 
     | 
|
| 
       66 
66 
     | 
    
         
             
                    version: '0'
         
     | 
| 
       67 
67 
     | 
    
         
             
              type: :runtime
         
     | 
| 
       68 
68 
     | 
    
         
             
              prerelease: false
         
     | 
| 
       69 
     | 
    
         
            -
              version_requirements: * 
     | 
| 
      
 69 
     | 
    
         
            +
              version_requirements: *70097385745160
         
     | 
| 
       70 
70 
     | 
    
         
             
            - !ruby/object:Gem::Dependency
         
     | 
| 
       71 
71 
     | 
    
         
             
              name: websocket-rack
         
     | 
| 
       72 
     | 
    
         
            -
              requirement: & 
     | 
| 
      
 72 
     | 
    
         
            +
              requirement: &70097385744740 !ruby/object:Gem::Requirement
         
     | 
| 
       73 
73 
     | 
    
         
             
                none: false
         
     | 
| 
       74 
74 
     | 
    
         
             
                requirements:
         
     | 
| 
       75 
75 
     | 
    
         
             
                - - ! '>='
         
     | 
| 
         @@ -77,10 +77,10 @@ dependencies: 
     | 
|
| 
       77 
77 
     | 
    
         
             
                    version: '0'
         
     | 
| 
       78 
78 
     | 
    
         
             
              type: :runtime
         
     | 
| 
       79 
79 
     | 
    
         
             
              prerelease: false
         
     | 
| 
       80 
     | 
    
         
            -
              version_requirements: * 
     | 
| 
      
 80 
     | 
    
         
            +
              version_requirements: *70097385744740
         
     | 
| 
       81 
81 
     | 
    
         
             
            - !ruby/object:Gem::Dependency
         
     | 
| 
       82 
82 
     | 
    
         
             
              name: em-http-request
         
     | 
| 
       83 
     | 
    
         
            -
              requirement: & 
     | 
| 
      
 83 
     | 
    
         
            +
              requirement: &70097385744240 !ruby/object:Gem::Requirement
         
     | 
| 
       84 
84 
     | 
    
         
             
                none: false
         
     | 
| 
       85 
85 
     | 
    
         
             
                requirements:
         
     | 
| 
       86 
86 
     | 
    
         
             
                - - ~>
         
     | 
| 
         @@ -88,10 +88,10 @@ dependencies: 
     | 
|
| 
       88 
88 
     | 
    
         
             
                    version: 1.0.0
         
     | 
| 
       89 
89 
     | 
    
         
             
              type: :runtime
         
     | 
| 
       90 
90 
     | 
    
         
             
              prerelease: false
         
     | 
| 
       91 
     | 
    
         
            -
              version_requirements: * 
     | 
| 
      
 91 
     | 
    
         
            +
              version_requirements: *70097385744240
         
     | 
| 
       92 
92 
     | 
    
         
             
            - !ruby/object:Gem::Dependency
         
     | 
| 
       93 
93 
     | 
    
         
             
              name: rspec
         
     | 
| 
       94 
     | 
    
         
            -
              requirement: & 
     | 
| 
      
 94 
     | 
    
         
            +
              requirement: &70097385743820 !ruby/object:Gem::Requirement
         
     | 
| 
       95 
95 
     | 
    
         
             
                none: false
         
     | 
| 
       96 
96 
     | 
    
         
             
                requirements:
         
     | 
| 
       97 
97 
     | 
    
         
             
                - - ! '>='
         
     | 
| 
         @@ -99,10 +99,10 @@ dependencies: 
     | 
|
| 
       99 
99 
     | 
    
         
             
                    version: '0'
         
     | 
| 
       100 
100 
     | 
    
         
             
              type: :development
         
     | 
| 
       101 
101 
     | 
    
         
             
              prerelease: false
         
     | 
| 
       102 
     | 
    
         
            -
              version_requirements: * 
     | 
| 
      
 102 
     | 
    
         
            +
              version_requirements: *70097385743820
         
     | 
| 
       103 
103 
     | 
    
         
             
            - !ruby/object:Gem::Dependency
         
     | 
| 
       104 
104 
     | 
    
         
             
              name: webmock
         
     | 
| 
       105 
     | 
    
         
            -
              requirement: & 
     | 
| 
      
 105 
     | 
    
         
            +
              requirement: &70097385743360 !ruby/object:Gem::Requirement
         
     | 
| 
       106 
106 
     | 
    
         
             
                none: false
         
     | 
| 
       107 
107 
     | 
    
         
             
                requirements:
         
     | 
| 
       108 
108 
     | 
    
         
             
                - - ! '>='
         
     | 
| 
         @@ -110,10 +110,10 @@ dependencies: 
     | 
|
| 
       110 
110 
     | 
    
         
             
                    version: '0'
         
     | 
| 
       111 
111 
     | 
    
         
             
              type: :development
         
     | 
| 
       112 
112 
     | 
    
         
             
              prerelease: false
         
     | 
| 
       113 
     | 
    
         
            -
              version_requirements: * 
     | 
| 
      
 113 
     | 
    
         
            +
              version_requirements: *70097385743360
         
     | 
| 
       114 
114 
     | 
    
         
             
            - !ruby/object:Gem::Dependency
         
     | 
| 
       115 
115 
     | 
    
         
             
              name: guard-rspec
         
     | 
| 
       116 
     | 
    
         
            -
              requirement: & 
     | 
| 
      
 116 
     | 
    
         
            +
              requirement: &70097385742940 !ruby/object:Gem::Requirement
         
     | 
| 
       117 
117 
     | 
    
         
             
                none: false
         
     | 
| 
       118 
118 
     | 
    
         
             
                requirements:
         
     | 
| 
       119 
119 
     | 
    
         
             
                - - ! '>='
         
     | 
| 
         @@ -121,10 +121,10 @@ dependencies: 
     | 
|
| 
       121 
121 
     | 
    
         
             
                    version: '0'
         
     | 
| 
       122 
122 
     | 
    
         
             
              type: :development
         
     | 
| 
       123 
123 
     | 
    
         
             
              prerelease: false
         
     | 
| 
       124 
     | 
    
         
            -
              version_requirements: * 
     | 
| 
      
 124 
     | 
    
         
            +
              version_requirements: *70097385742940
         
     | 
| 
       125 
125 
     | 
    
         
             
            - !ruby/object:Gem::Dependency
         
     | 
| 
       126 
126 
     | 
    
         
             
              name: guard-bundler
         
     | 
| 
       127 
     | 
    
         
            -
              requirement: & 
     | 
| 
      
 127 
     | 
    
         
            +
              requirement: &70097385742520 !ruby/object:Gem::Requirement
         
     | 
| 
       128 
128 
     | 
    
         
             
                none: false
         
     | 
| 
       129 
129 
     | 
    
         
             
                requirements:
         
     | 
| 
       130 
130 
     | 
    
         
             
                - - ! '>='
         
     | 
| 
         @@ -132,10 +132,10 @@ dependencies: 
     | 
|
| 
       132 
132 
     | 
    
         
             
                    version: '0'
         
     | 
| 
       133 
133 
     | 
    
         
             
              type: :development
         
     | 
| 
       134 
134 
     | 
    
         
             
              prerelease: false
         
     | 
| 
       135 
     | 
    
         
            -
              version_requirements: * 
     | 
| 
      
 135 
     | 
    
         
            +
              version_requirements: *70097385742520
         
     | 
| 
       136 
136 
     | 
    
         
             
            - !ruby/object:Gem::Dependency
         
     | 
| 
       137 
137 
     | 
    
         
             
              name: guard-coffeescript
         
     | 
| 
       138 
     | 
    
         
            -
              requirement: & 
     | 
| 
      
 138 
     | 
    
         
            +
              requirement: &70097385742100 !ruby/object:Gem::Requirement
         
     | 
| 
       139 
139 
     | 
    
         
             
                none: false
         
     | 
| 
       140 
140 
     | 
    
         
             
                requirements:
         
     | 
| 
       141 
141 
     | 
    
         
             
                - - ! '>='
         
     | 
| 
         @@ -143,10 +143,10 @@ dependencies: 
     | 
|
| 
       143 
143 
     | 
    
         
             
                    version: '0'
         
     | 
| 
       144 
144 
     | 
    
         
             
              type: :development
         
     | 
| 
       145 
145 
     | 
    
         
             
              prerelease: false
         
     | 
| 
       146 
     | 
    
         
            -
              version_requirements: * 
     | 
| 
      
 146 
     | 
    
         
            +
              version_requirements: *70097385742100
         
     | 
| 
       147 
147 
     | 
    
         
             
            - !ruby/object:Gem::Dependency
         
     | 
| 
       148 
148 
     | 
    
         
             
              name: em-websocket-client
         
     | 
| 
       149 
     | 
    
         
            -
              requirement: & 
     | 
| 
      
 149 
     | 
    
         
            +
              requirement: &70097385741680 !ruby/object:Gem::Requirement
         
     | 
| 
       150 
150 
     | 
    
         
             
                none: false
         
     | 
| 
       151 
151 
     | 
    
         
             
                requirements:
         
     | 
| 
       152 
152 
     | 
    
         
             
                - - ! '>='
         
     | 
| 
         @@ -154,7 +154,7 @@ dependencies: 
     | 
|
| 
       154 
154 
     | 
    
         
             
                    version: '0'
         
     | 
| 
       155 
155 
     | 
    
         
             
              type: :development
         
     | 
| 
       156 
156 
     | 
    
         
             
              prerelease: false
         
     | 
| 
       157 
     | 
    
         
            -
              version_requirements: * 
     | 
| 
      
 157 
     | 
    
         
            +
              version_requirements: *70097385741680
         
     | 
| 
       158 
158 
     | 
    
         
             
            description: Firehose is a realtime web application toolkit for building realtime
         
     | 
| 
       159 
159 
     | 
    
         
             
              Ruby web applications.
         
     | 
| 
       160 
160 
     | 
    
         
             
            email:
         
     | 
| 
         @@ -184,16 +184,13 @@ files: 
     | 
|
| 
       184 
184 
     | 
    
         
             
            - lib/assets/javascripts/firehose/transport.js.coffee
         
     | 
| 
       185 
185 
     | 
    
         
             
            - lib/assets/javascripts/firehose/web_socket.js.coffee
         
     | 
| 
       186 
186 
     | 
    
         
             
            - lib/firehose.rb
         
     | 
| 
       187 
     | 
    
         
            -
            - lib/firehose/broker.rb
         
     | 
| 
       188 
187 
     | 
    
         
             
            - lib/firehose/cli.rb
         
     | 
| 
       189 
     | 
    
         
            -
            - lib/firehose/consumer.rb
         
     | 
| 
       190 
188 
     | 
    
         
             
            - lib/firehose/default.rb
         
     | 
| 
       191 
189 
     | 
    
         
             
            - lib/firehose/producer.rb
         
     | 
| 
       192 
190 
     | 
    
         
             
            - lib/firehose/publisher.rb
         
     | 
| 
       193 
191 
     | 
    
         
             
            - lib/firehose/rack.rb
         
     | 
| 
       194 
192 
     | 
    
         
             
            - lib/firehose/subscription.rb
         
     | 
| 
       195 
193 
     | 
    
         
             
            - lib/firehose/version.rb
         
     | 
| 
       196 
     | 
    
         
            -
            - spec/integrations/amqp_resources_spec.rb
         
     | 
| 
       197 
194 
     | 
    
         
             
            - spec/integrations/thin_spec.rb
         
     | 
| 
       198 
195 
     | 
    
         
             
            - spec/lib/broker_spec.rb
         
     | 
| 
       199 
196 
     | 
    
         
             
            - spec/lib/consumer_spec.rb
         
     | 
| 
         @@ -225,7 +222,6 @@ signing_key: 
     | 
|
| 
       225 
222 
     | 
    
         
             
            specification_version: 3
         
     | 
| 
       226 
223 
     | 
    
         
             
            summary: Build realtime Ruby web applications
         
     | 
| 
       227 
224 
     | 
    
         
             
            test_files:
         
     | 
| 
       228 
     | 
    
         
            -
            - spec/integrations/amqp_resources_spec.rb
         
     | 
| 
       229 
225 
     | 
    
         
             
            - spec/integrations/thin_spec.rb
         
     | 
| 
       230 
226 
     | 
    
         
             
            - spec/lib/broker_spec.rb
         
     | 
| 
       231 
227 
     | 
    
         
             
            - spec/lib/consumer_spec.rb
         
     | 
    
        data/lib/firehose/broker.rb
    DELETED
    
    | 
         @@ -1,34 +0,0 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            module Firehose
         
     | 
| 
       2 
     | 
    
         
            -
              # TODO add support to broker for publishing, then abstract this out into a backend. A
         
     | 
| 
       3 
     | 
    
         
            -
              # broker will eventually be passed into a web server front-end to serve up the web requests.
         
     | 
| 
       4 
     | 
    
         
            -
              class Broker
         
     | 
| 
       5 
     | 
    
         
            -
                def consumers
         
     | 
| 
       6 
     | 
    
         
            -
                  @consumers ||= Hash.new do |consumers, consumer_id|
         
     | 
| 
       7 
     | 
    
         
            -
                    consumer = Firehose::Consumer.new(consumer_id)
         
     | 
| 
       8 
     | 
    
         
            -
                    consumer.on_unsubscribe do
         
     | 
| 
       9 
     | 
    
         
            -
                      consumers.delete consumer_id
         
     | 
| 
       10 
     | 
    
         
            -
                    end
         
     | 
| 
       11 
     | 
    
         
            -
                    consumers[consumer_id] = consumer
         
     | 
| 
       12 
     | 
    
         
            -
                  end
         
     | 
| 
       13 
     | 
    
         
            -
                end
         
     | 
| 
       14 
     | 
    
         
            -
             
     | 
| 
       15 
     | 
    
         
            -
                # Don't like the [] syntax to get at consumers? No worries mate!
         
     | 
| 
       16 
     | 
    
         
            -
                def consumer(consumer_id)
         
     | 
| 
       17 
     | 
    
         
            -
                  consumers[consumer_id]
         
     | 
| 
       18 
     | 
    
         
            -
                end
         
     | 
| 
       19 
     | 
    
         
            -
             
     | 
| 
       20 
     | 
    
         
            -
                # Gracefully unsubscribe all of the consumers and get rid of them from the consumers 
         
     | 
| 
       21 
     | 
    
         
            -
                # collection.
         
     | 
| 
       22 
     | 
    
         
            -
                def stop
         
     | 
| 
       23 
     | 
    
         
            -
                  consumers.values.each(&:unsubscribe)
         
     | 
| 
       24 
     | 
    
         
            -
                end
         
     | 
| 
       25 
     | 
    
         
            -
             
     | 
| 
       26 
     | 
    
         
            -
                # Returns a hash of the connected consumers with the number of their subscriptions
         
     | 
| 
       27 
     | 
    
         
            -
                def stats
         
     | 
| 
       28 
     | 
    
         
            -
                  consumers.inject({}) do |memo, (consumer_id, consumer)|
         
     | 
| 
       29 
     | 
    
         
            -
                    memo[consumer_id] = { 'subscriptions' => consumer.subscriptions.keys }
         
     | 
| 
       30 
     | 
    
         
            -
                    memo
         
     | 
| 
       31 
     | 
    
         
            -
                  end
         
     | 
| 
       32 
     | 
    
         
            -
                end
         
     | 
| 
       33 
     | 
    
         
            -
              end
         
     | 
| 
       34 
     | 
    
         
            -
            end
         
     | 
    
        data/lib/firehose/consumer.rb
    DELETED
    
    | 
         @@ -1,49 +0,0 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            module Firehose
         
     | 
| 
       2 
     | 
    
         
            -
              class Consumer
         
     | 
| 
       3 
     | 
    
         
            -
                # Unique identifier for a consumer. Note that a consumer does not map directly to
         
     | 
| 
       4 
     | 
    
         
            -
                # a user_id. In a web browser world, you might have a user with multiple tabs open,
         
     | 
| 
       5 
     | 
    
         
            -
                # so you'll went to send each users tab a seperate message stream. Consider a convention
         
     | 
| 
       6 
     | 
    
         
            -
                # such as :user_id-:guid for your application.
         
     | 
| 
       7 
     | 
    
         
            -
                attr_reader :guid
         
     | 
| 
       8 
     | 
    
         
            -
             
     | 
| 
       9 
     | 
    
         
            -
                def initialize(guid = self.class.next_guid)
         
     | 
| 
       10 
     | 
    
         
            -
                  @guid = guid
         
     | 
| 
       11 
     | 
    
         
            -
                end
         
     | 
| 
       12 
     | 
    
         
            -
             
     | 
| 
       13 
     | 
    
         
            -
                # Create a subscription and subscribe to a channel.
         
     | 
| 
       14 
     | 
    
         
            -
                def subscribe_to(channel, &block)
         
     | 
| 
       15 
     | 
    
         
            -
                  subscriptions[channel].subscribe(&block)
         
     | 
| 
       16 
     | 
    
         
            -
                end
         
     | 
| 
       17 
     | 
    
         
            -
             
     | 
| 
       18 
     | 
    
         
            -
                # Active subscriptions to the backend.
         
     | 
| 
       19 
     | 
    
         
            -
                def subscriptions
         
     | 
| 
       20 
     | 
    
         
            -
                  @subscriptions ||= Hash.new do |subscriptions, channel|
         
     | 
| 
       21 
     | 
    
         
            -
                    # Setup the hash to generate subscriptions that can delete themselves from
         
     | 
| 
       22 
     | 
    
         
            -
                    # their own collection on an unsubscription event.
         
     | 
| 
       23 
     | 
    
         
            -
                    subscription = Subscription.new(self, channel)
         
     | 
| 
       24 
     | 
    
         
            -
                    subscription.on_unsubscribe do
         
     | 
| 
       25 
     | 
    
         
            -
                      # Remove the subscription from the consumer.subscriptions 
         
     | 
| 
       26 
     | 
    
         
            -
                      # list when unsubscribe.
         
     | 
| 
       27 
     | 
    
         
            -
                      subscriptions.delete channel
         
     | 
| 
       28 
     | 
    
         
            -
                    end
         
     | 
| 
       29 
     | 
    
         
            -
                    subscriptions[channel] = subscription
         
     | 
| 
       30 
     | 
    
         
            -
                  end
         
     | 
| 
       31 
     | 
    
         
            -
                end
         
     | 
| 
       32 
     | 
    
         
            -
             
     | 
| 
       33 
     | 
    
         
            -
                # Unsubscribe from all subscriptions.
         
     | 
| 
       34 
     | 
    
         
            -
                def unsubscribe
         
     | 
| 
       35 
     | 
    
         
            -
                  subscriptions.values.each(&:unsubscribe)
         
     | 
| 
       36 
     | 
    
         
            -
                  @on_unsubscribe.call(self) if @on_unsubscribe
         
     | 
| 
       37 
     | 
    
         
            -
                end
         
     | 
| 
       38 
     | 
    
         
            -
             
     | 
| 
       39 
     | 
    
         
            -
                # Define callback for when unsubscribe is called from the consumer.
         
     | 
| 
       40 
     | 
    
         
            -
                def on_unsubscribe(&block)
         
     | 
| 
       41 
     | 
    
         
            -
                  @on_unsubscribe = block
         
     | 
| 
       42 
     | 
    
         
            -
                end
         
     | 
| 
       43 
     | 
    
         
            -
             
     | 
| 
       44 
     | 
    
         
            -
              protected
         
     | 
| 
       45 
     | 
    
         
            -
                def self.next_guid
         
     | 
| 
       46 
     | 
    
         
            -
                  rand(999_999_999_999).to_s
         
     | 
| 
       47 
     | 
    
         
            -
                end
         
     | 
| 
       48 
     | 
    
         
            -
              end
         
     | 
| 
       49 
     | 
    
         
            -
            end
         
     | 
| 
         @@ -1,51 +0,0 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            require 'spec_helper'
         
     | 
| 
       2 
     | 
    
         
            -
             
     | 
| 
       3 
     | 
    
         
            -
            describe "Firehose amqp resources" do
         
     | 
| 
       4 
     | 
    
         
            -
             
     | 
| 
       5 
     | 
    
         
            -
              let(:channel)   { "/resource-test-#{Time.now.to_i}" }
         
     | 
| 
       6 
     | 
    
         
            -
              let(:consumer)  { Firehose::Consumer.new }
         
     | 
| 
       7 
     | 
    
         
            -
             
     | 
| 
       8 
     | 
    
         
            -
              it "should clean up exchanges and queues" do
         
     | 
| 
       9 
     | 
    
         
            -
                sent, received = 'howdy!', nil
         
     | 
| 
       10 
     | 
    
         
            -
                
         
     | 
| 
       11 
     | 
    
         
            -
                before_exchange_count = `rabbitmqctl list_exchanges`.lines.count
         
     | 
| 
       12 
     | 
    
         
            -
                before_queue_count    = `rabbitmqctl list_queues`.lines.count
         
     | 
| 
       13 
     | 
    
         
            -
             
     | 
| 
       14 
     | 
    
         
            -
                during_exchange_count = nil
         
     | 
| 
       15 
     | 
    
         
            -
                during_queue_count    = nil
         
     | 
| 
       16 
     | 
    
         
            -
             
     | 
| 
       17 
     | 
    
         
            -
                EM.run do
         
     | 
| 
       18 
     | 
    
         
            -
                  # Kill test if it runs longer than 5s
         
     | 
| 
       19 
     | 
    
         
            -
                  EM.add_timer(5) { EM.stop }
         
     | 
| 
       20 
     | 
    
         
            -
             
     | 
| 
       21 
     | 
    
         
            -
                  subscription = Firehose::Subscription.new(consumer, channel)
         
     | 
| 
       22 
     | 
    
         
            -
                  subscription.ttl = 1
         
     | 
| 
       23 
     | 
    
         
            -
             
     | 
| 
       24 
     | 
    
         
            -
                  subscription.subscribe do |payload|
         
     | 
| 
       25 
     | 
    
         
            -
                    received = payload
         
     | 
| 
       26 
     | 
    
         
            -
                    subscription.unsubscribe
         
     | 
| 
       27 
     | 
    
         
            -
             
     | 
| 
       28 
     | 
    
         
            -
                    during_exchange_count = `rabbitmqctl list_exchanges`.lines.count
         
     | 
| 
       29 
     | 
    
         
            -
                    during_queue_count = `rabbitmqctl list_queues`.lines.count
         
     | 
| 
       30 
     | 
    
         
            -
             
     | 
| 
       31 
     | 
    
         
            -
                    # I wait 1 second before killing em so that unsubscribe
         
     | 
| 
       32 
     | 
    
         
            -
                    # can talk to AMQP before the whole thing dies.
         
     | 
| 
       33 
     | 
    
         
            -
                    EM.add_timer(1){ EM.stop }
         
     | 
| 
       34 
     | 
    
         
            -
                  end
         
     | 
| 
       35 
     | 
    
         
            -
             
     | 
| 
       36 
     | 
    
         
            -
                  # Let the subscriber subscribe before publishing messages.
         
     | 
| 
       37 
     | 
    
         
            -
                  EM.add_timer(1){ Firehose::Publisher.new.publish(channel, sent) }
         
     | 
| 
       38 
     | 
    
         
            -
                end
         
     | 
| 
       39 
     | 
    
         
            -
             
     | 
| 
       40 
     | 
    
         
            -
                after_exchange_count = `rabbitmqctl list_exchanges`.lines.count
         
     | 
| 
       41 
     | 
    
         
            -
                after_queue_count =    `rabbitmqctl list_queues`.lines.count
         
     | 
| 
       42 
     | 
    
         
            -
             
     | 
| 
       43 
     | 
    
         
            -
                received.should == sent
         
     | 
| 
       44 
     | 
    
         
            -
             
     | 
| 
       45 
     | 
    
         
            -
                after_exchange_count.should == before_exchange_count
         
     | 
| 
       46 
     | 
    
         
            -
                after_queue_count.should    == before_queue_count
         
     | 
| 
       47 
     | 
    
         
            -
             
     | 
| 
       48 
     | 
    
         
            -
                during_exchange_count.should == before_exchange_count + 1
         
     | 
| 
       49 
     | 
    
         
            -
                during_queue_count.should    == before_queue_count + 1
         
     | 
| 
       50 
     | 
    
         
            -
              end
         
     | 
| 
       51 
     | 
    
         
            -
            end
         
     |