mirror of
https://github.com/plashchynski/crono.git
synced 2026-03-07 14:50:03 +01:00
Compare commits
33 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
186fe43fc0 | ||
|
|
0351079961 | ||
|
|
2b53dc7ea1 | ||
|
|
b649594084 | ||
|
|
9138826324 | ||
|
|
3a3620d55c | ||
|
|
fe24b435b3 | ||
|
|
c8a4189fd4 | ||
|
|
78b1d8d6e1 | ||
|
|
c54f52a71d | ||
|
|
d0b35aaa6e | ||
|
|
05113b57ee | ||
|
|
20135b87ae | ||
|
|
0f32f8a5a4 | ||
|
|
59e71e89f3 | ||
|
|
368d9ee0a9 | ||
|
|
db6df90beb | ||
|
|
2109be7bba | ||
|
|
9ca68b305f | ||
|
|
750ecb98dd | ||
|
|
8ce3673368 | ||
|
|
007989fa2c | ||
|
|
5b66e9049b | ||
|
|
527f4768bc | ||
|
|
dfae4015f8 | ||
|
|
27f949de10 | ||
|
|
78fa0f9dae | ||
|
|
828488a6bc | ||
|
|
36c35bce7d | ||
|
|
ec53c8376f | ||
|
|
905b02a276 | ||
|
|
e96d71552e | ||
|
|
0afdab02ac |
15
Changes.md
15
Changes.md
@@ -2,3 +2,18 @@
|
||||
-----------
|
||||
|
||||
- Initial release!
|
||||
|
||||
0.5.1
|
||||
-----------
|
||||
|
||||
- Added -e/--environment ENV option to set the daemon rails environment.
|
||||
|
||||
0.5.2
|
||||
-----------
|
||||
|
||||
- Fix: Scheduled time now related to the last performing time.
|
||||
|
||||
0.6.1
|
||||
-----------
|
||||
|
||||
- Persist job state to your database.
|
||||
|
||||
14
Gemfile.lock
14
Gemfile.lock
@@ -1,8 +1,9 @@
|
||||
PATH
|
||||
remote: .
|
||||
specs:
|
||||
crono (0.5.0)
|
||||
crono (0.6.1)
|
||||
activejob (~> 4.0)
|
||||
activerecord (~> 4.0)
|
||||
activesupport (~> 4.0)
|
||||
|
||||
GEM
|
||||
@@ -11,12 +12,21 @@ GEM
|
||||
activejob (4.2.0)
|
||||
activesupport (= 4.2.0)
|
||||
globalid (>= 0.3.0)
|
||||
activemodel (4.2.0)
|
||||
activesupport (= 4.2.0)
|
||||
builder (~> 3.1)
|
||||
activerecord (4.2.0)
|
||||
activemodel (= 4.2.0)
|
||||
activesupport (= 4.2.0)
|
||||
arel (~> 6.0)
|
||||
activesupport (4.2.0)
|
||||
i18n (~> 0.7)
|
||||
json (~> 1.7, >= 1.7.7)
|
||||
minitest (~> 5.1)
|
||||
thread_safe (~> 0.3, >= 0.3.4)
|
||||
tzinfo (~> 1.1)
|
||||
arel (6.0.0)
|
||||
builder (3.2.2)
|
||||
diff-lcs (1.2.5)
|
||||
globalid (0.3.3)
|
||||
activesupport (>= 4.1.0)
|
||||
@@ -37,6 +47,7 @@ GEM
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.2.0)
|
||||
rspec-support (3.2.2)
|
||||
sqlite3 (1.3.10)
|
||||
thread_safe (0.3.4)
|
||||
timecop (0.7.3)
|
||||
tzinfo (1.2.2)
|
||||
@@ -50,4 +61,5 @@ DEPENDENCIES
|
||||
crono!
|
||||
rake (~> 10.0)
|
||||
rspec (~> 3.0)
|
||||
sqlite3
|
||||
timecop (~> 0.7)
|
||||
|
||||
26
README.md
26
README.md
@@ -11,7 +11,7 @@ Crono is a time-based background job scheduler daemon (just like Cron) for Ruby
|
||||
|
||||
## The Idea
|
||||
|
||||
Currently there is no such thing as Cron in Ruby for Rails. Well, there's [Whenever](https://github.com/javan/whenever) but it works on top of Unix Cron, so you have no total control of it from Ruby. Crono is pure Ruby. It doesn't use Unix Cron and other platform-dependent things. So you can use it on all platforms supported by Ruby. You have total control of jobs performing process. You have the code in Ruby, so you can understand and modify it to fit your needs.
|
||||
Currently there is no such thing as Cron in Ruby for Rails. Well, there's [Whenever](https://github.com/javan/whenever) but it works on top of Unix Cron, so you have no total control of it from Ruby. Crono is pure Ruby. It doesn't use Unix Cron and other platform-dependent things. So you can use it on all platforms supported by Ruby. It persists job state to your database using Active Record. You have full control of jobs performing process. You have Ruby code, so you can understand and modify it to fit your needs.
|
||||
|
||||
|
||||
## Requirements
|
||||
@@ -26,12 +26,16 @@ Add the following line to your application's Gemfile:
|
||||
|
||||
gem 'crono'
|
||||
|
||||
Run the bundle command to install it.
|
||||
Run the `bundle` command to install it.
|
||||
After you install Crono, you can run the generator:
|
||||
|
||||
rails generate crono:install
|
||||
|
||||
It will create a configuration file `config/cronotab.rb`
|
||||
It will create a configuration file `config/cronotab.rb` and migration
|
||||
Run the migration:
|
||||
|
||||
rake db:migrate
|
||||
|
||||
Now you are ready to move forward to create a job and schedule it.
|
||||
|
||||
|
||||
@@ -42,8 +46,8 @@ Now you are ready to move forward to create a job and schedule it.
|
||||
Crono can use Active Job jobs from `app/jobs/`. The only requirements is that the `perform` method should take no arguments.
|
||||
|
||||
Here's an example of a test job:
|
||||
app/jobs/test_job.rb
|
||||
|
||||
# app/jobs/test_job.rb
|
||||
class TestJob < ActiveJob::Base
|
||||
def perform
|
||||
# put you scheduled code here
|
||||
@@ -65,6 +69,7 @@ The ActiveJob jobs is convenient because you can use one job in both periodic an
|
||||
|
||||
The schedule described in the configuration file `config/cronotab.rb`, that created using `crono:install` or manually. The semantic is pretty straightforward:
|
||||
|
||||
# config/cronotab.rb
|
||||
Crono.perform(TestJob).every 2.days, at: "15:30"
|
||||
|
||||
You can schedule one job a few times, if you want a job to be performed a few times a day:
|
||||
@@ -90,10 +95,19 @@ Usage: crono [options]
|
||||
-L, --logfile PATH Path to writable logfile (Default: log/crono.log)
|
||||
-P, --pidfile PATH Path to pidfile (Default: tmp/pids/crono.pid)
|
||||
-d, --[no-]daemonize Daemonize process (Default: false)
|
||||
-e, --environment ENV Application environment (Default: development)
|
||||
```
|
||||
|
||||
## Capistrano
|
||||
|
||||
Use the `capistrano-crono` gem ([github](https://github.com/plashchynski/capistrano-crono/)).
|
||||
|
||||
|
||||
## Support
|
||||
|
||||
Feel free to create [issues](https://github.com/plashchynski/crono/issues)
|
||||
|
||||
|
||||
## License
|
||||
|
||||
Copyright 2015 Dzmitry Plashchynski <plashchynski@gmail.com>
|
||||
Licensed under the Apache License, Version 2.0
|
||||
Please see [LICENSE](https://github.com/plashchynski/crono/blob/master/LICENSE) for licensing details.
|
||||
|
||||
@@ -5,8 +5,7 @@ $LOAD_PATH.unshift File.expand_path("../../lib", __FILE__)
|
||||
require "crono/cli"
|
||||
|
||||
begin
|
||||
cli = Crono::CLI.instance
|
||||
cli.run
|
||||
Crono::CLI.instance.run
|
||||
rescue => e
|
||||
raise e if $DEBUG
|
||||
STDERR.puts e.message
|
||||
|
||||
@@ -15,10 +15,12 @@ Gem::Specification.new do |s|
|
||||
|
||||
s.add_runtime_dependency "activejob", "~> 4.0"
|
||||
s.add_runtime_dependency "activesupport", "~> 4.0"
|
||||
s.add_runtime_dependency "activerecord", "~> 4.0"
|
||||
s.add_development_dependency "rake", "~> 10.0"
|
||||
s.add_development_dependency "bundler", ">= 1.0.0"
|
||||
s.add_development_dependency "rspec", "~> 3.0"
|
||||
s.add_development_dependency "timecop", "~> 0.7"
|
||||
s.add_development_dependency "sqlite3"
|
||||
|
||||
s.files = `git ls-files`.split("\n")
|
||||
s.executables = `git ls-files`.split("\n").map{|f| f =~ /^bin\/(.*)/ ? $1 : nil}.compact
|
||||
|
||||
15
examples/cronotab.rb
Normal file
15
examples/cronotab.rb
Normal file
@@ -0,0 +1,15 @@
|
||||
# cronotab.rb — Crono configuration example file
|
||||
#
|
||||
# Here you can specify periodic jobs and their schedule.
|
||||
# You can specify a periodic job as a ActiveJob class in `app/jobs/`
|
||||
# Actually you can use any class. The only requirement is that
|
||||
# the class should implement a method `perform` without arguments.
|
||||
#
|
||||
|
||||
class TestJob
|
||||
def perform
|
||||
puts "Test!"
|
||||
end
|
||||
end
|
||||
|
||||
Crono.perform(TestJob).every 5.second
|
||||
6
examples/monitrc.conf
Normal file
6
examples/monitrc.conf
Normal file
@@ -0,0 +1,6 @@
|
||||
check process crono_myapp
|
||||
with pidfile /path/to/crono.pid
|
||||
start program = "bundle exec crono -e production" with timeout 30 seconds
|
||||
stop program = "kill -s TERM `cat /path/to/crono.pid`" with timeout 30 seconds
|
||||
if totalmem is greater than 500 MB for 2 cycles then restart
|
||||
group myapp_crono
|
||||
@@ -3,7 +3,10 @@ end
|
||||
|
||||
require "active_support/all"
|
||||
require "crono/version.rb"
|
||||
require "crono/logging.rb"
|
||||
require "crono/period.rb"
|
||||
require "crono/schedule.rb"
|
||||
require "crono/job.rb"
|
||||
require "crono/scheduler.rb"
|
||||
require "crono/config.rb"
|
||||
require "crono/performer_proxy.rb"
|
||||
require "crono/orm/active_record/crono_job.rb"
|
||||
|
||||
@@ -2,27 +2,34 @@ require 'crono'
|
||||
require 'optparse'
|
||||
|
||||
module Crono
|
||||
mattr_accessor :schedule
|
||||
mattr_accessor :scheduler
|
||||
|
||||
class CLI
|
||||
include Singleton
|
||||
include Logging
|
||||
|
||||
attr_accessor :config
|
||||
attr_accessor :schedule
|
||||
attr_accessor :logger
|
||||
|
||||
def initialize
|
||||
self.config = Config.new
|
||||
self.schedule = Schedule.new
|
||||
Crono.schedule = schedule
|
||||
Crono.scheduler = Scheduler.new
|
||||
end
|
||||
|
||||
def run
|
||||
parse_options(ARGV)
|
||||
init_logger
|
||||
daemonize if config.daemonize
|
||||
|
||||
if config.daemonize
|
||||
set_log_to(config.logfile)
|
||||
daemonize
|
||||
else
|
||||
set_log_to(STDOUT)
|
||||
end
|
||||
|
||||
write_pid
|
||||
load_rails
|
||||
print_banner
|
||||
|
||||
check_jobs
|
||||
start_working_loop
|
||||
end
|
||||
|
||||
@@ -34,6 +41,7 @@ module Crono
|
||||
File.open(config.logfile, 'ab') { |f| io.reopen(f) }
|
||||
io.sync = true
|
||||
end
|
||||
|
||||
$stdin.reopen("/dev/null")
|
||||
end
|
||||
|
||||
@@ -42,33 +50,35 @@ module Crono
|
||||
File.write(pidfile, ::Process.pid)
|
||||
end
|
||||
|
||||
def init_logger
|
||||
logfile = config.daemonize ? config.logfile : STDOUT
|
||||
self.logger = Logger.new(logfile)
|
||||
end
|
||||
|
||||
def print_banner
|
||||
logger.info "Loading Crono #{Crono::VERSION}"
|
||||
logger.info "Running in #{RUBY_DESCRIPTION}"
|
||||
|
||||
logger.info "Jobs:"
|
||||
Crono.scheduler.jobs.each do |job|
|
||||
logger.info job.description
|
||||
end
|
||||
end
|
||||
|
||||
def load_rails
|
||||
ENV['RACK_ENV'] = ENV['RAILS_ENV'] = config.environment
|
||||
require 'rails'
|
||||
require File.expand_path("config/environment.rb")
|
||||
::Rails.application.eager_load!
|
||||
require File.expand_path(config.cronotab)
|
||||
end
|
||||
|
||||
def run_job(klass)
|
||||
logger.info "Perform #{klass}"
|
||||
Thread.new { klass.new.perform }
|
||||
def check_jobs
|
||||
if Crono.scheduler.jobs.empty?
|
||||
logger.error "You have no jobs defined in you cronotab file #{config.cronotab}"
|
||||
end
|
||||
end
|
||||
|
||||
def start_working_loop
|
||||
loop do
|
||||
klass, time = schedule.next
|
||||
sleep(time - Time.now)
|
||||
run_job(klass)
|
||||
Thread.abort_on_exception = true
|
||||
while job = Crono.scheduler.next do
|
||||
sleep(job.next - Time.now)
|
||||
job.perform
|
||||
end
|
||||
end
|
||||
|
||||
@@ -91,7 +101,10 @@ module Crono
|
||||
opts.on("-d", "--[no-]daemonize", "Daemonize process (Default: #{config.daemonize})") do |daemonize|
|
||||
config.daemonize = daemonize
|
||||
end
|
||||
|
||||
|
||||
opts.on '-e', '--environment ENV', "Application environment (Default: #{config.environment})" do |env|
|
||||
config.environment = env
|
||||
end
|
||||
end.parse!(argv)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -8,12 +8,14 @@ module Crono
|
||||
attr_accessor :logfile
|
||||
attr_accessor :pidfile
|
||||
attr_accessor :daemonize
|
||||
attr_accessor :environment
|
||||
|
||||
def initialize
|
||||
self.cronotab = CRONOTAB
|
||||
self.logfile = LOGFILE
|
||||
self.pidfile = PIDFILE
|
||||
self.daemonize = false
|
||||
self.environment = ENV['RAILS_ENV'] || ENV['RACK_ENV'] || "development"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
49
lib/crono/job.rb
Normal file
49
lib/crono/job.rb
Normal file
@@ -0,0 +1,49 @@
|
||||
module Crono
|
||||
class Job
|
||||
include Logging
|
||||
|
||||
attr_accessor :performer
|
||||
attr_accessor :period
|
||||
attr_accessor :last_performed_at
|
||||
|
||||
def initialize(performer, period)
|
||||
self.performer, self.period = performer, period
|
||||
end
|
||||
|
||||
def next
|
||||
next_time = period.next(since: last_performed_at)
|
||||
next_time.past? ? period.next : next_time
|
||||
end
|
||||
|
||||
def description
|
||||
"Perform #{performer} #{period.description}"
|
||||
end
|
||||
|
||||
def job_id
|
||||
description
|
||||
end
|
||||
|
||||
def perform
|
||||
logger.info "Perform #{performer}"
|
||||
self.last_performed_at = Time.now
|
||||
save
|
||||
Thread.new do
|
||||
performer.new.perform
|
||||
logger.info "Finished #{performer} in %.2f seconds" % (Time.now - last_performed_at)
|
||||
end
|
||||
end
|
||||
|
||||
def save
|
||||
model.update(last_performed_at: last_performed_at)
|
||||
end
|
||||
|
||||
def load
|
||||
self.last_performed_at = model.last_performed_at
|
||||
end
|
||||
|
||||
private
|
||||
def model
|
||||
@model ||= Crono::CronoJob.find_or_create_by(job_id: job_id)
|
||||
end
|
||||
end
|
||||
end
|
||||
13
lib/crono/logging.rb
Normal file
13
lib/crono/logging.rb
Normal file
@@ -0,0 +1,13 @@
|
||||
module Crono
|
||||
mattr_accessor :logger
|
||||
|
||||
module Logging
|
||||
def set_log_to(logfile)
|
||||
Crono.logger = Logger.new(logfile)
|
||||
end
|
||||
|
||||
def logger
|
||||
Crono.logger
|
||||
end
|
||||
end
|
||||
end
|
||||
8
lib/crono/orm/active_record/crono_job.rb
Normal file
8
lib/crono/orm/active_record/crono_job.rb
Normal file
@@ -0,0 +1,8 @@
|
||||
require 'active_record'
|
||||
|
||||
module Crono
|
||||
class CronoJob < ActiveRecord::Base
|
||||
self.table_name = "crono_jobs"
|
||||
validates :job_id, presence: true, uniqueness: true
|
||||
end
|
||||
end
|
||||
@@ -1,16 +1,17 @@
|
||||
module Crono
|
||||
class PerformerProxy
|
||||
def initialize(performer, schedule)
|
||||
def initialize(performer, scheduler)
|
||||
@performer = performer
|
||||
@schedule = schedule
|
||||
@scheduler = scheduler
|
||||
end
|
||||
|
||||
def every(period, *args)
|
||||
@schedule.add(@performer, Period.new(period, *args))
|
||||
job = Job.new(@performer, Period.new(period, *args))
|
||||
@scheduler.add_job(job)
|
||||
end
|
||||
end
|
||||
|
||||
def self.perform(performer)
|
||||
PerformerProxy.new(performer, Crono.schedule)
|
||||
PerformerProxy.new(performer, Crono.scheduler)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -5,8 +5,15 @@ module Crono
|
||||
@at_hour, @at_min = parse_at(at) if at
|
||||
end
|
||||
|
||||
def next
|
||||
@period.from_now.change({hour: @at_hour, min: @at_min}.compact)
|
||||
def next(since: nil)
|
||||
since ||= Time.now
|
||||
@period.since(since).change({hour: @at_hour, min: @at_min}.compact)
|
||||
end
|
||||
|
||||
def description
|
||||
desc = "every #{@period.inspect}"
|
||||
desc += " at %.2i:%.2i" % [@at_hour, @at_min] if @at_hour && @at_min
|
||||
desc
|
||||
end
|
||||
|
||||
def parse_at(at)
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
module Crono
|
||||
class Schedule
|
||||
def initialize
|
||||
@schedule = []
|
||||
end
|
||||
|
||||
def add(peformer, period)
|
||||
@schedule << [peformer, period]
|
||||
end
|
||||
|
||||
def next
|
||||
[queue.first[0], queue.first[1].next]
|
||||
end
|
||||
|
||||
private
|
||||
def queue
|
||||
@schedule.sort { |a,b| a[1].next <=> b[1].next }
|
||||
end
|
||||
end
|
||||
end
|
||||
23
lib/crono/scheduler.rb
Normal file
23
lib/crono/scheduler.rb
Normal file
@@ -0,0 +1,23 @@
|
||||
module Crono
|
||||
class Scheduler
|
||||
attr_accessor :jobs
|
||||
|
||||
def initialize
|
||||
self.jobs = []
|
||||
end
|
||||
|
||||
def add_job(job)
|
||||
job.load
|
||||
jobs << job
|
||||
end
|
||||
|
||||
def next
|
||||
queue.first
|
||||
end
|
||||
|
||||
private
|
||||
def queue
|
||||
jobs.sort_by(&:next)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,3 +1,3 @@
|
||||
module Crono
|
||||
VERSION = "0.5.0"
|
||||
VERSION = '0.6.1'
|
||||
end
|
||||
|
||||
@@ -1,12 +1,26 @@
|
||||
require 'rails/generators'
|
||||
require 'rails/generators/migration'
|
||||
require 'rails/generators/active_record'
|
||||
|
||||
module Crono
|
||||
module Generators
|
||||
class InstallGenerator < ::Rails::Generators::Base
|
||||
include Rails::Generators::Migration
|
||||
|
||||
def self.next_migration_number(path)
|
||||
ActiveRecord::Generators::Base.next_migration_number(path)
|
||||
end
|
||||
|
||||
desc "Installs crono and generates the necessary configuration files"
|
||||
source_root File.expand_path("../templates", __FILE__)
|
||||
|
||||
def copy_config
|
||||
template 'cronotab.rb.erb', 'config/cronotab.rb'
|
||||
end
|
||||
|
||||
def create_migrations
|
||||
migration_template 'migrations/create_crono_jobs.rb', 'db/migrate/create_crono_jobs.rb'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
class CreateCronoJobs < ActiveRecord::Migration
|
||||
def self.up
|
||||
create_table :crono_jobs do |t|
|
||||
t.string :job_id, null: false
|
||||
t.text :log
|
||||
t.datetime :last_performed_at
|
||||
t.timestamps
|
||||
end
|
||||
add_index :crono_jobs, [:job_id], unique: true
|
||||
end
|
||||
|
||||
def self.down
|
||||
drop_table :crono_jobs
|
||||
end
|
||||
end
|
||||
@@ -18,13 +18,6 @@ describe Crono::CLI do
|
||||
end
|
||||
end
|
||||
|
||||
describe "#run_job" do
|
||||
it "should run job in separate thread" do
|
||||
thread = cli.send(:run_job, TestJob).join
|
||||
expect(thread).to be_stop
|
||||
end
|
||||
end
|
||||
|
||||
describe "#start_working_loop" do
|
||||
it "should start working loop"
|
||||
end
|
||||
@@ -49,5 +42,10 @@ describe Crono::CLI do
|
||||
cli.send(:parse_options, ["--daemonize"])
|
||||
expect(cli.config.daemonize).to be true
|
||||
end
|
||||
|
||||
it "should set environment" do
|
||||
cli.send(:parse_options, ["--environment", "production"])
|
||||
expect(cli.config.environment).to be_eql("production")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -3,11 +3,13 @@ require "spec_helper"
|
||||
describe Crono::Config do
|
||||
describe "#initialize" do
|
||||
it "should initialize with default configuration options" do
|
||||
ENV["RAILS_ENV"] = "test"
|
||||
@config = Crono::Config.new
|
||||
expect(@config.cronotab).to be Crono::Config::CRONOTAB
|
||||
expect(@config.logfile).to be Crono::Config::LOGFILE
|
||||
expect(@config.pidfile).to be Crono::Config::PIDFILE
|
||||
expect(@config.daemonize).to be false
|
||||
expect(@config.environment).to be_eql ENV["RAILS_ENV"]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
62
spec/job_spec.rb
Normal file
62
spec/job_spec.rb
Normal file
@@ -0,0 +1,62 @@
|
||||
require "spec_helper"
|
||||
|
||||
class TestJob
|
||||
def perform;end
|
||||
end
|
||||
|
||||
describe Crono::Job do
|
||||
let(:period) { Crono::Period.new(2.day) }
|
||||
let(:job) { Crono::Job.new(TestJob, period) }
|
||||
|
||||
it "should contain performer and period" do
|
||||
expect(job.performer).to be TestJob
|
||||
expect(job.period).to be period
|
||||
end
|
||||
|
||||
describe "#perform" do
|
||||
it "should run performer in separate thread" do
|
||||
thread = job.perform.join
|
||||
expect(thread).to be_stop
|
||||
end
|
||||
|
||||
it "should call Job#save after run" do
|
||||
expect(job).to receive(:save)
|
||||
job.perform.join
|
||||
job.send(:model).destroy
|
||||
end
|
||||
end
|
||||
|
||||
describe "#description" do
|
||||
it "should return job identificator" do
|
||||
expect(job.description).to be_eql("Perform TestJob every 2 days")
|
||||
end
|
||||
end
|
||||
|
||||
describe "#save" do
|
||||
it "should save new job to DB" do
|
||||
expect(Crono::CronoJob.where(job_id: job.job_id)).to_not exist
|
||||
job.save
|
||||
expect(Crono::CronoJob.where(job_id: job.job_id)).to exist
|
||||
end
|
||||
|
||||
it "should update saved job" do
|
||||
job.last_performed_at = Time.now
|
||||
job.save
|
||||
@crono_job = Crono::CronoJob.find_by(job_id: job.job_id)
|
||||
expect(@crono_job.last_performed_at).to be_eql(job.last_performed_at)
|
||||
end
|
||||
end
|
||||
|
||||
describe "#load" do
|
||||
before do
|
||||
@saved_last_performed_at = job.last_performed_at = Time.now
|
||||
job.save
|
||||
end
|
||||
|
||||
it "should load info from DB" do
|
||||
@job = Crono::Job.new(TestJob, period)
|
||||
@job.load
|
||||
expect(@job.last_performed_at).to be_eql @saved_last_performed_at
|
||||
end
|
||||
end
|
||||
end
|
||||
28
spec/orm/active_record/crono_job_spec.rb
Normal file
28
spec/orm/active_record/crono_job_spec.rb
Normal file
@@ -0,0 +1,28 @@
|
||||
require "spec_helper"
|
||||
|
||||
describe Crono::CronoJob do
|
||||
let(:valid_attrs) do
|
||||
{
|
||||
job_id: "Perform TestJob every 3 days"
|
||||
}
|
||||
end
|
||||
|
||||
it "should validate presence of job_id" do
|
||||
@crono_job = Crono::CronoJob.new()
|
||||
expect(@crono_job).not_to be_valid
|
||||
expect(@crono_job.errors.added?(:job_id, :blank)).to be true
|
||||
end
|
||||
|
||||
it "should validate uniqueness of job_id" do
|
||||
Crono::CronoJob.create!(job_id: "TestJob every 2 days")
|
||||
@crono_job = Crono::CronoJob.create(job_id: "TestJob every 2 days")
|
||||
expect(@crono_job).not_to be_valid
|
||||
expect(@crono_job.errors.added?(:job_id, :taken)).to be true
|
||||
end
|
||||
|
||||
it "should save job_id to DB" do
|
||||
Crono::CronoJob.create!(valid_attrs)
|
||||
@crono_job = Crono::CronoJob.find_by(job_id: valid_attrs[:job_id])
|
||||
expect(@crono_job).to be_present
|
||||
end
|
||||
end
|
||||
@@ -5,8 +5,8 @@ class TestJob
|
||||
end
|
||||
|
||||
describe Crono::PerformerProxy do
|
||||
it "should add job and period to schedule" do
|
||||
expect(Crono.schedule).to receive(:add).with(TestJob, kind_of(Crono::Period))
|
||||
it "should add job to schedule" do
|
||||
expect(Crono.scheduler).to receive(:add_job).with(kind_of(Crono::Job))
|
||||
Crono.perform(TestJob).every(2.days, at: "15:30")
|
||||
end
|
||||
end
|
||||
|
||||
@@ -7,6 +7,13 @@ describe Crono::Period do
|
||||
end
|
||||
end
|
||||
|
||||
describe "#description" do
|
||||
it "should return period description" do
|
||||
@period = Crono::Period.new(2.day, at: "15:20")
|
||||
expect(@period.description).to be_eql("every 2 days at 15:20")
|
||||
end
|
||||
end
|
||||
|
||||
describe "#next" do
|
||||
context "in daily basis" do
|
||||
it "should return the time 2 days from now" do
|
||||
@@ -30,6 +37,11 @@ describe Crono::Period do
|
||||
@period = Crono::Period.new(2.day, at: 1)
|
||||
}.to raise_error("Unknown 'at' format")
|
||||
end
|
||||
|
||||
it "should return time in relation to last time" do
|
||||
@period = Crono::Period.new(2.day)
|
||||
expect(@period.next(since: 1.day.ago)).to be_eql(1.day.from_now)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
require "spec_helper"
|
||||
|
||||
class TestJob
|
||||
def perform;end
|
||||
end
|
||||
|
||||
describe Crono::Schedule do
|
||||
describe "#next" do
|
||||
it "should return next job in schedule" do
|
||||
@schedule = Crono::Schedule.new
|
||||
[
|
||||
Crono::Period.new(3.day, at: "18:55"),
|
||||
Crono::Period.new(1.day, at: "15:30"),
|
||||
Crono::Period.new(7.day, at: "06:05")
|
||||
].each { |period| @schedule.add(TestJob, period) }
|
||||
|
||||
expect(@schedule.next).to be_eql([TestJob, 1.day.from_now.change(hour: 15, min: 30)])
|
||||
end
|
||||
end
|
||||
end
|
||||
35
spec/scheduler_spec.rb
Normal file
35
spec/scheduler_spec.rb
Normal file
@@ -0,0 +1,35 @@
|
||||
require "spec_helper"
|
||||
|
||||
class TestJob
|
||||
def perform;end
|
||||
end
|
||||
|
||||
describe Crono::Scheduler do
|
||||
before(:each) do
|
||||
@scheduler = Crono::Scheduler.new
|
||||
@jobs = [
|
||||
Crono::Period.new(3.day, at: "18:55"),
|
||||
Crono::Period.new(1.day, at: "15:30"),
|
||||
Crono::Period.new(7.day, at: "06:05")
|
||||
].map { |period| Crono::Job.new(TestJob, period) }
|
||||
@scheduler.jobs = @jobs
|
||||
end
|
||||
|
||||
describe "#add_job" do
|
||||
it "should call Job#load on Job" do
|
||||
@job = Crono::Job.new(TestJob, Crono::Period.new(10.day, at: "04:05"))
|
||||
expect(@job).to receive(:load)
|
||||
@scheduler.add_job(@job)
|
||||
end
|
||||
end
|
||||
|
||||
describe "#next" do
|
||||
it "should return next job in schedule" do
|
||||
expect(@scheduler.next).to be @jobs[1]
|
||||
end
|
||||
|
||||
it "should return next based on last" do
|
||||
expect(@scheduler.next)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -3,6 +3,11 @@ Bundler.setup
|
||||
|
||||
require 'timecop'
|
||||
require 'crono'
|
||||
require 'generators/crono/install/templates/migrations/create_crono_jobs.rb'
|
||||
|
||||
ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')
|
||||
ActiveRecord::Base.logger = Logger.new(STDOUT)
|
||||
CreateCronoJobs.up
|
||||
|
||||
RSpec.configure do |config|
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user