Skip to content

Instantly share code, notes, and snippets.

@ianneub
Last active December 24, 2015 16:08
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save ianneub/6468925 to your computer and use it in GitHub Desktop.
Save ianneub/6468925 to your computer and use it in GitHub Desktop.
First attempt at a database backed clockwork scheduler. This was made against Rails 4 with a Job model that can be created like so: rails g model Job period:string name:string code:text See: https://github.com/tomykaira/clockwork/issues/25
# version 1
require 'clockwork'
require './config/boot'
require './config/environment'
module DBBackedClockwork
extend Clockwork
# add a periodic job to update @@events
# I think a thread is too complex
configure do |config|
config[:sleep_timeout] = 1
config[:tz] = 'Pacific Time (US & Canada)'
config[:max_threads] = 15
end
every 1.minute, "update jobs" do
db_events.each do |event|
event.update_from_db
end
# add database events
Job.all.each do |e|
events << DBBackedEvent.new(e) unless db_events.map(&:id).include?(e.id)
end
end
# get the manager object
def self.manager
Clockwork.class_variable_get(:@@manager)
end
# get the events array
def self.events
manager.instance_variable_get(:@events)
end
# get the db backed events from the events array
# NOTE: this creates a new array and is not associated to the "official" events instance variable
def self.db_events
events.reject{|e| !e.respond_to?(:id) }
end
class DBBackedEvent < Clockwork::Event
# add @id tagged to DB to update a job
attr_accessor :id, :updated_at
def initialize(event)
id = event.id
updated_at = event.updated_at
super(DBBackedClockwork.manager, eval(event.period), event.name, Proc.new {eval event.code})
end
# find the job in the database and update or remove it if necessary
def update_from_db
begin
job = Job.find(id)
if job.updated_at != updated_at
self.remove
DBBackedClockwork.events << DBBackedEvent.new(job)
end
rescue ActiveRecord::RecordNotFound
# remove the event
self.remove
end
end
# remove this event from the events array
def remove
DBBackedClockwork.events.reject!{|e| e.id == id rescue false}
end
end
end
# version 2
# this allows me to create jobs with an array of at times, version 1 would not
require 'clockwork'
require './config/boot'
require './config/environment'
module DBBackedClockwork
extend Clockwork
configure do |config|
config[:sleep_timeout] = 1
config[:tz] = 'America/Los_Angeles'
config[:max_threads] = 15
end
every 1.minute, "update db jobs" do
events.each do |event|
# if job has changed then delete event and reload it
if event.id
begin
job = Job.find(event.id)
if job.updated_at != event.updated_at
remove_event event
create_from_db job
end
rescue ActiveRecord::RecordNotFound
# remove job
remove_event event
end
end
end
# add database events
Job.all.each do |e|
unless events.map(&:id).include?(e.id)
create_from_db(e)
end
end
end
def self.remove_event(event)
puts "Removing #{event.job}..."
events.reject!{|e| e.id == event.id rescue false}
end
def self.create_from_db(event)
puts "Adding #{event.name}..."
options = Hash.new
options[:tz] = 'America/Los_Angeles'
options[:at] = event.at.split(',') if event.at
jobs = every eval("#{event.period}.#{event.period_type}"), event.name, options do
# do some work
end
jobs = [jobs] # ensure jobs is an array
jobs.flatten.each do |job|
job.id = event.id
job.updated_at = event.updated_at
end
jobs
end
# get the manager object
def self.manager
Clockwork.class_variable_get(:@@manager)
end
# get the events array
def self.events
manager.instance_variable_get(:@events)
end
class Clockwork::Event
attr_accessor :id, :updated_at
end
class Clockwork::Manager
def every_with_multiple_times(period, job, options={}, &block)
events = Array.new
each_options = options.clone
options[:at].each do |at|
each_options[:at] = at
events << register(period, job, block, each_options)
end
events
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment