Using EventSource as remote:true

When Rails introduced remote:true option for links (AJAX requests) I changed my view on how to handle javascript on my websites. I’m playing with streaming in Rails 4.1 (ActionController::Live) and I realized that a similar solution is needed for handling server events (EventSource).

Case:

My website displays a list of tweets (http://localhost:3000/tweets). Every X seconds a new post is added to the list.

Solution:

Let’s forget about live updates for a moment and imagine I would add a form above the list where I could write a tweet and post it to the server (POST /tweets) with <form … remote:true>. In this case the create.js action view will execute a javascript code (that I would define) for adding a new tweet to the list (as described here: http://edgeguides.rubyonrails.org/contributing_to_ruby_on_rails.html#reporting-an-issue). The logic for live updates should be very similar. The “only difference” in this case is that the create.js action is executed multiple times without refreshing the page.

I built the server events logic similar to railscasts episode 401. When a new tweet is created send the create.js code to $redis channel, the content is sent to a client over /server_events controller-action and the code is evaluate with jquery.

SERVER

GET /server_events

def server_events

response.headers[“Content-Type”] = “text/event-stream”

redis = Redis.new

redis.psubscribe(“tweets.create”) do |on|

  on.pmessage do |pattern, event, data|

     response.stream.write("id: #{Time.now.to_i}\n")

     response.stream.write("event: #{event}\n")

     response.stream.write("data: #{data}\n\n")

  end

end

rescue IOError

logger.info "Stream closed"

ensure

redis.quit

response.stream.close

end

POST /tweets

def create

$redis.publish(“tweets.create”, render_to_string(“tweets/create.js”, layout: false, item: @tweet).to_json)

end

CLIENT

sse = new EventSource(’/server_events’)

sse.addEventListener ‘tweets.create’, (e) ->

$.globalEval($.parseJSON(e.data))

What do you think? Suggestions?

I’m not clear what you’re proposing to add to Rails. Remote requests do 2 things:

  1. Send request to server via XHR, and

  2. Receive (and possibly run) response from the server.

The remote helpers in Rails just hook up DOM elements, links and forms, to trigger (1), which will then trigger (2). Server-sent events are basically just (2), but without the need to be triggered by (1). Because (1) isn’t needed with SSE (or EventSource on the JavaScript side), there’s not as tangible a need for similar Rails view helpers that I can see.

From your example, it seems like you’re just trying to publish some JSON from a template from SSE. Instead of rendering a JS file to string and then coercing that to JSON, couldn’t you instead render a json.erb template file?

That said, I’m not extremely familiar with redis publish/psubscribe, so maybe I’m misunderstanding what you’re proposing.

– Steve Schwartz