Using Private-pub with channels that have segment keys and coffeescript variant

1.2k views Asked by At

I'm trying to replicate a push notification system similar to facebook's using private_pub. Ideally I would want to link this to show notifications using a gem such as gritter (other gem suggestions are welcome)

Whenever a certain action from a controller is called, I want to send a notification to all subscribers that are part of a specific id. As long you are logged in, you are subscribed to the channel, achieved by putting the subscribe_to in the layouts.

in the view:

<%= subscribe_to "/messages/#{@group_id}" %>

in the controller

PrivatePub.publish_to("/messages/#{@group_id}", "alert('test')")

this works just fine, however I would like to have something more sophisticated than an alert as a response (such as a gritter notification), so instead:

PrivatePub.publish_to("/messages/#{@group_id}", data: @some_data)

Following the tutorial, they use coffeescript for this. However, I cannot get the simple alert going (probably due to the id in the channel)

In this question, the OP was able to solve this using a js.erb view. But I can't get it to work.

disclaimer: my js and coffeescript knowledge is almost zero.

Any help is appreciated :)


EDIT

Some more info: I've a method in a controller that's part of a public API, and expects POST request. If everything is ok it sends out a JSON success response. Aside from this, the same method sends a notification to all users of a specific group.

I've actually managed to get this working, putting this in the controller:

callback method:

respond_to do |format|
    format.js #-> calls callback.js.erb
    #format.json { render json: {"success" => true}.to_json }
end

and putting the gritter stuff in my_api_controller/callback.js.erb:

<% publish_to "/messages/#{@group_id}" do %>
    <%= add_gritter(
    "Nova " + link_to("reserva", reservation_path(@r)) + " de #{@channel} para " + 
    link_to(@hostel_name, hostel_path(@hostel_id)),
    :title => "Nova reserva!",
    :sticky => true,
    :image => :notice
) %>
<% end %>

note: since the subscription to the channel is done in every view (through the layout), you can receive a notification on any page/view

My problem at the momento is, as you can guess, the JSON response. Since I cant render two responses, only the js.erb is called, but the JSON response is never sent

3

There are 3 answers

0
Cristiano Sousa On BEST ANSWER

I was able to accomplish this by directly adding the gritter script in the publish_to method of Privat pub.

In my controller:

PrivatePub.publish_to 
  "/some/URI/#{entity.id}"
 ,"jQuery.gritter.add({
      image: '#{ActionController::Base.helpers.asset_path('notice.png')}'
    , sticky: true
    ,title:'#{t('some_title')}'
    , text: '#{t('some text'}' 
});"

render json: {"error"=>{"code"=>20,"msg"=>e.message},"success" => false}.to_json

Basically, I was able to publish to PrivatePub witouth resorting to the html response, wich enabled me to return a JSON response as intended.

1
Richard Peck On

Although I've not got much experience with this gem, here's something which may help:


JS

Client-side, your JS is basically running an eventlistener on the private_pub object (defined when you include the private_pub / gritter JS on your page), which you can use to perform other actions (call alerts, append data to page, etc)

It seems your back-end is working, it's just the receipt & processing of the data from the server you're struggling with. To remedy this, you can do 2 things: 1) run a standard JS call from application.js or run a js file from your controller action:


Controller

According to the private_pub documentation, you should do this to create a JS file:

#app/controllers/your_controller.rb
def create
    @message = "Hello"

    respond_to do |format| 
        format.html { PrivatePub.publish_to("/messages/#{@group_id}", "alert('test')") }
        format.js #-> calls create.js.erb
    end
end 

#app/views/your_controller/create.js.erb
<% publish_to "/messages/new" do %>
  $("#chat").append("<%= j render(@messages) %>");
<% end %>

Browser

#app/assets/javascripts/application.js.coffee
PrivatePub.subscribe("/messages/new", (data, channel) ->
      alert data.message.content
0
przbadu On

I guess, you can handle your problem with the help of gon gem like below:

In view

<%= subscribe_to "/messages/#{@group_id}" %>

In controller

gon.group_id = @group_id
PrivatePub.publish_to("/messages/#{@group_id}", message: @message)

In messages.coffee

if gon.group_id
  PrivatePub.subscribe "/messages/#{gon.group_id}", (data, channel) ->
    jQuery.gritter.add
      image: '/assets/notice.png'
      title: 'Notification!'
      text: data.message.content

But, gon.group_id can make trouble sometimes so you need to take care of that.

So, I recommend to use js.erb which is easy and we can access to controller's variable easily in js.erb file.

Hope that answer your problem.