Skip to content

Commit

Permalink
saner interface & executable
Browse files Browse the repository at this point in the history
  • Loading branch information
nickelser committed May 6, 2015
1 parent abe7f90 commit c57d56e
Show file tree
Hide file tree
Showing 7 changed files with 86 additions and 33 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ lib/bundler/man
pkg
rdoc
spec/reports
zhong.rb
test/tmp
test/version_tmp
tmp
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@ gem 'zhong'
```ruby
r = Redis.new

Zhong.schedule(redis: r) do
category "stuff" do
every(5.seconds, "foo") { puts "foo" }
every(1.week, "baz", at: "mon 22:45") { puts "baz" }
Zhong.schedule(redis: r) do |s|
s.category "stuff" do
s.every(5.seconds, "foo") { puts "foo" }
s.every(1.week, "baz", at: "mon 22:45") { puts "baz" }
end

category "clutter" do
every(1.second, "compute", if: -> (t) { rand < 0.5 }) { puts "something happened" }
s.category "clutter" do
s.every(1.second, "compute", if: -> (t) { rand < 0.5 }) { puts "something happened" }
end
end
```
Expand Down
15 changes: 15 additions & 0 deletions bin/zhong
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#!/usr/bin/env ruby

STDERR.sync = STDOUT.sync = true

require "bundler/setup"
require "zhong"

usage = "zhong <zhong.rb>"
file = ARGV.shift || abort(usage)

file = "./#{file}" unless file.match(/^[\/.]/)

require file

Zhong.start
19 changes: 15 additions & 4 deletions lib/zhong.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

require "zhong/version"

require "zhong/util"

require "zhong/at"
require "zhong/every"

Expand All @@ -13,10 +15,19 @@

module Zhong
class << self
def schedule(**opts, &block)
@scheduler = Scheduler.new(opts)
@scheduler.instance_eval(&block)
@scheduler.start
def schedule(**opts)
@scheduler = Scheduler.new(opts).tap do |s|
yield(s)
end
end

def start
fail "You must run `Zhong.schedule` first" unless scheduler
scheduler.start
end

def scheduler
@scheduler
end
end
end
31 changes: 21 additions & 10 deletions lib/zhong/job.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,26 @@ module Zhong
class Job
attr_reader :name, :category

def initialize(scheduler:, name:, every: nil, at: nil, only_if: nil, category: nil, &block)
def initialize(name, config = {}, &block)
@name = name
@category = category
@category = config[:category]

@at = At.parse(at, grace: scheduler.config[:grace])
@every = Every.parse(every)
@at = At.parse(config[:at], grace: config.fetch(:grace, 15.minutes))
@every = Every.parse(config[:every])

if @at && !@every
@logger.error "warning: #{self} has `at` but no `every`; could run far more often than expected!"
end

fail "must specific either `at` or `every` for a job" unless @at || @every

@block = block
@redis = scheduler.config[:redis]
@logger = scheduler.config[:logger]
@tz = scheduler.config[:tz]
@if = only_if
@lock = Suo::Client::Redis.new(lock_key, client: @redis, stale_lock_expiration: scheduler.config[:long_running_timeout])

@redis = config[:redis]
@logger = config[:logger]
@tz = config[:tz]
@if = config[:if]
@lock = Suo::Client::Redis.new(lock_key, client: @redis, stale_lock_expiration: config[:long_running_timeout])
@timeout = 5

refresh_last_ran
Expand All @@ -36,6 +39,8 @@ def run(time = Time.now)
return
end

@thread = nil

ran_set = @lock.lock do
refresh_last_ran

Expand Down Expand Up @@ -85,7 +90,13 @@ def disabled?
end

def to_s
[@category, @name].compact.join(".")
[@category, @name].compact.join(".").freeze
end

def next_at
every_time = @every.next_at(@last_ran) if @last_ran && @every
at_time = @at.next_at(time) if @at
[every_time, at_time, Time.now].compact.max || "now"
end

private
Expand Down
30 changes: 17 additions & 13 deletions lib/zhong/scheduler.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,37 @@ module Zhong
class Scheduler
attr_reader :config, :redis, :jobs

DEFAULT_CONFIG = {
timeout: 0.5,
grace: 15.minutes,
long_running_timeout: 5.minutes
}.freeze

TRAPPED_SIGNALS = %w(QUIT INT TERM).freeze

def initialize(config = {})
@jobs = {}
@config = {timeout: 0.5, grace: 15.minutes, long_running_timeout: 5.minutes}.merge(config)
@logger = @config[:logger] ||= self.class.default_logger
@redis = @config[:redis] ||= Redis.new
@config = DEFAULT_CONFIG.merge(config)
@logger = @config[:logger] ||= Util.default_logger
@redis = @config[:redis] ||= Redis.new(ENV["REDIS_URL"])
end

def category(name)
@category = name
fail "cannot nest categories: #{name} would be nested in #{@category}" if @category

@category = name.to_s

yield
yield(self)

@category = nil
end

def every(period, name, opts = {}, &block)
add(Job.new(scheduler: self, name: name, every: period, at: opts[:at], only_if: opts[:if], category: @category, &block))
add(Job.new(name, opts.merge(@config).merge(every: period, category: @category), &block))
end

def start
%w(QUIT INT TERM).each do |sig|
TRAPPED_SIGNALS.each do |sig|
Signal.trap(sig) { stop }
end

Expand Down Expand Up @@ -66,11 +76,5 @@ def redis_time
now = Time.at(s + ms / (10**6))
config[:tz] ? now.in_time_zone(config[:tz]) : now
end

def self.default_logger
Logger.new(STDOUT).tap do |logger|
logger.formatter = -> (_, datetime, _, msg) { "#{datetime}: #{msg}\n" }
end
end
end
end
11 changes: 11 additions & 0 deletions lib/zhong/util.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module Zhong
module Util
class << self
def default_logger
Logger.new(STDOUT).tap do |logger|
logger.formatter = -> (_, datetime, _, msg) { "#{datetime}: #{msg}\n" }
end
end
end
end
end

0 comments on commit c57d56e

Please sign in to comment.