crono 0.7.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Changes.md +19 -8
- data/Gemfile.lock +5 -2
- data/README.md +11 -9
- data/bin/crono +2 -2
- data/crono.gemspec +21 -20
- data/examples/cronotab.rb +3 -4
- data/lib/crono.rb +12 -9
- data/lib/crono/cli.rb +21 -15
- data/lib/crono/config.rb +6 -9
- data/lib/crono/job.rb +49 -25
- data/lib/crono/logging.rb +2 -1
- data/lib/crono/orm/active_record/crono_job.rb +2 -1
- data/lib/crono/performer_proxy.rb +1 -0
- data/lib/crono/period.rb +37 -12
- data/lib/crono/scheduler.rb +3 -1
- data/lib/crono/version.rb +1 -1
- data/lib/crono/web.rb +6 -4
- data/lib/generators/crono/install/install_generator.rb +5 -3
- data/lib/generators/crono/install/templates/cronotab.rb.erb +2 -2
- data/lib/generators/crono/install/templates/migrations/create_crono_jobs.rb +1 -0
- data/spec/cli_spec.rb +20 -23
- data/spec/config_spec.rb +5 -5
- data/spec/job_spec.rb +49 -27
- data/spec/orm/active_record/crono_job_spec.rb +8 -8
- data/spec/performer_proxy_spec.rb +5 -4
- data/spec/period_spec.rb +59 -15
- data/spec/scheduler_spec.rb +11 -10
- data/spec/spec_helper.rb +5 -4
- data/spec/web_spec.rb +48 -0
- data/web/views/dashboard.haml +8 -3
- data/web/views/job.haml +7 -1
- data/web/views/layout.haml +9 -9
- metadata +18 -3
@@ -1,12 +1,13 @@
|
|
1
|
-
require
|
1
|
+
require 'spec_helper'
|
2
2
|
|
3
3
|
class TestJob
|
4
|
-
def perform
|
4
|
+
def perform
|
5
|
+
end
|
5
6
|
end
|
6
7
|
|
7
8
|
describe Crono::PerformerProxy do
|
8
|
-
it
|
9
|
+
it 'should add job to schedule' do
|
9
10
|
expect(Crono.scheduler).to receive(:add_job).with(kind_of(Crono::Job))
|
10
|
-
Crono.perform(TestJob).every(2.days, at:
|
11
|
+
Crono.perform(TestJob).every(2.days, at: '15:30')
|
11
12
|
end
|
12
13
|
end
|
data/spec/period_spec.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Crono::Period do
|
4
4
|
around(:each) do |example|
|
@@ -7,49 +7,93 @@ describe Crono::Period do
|
|
7
7
|
end
|
8
8
|
end
|
9
9
|
|
10
|
-
describe
|
11
|
-
it
|
12
|
-
@period = Crono::Period.new(
|
13
|
-
expect(@period.description).to be_eql(
|
10
|
+
describe '#description' do
|
11
|
+
it 'should return period description' do
|
12
|
+
@period = Crono::Period.new(1.week, on: :monday, at: '15:20')
|
13
|
+
expect(@period.description).to be_eql('every 7 days at 15:20 on Monday')
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
17
|
-
describe
|
18
|
-
context
|
19
|
-
it "should
|
17
|
+
describe '#next' do
|
18
|
+
context 'in weakly basis' do
|
19
|
+
it "should raise error if 'on' is wrong" do
|
20
|
+
expect { @period = Crono::Period.new(7.days, on: :bad_day) }
|
21
|
+
.to raise_error("Wrong 'on' day")
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'should raise error when period is less than 1 week' do
|
25
|
+
expect { @period = Crono::Period.new(6.days, on: :monday) }
|
26
|
+
.to raise_error("period should be at least 1 week to use 'on'")
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should return a 'on' day" do
|
30
|
+
@period = Crono::Period.new(1.week, on: :thursday, at: '15:30')
|
31
|
+
current_week = Time.now.beginning_of_week
|
32
|
+
last_run_time = current_week.advance(days: 1) # last run on the tuesday
|
33
|
+
next_run_at = Time.now.next_week.advance(days: 3)
|
34
|
+
.change(hour: 15, min: 30)
|
35
|
+
expect(@period.next(since: last_run_time)).to be_eql(next_run_at)
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should return a next week day 'on'" do
|
39
|
+
@period = Crono::Period.new(1.week, on: :thursday)
|
40
|
+
Timecop.freeze(Time.now.beginning_of_week.advance(days: 4)) do
|
41
|
+
expect(@period.next).to be_eql(Time.now.next_week.advance(days: 3))
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'should return a current week day on the first run if not too late' do
|
46
|
+
@period = Crono::Period.new(7.days, on: :tuesday)
|
47
|
+
beginning_of_the_week = Time.now.beginning_of_week
|
48
|
+
tuesday = beginning_of_the_week.advance(days: 1)
|
49
|
+
Timecop.freeze(beginning_of_the_week) do
|
50
|
+
expect(@period.next).to be_eql(tuesday)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
context 'in daily basis' do
|
56
|
+
it 'should return the time 2 days from now' do
|
20
57
|
@period = Crono::Period.new(2.day)
|
21
58
|
expect(@period.next).to be_eql(2.day.from_now)
|
22
59
|
end
|
23
60
|
|
24
61
|
it "should set time to 'at' time as a string" do
|
25
62
|
time = 10.minutes.ago
|
26
|
-
|
63
|
+
at = [time.hour, time.min].join(':')
|
64
|
+
@period = Crono::Period.new(2.day, at: at)
|
27
65
|
expect(@period.next).to be_eql(2.day.from_now.change(hour: time.hour, min: time.min))
|
28
66
|
end
|
29
67
|
|
30
68
|
it "should set time to 'at' time as a hash" do
|
31
69
|
time = 10.minutes.ago
|
32
|
-
at = {hour: time.hour, min: time.min}
|
70
|
+
at = { hour: time.hour, min: time.min }
|
33
71
|
@period = Crono::Period.new(2.day, at: at)
|
34
72
|
expect(@period.next).to be_eql(2.day.from_now.change(at))
|
35
73
|
end
|
36
74
|
|
37
75
|
it "should raise error when 'at' is wrong" do
|
38
76
|
expect {
|
39
|
-
|
77
|
+
Crono::Period.new(2.day, at: 1)
|
40
78
|
}.to raise_error("Unknown 'at' format")
|
41
79
|
end
|
42
80
|
|
43
|
-
it
|
81
|
+
it 'should raise error when period is less than 1 day' do
|
82
|
+
expect {
|
83
|
+
Crono::Period.new(5.hours, at: '15:30')
|
84
|
+
}.to raise_error("period should be at least 1 day to use 'at'")
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'should return time in relation to last time' do
|
44
88
|
@period = Crono::Period.new(2.day)
|
45
89
|
expect(@period.next(since: 1.day.ago)).to be_eql(1.day.from_now)
|
46
90
|
end
|
47
91
|
|
48
|
-
it
|
92
|
+
it 'should return today time if it is first run and not too late' do
|
49
93
|
time = 10.minutes.from_now
|
50
|
-
at = {hour: time.hour, min: time.min}
|
94
|
+
at = { hour: time.hour, min: time.min }
|
51
95
|
@period = Crono::Period.new(2.day, at: at)
|
52
|
-
expect(@period.next).to be_eql(Time.now.change(at))
|
96
|
+
expect(@period.next.utc.to_s).to be_eql(Time.now.change(at).utc.to_s)
|
53
97
|
end
|
54
98
|
end
|
55
99
|
end
|
data/spec/scheduler_spec.rb
CHANGED
@@ -1,30 +1,31 @@
|
|
1
|
-
require
|
1
|
+
require 'spec_helper'
|
2
2
|
|
3
3
|
class TestJob
|
4
|
-
def perform
|
4
|
+
def perform
|
5
|
+
end
|
5
6
|
end
|
6
7
|
|
7
8
|
describe Crono::Scheduler do
|
8
9
|
before(:each) do
|
9
10
|
@scheduler = Crono::Scheduler.new
|
10
11
|
@jobs = [
|
11
|
-
Crono::Period.new(3.day, at: 10.minutes.from_now.strftime(
|
12
|
-
Crono::Period.new(1.day, at: 20.minutes.from_now.strftime(
|
13
|
-
Crono::Period.new(7.day, at: 40.minutes.from_now.strftime(
|
12
|
+
Crono::Period.new(3.day, at: 10.minutes.from_now.strftime('%H:%M')),
|
13
|
+
Crono::Period.new(1.day, at: 20.minutes.from_now.strftime('%H:%M')),
|
14
|
+
Crono::Period.new(7.day, at: 40.minutes.from_now.strftime('%H:%M'))
|
14
15
|
].map { |period| Crono::Job.new(TestJob, period) }
|
15
16
|
@scheduler.jobs = @jobs
|
16
17
|
end
|
17
18
|
|
18
|
-
describe
|
19
|
-
it
|
20
|
-
@job = Crono::Job.new(TestJob, Crono::Period.new(10.day, at:
|
19
|
+
describe '#add_job' do
|
20
|
+
it 'should call Job#load on Job' do
|
21
|
+
@job = Crono::Job.new(TestJob, Crono::Period.new(10.day, at: '04:05'))
|
21
22
|
expect(@job).to receive(:load)
|
22
23
|
@scheduler.add_job(@job)
|
23
24
|
end
|
24
25
|
end
|
25
26
|
|
26
|
-
describe
|
27
|
-
it
|
27
|
+
describe '#next' do
|
28
|
+
it 'should return next job in schedule' do
|
28
29
|
expect(@scheduler.next).to be @jobs[0]
|
29
30
|
end
|
30
31
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -6,9 +6,10 @@ require 'byebug'
|
|
6
6
|
require 'crono'
|
7
7
|
require 'generators/crono/install/templates/migrations/create_crono_jobs.rb'
|
8
8
|
|
9
|
-
ActiveRecord::Base.establish_connection(
|
9
|
+
ActiveRecord::Base.establish_connection(
|
10
|
+
adapter: 'sqlite3',
|
11
|
+
database: 'file::memory:?cache=shared'
|
12
|
+
)
|
13
|
+
|
10
14
|
ActiveRecord::Base.logger = Logger.new(STDOUT)
|
11
15
|
CreateCronoJobs.up
|
12
|
-
|
13
|
-
RSpec.configure do |config|
|
14
|
-
end
|
data/spec/web_spec.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'rack/test'
|
3
|
+
include Rack::Test::Methods
|
4
|
+
|
5
|
+
describe Crono::Web do
|
6
|
+
let(:app) { Crono::Web }
|
7
|
+
|
8
|
+
before do
|
9
|
+
@test_job_id = 'Perform TestJob every 5 seconds'
|
10
|
+
@test_job_log = 'All runs ok'
|
11
|
+
@test_job = Crono::CronoJob.create!(
|
12
|
+
job_id: @test_job_id,
|
13
|
+
log: @test_job_log
|
14
|
+
)
|
15
|
+
end
|
16
|
+
|
17
|
+
after { @test_job.destroy }
|
18
|
+
|
19
|
+
describe '/' do
|
20
|
+
it 'should show all jobs' do
|
21
|
+
get '/'
|
22
|
+
expect(last_response).to be_ok
|
23
|
+
expect(last_response.body).to include @test_job_id
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'should show a error mark when a job is unhealthy' do
|
27
|
+
@test_job.update(healthy: false)
|
28
|
+
get '/'
|
29
|
+
expect(last_response.body).to include 'Error'
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe '/job/:id' do
|
34
|
+
it 'should show job log' do
|
35
|
+
get "/job/#{@test_job.id}"
|
36
|
+
expect(last_response).to be_ok
|
37
|
+
expect(last_response.body).to include @test_job_id
|
38
|
+
expect(last_response.body).to include @test_job_log
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'should show a message about the unhealthy job' do
|
42
|
+
message = 'An error occurs during the last execution of this job'
|
43
|
+
@test_job.update(healthy: false)
|
44
|
+
get "/job/#{@test_job.id}"
|
45
|
+
expect(last_response.body).to include message
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/web/views/dashboard.haml
CHANGED
@@ -7,11 +7,16 @@
|
|
7
7
|
%th Job
|
8
8
|
%th Last performed at
|
9
9
|
%th
|
10
|
-
|
10
|
+
%th
|
11
|
+
- @jobs.each do |job|
|
11
12
|
%tr
|
12
13
|
%td= job.job_id
|
13
|
-
%td= job.last_performed_at ||
|
14
|
+
%td= job.last_performed_at || '-'
|
15
|
+
%td
|
16
|
+
- if job.healthy == false
|
17
|
+
%a{ href: url("/job/#{job.id}") }
|
18
|
+
%span.label.label-danger Error
|
14
19
|
%td
|
15
|
-
%a{href: url("/
|
20
|
+
%a{ href: url("/job/#{job.id}") }
|
16
21
|
Log
|
17
22
|
%span.glyphicon.glyphicon-menu-right
|
data/web/views/job.haml
CHANGED
@@ -1,8 +1,14 @@
|
|
1
1
|
%ol.breadcrumb
|
2
2
|
%li
|
3
|
-
%a{href: url(
|
3
|
+
%a{ href: url('/') } Home
|
4
4
|
%li.active= @job.job_id
|
5
5
|
|
6
6
|
%h2
|
7
7
|
"#{@job.job_id}" Log:
|
8
|
+
|
9
|
+
- if @job.healthy == false
|
10
|
+
.alert.alert-danger{ role: 'alert' }
|
11
|
+
An error occurs during the last execution of this job.
|
12
|
+
Check the log below for details.
|
13
|
+
|
8
14
|
%pre= @job.log
|
data/web/views/layout.haml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
!!! 5
|
2
|
-
%html{lang:
|
2
|
+
%html{ lang: 'en' }
|
3
3
|
%head
|
4
|
-
%meta{charset:
|
5
|
-
%meta{
|
6
|
-
%meta{name:
|
4
|
+
%meta{ charset: 'utf-8' }
|
5
|
+
%meta{ 'http-equiv' => 'X-UA-Compatible', content: 'IE=edge' }
|
6
|
+
%meta{ name: 'viewport', content: 'width=device-width, initial-scale=1' }
|
7
7
|
|
8
8
|
%title Crono Dashboard
|
9
9
|
|
10
|
-
%link{href:
|
11
|
-
%link{href:
|
12
|
-
%link{href: "#{env['SCRIPT_NAME']}/custom.css", rel:
|
10
|
+
%link{ href: 'https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css', rel: 'stylesheet' }
|
11
|
+
%link{ href: 'https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap-theme.min.css', rel: 'stylesheet' }
|
12
|
+
%link{ href: "#{env['SCRIPT_NAME']}/custom.css", rel: 'stylesheet' }
|
13
13
|
|
14
14
|
%body
|
15
15
|
%br
|
@@ -21,5 +21,5 @@
|
|
21
21
|
%small Dashboard
|
22
22
|
= yield
|
23
23
|
|
24
|
-
%script{src:
|
25
|
-
%script{src:
|
24
|
+
%script{ src: 'https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js' }
|
25
|
+
%script{ src: 'https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/js/bootstrap.min.js' }
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: crono
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.8.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dzmitry Plashchynski
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-03-
|
11
|
+
date: 2015-03-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activejob
|
@@ -164,6 +164,20 @@ dependencies:
|
|
164
164
|
- - ">="
|
165
165
|
- !ruby/object:Gem::Version
|
166
166
|
version: '0'
|
167
|
+
- !ruby/object:Gem::Dependency
|
168
|
+
name: rack-test
|
169
|
+
requirement: !ruby/object:Gem::Requirement
|
170
|
+
requirements:
|
171
|
+
- - ">="
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '0'
|
174
|
+
type: :development
|
175
|
+
prerelease: false
|
176
|
+
version_requirements: !ruby/object:Gem::Requirement
|
177
|
+
requirements:
|
178
|
+
- - ">="
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
version: '0'
|
167
181
|
description: Job scheduler for Rails
|
168
182
|
email:
|
169
183
|
- plashchynski@gmail.com
|
@@ -209,6 +223,7 @@ files:
|
|
209
223
|
- spec/period_spec.rb
|
210
224
|
- spec/scheduler_spec.rb
|
211
225
|
- spec/spec_helper.rb
|
226
|
+
- spec/web_spec.rb
|
212
227
|
- tmp/.gitkeep
|
213
228
|
- web/assets/custom.css
|
214
229
|
- web/views/dashboard.haml
|
@@ -234,7 +249,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
234
249
|
version: 1.3.6
|
235
250
|
requirements: []
|
236
251
|
rubyforge_project: crono
|
237
|
-
rubygems_version: 2.4.
|
252
|
+
rubygems_version: 2.4.6
|
238
253
|
signing_key:
|
239
254
|
specification_version: 4
|
240
255
|
summary: Job scheduler for Rails
|