module Redisse

Public: A HTTP API to serve Server-Sent Events via a Redis backend.

Constants

TestEvent

Define then reopen instead of using the block of Struct.new for YARD.

VERSION

Attributes

default_port[RW]

Public: The port on which the server listens.

Defaults to the REDISSE_PORT environment variable and if it is not set, to 8080.

nginx_internal_url[RW]

Public: The internal URL hierarchy to redirect to with X-Accel-Redirect.

When this property is set, Redisse will work totally differently. Your Ruby code will not be loaded by the events server itself, but only by the {#redirect_endpoint} Rack app that you will have to route to in your Rack app (e.g. using map in config.ru) and this endpoint will redirect to this internal URL hierarchy.

Defaults to /redisse.

redis_server[RW]

Public: Gets/Sets the String URL of the Redis server to connect to.

Note that while the Redis pubsub mechanism works outside of the Redis key namespace and ignores the database (the path part of the URL), the database will still be used to store an history of the events sent to support Last-Event-Id.

Defaults to the REDISSE_REDIS environment variable and if it is not set, to redis://localhost:6379/.

Public Class Methods

channels(*, &block) click to toggle source

Public: Define the list of channels to subscribe to.

Calls the given block with a Rack environment, the block is expected to return a list of channels the current user has access to. The list is then coerced using +Kernel#Array+.

Once the block is defined, other calls will be handled by the block directly, as if the method had been redefined directly. It simply gives a nicer API:

Redisse.channels do |env|
end

vs

def Redisse.channels(env)
end

block - The block that lists the channels for the given Rack environment.

Examples

Redisse.channels do |env|
  %w( comment post )
end
# will result in subscriptions to 'comment' and 'post' channels.

Redisse.channels({})
# => ["comment", "post"]
Calls superclass method
# File lib/redisse/configuration.rb, line 33
def self.channels(*, &block)
  if block
    # overwrite method with block
    define_singleton_method :channels, &block
  else
    super
  end
end

Public Instance Methods

channels(env) click to toggle source

Public: The list of channels to subscribe to.

Once {Redisse.channels} has been called, the given block is this method. The block must satisfy this interface:

env - The Rack environment for this request.

Returns an Array of String naming the channels to subscribe to.

Raises NotImplementedError unless {Redisse.channels} has been called.

# File lib/redisse.rb, line 74
def channels(env)
  raise NotImplementedError, "you must call Redisse.channels first"
end
middlewares() click to toggle source

Internal: List of middlewares defined with {#use}.

Used by Goliath to build the server.

# File lib/redisse.rb, line 127
def middlewares
  @middlewares ||= []
end
plugin(name, *args) click to toggle source

Public: Define a Goliath plugin to run with the server.

See {github.com/postrank-labs/goliath/wiki/Plugins Goliath plugins}.

# File lib/redisse.rb, line 145
def plugin(name, *args)
  plugins << [name, args]
end
publish(channel, message) click to toggle source

Public: Send an event to subscribers, of the given type.

All browsers subscribing to the events server will receive a Server-Sent Event of the chosen type.

channel - The channel to publish the message to. type_message - The type of the event and the content of the message, as a

Hash of form { type => message } or simply the message as
a String, for the default event type :message.

Examples

Redisse.publish(:global, notice: 'This is a server-sent event.')
Redisse.publish(:global, 'Hello, World!')

# on the browser side:
var source = new EventSource(eventsURL);
source.addEventListener('notice', function(e) {
  console.log(e.data) // logs 'This is a server-sent event.'
}, false)
source.addEventListener('message', function(e) {
  console.log(e.data) // logs 'Hello, World!'
}, false)
# File lib/redisse.rb, line 58
def publish(channel, message)
  type, message = Hash(message).first if message.respond_to?(:to_h)
  type ||= :message
  publisher.publish(channel, message, type)
end
published() click to toggle source

Public: Returns the published events.

Fails unless {#test_mode!} is set.

# File lib/redisse.rb, line 119
def published
  fail "Call #{self}.test_mode! first" unless publisher.respond_to?(:published)
  publisher.published
end
redirect_endpoint() click to toggle source

Public: The Rack application that redirects to {#nginx_internal_url}.

If you set {#nginx_internal_url}, you need to call this Rack application to redirect to the Redisse server.

Also note that when using the redirect endpoint, two channel names are reserved, and cannot be used: polling and lastEventId.

Examples

map "/events" { run Redisse.redirect_endpoint }
# File lib/redisse.rb, line 160
def redirect_endpoint
  @redirect_endpoint ||= RedirectEndpoint.new self
end
run() click to toggle source

Public: Run the server.

If you use the provided binary you don’t need to call this method.

By default, the {#channels} method is called directly.

If {#nginx_internal_url} is set, the channels will actually come from the internal redirect URL generated in the Rack app by {#redirect_endpoint}.

# File lib/redisse/server.rb, line 17
def run
  run_as_standalone if nginx_internal_url
  server = Server.new(self)
  runner = Goliath::Runner.new(ARGV, server)
  runner.app = Goliath::Rack::Builder.build(self, server)
  runner.load_plugins([Server::Stats] + plugins)
  runner.run
end
test_filter=(filter) click to toggle source

Public: Filter events stored in test mode.

If set, only events whose type match with the filter are stored in {#published}. A filter matches by using case equality, which allows using a simple Symbol or a Proc for more advanced filters:

Automatically sets {#test_mode!}, so it also clears the previous events.

Examples

Redisse.test_filter = -> type { %i(foo baz).include? type }
Redisse.publish :global, foo: 'stored'
Redisse.publish :global, bar: 'skipped'
Redisse.publish :global, baz: 'stored'
Redisse.published.size # => 2
# File lib/redisse.rb, line 111
def test_filter=(filter)
  test_mode!
  publisher.filter = filter
end
test_mode!() click to toggle source

Public: Use test mode.

Instead of actually publishing to Redis, events will be stored in {#published} to use for tests.

Must be called before each test in order for published events to be emptied.

See also {#test_filter=}.

Examples

# RSpec
before { Redisse.test_mode! }
# File lib/redisse.rb, line 92
def test_mode!
  @publisher = TestPublisher.new
end
use(middleware, *args, &block) click to toggle source

Public: Define a middleware for the server.

See {github.com/postrank-labs/goliath/wiki/Middleware Goliath middlewares}.

Examples

Redisse.use MyMiddleware, foo: true
# File lib/redisse.rb, line 138
def use(middleware, *args, &block)
  middlewares << [middleware, args, block]
end

Private Instance Methods

plugins() click to toggle source
# File lib/redisse.rb, line 168
def plugins
  @plugins ||= []
end
publisher() click to toggle source
# File lib/redisse.rb, line 172
def publisher
  @publisher ||= RedisPublisher.new(redis)
end
redis() click to toggle source
# File lib/redisse.rb, line 176
def redis
  @redis ||= Redis.new(url: redis_server)
end
run_as_standalone() click to toggle source

Internal: Redefine {#channels} to find channels in the redirect URL.

# File lib/redisse/server.rb, line 29
def run_as_standalone
  channels do |env|
    query_string = env['QUERY_STRING'] || ''
    channels = query_string.split('&').map { |channel|
      URI.decode_www_form_component(channel)
    }
    channels.delete('polling')
    channels.delete_if {|channel| channel.start_with?('lastEventId=') }
  end
end