Toastr
Acts like standard cache, but instead of blocking and recalculating, it serves stale data and kicks off a job to refresh it in the background.
INSTALLATION
- Install the gem
gem 'toastr', github: 'drush/toastr'
- Run the generator
rails generate toastr:install
- Migrate your database
rake db:migrate
USAGE
- Define an instance method on an ActiveRecord.
- After defining the method, declare
has_toast :method_name
- Three options for expiring the cached data are available, shown below
class Oat < ActiveRecord::Base
has_many :related_records
def breakfast
sleep 2
{oat: :meal}
end
# by default only updates if this Oat instance is updated after the toast was calculated
has_toast :breakfast
def daily_report
sleep 2
{daily: :report}
end
# update if toast is older than 1 day
has_toast :daily_report, expire_in: 1.day
def arbitrary_expiration
sleep 2
{arbitrary: :expiration}
end
# update on arbitrary block. example updates toast if any of its :related_records
# have been updated since the toast was last updated
has_toast :special, expire_if: ->(toast) { toast.parent.related_records.pluck(:updated_at).max > toast.updated_at }
end
2.1.5 :001 > @oat = Oat.create
=> #<Oat id: 1, created_at: "2015-06-09 23:13:41", updated_at: "2015-06-09 23:13:41">
2.1.5 :002 > @oat.breakfast
=> {:error=>"Data not yet available"}
2.1.5 :003 > @oat.breakfast # after waiting enough time for the toast to calculate
=> {"oat"=>"meal", "toastr"=>{"elapsed"=>2.010508, "cached_at"=>"2015-06-09T16:14:25.701-07:00"}}
REPORTS
Generate a STI base class Toastr::Report that uses toastr
- Run the generator
rails generate toastr:reports
create db/migrate/20150701004451_create_toastr_reports.rb
create app/models/toastr/report.rb
- Migrate your database
rake db:migrate
- Define your own class and its
#build!
method
class MyReport < Toastr::Report
def build!
sleep 5
# :key parameter should be a hash of arguments to be used in query
self.key.stringify_keys
end
has_toast :build!
end
- Create a report with a :key parameter and call #build!
2.1.5 :001 > r = MyReport.create(key: {month: 'January', verbose: true})
=> #<MyReport id: 2, type: "MyReport", key: {:month=>"January", :verbose=>true}, created_at: "2015-07-01 00:56:16", updated_at: "2015-07-01 00:56:16">
2.1.5 :002 > r.build! # blocks and runs for 5 seconds if Rails.application.config.active_job.queue_adapter == :inline
=> "{\"month\"=>\"January\", \"verbose\"=>true, :toastr=>{:elapsed=>5.007665, :cached_at=>2015-06-30 18:07:42 -0700}}"
TODO
- Extended test coverage
- Reports test coverage
- How to resolve conflicts between job_state and cache_state when errors occur
CHANGELOG
- Installation generator
- Basic test coverage
- Adopt ActiveJob support instead of DJ
This project rocks and uses MIT-LICENSE.