Jobler

Jobler helps you generate large reports or very large and slow pages through background jobs.

Install

Add it to your Gemfile and bundle it:

gem "jobler"

Install the migrations and run them:

rake railties:install:migrations
rake db:migrate

Add it to your routes:

mount Jobler::Engine => "/jobler"

Autoload the "Joblers" you are going to write through application.rb:

config.autoload_paths << Rails.root.join("app", "joblers")

Add a ApplicationJobler to follow the ApplicationController and ApplicationRecord pattern:

class ApplicationJobler < Jobler::BaseJobler
end

Jobler is going to queue its jobs through the ActiveJob queue called :jobler, so make sure a worker is listening to that queue. This is done like this in Sidekiq:

bundle exec sidekiq --queue default --queue jobler --queue mailers

Usage

Write a new Jobler located in "app/joblers":

class MyJobler < ApplicationJobler
  # This method will be executed in the background
  def execute!
    my_temp_file = Tempfile.new

    # Do some heavy lifting code wise...

    create_result!(name: "my-file", temp_file: my_temp_file)
  end

  # This method will be called from the web when the execute is completed and successful
  def result
    Jobler::FileDownload.new(
      file_name: "some-file.zip",
      temp_file: temp_file_for_result(name: "my-file")
    )
  end
end

You can do something like this in your controller:

class MyController < Jobler::BaseController
  def some_action
    scheduler = Jobler::JobScheduler.create! jobler_type: "MyJobler", job_args: {
      current_user_id: current_user.id,
      query_parameters: request.query_parameters
    }

    redirect_to jobler.job_path(scheduler.job)
  end
end

This will show a wait page and them a complete page with a download link, once the job is completed.

Rendering views

First add a special controller that your Jobler's can use:

class ApplicationJoblerController < ApplicationController
end

Then call render from within the execute method:

class TestRenderJobler < Jobler::BaseJobler
  def execute!
    create_result!(
      name: "render",
      content: render(:show)
    )
  end

  def result
    Jobler::RedirectTo.new(url: "/jobler_jobs/jobs/#{job.to_param}")
  end
end

This will render the view located at "app/joblers/test_render_jobler/show.*"

You should then create a controller something like this:

class JoblerJobsController < ApplicationController
  def show
    @job = Jobler::Job.find_by!(slug: params[:id])
    @result = @job.results.find_by!(name: "render")
  end
end

And a view in "app/views/jobler_jobs/show.html.erb":

<%= @result.result.force_encoding("utf-8").html_safe %>

You should also add a route like this:

Rails.application.routes.draw do
  resources :jobler_jobs, only: :show
end

Progress bar

In order to utilize the progress bar and return some feedback on the progress to the user, you can implement a couple of calls to do that:

class MyJobler < ApplicationJobler
  def execute!
    progress_total collection.size

    collection.find_each do |model|
      increment_progress!
    end
  end
end

You can also call it from a view, if you a doing a render like this:

<% jobler.increment_progress! %>

You can also specify a custom value if it isn't 1:

<% jobler.increment_progress!(value: 5.0) %>

License

This project rocks and uses MIT-LICENSE.