Search code examples
ruby-on-railsrubytwiliosucker-punch

flash error from delayed job in rails


In my rails app, I'm running delayed jobs with Sucker Punch gem. I'm looping through some phone numbers sending an sms message to each with Twilio.

If there is an error with Twilio sending it, I can capture it with a rescue just fine, but is there a way to notify the user without just raising an exception showing an error page?

Ideally, it would be as a flash message (or other notification) when it happens. Is there some way to do this?


Solution

  • As mentioned in the chat by Nick Veys, the solution to this will be to create some sort of persistence with the data.

    --

    Push

    I think the functionality you're looking for will be achieved by using some sort of "push" technology, notably Pusher. This will give you the ability to create a set of files / settings which will provide the ability for the message to appear for the user.

    I would personally set up the following:

    1. DelayedJob processes requests as required
    2. If the message raised an error, store the error
    3. Send the error message to the user with the push implementation

    So I would do this:

    #Gemfile
    gem 'aasm', '~> 3.3.3'
    gem 'pusher', '~> 0.14.1'
    
    #app/models/message.rb
    class Message < ActiveRecord::Base
       include AASM
    
       aasm do
         state :queued, :initial => true
         state :sending
         state :delivered
         state :error
    
         event :sent do
           transitions :from => [:queued, :sending], :to => :sent
         end
    
         event :raise_error do
           transition :from => [:queued, :sending], :to => :error, :on_transition => Proc.new {|obj, *args| obj.set_message(*args) }
         end
    
         def set_message(message)
            self.update(error: message)
            Redis.lpush(self.user.id, self.id)
         end
       end
    
    end
    

    This means that if you attempt to send the message, you'll be able to update the message as follows:

    @message = Message.find params[:id]
    if [[message.send_error]]
        @message.raise_error(error.message)
    end
    

    This means that you'll then be able to append the messages into a Redis instance, which you can then cycle through with Pusher:

    --

    Pusher

    The value of this will be that when you use a front-end notification service such as Pusher, what you're really doing is taking data from your application, and sending it via a websocket, or maybe SSE, to the front-end browser

    This is really what you need -- the ability to display the message for the user as / when it happens. To do this, I would use the following:

    #app/assets/javascripts/application.js
    pusher = new Pusher("**************")
    
    channel = pusher.subscribe("private-user-[[username]]")             
    channel.bind "error", (data) ->
        alert data.message
    

    This will give you the ability to process the Redis queue you made, allowing you to send the requested updates as they are made:

    #app/workers/pusher.rb
    class PusherQueue
       @queue = :pusher
       def self.perform(user, message)
           # push message here
       end
    end
    

    This connects with the Pusher service, allowing you to "listen" for the updates you'll receive from the server. This means that if you are able to populate these updates (error messages) using the above code, you'll be able to send the errors to the users as they interact with your app

    You read up more about Pusher / push technologies here: