Compare commits

..

28 Commits

Author SHA1 Message Date
Dzmitry Plashchynski
0c2f52d9bc Bump 1.1.1 2016-11-30 01:06:29 +02:00
Dzmitry Plashchynski
33e9794621 Last log instead of Log since it can be truncated 2016-11-30 01:05:47 +02:00
Dzmitry Plashchynski
c336b6d00b Fix empty job_options error 2016-11-30 00:52:54 +02:00
Dzmitry Plashchynski
1cf37ee30f Bump 1.1.0 2016-11-30 00:43:52 +02:00
Dzmitry Plashchynski
137dfe6d19 Fix MySQL default text size limit 2016-11-30 00:16:58 +02:00
Dzmitry Plashchynski
14c3e3162e Log truncating Close #41 2016-11-30 00:11:56 +02:00
Dzmitry Plashchynski
d1e15b8537 Ignore .byebug_history 2016-11-30 00:01:54 +02:00
Dzmitry Plashchynski
f5d65b6cc7 Job options 2016-11-29 15:41:39 +02:00
Dzmitry Plashchynski
64dde82342 Update gems 2016-11-29 15:39:49 +02:00
Dzmitry Plashchynski
e331490c48 Update ruby versions for travis 2016-11-29 15:35:09 +02:00
Dzmitry Plashchynski
8a61e65963 Merge pull request #53 from acolyer/same_day
schedule on: today if at: time not passed
2016-11-16 23:25:56 +02:00
Adrian Colyer
3f9ea8350d schedule today if at: time not passed 2016-11-06 15:47:50 +00:00
Dzmitry Plashchynski
1d9d30ef89 Fix issue link 2016-09-22 18:39:56 +03:00
Dzmitry Plashchynski
72810a95ae Add information about the showexceptions issue. Closes #1055 2016-09-22 18:38:44 +03:00
Dzmitry Plashchynski
4401c6bba4 Merge branch 'master' of github.com:plashchynski/crono 2016-09-22 18:24:56 +03:00
Dzmitry Plashchynski
e7df4bb067 Fix crash when no jobs defined 2016-09-22 18:24:46 +03:00
Dzmitry Plashchynski
935bdebd84 Merge pull request #51 from pachacamac/patch-1
deprecate need to config your app name.
2016-09-22 18:08:51 +03:00
Marc
b22845cba6 deprecate need to config your app name.
Instead of `<AppName>::Application.load_tasks` the same can be achieved by `Rails.app_class.load_tasks`, therefore not requiring the user to configure the AppName. Less margin for errors :)
2016-09-17 12:38:48 +02:00
Dzmitry Plashchynski
77481f1143 Fix asterisk 2016-07-02 21:27:55 +03:00
Dzmitry Plashchynski
42dc1bbc4f Remove gitter 2016-07-02 21:23:34 +03:00
Dzmitry Plashchynski
63c52896f7 Since Rails 5 requires Ruby 2.2.2 or newer. 2016-07-01 04:46:02 +03:00
Dzmitry Plashchynski
977f49a3a4 Rails 3 is not supported anymore 2016-07-01 04:40:00 +03:00
Dzmitry Plashchynski
7155ce797c Bump 1.0.3 2016-07-01 04:36:45 +03:00
Dzmitry Plashchynski
d18866564a Update change log 2016-07-01 04:35:56 +03:00
Dzmitry Plashchynski
b39be015b1 Rails 5 supports "week" in time notations 2016-07-01 04:35:09 +03:00
Dzmitry Plashchynski
51c914ea8a Tested on Rails 5, closes #49 2016-07-01 04:04:48 +03:00
Dzmitry Plashchynski
ac71db0d68 Added Known Issues section to Readme. Closes #43 2016-06-26 15:50:09 +03:00
Dzmitry Plashchynski
4e3ca885a8 Fix table_name_suffix/prefix issue. Closes #33 2016-06-26 02:12:04 +03:00
18 changed files with 172 additions and 70 deletions

1
.gitignore vendored
View File

@@ -7,3 +7,4 @@
/spec/reports/
/tmp/
log/*.log
.byebug_history

View File

@@ -3,14 +3,8 @@ os:
- linux
- osx
rvm:
- 2.0.0
- 2.1.7
- 2.2.3
- 2.3.1
matrix:
exclude:
- rvm: 2.3.1
os: osx
- 2.2.6
- 2.3.3
notifications:
webhooks:
urls:

View File

@@ -1,3 +1,39 @@
1.1.0
-----------
- Rails 3 and old Rubies are not supported anymore, sorry rails 3 guys...
- Requires Ruby 2.2.2 or newer
- Fixed crash when no jobs defined in your cronotab
- Some doc updates (thanks to @pachacamac)
- Job will schedule on: today if at: time not passed (thanks to @acolyer)
- Job log truncating (thanks to @reiz)
1.0.3
-----------
- "every 1 week" jobs now displaying on Rails 5 as "1 week" not as "7 days"
- Liberal gem dependencies to support both Rails 4 and Rails 5
1.0.2
-----------
- Fix table_name_suffix/prefix issue: https://github.com/plashchynski/crono/issues/33
1.0.1
-----------
- Fix job saving
1.0.0
-----------
- Rails 5 support (thanks to @adamico)
- Possibility to schedule jobs with arguments (thanks to @preisanalytics)
- Added :within option to run only within given time interval (thanks to @lhz)
- daemon gem support (thanks to @preisanalytics) https://github.com/plashchynski/crono/pull/37
- Support multiple nodes (thanks to @Natural-Intelligence)
- Fixed DB connection pool issue (thanks to @ChandravatiSG)
0.9.1
-----------
- Add ability to define minimal time between job executions to support multiple corno nodes, so two different nodes will not execute the same job

View File

@@ -1,62 +1,59 @@
PATH
remote: .
specs:
crono (1.0.1)
crono (1.1.1)
activerecord (>= 4.0)
activesupport (>= 4.0)
GEM
remote: https://rubygems.org/
specs:
activemodel (4.2.6)
activesupport (= 4.2.6)
builder (~> 3.1)
activerecord (4.2.6)
activemodel (= 4.2.6)
activesupport (= 4.2.6)
arel (~> 6.0)
activesupport (4.2.6)
activemodel (5.0.0.1)
activesupport (= 5.0.0.1)
activerecord (5.0.0.1)
activemodel (= 5.0.0.1)
activesupport (= 5.0.0.1)
arel (~> 7.0)
activesupport (5.0.0.1)
concurrent-ruby (~> 1.0, >= 1.0.2)
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.3)
builder (3.2.2)
byebug (9.0.5)
daemons (1.2.3)
arel (7.1.4)
byebug (9.0.6)
concurrent-ruby (1.0.2)
daemons (1.2.4)
diff-lcs (1.2.5)
haml (4.0.7)
tilt
i18n (0.7.0)
json (1.8.3)
minitest (5.9.0)
rack (1.6.4)
minitest (5.9.1)
rack (1.6.5)
rack-protection (1.5.3)
rack
rack-test (0.6.3)
rack (>= 1.0)
rake (10.5.0)
rspec (3.4.0)
rspec-core (~> 3.4.0)
rspec-expectations (~> 3.4.0)
rspec-mocks (~> 3.4.0)
rspec-core (3.4.4)
rspec-support (~> 3.4.0)
rspec-expectations (3.4.0)
rake (11.3.0)
rspec (3.5.0)
rspec-core (~> 3.5.0)
rspec-expectations (~> 3.5.0)
rspec-mocks (~> 3.5.0)
rspec-core (3.5.4)
rspec-support (~> 3.5.0)
rspec-expectations (3.5.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.4.0)
rspec-mocks (3.4.1)
rspec-support (~> 3.5.0)
rspec-mocks (3.5.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.4.0)
rspec-support (3.4.1)
rspec-support (~> 3.5.0)
rspec-support (3.5.0)
sinatra (1.4.7)
rack (~> 1.5)
rack-protection (~> 1.4)
tilt (>= 1.3, < 3)
sqlite3 (1.3.11)
sqlite3 (1.3.12)
thread_safe (0.3.5)
tilt (2.0.2)
tilt (2.0.5)
timecop (0.8.1)
tzinfo (1.2.2)
thread_safe (~> 0.1)
@@ -71,11 +68,11 @@ DEPENDENCIES
daemons
haml
rack-test
rake (~> 10.0)
rspec (~> 3.0)
rake (>= 10.0)
rspec (>= 3.0)
sinatra
sqlite3
timecop (~> 0.7)
timecop (>= 0.7)
BUNDLED WITH
1.11.2
1.13.6

View File

@@ -4,7 +4,6 @@ Crono — Job scheduler for Rails
[![Build Status](https://travis-ci.org/plashchynski/crono.svg?branch=master)](https://travis-ci.org/plashchynski/crono)
[![Code Climate](https://codeclimate.com/github/plashchynski/crono/badges/gpa.svg)](https://codeclimate.com/github/plashchynski/crono)
[![security](https://hakiri.io/github/plashchynski/crono/master.svg)](https://hakiri.io/github/plashchynski/crono/master)
[![Join the chat at https://gitter.im/plashchynski/crono](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/plashchynski/crono?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
Crono is a time-based background job scheduler daemon (just like Cron) for Ruby on Rails.
@@ -18,7 +17,7 @@ Currently, there is no such thing as Ruby Cron for Rails. Well, there's [Wheneve
## Requirements
Tested with latest MRI Ruby (2.3, 2.2, 2.1 and 2.0) and Rails 3.2+
Tested with latest MRI Ruby 2.2+, 2.3+, Rails 4.\*, and Rails 5.\*.
Other versions are untested but might work fine.
@@ -77,8 +76,8 @@ Here's an example of a Rake Task within a job:
```ruby
# config/cronotab.rb
require 'rake'
# Be sure to change AppName to your application name!
AppName::Application.load_tasks
Rails.app_class.load_tasks
class Test
def perform
@@ -131,6 +130,12 @@ serialized using JSON.generate
Crono.perform(TestJob, 'some', 'args').every 1.day, at: {hour: 12, min: 15}
```
You can set some options that not passed to the job but affect how the job will be treated by Crono. For example, you can set to truncate job logs (which stored in the database) to a certain number of records:
```ruby
Crono.perform(TestJob).with_options(truncate_log: 100).every 1.week, on: :monday
```
#### Run
To run Crono, in your Rails project root directory:
@@ -184,6 +189,14 @@ Rails.application.routes.draw do
Access management and other questions described in the [wiki](https://github.com/plashchynski/crono/wiki/Web-UI).
#### Known issues
For Rails 5, in case of the errors:
```
`require': cannot load such file -- rack/showexceptions (LoadError)
```
See the related issue [#52](https://github.com/plashchynski/crono/issues/52)
## Capistrano
@@ -195,6 +208,11 @@ Use the `capistrano-crono` gem ([github](https://github.com/plashchynski/capistr
Feel free to create [issues](https://github.com/plashchynski/crono/issues)
## Known Issues
* Is not compatible with the `protected_attributes` gem. See: [https://github.com/plashchynski/crono/issues/43](https://github.com/plashchynski/crono/issues/43)
## License
Please see [LICENSE](https://github.com/plashchynski/crono/blob/master/LICENSE) for licensing details.

View File

@@ -21,10 +21,10 @@ Gem::Specification.new do |spec|
spec.add_runtime_dependency 'activesupport', '>= 4.0'
spec.add_runtime_dependency 'activerecord', '>= 4.0'
spec.add_development_dependency 'rake', '~> 10.0'
spec.add_development_dependency 'rake', '>= 10.0'
spec.add_development_dependency 'bundler', '>= 1.0.0'
spec.add_development_dependency 'rspec', '~> 3.0'
spec.add_development_dependency 'timecop', '~> 0.7'
spec.add_development_dependency 'rspec', '>= 3.0'
spec.add_development_dependency 'timecop', '>= 0.7'
spec.add_development_dependency 'sqlite3'
spec.add_development_dependency 'byebug'
spec.add_development_dependency 'sinatra'

View File

@@ -29,7 +29,11 @@ module Crono
Cronotab.process(File.expand_path(config.cronotab))
print_banner
check_jobs
unless have_jobs?
logger.error "You have no jobs in you cronotab file #{config.cronotab}"
return
end
if config.daemonize
start_working_loop_in_daemon
else
@@ -39,6 +43,10 @@ module Crono
private
def have_jobs?
Crono.scheduler.jobs.present?
end
def setup_log
if config.daemonize
self.logfile = config.logfile
@@ -85,11 +93,6 @@ module Crono
::Rails.application.eager_load!
end
def check_jobs
return if Crono.scheduler.jobs.present?
logger.error "You have no jobs in you cronotab file #{config.cronotab}"
end
def start_working_loop_in_daemon
unless ENV['RAILS_ENV'] == 'test'
begin

View File

@@ -6,15 +6,16 @@ module Crono
class Job
include Logging
attr_accessor :performer, :period, :job_args, :last_performed_at,
attr_accessor :performer, :period, :job_args, :last_performed_at, :job_options,
:next_performed_at, :job_log, :job_logger, :healthy, :execution_interval
def initialize(performer, period, job_args)
def initialize(performer, period, job_args, job_options = nil)
self.execution_interval = 0.minutes
self.performer, self.period = performer, period
self.job_args = JSON.generate(job_args)
self.job_log = StringIO.new
self.job_logger = Logger.new(job_log)
self.job_options = job_options || {}
self.next_performed_at = period.next
@semaphore = Mutex.new
end
@@ -61,9 +62,15 @@ module Crono
job_log.truncate(job_log.rewind)
end
def truncate_log(log)
return log.lines.last(job_options[:truncate_log]).join if job_options[:truncate_log]
return log
end
def update_model
saved_log = model.reload.log || ''
log_to_save = saved_log + job_log.string
log_to_save = truncate_log(log_to_save)
model.update(last_performed_at: last_performed_at, log: log_to_save,
healthy: healthy)
end
@@ -98,7 +105,7 @@ module Crono
def log(message, severity = Logger::INFO)
@semaphore.synchronize do
logger.log severity, message
logger.log(severity, message) if logger
job_logger.log severity, message
end
end

View File

@@ -3,7 +3,6 @@ require 'active_record'
module Crono
# Crono::CronoJob is a ActiveRecord model to store job state
class CronoJob < ActiveRecord::Base
self.table_name = 'crono_jobs'
validates :job_id, presence: true, uniqueness: true
def self.outdated

View File

@@ -8,7 +8,7 @@ module Crono
end
def every(period, *args)
@job = Job.new(@performer, Period.new(period, *args), @job_args)
@job = Job.new(@performer, Period.new(period, *args), @job_args, @options)
@scheduler.add_job(@job)
self
end
@@ -17,6 +17,11 @@ module Crono
@job.execution_interval = execution_interval if @job
self
end
def with_options(options)
@options = options
self
end
end
def self.perform(performer, *job_args)

View File

@@ -23,6 +23,7 @@ module Crono
return initial_next unless since
@next = @period.since(since)
end
@next = @next.beginning_of_week.advance(days: @on) if @on
@next = @next.change(time_atts)
return @next if @next.future?
@@ -48,6 +49,7 @@ module Crono
def initial_day
return Time.now unless @on
day = Time.now.beginning_of_week.advance(days: @on)
day = day.change(time_atts)
return day if day.future?
@period.from_now.beginning_of_week.advance(days: @on)
end

View File

@@ -1,3 +1,3 @@
module Crono
VERSION = '1.0.1'
VERSION = '1.1.1'
end

View File

@@ -2,7 +2,7 @@ class CreateCronoJobs < ActiveRecord::Migration
def self.up
create_table :crono_jobs do |t|
t.string :job_id, null: false
t.text :log
t.text :log, limit: 4294967295 # LONGTEXT for MySQL
t.datetime :last_performed_at
t.boolean :healthy
t.timestamps null: false

View File

@@ -7,6 +7,7 @@ describe Crono::CLI do
describe '#run' do
it 'should initialize rails with #load_rails and start working loop' do
expect(cli).to receive(:load_rails)
expect(cli).to receive(:have_jobs?).and_return(true)
expect(cli).to receive(:start_working_loop)
expect(cli).to receive(:parse_options)
expect(cli).to receive(:parse_command)
@@ -14,12 +15,13 @@ describe Crono::CLI do
expect(Crono::Cronotab).to receive(:process)
cli.run
end
context 'should run as daemon' do
before {cli.config.daemonize = true}
context 'should run as daemon' do
before { cli.config.daemonize = true }
it 'should initialize rails with #load_rails and start working loop' do
expect(cli).to receive(:load_rails)
expect(cli).to receive(:have_jobs?).and_return(true)
expect(cli).to receive(:start_working_loop_in_daemon)
expect(cli).to receive(:parse_options)
expect(cli).to receive(:parse_command)

View File

@@ -107,13 +107,29 @@ describe Crono::Job do
expect(@crono_job.healthy).to be true
end
it 'should save and truncate job log' do
it 'should save log' do
message = 'test message'
job.send(:log, message)
job.save
expect(job.send(:model).reload.log).to include message
expect(job.job_log.string).to be_empty
end
it 'should not truncate log if not specified' do
log = (1..100).map {|n| "line #{n}" }.join("\n")
job = Crono::Job.new(TestJob, period, [])
job.send(:log, log)
job.save
expect(job.send(:model).reload.log.lines.size).to be >= log.lines.size
end
it 'should truncate log if specified' do
log = (1..100).map {|n| "line #{n}" }.join("\n")
job = Crono::Job.new(TestJob, period, [], truncate_log: 50)
job.send(:log, log)
job.save
expect(job.send(:model).reload.log.lines.size).to be 50
end
end
describe '#load' do
@@ -132,6 +148,7 @@ describe Crono::Job do
describe '#log' do
it 'should write log messages to both common and job log' do
message = 'Test message'
job.logfile = "/dev/null"
expect(job.logger).to receive(:log).with(Logger::INFO, message)
expect(job.job_logger).to receive(:log).with(Logger::INFO, message)
job.send(:log, message)

View File

@@ -20,8 +20,14 @@ describe Crono::PerformerProxy do
end
it 'should add job with args to schedule' do
expect(Crono::Job).to receive(:new).with(TestJob, kind_of(Crono::Period), [:some, {some: 'data'}])
expect(Crono::Job).to receive(:new).with(TestJob, kind_of(Crono::Period), [:some, {some: 'data'}], nil)
allow(Crono.scheduler).to receive(:add_job)
Crono.perform(TestJob, :some, {some: 'data'}).every(2.days, at: '15:30')
end
it 'should add job with options to schedule' do
expect(Crono::Job).to receive(:new).with(TestJob, kind_of(Crono::Period), [], {some_option: true})
allow(Crono.scheduler).to receive(:add_job)
Crono.perform(TestJob).with_options(some_option: true).every(2.days, at: '15:30')
end
end

View File

@@ -4,7 +4,12 @@ describe Crono::Period do
describe '#description' do
it 'should return period description' do
@period = Crono::Period.new(1.week, on: :monday, at: '15:20')
expect(@period.description).to be_eql('every 7 days at 15:20 on Monday')
expected_description = if ActiveSupport::VERSION::MAJOR >= 5
'every 1 week at 15:20 on Monday'
else
'every 7 days at 15:20 on Monday'
end
expect(@period.description).to be_eql(expected_description)
end
end
@@ -44,6 +49,16 @@ describe Crono::Period do
expect(@period.next).to be_eql(tuesday)
end
end
it 'should return today on the first run if not too late' do
@period = Crono::Period.new(1.week, on: :sunday, at: '22:00')
Timecop.freeze(Time.now.beginning_of_week.advance(days: 6)
.change(hour: 21, min: 0)) do
expect(@period.next).to be_eql(
Time.now.beginning_of_week.advance(days: 6).change(hour: 22, min: 0)
)
end
end
end
context 'in daily basis' do

View File

@@ -2,7 +2,7 @@
%a{ href: url('/') }
%i.mdi-navigation-chevron-left
Back to Home
%h4 "#{@job.job_id}" Log:
%h4 "#{@job.job_id}" Last log:
%main.container.blue-grey.lighten-4.grey-text.text-darken-4
- if @job.healthy == false