tasks_scheduler 0.3.0 → 0.5.1

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.
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.