Skip to content

Rails engine to persist job results and notify job status changes

License

Notifications You must be signed in to change notification settings

platanus/job_notifier

Repository files navigation

Job Notifier

Gem Version Build Status Coverage Status

It's a Rails engine built on top of Active Job in order to improve user experience related to background processes. To achieve this goal, the gem allows us to:

  • Relate jobs with entities

Using Active Job, you can perform background jobs but, you can not relate them with, for example, a logged user. Job Notifier allows you to connect an entity with a job easily.

  • Persist job results

Using Active Job, you can run jobs in background but, you are not be able to store the result of those jobs. This is a desired behavior if you, for example, want to provide validation feedback to users.

  • Notify users when their jobs change state.

Using Active Job, you can run jobs in background but, you lose a real time response. To bring a solution to this issue, Job Notifier, through polling technique, gives you a way to know what's happening with your jobs.

Installation

Add to your Gemfile:

gem "job_notifier"
bundle install

Run the installer:

rails generate job_notifier:install

The installer will copy /config/initializers/job_notifier.rb. There, you can change the default configuration.

Then, include notifier.js where you go to use it. For example, in: /app/assets/javascripts/application.js

//= require job_notifier/notifier

Flow

  1. An entity (current_user for example), creates a job.
  2. The job related to the current_user is queued with "pending" status.
  3. The js code stored on notifier.js executes, using the polling technique, a request asking for the jobs related to the current_user. The result is an array containing the created job with "pending" status.
  4. After a few seconds, a new request is performed but, this time, it returns an empty array. This occurs because the created job didn't change its status.
  5. After a couple of request with empty arrays, an object its returned with "finished" or "failed" status and a result attribute containing feedback.

Usage

First, relate jobs with an entity (User for example). To do this, include the JobNotifier::Identifier mixin in your entity class and execute the identify_job_through method, passing the attributes you want to use to build the key to identify jobs for the entity. For example, if you have defined the User class like this,

class User < ActiveRecord::Base
  include JobNotifier::Identifier

  identify_job_through :id, :email
end

user = User.create!(email: "leandro@platan.us")
# => #<User:0x007f877ef5c2a8 id: 1, email: "leandro@platan.us">

the jobs for user will be identified by this code: "1b1bcc253675df5eb91603dbda06af11". (This is result of: Digest::MD5.hexdigest("email::leandro@platan.us::id::1"), if you wanna know)

Second, pass to notifier.js the identifier for that user. This is because the timed requests (polling) needs to know from what user bring the data. To do this, you can use the helper shipped in the gem:

<body <%= job_identifier_for(@current_user) %>>
<!-- Something like this is what the previous code produces -->
<body data-identifier="1b1bcc253675df5eb91603dbda06af11" data-root-url="/">

I'm assuming you have a current_user instance of User class.

The previous code, takes the identifier from @current_user and sets a data attribute, on body tag, that will be used in somewhere of notifier.js to perform requests with a valid identifier.

To catch the response of polling requests, you can set a callback function:

JobNotifier.onNotify = function(data) {
  console.info('On notify', data);
};

Now, you are be able to get feedback of your jobs but, you don't have any! so, the Third step is to define a job.

class MyJob < ApplicationJob
  def perform_with_feedback(param1, param2)
    MyService.run(param1, param2)
  end
end

As you can see, the only difference with a regular ActiveJob's job is that you have perform_with_feedback method instead of perform method. Inside perform_with_feedback you need to return the "success response" of your job. And, to save the "error response", you need to raise a JobNotifier::Error::Validation exception. Let's see MyService#run method definition to make it clear.

class MyService
  def self.run(param1, param2)
    return "SUCCESS!!!" if param1 == param2
    raise JobNotifier::Error::Validation.new(error: "ERROR!!!")
  end
end

If you define perform instead of perform_with_feedback, ActiveJob will work as always do.

The Fourth step is queue a new job. Executing JobNotifier::Job.where(identifier: current_user.job_identifier) you can see jobs related with the passed identifier. So, according to MyService#run definition:

The following code: MyJob.perform_later(current_user.job_identifier, "lean", "lean") will create a new JobNotifier::Job instance with:

  • result: nil
  • state: "pending"

After execution, the job will have:

  • result: "SUCCESS!!!"
  • state: "finished"

If this code MyJob.perform_later(current_user.job_identifier, "lean", "leandro") is executed instead of the previous one, the result will be:

  • result: "ERROR!!!"
  • state: "failed"

Non Rails Client App

If you are building an API or your client app is not part of the project you have installed this gem, you will need:

  • To include the notifier.js using this url: http://your_app.platan.us/job_notifier/adapters/notifier
  • To mimic the job_identifier_for functionality, passing data-identifier and data-root-url
<body data-identifier="1b1bcc253675df5eb91603dbda06af11" data-root-url="http://your_app.platan.us/">

You can get the identifier executing in server side something like this: @current_user.job_identifier (Remember, I'm assuming you have a @current_user instance of User class that includes the JobNotifier::Identifier mixin).

The root url is your server url (where Job Notifier gem was installed).

Publishing

On master/main branch...

  1. Change VERSION in lib/job_notifier/version.rb.
  2. Change Unreleased title to current version in CHANGELOG.md.
  3. Commit new release. For example: Releasing v0.1.0.
  4. Create tag. For example: git tag v0.1.0.
  5. Push tag. For example: git push origin v0.1.0.

Contributing

  1. Fork it
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create new Pull Request

Credits

Thank you contributors!

Platanus

Job Notifier is maintained by platanus.

License

Job Notifier is © 2016 platanus, spa. It is free software and may be redistributed under the terms specified in the LICENSE file.