tasks_scheduler 0.3.0 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +3 -1
  3. data/app/assets/javascripts/tasks_scheduler.js +2 -109
  4. data/app/assets/javascripts/tasks_scheduler/alert.js +58 -0
  5. data/app/assets/javascripts/tasks_scheduler/status.js +58 -0
  6. data/app/assets/stylesheets/tasks_scheduler.scss +4 -0
  7. data/app/controllers/scheduled_tasks_controller.rb +5 -4
  8. data/app/controllers/tasks_scheduler_daemon_controller.rb +4 -3
  9. data/app/controllers/tasks_scheduler_daemon_controller/_download_log.rb +6 -0
  10. data/app/helpers/scheduled_tasks_helper.rb +2 -0
  11. data/app/models/scheduled_task.rb +16 -14
  12. data/app/models/scheduled_task/checker.rb +17 -5
  13. data/app/models/scheduled_task/log.rb +9 -6
  14. data/app/models/scheduled_task/runner.rb +9 -5
  15. data/app/models/scheduled_task/status.rb +8 -4
  16. data/config/initializers/assets.rb +3 -1
  17. data/config/routes.rb +2 -0
  18. data/db/migrate/20161122123828_create_scheduled_tasks.rb +5 -1
  19. data/db/migrate/20161123130153_add_status_attributes_to_scheduled_tasks.rb +5 -1
  20. data/db/migrate/20161124200712_add_pid_to_scheduled_tasks.rb +5 -1
  21. data/db/migrate/20161128163702_add_args_to_scheduled_tasks.rb +5 -1
  22. data/db/migrate/20180223155025_add_last_fail_status_to_scheduled_tasks.rb +5 -1
  23. data/db/migrate/20180302170138_add_enabled_to_scheduled_tasks.rb +5 -1
  24. data/exe/tasks_scheduler +3 -1
  25. data/exe/tasks_scheduler_run_task +3 -1
  26. data/lib/tasks_scheduler.rb +4 -8
  27. data/lib/tasks_scheduler/app_gem.rb +18 -0
  28. data/lib/tasks_scheduler/checker.rb +5 -2
  29. data/lib/tasks_scheduler/checker/log.rb +2 -0
  30. data/lib/tasks_scheduler/cron_parser_patch.rb +5 -1
  31. data/lib/tasks_scheduler/cron_scheduling_validator.rb +3 -0
  32. data/lib/tasks_scheduler/daemon.rb +20 -13
  33. data/lib/tasks_scheduler/engine.rb +5 -0
  34. data/lib/tasks_scheduler/version.rb +3 -1
  35. data/test/dummy/Rakefile +3 -1
  36. data/test/dummy/app/controllers/application_controller.rb +2 -0
  37. data/test/dummy/app/helpers/application_helper.rb +2 -0
  38. data/test/dummy/bin/bundle +3 -1
  39. data/test/dummy/bin/rails +3 -1
  40. data/test/dummy/bin/rake +2 -0
  41. data/test/dummy/bin/setup +3 -1
  42. data/test/dummy/config.ru +2 -0
  43. data/test/dummy/config/application.rb +3 -1
  44. data/test/dummy/config/boot.rb +4 -2
  45. data/test/dummy/config/environment.rb +3 -1
  46. data/test/dummy/config/environments/development.rb +2 -0
  47. data/test/dummy/config/environments/production.rb +2 -0
  48. data/test/dummy/config/environments/test.rb +2 -0
  49. data/test/dummy/config/initializers/assets.rb +2 -0
  50. data/test/dummy/config/initializers/backtrace_silencers.rb +1 -0
  51. data/test/dummy/config/initializers/cookies_serializer.rb +2 -0
  52. data/test/dummy/config/initializers/filter_parameter_logging.rb +2 -0
  53. data/test/dummy/config/initializers/inflections.rb +1 -0
  54. data/test/dummy/config/initializers/mime_types.rb +1 -0
  55. data/test/dummy/config/initializers/session_store.rb +2 -0
  56. data/test/dummy/config/initializers/wrap_parameters.rb +2 -0
  57. data/test/dummy/config/routes.rb +2 -0
  58. data/test/fixtures/scheduled_tasks.yml +2 -0
  59. data/test/models/scheduled_task_test.rb +6 -4
  60. data/test/test_helper.rb +6 -4
  61. metadata +88 -32
@@ -1,9 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class ScheduledTask < ActiveRecord::Base
2
4
  module Log
3
5
  def log_file(identifier)
4
6
  unless log_identifiers.include?(identifier)
5
- fail "Log identifier unknown: \"#{identifier}\" (Valid: #{log_identifiers})"
7
+ raise "Log identifier unknown: \"#{identifier}\" (Valid: #{log_identifiers})"
6
8
  end
9
+
7
10
  Rails.root.join('log', 'tasks_scheduler', "#{id}_#{identifier}.log")
8
11
  end
9
12
 
@@ -24,11 +27,11 @@ class ScheduledTask < ActiveRecord::Base
24
27
 
25
28
  def log_on_end(exception)
26
29
  running_log = log_file(LOG_RUNNING)
27
- if ::File.exist?(running_log)
28
- target_log = exception ? log_file(LOG_UNSUCCESSFUL) : log_file(LOG_SUCCESSFUL)
29
- File.unlink(target_log) if File.exist?(target_log)
30
- File.rename(running_log, target_log)
31
- end
30
+ return unless ::File.exist?(running_log)
31
+
32
+ target_log = exception ? log_file(LOG_UNSUCCESSFUL) : log_file(LOG_SUCCESSFUL)
33
+ File.unlink(target_log) if File.exist?(target_log)
34
+ File.rename(running_log, target_log)
32
35
  end
33
36
  end
34
37
  end
@@ -1,9 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class ScheduledTask < ActiveRecord::Base
2
4
  module Runner
3
5
  def run
4
6
  log_on_start
5
7
  run_banner
6
8
  return if process_running? && pid != Process.pid
9
+
7
10
  status_on_start
8
11
  exception = invoke_task
9
12
  on_end_running(exception, STATUS_FAILED)
@@ -33,7 +36,7 @@ class ScheduledTask < ActiveRecord::Base
33
36
 
34
37
  def run_banner
35
38
  run_log("Task: #{self}")
36
- run_log("PID: #{pid ? pid : '-'} (Current: #{Process.pid})")
39
+ run_log("PID: #{pid || '-'} (Current: #{Process.pid})")
37
40
  run_log("Process running? #{process_running? ? 'Yes' : 'No'}")
38
41
  run_log("Rails.env: #{Rails.env}")
39
42
  end
@@ -44,15 +47,16 @@ class ScheduledTask < ActiveRecord::Base
44
47
  Rake::Task.clear
45
48
  Rails.application.load_tasks
46
49
  Rake::Task[task].invoke(*invoke_args)
47
- rescue StandardError => ex
48
- run_log(ex, :fatal)
49
- exception = ex
50
+ rescue StandardError => e
51
+ run_log(e, :fatal)
52
+ exception = e
50
53
  end
51
54
  exception
52
55
  end
53
56
 
54
57
  def invoke_args
55
- return [] unless args.present?
58
+ return [] if args.blank?
59
+
56
60
  args.split('|')
57
61
  end
58
62
  end
@@ -1,10 +1,13 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class ScheduledTask < ActiveRecord::Base
2
4
  module Status
3
5
  def status
4
6
  return STATUS_DISABLED unless enabled?
5
7
  return STATUS_RUNNING if running?
6
8
  return STATUS_WAITING if waiting?
7
- last_fail_status.present? ? last_fail_status : STATUS_FAILED
9
+
10
+ last_fail_status.presence || STATUS_FAILED
8
11
  end
9
12
 
10
13
  def failed?
@@ -17,6 +20,7 @@ class ScheduledTask < ActiveRecord::Base
17
20
 
18
21
  def waiting?
19
22
  return true if ended?(last_run_successful_end, last_run_unsuccessful_end)
23
+
20
24
  status_attributes.all? { |a| send(a).blank? }
21
25
  end
22
26
 
@@ -27,11 +31,11 @@ class ScheduledTask < ActiveRecord::Base
27
31
  end
28
32
 
29
33
  def status_on_start
30
- update_attributes!(last_run_start: Time.zone.now)
34
+ update!(last_run_start: Time.zone.now)
31
35
  end
32
36
 
33
37
  def status_on_end(exception, last_fail_status)
34
- update_attributes!(
38
+ update!(
35
39
  next_run: calculate_next_run,
36
40
  (exception ? :last_run_unsuccessful_start : :last_run_successful_start) => last_run_start,
37
41
  (exception ? :last_run_unsuccessful_end : :last_run_successful_end) => Time.zone.now,
@@ -42,7 +46,7 @@ class ScheduledTask < ActiveRecord::Base
42
46
  end
43
47
 
44
48
  def status_attributes
45
- %w(start successful_start successful_end unsuccessful_start unsuccessful_end).map do |a|
49
+ %w[start successful_start successful_end unsuccessful_start unsuccessful_end].map do |a|
46
50
  "last_run_#{a}"
47
51
  end
48
52
  end
@@ -1 +1,3 @@
1
- Rails.application.config.assets.precompile += %w( active_scaffold/indicator.gif )
1
+ # frozen_string_literal: true
2
+
3
+ Rails.application.config.assets.precompile += %w[active_scaffold/indicator.gif]
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  Rails.application.routes.draw do
2
4
  resources(:scheduled_tasks) do
3
5
  as_routes
@@ -1,4 +1,8 @@
1
- class CreateScheduledTasks < ActiveRecord::Migration
1
+ # frozen_string_literal: true
2
+
3
+ class CreateScheduledTasks < (
4
+ Rails.version < '5.2' ? ActiveRecord::Migration : ActiveRecord::Migration[4.2]
5
+ )
2
6
  def change
3
7
  create_table :scheduled_tasks do |t|
4
8
  t.string :scheduling
@@ -1,4 +1,8 @@
1
- class AddStatusAttributesToScheduledTasks < ActiveRecord::Migration
1
+ # frozen_string_literal: true
2
+
3
+ class AddStatusAttributesToScheduledTasks < (
4
+ Rails.version < '5.2' ? ActiveRecord::Migration : ActiveRecord::Migration[4.2]
5
+ )
2
6
  def change
3
7
  add_column :scheduled_tasks, :last_run_start, :datetime
4
8
  add_column :scheduled_tasks, :last_run_successful_start, :datetime
@@ -1,4 +1,8 @@
1
- class AddPidToScheduledTasks < ActiveRecord::Migration
1
+ # frozen_string_literal: true
2
+
3
+ class AddPidToScheduledTasks < (
4
+ Rails.version < '5.2' ? ActiveRecord::Migration : ActiveRecord::Migration[4.2]
5
+ )
2
6
  def change
3
7
  add_column :scheduled_tasks, :pid, :integer
4
8
  end
@@ -1,4 +1,8 @@
1
- class AddArgsToScheduledTasks < ActiveRecord::Migration
1
+ # frozen_string_literal: true
2
+
3
+ class AddArgsToScheduledTasks < (
4
+ Rails.version < '5.2' ? ActiveRecord::Migration : ActiveRecord::Migration[4.2]
5
+ )
2
6
  def change
3
7
  add_column :scheduled_tasks, :args, :string
4
8
  end
@@ -1,4 +1,8 @@
1
- class AddLastFailStatusToScheduledTasks < ActiveRecord::Migration
1
+ # frozen_string_literal: true
2
+
3
+ class AddLastFailStatusToScheduledTasks < (
4
+ Rails.version < '5.2' ? ActiveRecord::Migration : ActiveRecord::Migration[4.2]
5
+ )
2
6
  def change
3
7
  add_column :scheduled_tasks, :last_fail_status, :string
4
8
  end
@@ -1,4 +1,8 @@
1
- class AddEnabledToScheduledTasks < ActiveRecord::Migration
1
+ # frozen_string_literal: true
2
+
3
+ class AddEnabledToScheduledTasks < (
4
+ Rails.version < '5.2' ? ActiveRecord::Migration : ActiveRecord::Migration[4.2]
5
+ )
2
6
  def change
3
7
  add_column :scheduled_tasks, :enabled, :boolean, null: false, default: true
4
8
  end
@@ -1,12 +1,14 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
4
  require 'rubygems'
4
5
  require 'daemons'
5
6
  require 'fileutils'
6
7
 
7
8
  def find_rails_root(dir = Dir.pwd)
8
- fail 'Rails root not found' if dir == '' || dir == '/' || dir == '.'
9
+ raise 'Rails root not found' if ['', '/', '.'].include?(dir)
9
10
  return dir if File.exist?(File.expand_path('config/environment.rb', dir))
11
+
10
12
  rails_root(File.dirname(dir))
11
13
  end
12
14
 
@@ -1,11 +1,13 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
4
  require 'rubygems'
4
5
  require 'daemons'
5
6
 
6
7
  def find_rails_root(dir = Dir.pwd)
7
- fail 'Rails root not found' if dir == '' || dir == '/' || dir == '.'
8
+ raise 'Rails root not found' if ['', '/', '.'].include?(dir)
8
9
  return dir if File.exist?(File.expand_path('config/environment.rb', dir))
10
+
9
11
  rails_root(File.dirname(dir))
10
12
  end
11
13
 
@@ -1,11 +1,7 @@
1
- require 'tasks_scheduler/engine'
2
- require 'active_scaffold'
3
- require 'js-routes'
4
- require 'parse-cron'
5
- require 'tasks_scheduler/cron_scheduling_validator'
6
- require 'tasks_scheduler/cron_parser_patch'
7
- require 'tasks_scheduler/checker'
8
- require 'tasks_scheduler/daemon'
1
+ # frozen_string_literal: true
2
+
3
+ require 'eac_ruby_utils/require_sub'
9
4
 
10
5
  module TasksScheduler
6
+ ::EacRubyUtils.require_sub __FILE__
11
7
  end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'eac_ruby_gems_utils/gem'
4
+ require 'singleton'
5
+
6
+ module TasksScheduler
7
+ class AppGem < ::SimpleDelegator
8
+ include ::Singleton
9
+
10
+ def initialize
11
+ super(::EacRubyGemsUtils::Gem.new(::Rails.root))
12
+ end
13
+
14
+ def bundle(*args)
15
+ super(*args).chdir_root.envvar('RAILS_ENV', ::Rails.env)
16
+ end
17
+ end
18
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'tasks_scheduler/checker/log'
2
4
 
3
5
  module TasksScheduler
@@ -5,8 +7,8 @@ module TasksScheduler
5
7
  include Singleton
6
8
 
7
9
  CHECK_INTERVAL = 15
8
- LOG_ON_FILE_ENV_KEY = 'TASKS_SCHEDULER_LOG_ON_FILE'.freeze
9
- LOGS_KEYS = %w(rails stdout stderr).freeze
10
+ LOG_ON_FILE_ENV_KEY = 'TASKS_SCHEDULER_LOG_ON_FILE'
11
+ LOGS_KEYS = %w[rails stdout stderr].freeze
10
12
 
11
13
  def run
12
14
  check_log
@@ -40,6 +42,7 @@ CODE
40
42
 
41
43
  def check_log
42
44
  return unless log_on_file?
45
+
43
46
  ::Rails.logger = ::Logger.new(rails_log.path)
44
47
  $stdout.reopen(stdout_log.path, 'w')
45
48
  $stderr.reopen(stderr_log.path, 'w')
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'fileutils'
2
4
 
3
5
  module TasksScheduler
@@ -1,8 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'parse-cron'
4
+
1
5
  module TasksScheduler
2
6
  module CronParserPatch
3
7
  class TasksSchedulerTimeSource
4
8
  class << self
5
- def local(year, month, day, hour, min, second)
9
+ def local(year, month, day, hour, min, second) # rubocop:disable Metrics/ParameterLists
6
10
  Time.utc(year, month, day, hour, min, second)
7
11
  end
8
12
 
@@ -1,7 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module TasksScheduler
2
4
  class CronSchedulingValidator < ActiveModel::EachValidator
3
5
  def validate_each(record, attribute, value)
4
6
  return if value_valid?(value)
7
+
5
8
  record.errors[attribute] << (options[:message] ||
6
9
  I18n.translate(:cron_scheduling_validator_error_message))
7
10
  end
@@ -1,28 +1,35 @@
1
- require 'open3'
1
+ # frozen_string_literal: true
2
+
3
+ require 'tasks_scheduler/app_gem'
2
4
 
3
5
  module TasksScheduler
4
6
  class Daemon
5
- ACTIONS = %w(status start stop restart).freeze
7
+ ACTIONS = %w[status start stop restart].freeze
6
8
 
7
9
  class << self
8
- def execute(action)
10
+ def daemon_command(action)
9
11
  raise "Action not allowed: #{action} (Allowed: #{ACTIONS})" unless ACTIONS.include?(action)
10
- command = ['bundle', 'exec', 'tasks_scheduler', action]
11
- Dir.chdir(Rails.root) do
12
- Open3.popen3(env_args, *command) do |_stdin, stdout, stderr, wait_thr|
13
- { action: action, env_args: env_args.map { |k, v| "#{k}=#{v}" }.join(' | '),
14
- command: command.join(' '), status: wait_thr.value.to_i, stdout: stdout.read,
15
- stderr: stderr.read }
16
- end
17
- end
12
+
13
+ ::TasksScheduler::AppGem.instance.bundle('exec', 'tasks_scheduler', action)
14
+ .envvar(::TasksScheduler::Checker::LOG_ON_FILE_ENV_KEY, '1')
15
+ end
16
+
17
+ def execute(action)
18
+ command = daemon_command(action)
19
+ result = command.execute
20
+ {
21
+ action: action, env_args: env_args_to_s(command), command: command.to_s,
22
+ status: result.fetch(:exit_code), stdout: result.fetch(:stdout),
23
+ stderr: result.fetch(:stderr)
24
+ }
18
25
  end
19
26
 
20
27
  def running?
21
28
  execute('status')[:status].zero?
22
29
  end
23
30
 
24
- def env_args
25
- { 'RAILS_ENV' => Rails.env, ::TasksScheduler::Checker::LOG_ON_FILE_ENV_KEY => '1' }
31
+ def env_args_to_s(command)
32
+ command.send(:extra_options).fetch(:envvars).map { |k, v| "#{k}=#{v}" }.join(' | ')
26
33
  end
27
34
  end
28
35
  end
@@ -1,3 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_scaffold'
4
+ require 'js-routes'
5
+
1
6
  module TasksScheduler
2
7
  class Engine < ::Rails::Engine
3
8
  initializer :append_migrations do |app|
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module TasksScheduler
2
- VERSION = '0.3.0'.freeze
4
+ VERSION = '0.5.1'
3
5
  end
@@ -1,6 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Add your own tasks in files placed in lib/tasks ending in .rake,
2
4
  # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
3
5
 
4
- require File.expand_path('../config/application', __FILE__)
6
+ require File.expand_path('config/application', __dir__)
5
7
 
6
8
  Rails.application.load_tasks
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class ApplicationController < ActionController::Base
2
4
  # Prevent CSRF attacks by raising an exception.
3
5
  # For APIs, you may want to use :null_session instead.
@@ -1,2 +1,4 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ApplicationHelper
2
4
  end
@@ -1,3 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
- ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
2
+ # frozen_string_literal: true
3
+
4
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
3
5
  load Gem.bin_path('bundler', 'bundle')
@@ -1,4 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
- APP_PATH = File.expand_path('../../config/application', __FILE__)
2
+ # frozen_string_literal: true
3
+
4
+ APP_PATH = File.expand_path('../config/application', __dir__)
3
5
  require_relative '../config/boot'
4
6
  require 'rails/commands'
@@ -1,4 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
2
4
  require_relative '../config/boot'
3
5
  require 'rake'
4
6
  Rake.application.run
@@ -1,8 +1,10 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
2
4
  require 'pathname'
3
5
 
4
6
  # path to your application root.
5
- APP_ROOT = Pathname.new File.expand_path('../../', __FILE__)
7
+ APP_ROOT = Pathname.new File.expand_path('..', __dir__)
6
8
 
7
9
  Dir.chdir APP_ROOT do
8
10
  # This script is a starting point to setup your application.