crono 0.7.0 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 88e53a7c4eee894469cb7980928acb90726d65c9
4
- data.tar.gz: b0cf49ee7f5edfb936271d5ba4cb7f71319e156e
3
+ metadata.gz: 6935e065a4c161dafada3370230b5f00ee0321e3
4
+ data.tar.gz: d1667f7a5efc7f356bab0ae8b867dd9f6d65e612
5
5
  SHA512:
6
- metadata.gz: 283289e7b9ef4cb6840d55513cbbce57b3c4773e816957e3e5394646c2920f2c50954abd240302c0d0c8dff2f623bc576d12ef682d6c3624e74f6486a7de8842
7
- data.tar.gz: 5c59231c15eb083f7ca341fdf1d0637cb50cd4ceec8cd05f17661ead5aa68507c664a217598cb1ae858b2ff784058b0fae3db760ffd10ab01fa106916460ea23
6
+ metadata.gz: f727c28c3d2d909fe1a8f12fb26074ca0ce7f574cc4323da62ec638f92514b4e4256b882f81b8685496f6c87ffe0e280e94d8204642c1ca54b6e3d035036819b
7
+ data.tar.gz: 54aadbd50534a87e59396c020da59ca8efeb04b708a209cf0cf891409034669781ec0ff54899ee0ae7432ecaba96a37a06d061c2942287a07e7e672170e75c63
data/Changes.md CHANGED
@@ -1,24 +1,35 @@
1
- 0.5.0
1
+ 0.8.0
2
2
  -----------
3
3
 
4
- - Initial release!
4
+ - Added `on` (day of week) option to cronotab.rb semantic
5
+ - Added job health check and job health indicator to the Web UI
5
6
 
6
- 0.5.1
7
+
8
+ 0.7.0
7
9
  -----------
8
10
 
9
- - Added -e/--environment ENV option to set the daemon rails environment.
11
+ - Added simple Web UI
12
+
13
+
14
+ 0.6.1
15
+ -----------
16
+
17
+ - Persist job state to your database.
18
+
10
19
 
11
20
  0.5.2
12
21
  -----------
13
22
 
14
23
  - Fix: Scheduled time now related to the last performing time.
15
24
 
16
- 0.6.1
25
+
26
+ 0.5.1
17
27
  -----------
18
28
 
19
- - Persist job state to your database.
29
+ - Added -e/--environment ENV option to set the daemon rails environment.
20
30
 
21
- 0.7.0
31
+
32
+ 0.5.0
22
33
  -----------
23
34
 
24
- - Added simple Web UI
35
+ - Initial release!
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- crono (0.7.0)
4
+ crono (0.8.0)
5
5
  activejob (~> 4.0)
6
6
  activerecord (~> 4.0)
7
7
  activesupport (~> 4.0)
@@ -44,6 +44,8 @@ GEM
44
44
  rack (1.6.0)
45
45
  rack-protection (1.5.3)
46
46
  rack
47
+ rack-test (0.6.3)
48
+ rack (>= 1.0)
47
49
  rake (10.4.2)
48
50
  rspec (3.2.0)
49
51
  rspec-core (~> 3.2.0)
@@ -64,7 +66,7 @@ GEM
64
66
  tilt (~> 1.3, >= 1.3.4)
65
67
  slop (3.6.0)
66
68
  sqlite3 (1.3.10)
67
- thread_safe (0.3.4)
69
+ thread_safe (0.3.5)
68
70
  tilt (1.4.1)
69
71
  timecop (0.7.3)
70
72
  tzinfo (1.2.2)
@@ -78,6 +80,7 @@ DEPENDENCIES
78
80
  byebug
79
81
  crono!
80
82
  haml
83
+ rack-test
81
84
  rake (~> 10.0)
82
85
  rspec (~> 3.0)
83
86
  sinatra
data/README.md CHANGED
@@ -9,12 +9,13 @@ Crono — Job scheduler for Rails
9
9
  Crono is a time-based background job scheduler daemon (just like Cron) for Ruby on Rails.
10
10
 
11
11
 
12
- ## The Idea
12
+ ## The Purpose
13
13
 
14
- 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.
14
+ Currently there is no such thing as Ruby Cron for Rails. Well, there's [Whenever](https://github.com/javan/whenever) but it works on top of Unix Cron, so you haven't 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 states to your database using Active Record. You have full control of jobs performing process. It's Ruby, so you can understand and modify it to fit your needs.
15
15
 
16
16
  ![Web UI](https://github.com/plashchynski/crono/raw/master/examples/crono_web_ui.png)
17
17
 
18
+
18
19
  ## Requirements
19
20
 
20
21
  Tested with latest MRI Ruby (2.2, 2.1 and 2.0) and Rails 3.2+
@@ -73,18 +74,19 @@ end
73
74
 
74
75
  #### Job Schedule
75
76
 
76
- The schedule described in the configuration file `config/cronotab.rb`, that created using `crono:install` or manually. The semantic is pretty straightforward:
77
+ Schedule list is defined in the file `config/cronotab.rb`, that created using `crono:install`. The semantic is pretty straightforward:
77
78
 
78
79
  ```ruby
79
80
  # config/cronotab.rb
80
- Crono.perform(TestJob).every 2.days, at: "15:30"
81
+ Crono.perform(TestJob).every 2.days, at: {hour: 15, min: 30}
82
+ Crono.perform(TestJob).every 1.week, on: :monday, at: "15:30"
81
83
  ```
82
84
 
83
- You can schedule one job a few times, if you want a job to be performed a few times a day:
85
+ You can schedule one job a few times, if you want the job to be performed a few times a day or a week:
84
86
 
85
87
  ```ruby
86
- Crono.perform(TestJob).every 1.day, at: "00:00"
87
- Crono.perform(TestJob).every 1.day, at: "12:00"
88
+ Crono.perform(TestJob).every 1.week, on: :monday
89
+ Crono.perform(TestJob).every 1.week, on: :thursday
88
90
  ```
89
91
 
90
92
  The `at` can be a Hash:
@@ -109,6 +111,7 @@ Usage: crono [options]
109
111
  -e, --environment ENV Application environment (Default: development)
110
112
  ```
111
113
 
114
+
112
115
  ## Web UI
113
116
 
114
117
  Crono comes with a Sinatra application that can display the current state of Crono jobs.
@@ -122,8 +125,6 @@ gem 'sinatra', require: nil
122
125
  Add the following to your `config/routes.rb`:
123
126
 
124
127
  ```ruby
125
- require 'crono/web'
126
-
127
128
  Rails.application.routes.draw do
128
129
  mount Crono::Web, at: '/crono'
129
130
  ...
@@ -131,6 +132,7 @@ Rails.application.routes.draw do
131
132
 
132
133
  Access management and other questions described in the [wiki](https://github.com/plashchynski/crono/wiki/Web-UI).
133
134
 
135
+
134
136
  ## Capistrano
135
137
 
136
138
  Use the `capistrano-crono` gem ([github](https://github.com/plashchynski/capistrano-crono/)).
data/bin/crono CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- $LOAD_PATH.unshift File.expand_path("../../lib", __FILE__)
3
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
4
4
 
5
- require "crono/cli"
5
+ require 'crono/cli'
6
6
 
7
7
  begin
8
8
  Crono::CLI.instance.run
@@ -2,30 +2,31 @@
2
2
  require File.expand_path('../lib/crono/version', __FILE__)
3
3
 
4
4
  Gem::Specification.new do |s|
5
- s.name = "crono"
5
+ s.name = 'crono'
6
6
  s.version = Crono::VERSION
7
- s.authors = ["Dzmitry Plashchynski"]
8
- s.email = ["plashchynski@gmail.com"]
9
- s.homepage = "https://github.com/plashchynski/crono"
10
- s.description = s.summary = "Job scheduler for Rails"
11
- s.license = "Apache-2.0"
7
+ s.authors = ['Dzmitry Plashchynski']
8
+ s.email = ['plashchynski@gmail.com']
9
+ s.homepage = 'https://github.com/plashchynski/crono'
10
+ s.description = s.summary = 'Job scheduler for Rails'
11
+ s.license = 'Apache-2.0'
12
12
 
13
- s.required_rubygems_version = ">= 1.3.6"
14
- s.rubyforge_project = "crono"
13
+ s.required_rubygems_version = '>= 1.3.6'
14
+ s.rubyforge_project = 'crono'
15
15
 
16
- s.add_runtime_dependency "activejob", "~> 4.0"
17
- s.add_runtime_dependency "activesupport", "~> 4.0"
18
- s.add_runtime_dependency "activerecord", "~> 4.0"
19
- s.add_development_dependency "rake", "~> 10.0"
20
- s.add_development_dependency "bundler", ">= 1.0.0"
21
- s.add_development_dependency "rspec", "~> 3.0"
22
- s.add_development_dependency "timecop", "~> 0.7"
23
- s.add_development_dependency "sqlite3"
24
- s.add_development_dependency "byebug"
25
- s.add_development_dependency "sinatra"
26
- s.add_development_dependency "haml"
16
+ s.add_runtime_dependency 'activejob', '~> 4.0'
17
+ s.add_runtime_dependency 'activesupport', '~> 4.0'
18
+ s.add_runtime_dependency 'activerecord', '~> 4.0'
19
+ s.add_development_dependency 'rake', '~> 10.0'
20
+ s.add_development_dependency 'bundler', '>= 1.0.0'
21
+ s.add_development_dependency 'rspec', '~> 3.0'
22
+ s.add_development_dependency 'timecop', '~> 0.7'
23
+ s.add_development_dependency 'sqlite3'
24
+ s.add_development_dependency 'byebug'
25
+ s.add_development_dependency 'sinatra'
26
+ s.add_development_dependency 'haml'
27
+ s.add_development_dependency 'rack-test'
27
28
 
28
29
  s.files = `git ls-files`.split("\n")
29
- s.executables = `git ls-files`.split("\n").map{|f| f =~ /^bin\/(.*)/ ? $1 : nil}.compact
30
+ s.executables = ['crono']
30
31
  s.require_path = 'lib'
31
32
  end
@@ -1,4 +1,4 @@
1
- # cronotab.rb Crono configuration file
1
+ # cronotab.rb - Crono configuration file
2
2
  #
3
3
  # Here you can specify periodic jobs and schedule.
4
4
  # You can use ActiveJob's jobs from `app/jobs/`
@@ -7,9 +7,8 @@
7
7
  #
8
8
  class TestJob
9
9
  def perform
10
- puts "Test!"
10
+ puts 'Test!'
11
11
  end
12
12
  end
13
13
 
14
- Crono.perform(TestJob).every 2.days, at: "15:30"
15
-
14
+ Crono.perform(TestJob).every 2.days, at: '15:30'
@@ -1,12 +1,15 @@
1
+ # Crono main module
1
2
  module Crono
2
3
  end
3
4
 
4
- require "active_support/all"
5
- require "crono/version.rb"
6
- require "crono/logging.rb"
7
- require "crono/period.rb"
8
- require "crono/job.rb"
9
- require "crono/scheduler.rb"
10
- require "crono/config.rb"
11
- require "crono/performer_proxy.rb"
12
- require "crono/orm/active_record/crono_job.rb"
5
+ require 'active_support/all'
6
+ require 'crono/version'
7
+ require 'crono/logging'
8
+ require 'crono/period'
9
+ require 'crono/job'
10
+ require 'crono/scheduler'
11
+ require 'crono/config'
12
+ require 'crono/performer_proxy'
13
+ require 'crono/orm/active_record/crono_job'
14
+
15
+ Crono.autoload :Web, 'crono/web'
@@ -4,6 +4,7 @@ require 'optparse'
4
4
  module Crono
5
5
  mattr_accessor :scheduler
6
6
 
7
+ # Crono::CLI - The main class for the crono daemon exacutable `bin/crono`
7
8
  class CLI
8
9
  include Singleton
9
10
  include Logging
@@ -18,12 +19,7 @@ module Crono
18
19
  def run
19
20
  parse_options(ARGV)
20
21
 
21
- if config.daemonize
22
- set_log_to(config.logfile)
23
- daemonize
24
- else
25
- set_log_to(STDOUT)
26
- end
22
+ setup_log
27
23
 
28
24
  write_pid
29
25
  load_rails
@@ -33,7 +29,17 @@ module Crono
33
29
  start_working_loop
34
30
  end
35
31
 
36
- private
32
+ private
33
+
34
+ def setup_log
35
+ if config.daemonize
36
+ self.logifile = config.logfile
37
+ daemonize
38
+ else
39
+ self.logfile = STDOUT
40
+ end
41
+ end
42
+
37
43
  def daemonize
38
44
  ::Process.daemon(true, true)
39
45
 
@@ -42,7 +48,7 @@ module Crono
42
48
  io.sync = true
43
49
  end
44
50
 
45
- $stdin.reopen("/dev/null")
51
+ $stdin.reopen('/dev/null')
46
52
  end
47
53
 
48
54
  def write_pid
@@ -54,28 +60,28 @@ module Crono
54
60
  logger.info "Loading Crono #{Crono::VERSION}"
55
61
  logger.info "Running in #{RUBY_DESCRIPTION}"
56
62
 
57
- logger.info "Jobs:"
63
+ logger.info 'Jobs:'
58
64
  Crono.scheduler.jobs.each do |job|
59
- logger.info %{"#{job.performer}" with rule "#{job.period.description}" next time will perform at #{job.next}}
65
+ logger.info "'#{job.performer}' with rule '#{job.period.description}'"\
66
+ "next time will perform at #{job.next}"
60
67
  end
61
68
  end
62
69
 
63
70
  def load_rails
64
71
  ENV['RACK_ENV'] = ENV['RAILS_ENV'] = config.environment
65
72
  require 'rails'
66
- require File.expand_path("config/environment.rb")
73
+ require File.expand_path('config/environment.rb')
67
74
  ::Rails.application.eager_load!
68
75
  require File.expand_path(config.cronotab)
69
76
  end
70
77
 
71
78
  def check_jobs
72
- if Crono.scheduler.jobs.empty?
73
- logger.error "You have no jobs defined in you cronotab file #{config.cronotab}"
74
- end
79
+ return if Crono.scheduler.jobs.present?
80
+ logger.error "You have no jobs in you cronotab file #{config.cronotab}"
75
81
  end
76
82
 
77
83
  def start_working_loop
78
- while job = Crono.scheduler.next do
84
+ while (job = Crono.scheduler.next)
79
85
  sleep(job.next - Time.now)
80
86
  job.perform
81
87
  end
@@ -1,21 +1,18 @@
1
1
  module Crono
2
+ # Crono::Config stores Crono configuration
2
3
  class Config
3
- CRONOTAB = "config/cronotab.rb"
4
- LOGFILE = "log/crono.log"
5
- PIDFILE = "tmp/pids/crono.pid"
4
+ CRONOTAB = 'config/cronotab.rb'
5
+ LOGFILE = 'log/crono.log'
6
+ PIDFILE = 'tmp/pids/crono.pid'
6
7
 
7
- attr_accessor :cronotab
8
- attr_accessor :logfile
9
- attr_accessor :pidfile
10
- attr_accessor :daemonize
11
- attr_accessor :environment
8
+ attr_accessor :cronotab, :logfile, :pidfile, :daemonize, :environment
12
9
 
13
10
  def initialize
14
11
  self.cronotab = CRONOTAB
15
12
  self.logfile = LOGFILE
16
13
  self.pidfile = PIDFILE
17
14
  self.daemonize = false
18
- self.environment = ENV['RAILS_ENV'] || ENV['RACK_ENV'] || "development"
15
+ self.environment = ENV['RAILS_ENV'] || ENV['RACK_ENV'] || 'development'
19
16
  end
20
17
  end
21
18
  end
@@ -2,14 +2,12 @@ require 'stringio'
2
2
  require 'logger'
3
3
 
4
4
  module Crono
5
+ # Crono::Job represents a Crono job
5
6
  class Job
6
7
  include Logging
7
8
 
8
- attr_accessor :performer
9
- attr_accessor :period
10
- attr_accessor :last_performed_at
11
- attr_accessor :job_log
12
- attr_accessor :job_logger
9
+ attr_accessor :performer, :period, :last_performed_at, :job_log,
10
+ :job_logger, :healthy
13
11
 
14
12
  def initialize(performer, period)
15
13
  self.performer, self.period = performer, period
@@ -35,26 +33,13 @@ module Crono
35
33
  log "Perform #{performer}"
36
34
  self.last_performed_at = Time.now
37
35
 
38
- Thread.new do
39
- begin
40
- performer.new.perform
41
- rescue Exception => e
42
- log "Finished #{performer} in %.2f seconds with error: #{e.message}" % (Time.now - last_performed_at)
43
- log e.backtrace.join("\n")
44
- else
45
- log "Finished #{performer} in %.2f seconds" % (Time.now - last_performed_at)
46
- ensure
47
- save
48
- end
49
- end
36
+ Thread.new { perform_job }
50
37
  end
51
38
 
52
39
  def save
53
40
  @semaphore.synchronize do
54
- log = model.reload.log || ""
55
- log << job_log.string
56
- job_log.truncate(job_log.rewind)
57
- model.update(last_performed_at: last_performed_at, log: log)
41
+ update_model
42
+ clear_job_log
58
43
  end
59
44
  end
60
45
 
@@ -62,11 +47,50 @@ module Crono
62
47
  self.last_performed_at = model.last_performed_at
63
48
  end
64
49
 
65
- private
66
- def log(message)
50
+ private
51
+
52
+ def clear_job_log
53
+ job_log.truncate(job_log.rewind)
54
+ end
55
+
56
+ def update_model
57
+ saved_log = model.reload.log || ''
58
+ log_to_save = saved_log + job_log.string
59
+ model.update(last_performed_at: last_performed_at, log: log_to_save,
60
+ healthy: healthy)
61
+ end
62
+
63
+ def perform_job
64
+ performer.new.perform
65
+ finished_time_sec = format('%.2f', Time.now - last_performed_at)
66
+ rescue StandardError => e
67
+ handle_job_fail(e, finished_time_sec)
68
+ else
69
+ handle_job_success(finished_time_sec)
70
+ ensure
71
+ save
72
+ end
73
+
74
+ def handle_job_fail(exception, finished_time_sec)
75
+ self.healthy = false
76
+ log_error "Finished #{performer} in #{finished_time_sec} seconds"\
77
+ "with error: #{exception.message}"
78
+ log_error exception.backtrace.join("\n")
79
+ end
80
+
81
+ def handle_job_success(finished_time_sec)
82
+ self.healthy = true
83
+ log "Finished #{performer} in #{finished_time_sec} seconds"
84
+ end
85
+
86
+ def log_error(message)
87
+ log(message, Logger::ERROR)
88
+ end
89
+
90
+ def log(message, severity = Logger::INFO)
67
91
  @semaphore.synchronize do
68
- logger.info message
69
- job_logger.info message
92
+ logger.log severity, message
93
+ job_logger.log severity, message
70
94
  end
71
95
  end
72
96