Lint the whole project

This commit is contained in:
Dzmitry Plashchynski
2015-03-13 20:32:27 +02:00
parent 1aa27baca8
commit a9798acb35
23 changed files with 203 additions and 172 deletions

View File

@@ -1,8 +1,8 @@
#!/usr/bin/env ruby
$LOAD_PATH.unshift File.expand_path("../../lib", __FILE__)
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
require "crono/cli"
require 'crono/cli'
begin
Crono::CLI.instance.run

View File

@@ -1,4 +1,4 @@
# cronotab.rb Crono configuration file
# cronotab.rb - Crono configuration file
#
# Here you can specify periodic jobs and schedule.
# You can use ActiveJob's jobs from `app/jobs/`
@@ -7,9 +7,8 @@
#
class TestJob
def perform
puts "Test!"
puts 'Test!'
end
end
Crono.perform(TestJob).every 2.days, at: "15:30"
Crono.perform(TestJob).every 2.days, at: '15:30'

View File

@@ -1,14 +1,15 @@
# Crono main module
module Crono
end
require "active_support/all"
require "crono/version"
require "crono/logging"
require "crono/period"
require "crono/job"
require "crono/scheduler"
require "crono/config"
require "crono/performer_proxy"
require "crono/orm/active_record/crono_job"
require 'active_support/all'
require 'crono/version'
require 'crono/logging'
require 'crono/period'
require 'crono/job'
require 'crono/scheduler'
require 'crono/config'
require 'crono/performer_proxy'
require 'crono/orm/active_record/crono_job'
Crono.autoload :Web, "crono/web"
Crono.autoload :Web, 'crono/web'

View File

@@ -4,6 +4,7 @@ require 'optparse'
module Crono
mattr_accessor :scheduler
# Crono::CLI - Main class for the crono daemon exacutable `bin/crono`
class CLI
include Singleton
include Logging
@@ -18,12 +19,7 @@ module Crono
def run
parse_options(ARGV)
if config.daemonize
set_log_to(config.logfile)
daemonize
else
set_log_to(STDOUT)
end
setup_log
write_pid
load_rails
@@ -33,7 +29,17 @@ module Crono
start_working_loop
end
private
private
def setup_log
if config.daemonize
self.logifile = config.logfile
daemonize
else
self.logfile = STDOUT
end
end
def daemonize
::Process.daemon(true, true)
@@ -42,7 +48,7 @@ module Crono
io.sync = true
end
$stdin.reopen("/dev/null")
$stdin.reopen('/dev/null')
end
def write_pid
@@ -54,16 +60,16 @@ module Crono
logger.info "Loading Crono #{Crono::VERSION}"
logger.info "Running in #{RUBY_DESCRIPTION}"
logger.info "Jobs:"
logger.info 'Jobs:'
Crono.scheduler.jobs.each do |job|
logger.info %{"#{job.performer}" with rule "#{job.period.description}" next time will perform at #{job.next}}
logger.info %("#{job.performer}" with rule "#{job.period.description}" next time will perform at #{job.next})
end
end
def load_rails
ENV['RACK_ENV'] = ENV['RAILS_ENV'] = config.environment
require 'rails'
require File.expand_path("config/environment.rb")
require File.expand_path('config/environment.rb')
::Rails.application.eager_load!
require File.expand_path(config.cronotab)
end
@@ -75,7 +81,7 @@ module Crono
end
def start_working_loop
while job = Crono.scheduler.next do
while (job = Crono.scheduler.next)
sleep(job.next - Time.now)
job.perform
end

View File

@@ -1,8 +1,9 @@
module Crono
# Crono::Config stores Crono configuration
class Config
CRONOTAB = "config/cronotab.rb"
LOGFILE = "log/crono.log"
PIDFILE = "tmp/pids/crono.pid"
CRONOTAB = 'config/cronotab.rb'
LOGFILE = 'log/crono.log'
PIDFILE = 'tmp/pids/crono.pid'
attr_accessor :cronotab, :logfile, :pidfile, :daemonize, :environment
@@ -11,7 +12,7 @@ module Crono
self.logfile = LOGFILE
self.pidfile = PIDFILE
self.daemonize = false
self.environment = ENV['RAILS_ENV'] || ENV['RACK_ENV'] || "development"
self.environment = ENV['RAILS_ENV'] || ENV['RACK_ENV'] || 'development'
end
end
end

View File

@@ -2,10 +2,12 @@ require 'stringio'
require 'logger'
module Crono
# Crono::Job represents a Crono job
class Job
include Logging
attr_accessor :performer, :period, :last_performed_at, :job_log, :job_logger, :healthy
attr_accessor :performer, :period, :last_performed_at, :job_log,
:job_logger, :healthy
def initialize(performer, period)
self.performer, self.period = performer, period
@@ -36,10 +38,14 @@ module Crono
def save
@semaphore.synchronize do
log = model.reload.log || ""
log = model.reload.log || ''
log << job_log.string
job_log.truncate(job_log.rewind)
model.update(last_performed_at: last_performed_at, log: log, healthy: healthy)
model.update(
last_performed_at: last_performed_at,
log: log,
healthy: healthy
)
end
end
@@ -47,20 +53,21 @@ module Crono
self.last_performed_at = model.last_performed_at
end
private
private
def perform_job
begin
performer.new.perform
rescue Exception => e
log_error "Finished #{performer} in %.2f seconds with error: #{e.message}" % (Time.now - last_performed_at)
log_error e.backtrace.join("\n")
self.healthy = false
else
self.healthy = true
log "Finished #{performer} in %.2f seconds" % (Time.now - last_performed_at)
ensure
save
end
performer.new.perform
finished_time_sec = format('%.2f', Time.now - last_performed_at)
rescue StandardError => e
log_error "Finished #{performer} in #{finished_time_sec} seconds"\
"with error: #{e.message}"
log_error e.backtrace.join("\n")
self.healthy = false
else
self.healthy = true
log "Finished #{performer} in #{finished_time_sec} seconds"
ensure
save
end
def log_error(message)

View File

@@ -1,8 +1,9 @@
module Crono
mattr_accessor :logger
# Crono::Logging is a standart Ruby logger wrapper
module Logging
def set_log_to(logfile)
def logfile=(logfile)
Crono.logger = Logger.new(logfile)
end

View File

@@ -1,8 +1,9 @@
require 'active_record'
module Crono
# Crono::CronoJob is a ActiveRecord model to store job state
class CronoJob < ActiveRecord::Base
self.table_name = "crono_jobs"
self.table_name = 'crono_jobs'
validates :job_id, presence: true, uniqueness: true
end
end

View File

@@ -1,4 +1,5 @@
module Crono
# PerformerProxy is a proxy used in cronotab.rb semantic
class PerformerProxy
def initialize(performer, scheduler)
@performer = performer

View File

@@ -1,4 +1,5 @@
module Crono
# Period describe frequency of performing a task
class Period
def initialize(period, at: nil)
@period = period
@@ -17,7 +18,7 @@ module Crono
def description
desc = "every #{@period.inspect}"
desc += " at %.2i:%.2i" % [@at_hour, @at_min] if @at_hour && @at_min
desc += format(' at %.2i:%.2i', @at_hour, @at_min) if @at_hour && @at_min
desc
end
@@ -29,13 +30,14 @@ module Crono
when Hash
return at[:hour], at[:min]
else
raise "Unknown 'at' format"
fail "Unknown 'at' format"
end
end
private
private
def time_atts
{hour: @at_hour, min: @at_min}.compact
{ hour: @at_hour, min: @at_min }.compact
end
end
end

View File

@@ -1,4 +1,5 @@
module Crono
# Scheduler is a container for job list and queue
class Scheduler
attr_accessor :jobs
@@ -15,7 +16,8 @@ module Crono
queue.first
end
private
private
def queue
jobs.sort_by(&:next)
end

View File

@@ -2,10 +2,11 @@ require 'haml'
require 'sinatra/base'
module Crono
# Web is a Web UI Sinatra app
class Web < Sinatra::Base
set :root, File.expand_path(File.dirname(__FILE__) + "/../../web")
set :public_folder, Proc.new { "#{root}/assets" }
set :views, Proc.new { "#{root}/views" }
set :root, File.expand_path(File.dirname(__FILE__) + '/../../web')
set :public_folder, proc { "#{root}/assets" }
set :views, proc { "#{root}/views" }
get '/' do
@jobs = Crono::CronoJob.all

View File

@@ -11,8 +11,8 @@ module Crono
ActiveRecord::Generators::Base.next_migration_number(path)
end
desc "Installs crono and generates the necessary configuration files"
source_root File.expand_path("../templates", __FILE__)
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'

View File

@@ -7,9 +7,9 @@
#
# class TestJob
# def perform
# puts "Test!"
# puts 'Test!'
# end
# end
#
# Crono.perform(TestJob).every 2.days, at: "15:30"
# Crono.perform(TestJob).every 2.days, at: '15:30'
#

View File

@@ -1,15 +1,16 @@
require "spec_helper"
require 'spec_helper'
require 'crono/cli'
class TestJob
def perform;end
def perform
end
end
describe Crono::CLI do
let(:cli) { Crono::CLI.instance }
describe "#run" do
it "should try to initialize rails with #load_rails and start working loop" 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(:start_working_loop)
expect(cli).to receive(:parse_options)
@@ -18,34 +19,34 @@ describe Crono::CLI do
end
end
describe "#start_working_loop" do
it "should start working loop"
describe '#start_working_loop' do
it 'should start working loop'
end
describe "#parse_options" do
it "should set cronotab" do
cli.send(:parse_options, ["--cronotab", "/tmp/cronotab.rb"])
expect(cli.config.cronotab).to be_eql "/tmp/cronotab.rb"
describe '#parse_options' do
it 'should set cronotab' do
cli.send(:parse_options, ['--cronotab', '/tmp/cronotab.rb'])
expect(cli.config.cronotab).to be_eql '/tmp/cronotab.rb'
end
it "should set logfile" do
cli.send(:parse_options, ["--logfile", "log/crono.log"])
expect(cli.config.logfile).to be_eql "log/crono.log"
it 'should set logfile' do
cli.send(:parse_options, ['--logfile', 'log/crono.log'])
expect(cli.config.logfile).to be_eql 'log/crono.log'
end
it "should set pidfile" do
cli.send(:parse_options, ["--pidfile", "tmp/pids/crono.0.log"])
expect(cli.config.pidfile).to be_eql "tmp/pids/crono.0.log"
it 'should set pidfile' do
cli.send(:parse_options, ['--pidfile', 'tmp/pids/crono.0.log'])
expect(cli.config.pidfile).to be_eql 'tmp/pids/crono.0.log'
end
it "should set daemonize" do
cli.send(:parse_options, ["--daemonize"])
it 'should set daemonize' 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")
it 'should set environment' do
cli.send(:parse_options, ['--environment', 'production'])
expect(cli.config.environment).to be_eql('production')
end
end
end

View File

@@ -1,15 +1,15 @@
require "spec_helper"
require 'spec_helper'
describe Crono::Config do
describe "#initialize" do
it "should initialize with default configuration options" do
ENV["RAILS_ENV"] = "test"
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"]
expect(@config.environment).to be_eql ENV['RAILS_ENV']
end
end
end

View File

@@ -1,66 +1,67 @@
require "spec_helper"
require 'spec_helper'
class TestJob
def perform;end
def perform
end
end
class TestFailingJob
def perform
raise "Some error"
fail 'Some error'
end
end
describe Crono::Job do
let(:period) { Crono::Period.new(2.day) }
let(:job) { Crono::Job.new(TestJob, period) }
let(:job) { Crono::Job.new(TestJob, period) }
let(:failing_job) { Crono::Job.new(TestFailingJob, period) }
it "should contain performer and period" do
it 'should contain performer and period' do
expect(job.performer).to be TestJob
expect(job.period).to be period
end
describe "#perform" do
describe '#perform' do
after { job.send(:model).destroy }
it "should run performer in separate thread" do
it 'should run performer in separate thread' do
expect(job).to receive(:save)
thread = job.perform.join
expect(thread).to be_stop
end
it "should save performin errors to log" do
it 'should save performin errors to log' do
thread = failing_job.perform.join
expect(thread).to be_stop
saved_log = Crono::CronoJob.find_by(job_id: failing_job.job_id).log
expect(saved_log).to include "Some error"
expect(saved_log).to include 'Some error'
end
it "should set Job#healthy to true if perform ok" do
thread = job.perform.join
it 'should set Job#healthy to true if perform ok' do
job.perform.join
expect(job.healthy).to be true
end
it "should set Job#healthy to false if perform with error" do
thread = failing_job.perform.join
it 'should set Job#healthy to false if perform with error' do
failing_job.perform.join
expect(failing_job.healthy).to be false
end
end
describe "#description" do
it "should return job identificator" do
expect(job.description).to be_eql("Perform TestJob every 2 days")
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
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
it 'should update saved job' do
job.last_performed_at = Time.now
job.healthy = true
job.save
@@ -69,8 +70,8 @@ describe Crono::Job do
expect(@crono_job.healthy).to be true
end
it "should save and truncate job log" do
message = "test message"
it 'should save and truncate job log' do
message = 'test message'
job.send(:log, message)
job.save
expect(job.send(:model).reload.log).to include message
@@ -78,37 +79,37 @@ describe Crono::Job do
end
end
describe "#load" do
describe '#load' do
before do
@saved_last_performed_at = job.last_performed_at = Time.now
job.save
end
it "should load last_performed_at from DB" do
it 'should load last_performed_at from DB' do
@job = Crono::Job.new(TestJob, period)
@job.load
expect(@job.last_performed_at.utc.to_s).to be_eql @saved_last_performed_at.utc.to_s
end
end
describe "#log" do
it "should write log messages to both common and job log" do
message = "Test message"
describe '#log' do
it 'should write log messages to both common and job log' do
message = 'Test message'
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)
end
it "should write job log to Job#job_log" do
message = "Test message"
it 'should write job log to Job#job_log' do
message = 'Test message'
job.send(:log, message)
expect(job.job_log.string).to include(message)
end
end
describe "#log_error" do
it "should call log with ERROR severity" do
message = "Test message"
describe '#log_error' do
it 'should call log with ERROR severity' do
message = 'Test message'
expect(job).to receive(:log).with(message, Logger::ERROR)
job.send(:log_error, message)
end

View File

@@ -1,26 +1,26 @@
require "spec_helper"
require 'spec_helper'
describe Crono::CronoJob do
let(:valid_attrs) do
{
job_id: "Perform TestJob every 3 days"
job_id: 'Perform TestJob every 3 days'
}
end
it "should validate presence of job_id" do
@crono_job = Crono::CronoJob.new()
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")
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
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

View File

@@ -1,12 +1,13 @@
require "spec_helper"
require 'spec_helper'
class TestJob
def perform;end
def perform
end
end
describe Crono::PerformerProxy do
it "should add job to schedule" do
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")
Crono.perform(TestJob).every(2.days, at: '15:30')
end
end

View File

@@ -1,4 +1,4 @@
require "spec_helper"
require 'spec_helper'
describe Crono::Period do
around(:each) do |example|
@@ -7,47 +7,48 @@ 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")
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
describe '#next' do
context 'in daily basis' do
it 'should return the time 2 days from now' do
@period = Crono::Period.new(2.day)
expect(@period.next).to be_eql(2.day.from_now)
end
it "should set time to 'at' time as a string" do
time = 10.minutes.ago
@period = Crono::Period.new(2.day, at: [time.hour, time.min].join(':'))
at = [time.hour, time.min].join(':')
@period = Crono::Period.new(2.day, at: at)
expect(@period.next).to be_eql(2.day.from_now.change(hour: time.hour, min: time.min))
end
it "should set time to 'at' time as a hash" do
time = 10.minutes.ago
at = {hour: time.hour, min: time.min}
at = { hour: time.hour, min: time.min }
@period = Crono::Period.new(2.day, at: at)
expect(@period.next).to be_eql(2.day.from_now.change(at))
end
it "should raise error when 'at' is wrong" do
expect {
@period = Crono::Period.new(2.day, at: 1)
Crono::Period.new(2.day, at: 1)
}.to raise_error("Unknown 'at' format")
end
it "should return time in relation to last time" do
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
it "should return today time if it is first run and not too late" do
it 'should return today time if it is first run and not too late' do
time = 10.minutes.from_now
at = {hour: time.hour, min: time.min}
at = { hour: time.hour, min: time.min }
@period = Crono::Period.new(2.day, at: at)
expect(@period.next.utc.to_s).to be_eql(Time.now.change(at).utc.to_s)
end

View File

@@ -1,30 +1,31 @@
require "spec_helper"
require 'spec_helper'
class TestJob
def perform;end
def perform
end
end
describe Crono::Scheduler do
before(:each) do
@scheduler = Crono::Scheduler.new
@jobs = [
Crono::Period.new(3.day, at: 10.minutes.from_now.strftime("%H:%M")),
Crono::Period.new(1.day, at: 20.minutes.from_now.strftime("%H:%M")),
Crono::Period.new(7.day, at: 40.minutes.from_now.strftime("%H:%M"))
Crono::Period.new(3.day, at: 10.minutes.from_now.strftime('%H:%M')),
Crono::Period.new(1.day, at: 20.minutes.from_now.strftime('%H:%M')),
Crono::Period.new(7.day, at: 40.minutes.from_now.strftime('%H:%M'))
].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"))
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
describe '#next' do
it 'should return next job in schedule' do
expect(@scheduler.next).to be @jobs[0]
end
end

View File

@@ -6,9 +6,10 @@ require 'byebug'
require 'crono'
require 'generators/crono/install/templates/migrations/create_crono_jobs.rb'
ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: "file::memory:?cache=shared")
ActiveRecord::Base.establish_connection(
adapter: 'sqlite3',
database: 'file::memory:?cache=shared'
)
ActiveRecord::Base.logger = Logger.new(STDOUT)
CreateCronoJobs.up
RSpec.configure do |config|
end

View File

@@ -1,44 +1,47 @@
require "spec_helper"
require "rack/test"
require 'spec_helper'
require 'rack/test'
include Rack::Test::Methods
describe Crono::Web do
let(:app) { Crono::Web }
before do
@test_job_id = "Perform TestJob every 5 seconds"
@test_job_log = "All runs ok"
@test_job = Crono::CronoJob.create!(job_id: @test_job_id, log: @test_job_log)
@test_job_id = 'Perform TestJob every 5 seconds'
@test_job_log = 'All runs ok'
@test_job = Crono::CronoJob.create!(
job_id: @test_job_id,
log: @test_job_log
)
end
after { @test_job.destroy }
describe "/" do
it "should show all jobs" do
describe '/' do
it 'should show all jobs' do
get '/'
expect(last_response).to be_ok
expect(last_response.body).to include @test_job_id
end
it "should show a error mark when a job is unhealthy" do
it 'should show a error mark when a job is unhealthy' do
@test_job.update(healthy: false)
get '/'
expect(last_response.body).to include "Error"
expect(last_response.body).to include 'Error'
end
end
describe "/job/:id" do
it "should show job log" do
describe '/job/:id' do
it 'should show job log' do
get "/job/#{@test_job.id}"
expect(last_response).to be_ok
expect(last_response.body).to include @test_job_id
expect(last_response.body).to include @test_job_log
end
it "should show a message about the unhealthy job" do
it 'should show a message about the unhealthy job' do
@test_job.update(healthy: false)
get "/job/#{@test_job.id}"
expect(last_response.body).to include "An error occurs during the last execution of this job"
expect(last_response.body).to include 'An error occurs during the last execution of this job'
end
end
end