teplohod 0.0.1.alpha1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.rubocop.yml +3 -0
- data/.travis.yml +5 -0
- data/Gemfile +4 -0
- data/README.md +5 -0
- data/Rakefile +21 -0
- data/bin/teplohod +14 -0
- data/example/main.rb +50 -0
- data/lib/teplohod/cli.rb +63 -0
- data/lib/teplohod/initializers/banner.rb +31 -0
- data/lib/teplohod/initializers/boot.rb +15 -0
- data/lib/teplohod/initializers/daemonizer.rb +51 -0
- data/lib/teplohod/initializers/logger.rb +14 -0
- data/lib/teplohod/initializers/options.rb +57 -0
- data/lib/teplohod/initializers/pid.rb +16 -0
- data/lib/teplohod/initializers/redis.rb +14 -0
- data/lib/teplohod/initializers/web.rb +13 -0
- data/lib/teplohod/initializers.rb +25 -0
- data/lib/teplohod/launcher.rb +28 -0
- data/lib/teplohod/logging.rb +87 -0
- data/lib/teplohod/manager.rb +19 -0
- data/lib/teplohod/pipeline/base.rb +89 -0
- data/lib/teplohod/processor/base.rb +9 -0
- data/lib/teplohod/processor/equality.rb +11 -0
- data/lib/teplohod/provider/base.rb +48 -0
- data/lib/teplohod/queue/base.rb +25 -0
- data/lib/teplohod/queue/memory/base.rb +33 -0
- data/lib/teplohod/signal_handler.rb +39 -0
- data/lib/teplohod/stats.rb +36 -0
- data/lib/teplohod/utils.rb +31 -0
- data/lib/teplohod/version.rb +3 -0
- data/lib/teplohod/web.rb +19 -0
- data/lib/teplohod.rb +57 -0
- data/teplohod.gemspec +38 -0
- data/web/assets/stylesheets/turret.css +7821 -0
- data/web/views/index.erb +48 -0
- data/web/views/layout.erb +18 -0
- metadata +291 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 0d50cedb72cdebc67d5e1f53545099061783de7d
|
4
|
+
data.tar.gz: 5af88cd316675cc4f24419de22a01b776500ff92
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 06ff005b1d8c27575757b9cf6e48aed4b0137ec2caf0959e2b8b13b8b2e9c9fec4e70bd5a10d821de694929f906d310d07f02a677b9511b7a7b0a58ea9382b22
|
7
|
+
data.tar.gz: e07d47c681221a83639ba9b7a0d0738e7644e1e3fbbee1230611b3ab73ce9320fc47aaf33951cb377d1e532c3402a95a3dcf3411427ff79a78a2fafd7571a669
|
data/.gitignore
ADDED
data/.rubocop.yml
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
2
|
+
require 'rake/testtask'
|
3
|
+
require 'rubocop/rake_task'
|
4
|
+
require 'yard'
|
5
|
+
|
6
|
+
Rake::TestTask.new(:test) do |t|
|
7
|
+
t.libs << 'test'
|
8
|
+
t.libs << 'lib'
|
9
|
+
t.test_files = FileList['test/**/*_test.rb']
|
10
|
+
end
|
11
|
+
|
12
|
+
RuboCop::RakeTask.new do |task|
|
13
|
+
task.fail_on_error = false
|
14
|
+
task.formatters = %w(simple)
|
15
|
+
end
|
16
|
+
|
17
|
+
YARD::Rake::YardocTask.new do |t|
|
18
|
+
t.files = ['lib/**/*.rb']
|
19
|
+
end
|
20
|
+
|
21
|
+
task default: %i(rubocop test yard)
|
data/bin/teplohod
ADDED
data/example/main.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'teplohod'
|
2
|
+
require 'teplohod/queue/memory/base'
|
3
|
+
|
4
|
+
class StartQueue < Teplohod::Queue::Memory::Base
|
5
|
+
end
|
6
|
+
|
7
|
+
class ResultQueue < Teplohod::Queue::Memory::Base
|
8
|
+
end
|
9
|
+
|
10
|
+
class InitialPipeline < Teplohod::Pipeline::Base
|
11
|
+
self.queue = StartQueue.new
|
12
|
+
|
13
|
+
def name
|
14
|
+
"Initial Pipeline"
|
15
|
+
end
|
16
|
+
|
17
|
+
def desription
|
18
|
+
"Initial data there"
|
19
|
+
end
|
20
|
+
|
21
|
+
def on_empty_load
|
22
|
+
10.times do
|
23
|
+
queue.push rand(100)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def on_finish
|
28
|
+
on_empty_load
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class ResultPipeline < Teplohod::Pipeline::Base
|
33
|
+
self.parent = InitialPipeline.new
|
34
|
+
self.queue = ResultQueue.new
|
35
|
+
|
36
|
+
def name
|
37
|
+
"Result Pipeline"
|
38
|
+
end
|
39
|
+
|
40
|
+
def result
|
41
|
+
"Result data there"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
class DemoProvider < Teplohod::Provider::Base
|
46
|
+
self.tail = ResultPipeline.new
|
47
|
+
end
|
48
|
+
|
49
|
+
Teplohod::Manager.reload_providers!
|
50
|
+
Teplohod::Manager.register_provider DemoProvider.new
|
data/lib/teplohod/cli.rb
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
$stdout.sync = true
|
2
|
+
|
3
|
+
require 'singleton'
|
4
|
+
require 'optparse'
|
5
|
+
require 'logger'
|
6
|
+
|
7
|
+
require 'teplohod/utils'
|
8
|
+
require 'teplohod/initializers'
|
9
|
+
require 'teplohod/signal_handler'
|
10
|
+
require 'teplohod'
|
11
|
+
|
12
|
+
module Teplohod
|
13
|
+
class CLI
|
14
|
+
include Singleton
|
15
|
+
include Utils
|
16
|
+
|
17
|
+
def prepare(_args = ARGV)
|
18
|
+
Teplohod::Initializers::Options.run!
|
19
|
+
Teplohod::Initializers::Redis.run!
|
20
|
+
Teplohod::Initializers::Logger.run! unless options[:daemonize]
|
21
|
+
Teplohod::Initializers::Daemonizer.run! if options[:daemonize]
|
22
|
+
Teplohod::Initializers::Pid.run!
|
23
|
+
end
|
24
|
+
|
25
|
+
def run
|
26
|
+
Teplohod::Initializers::Boot.run!
|
27
|
+
Teplohod::Initializers::Web.run! if options[:web]
|
28
|
+
Teplohod::Initializers::Banner.run!
|
29
|
+
|
30
|
+
self_read, self_write = IO.pipe
|
31
|
+
|
32
|
+
%w(INT TERM USR1 USR2 TTIN).each do |sig|
|
33
|
+
begin
|
34
|
+
trap sig do
|
35
|
+
self_write.puts(sig)
|
36
|
+
end
|
37
|
+
rescue ArgumentError
|
38
|
+
puts "Signal #{sig} not supported"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
require 'teplohod/launcher'
|
43
|
+
launcher = Teplohod::Launcher.new
|
44
|
+
|
45
|
+
begin
|
46
|
+
launcher.run
|
47
|
+
|
48
|
+
while readable_io = IO.select([self_read])
|
49
|
+
signal = readable_io.first[0].gets.strip
|
50
|
+
Teplohod::SignalHandler.handle(signal)
|
51
|
+
end
|
52
|
+
rescue Interrupt
|
53
|
+
logger.tagged('system') { logger.info 'Shutting down' }
|
54
|
+
launcher.stop
|
55
|
+
logger.tagged('system') { logger.info 'Bye!' }
|
56
|
+
exit 0
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def handle_signal(sig)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Teplohod
|
2
|
+
module Initializers
|
3
|
+
class Banner
|
4
|
+
include Initializable
|
5
|
+
|
6
|
+
def run
|
7
|
+
puts "\e[32m"
|
8
|
+
puts banner
|
9
|
+
puts "\e[0m"
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
# TODO: Add teplohod
|
15
|
+
def banner
|
16
|
+
%(
|
17
|
+
Welcome to #{Teplohod::NAME}!
|
18
|
+
Current version: #{Teplohod::VERSION}
|
19
|
+
|
20
|
+
Options:
|
21
|
+
Environment: #{environment}
|
22
|
+
Mount Point: #{options[:require]}
|
23
|
+
Log Level: #{logger.level}
|
24
|
+
Pid File Path: #{options[:pidfile]}
|
25
|
+
Daemonize: #{options[:daemonize]}
|
26
|
+
Web: #{options[:web]}
|
27
|
+
)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Teplohod
|
2
|
+
class NoRequiredFile < ArgumentError
|
3
|
+
end
|
4
|
+
|
5
|
+
module Initializers
|
6
|
+
class Boot
|
7
|
+
include Initializable
|
8
|
+
|
9
|
+
def run
|
10
|
+
raise NoRequiredFile, "#{options[:require]} does not exist" unless File.exist?(options[:require])
|
11
|
+
require options[:require]
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'teplohod/initializers/logger'
|
2
|
+
|
3
|
+
module Teplohod
|
4
|
+
class NoLogfileError < ArgumentError
|
5
|
+
end
|
6
|
+
|
7
|
+
module Initializers
|
8
|
+
class Daemonizer
|
9
|
+
include Initializable
|
10
|
+
|
11
|
+
def run
|
12
|
+
validate!
|
13
|
+
|
14
|
+
files_to_reopen = []
|
15
|
+
ObjectSpace.each_object(File) do |file|
|
16
|
+
files_to_reopen << file unless file.closed?
|
17
|
+
end
|
18
|
+
|
19
|
+
::Process.daemon(true, true)
|
20
|
+
|
21
|
+
files_to_reopen.each do |file|
|
22
|
+
begin
|
23
|
+
file.reopen file.path, 'a+'
|
24
|
+
file.sync = true
|
25
|
+
rescue ::Exception
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
[$stdout, $stderr].each do |io|
|
30
|
+
File.open(options[:logfile], 'ab') do |f|
|
31
|
+
io.reopen(f)
|
32
|
+
end
|
33
|
+
io.sync = true
|
34
|
+
end
|
35
|
+
$stdin.reopen('/dev/null')
|
36
|
+
|
37
|
+
Teplohod::Initializers::Logger.run!
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def logfile?
|
43
|
+
options[:logfile]
|
44
|
+
end
|
45
|
+
|
46
|
+
def validate!
|
47
|
+
raise NoLogfileError, "You should specify a logfile if you're going to daemonize" unless logfile?
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'teplohod/logging'
|
2
|
+
|
3
|
+
module Teplohod
|
4
|
+
module Initializers
|
5
|
+
class Logger
|
6
|
+
include Initializable
|
7
|
+
|
8
|
+
def run
|
9
|
+
Teplohod::Logging.initialize_logger(options[:logfile]) if options[:logfile]
|
10
|
+
Teplohod.logger.level = ::Logger::DEBUG if options[:verbose]
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
|
3
|
+
require 'teplohod'
|
4
|
+
|
5
|
+
module Teplohod
|
6
|
+
module Initializers
|
7
|
+
class Options
|
8
|
+
include Initializable
|
9
|
+
|
10
|
+
def self.run!
|
11
|
+
new.run
|
12
|
+
end
|
13
|
+
|
14
|
+
def run
|
15
|
+
cli_options = parse
|
16
|
+
Teplohod.options = options.merge cli_options
|
17
|
+
puts "Start #{Teplohod::NAME} with #{options}"
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def parse
|
23
|
+
opts = {}
|
24
|
+
parser = ::OptionParser.new do |o|
|
25
|
+
o.on '-d', '--daemon', 'Daemonize process' do |arg|
|
26
|
+
opts[:daemonize] = arg
|
27
|
+
end
|
28
|
+
o.on '-r', '--require [PATH|DIR]', 'Location to require' do |arg|
|
29
|
+
opts[:require] = arg
|
30
|
+
end
|
31
|
+
o.on '-e', '--environment ENV', 'Application environment' do |arg|
|
32
|
+
opts[:environment] = arg
|
33
|
+
end
|
34
|
+
o.on '-v', '--verbose', 'Print more verbose output' do |arg|
|
35
|
+
opts[:verbose] = arg
|
36
|
+
end
|
37
|
+
o.on '-L', '--logfile PATH', 'path to writable logfile' do |arg|
|
38
|
+
opts[:logfile] = arg
|
39
|
+
end
|
40
|
+
o.on '-P', '--pidfile PATH', 'path to pidfile' do |arg|
|
41
|
+
opts[:pidfile] = arg
|
42
|
+
end
|
43
|
+
o.on '-w', '--web', 'run web dashboard' do |arg|
|
44
|
+
opts[:web] = arg
|
45
|
+
end
|
46
|
+
end
|
47
|
+
parser.banner = 'teplohod [options]'
|
48
|
+
parser.on_tail '-h', '--help', 'Show help' do
|
49
|
+
puts parser.to_s
|
50
|
+
exit 1
|
51
|
+
end
|
52
|
+
parser.parse! ARGV
|
53
|
+
opts
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Teplohod
|
2
|
+
module Initializers
|
3
|
+
class Pid
|
4
|
+
include Initializable
|
5
|
+
|
6
|
+
def run
|
7
|
+
if path = options[:pidfile]
|
8
|
+
pidfile = File.expand_path(path)
|
9
|
+
File.open(pidfile, 'w') do |f|
|
10
|
+
f.puts ::Process.pid
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'active_support/concern'
|
2
|
+
|
3
|
+
require 'teplohod/utils'
|
4
|
+
|
5
|
+
module Teplohod
|
6
|
+
module Initializable
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
include Utils
|
9
|
+
|
10
|
+
module ClassMethods
|
11
|
+
def run!
|
12
|
+
new.run
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
require 'teplohod/initializers/options'
|
19
|
+
require 'teplohod/initializers/banner'
|
20
|
+
require 'teplohod/initializers/logger'
|
21
|
+
require 'teplohod/initializers/daemonizer'
|
22
|
+
require 'teplohod/initializers/pid'
|
23
|
+
require 'teplohod/initializers/redis'
|
24
|
+
require 'teplohod/initializers/boot'
|
25
|
+
require 'teplohod/initializers/web'
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'teplohod/utils'
|
2
|
+
|
3
|
+
module Teplohod
|
4
|
+
class Launcher
|
5
|
+
include Utils
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@providers = Teplohod::Manager.providers
|
9
|
+
end
|
10
|
+
|
11
|
+
def run
|
12
|
+
log { logger.info { 'Running..' } }
|
13
|
+
@providers.each(&:run)
|
14
|
+
end
|
15
|
+
|
16
|
+
def stop
|
17
|
+
log { logger.info { 'Stopping..' } }
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def log
|
23
|
+
logger.tagged 'system' do
|
24
|
+
yield
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'time'
|
3
|
+
require 'logger'
|
4
|
+
require 'fcntl'
|
5
|
+
require 'active_support/tagged_logging'
|
6
|
+
|
7
|
+
module Teplohod
|
8
|
+
module Logging
|
9
|
+
class Formatter
|
10
|
+
include ActiveSupport::TaggedLogging::Formatter
|
11
|
+
|
12
|
+
def call(severity, time, _program_name, message)
|
13
|
+
tags = current_tags.join('/')
|
14
|
+
"#{time.utc.iso8601(3)} #{::Process.pid} TID-#{Thread.current.object_id.to_s(36)} #{severity}: [#{tags}] #{message}\n"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.initialize_logger(log_target = STDOUT)
|
19
|
+
oldlogger = defined?(@logger) ? @logger : nil
|
20
|
+
@logger = ActiveSupport::TaggedLogging.new(Logger.new(log_target))
|
21
|
+
@logger.formatter = Formatter.new
|
22
|
+
@logger.level = Logger::INFO
|
23
|
+
oldlogger.close if oldlogger
|
24
|
+
@logger
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.logger
|
28
|
+
defined?(@logger) ? @logger : initialize_logger
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.logger=(log)
|
32
|
+
@logger = (log ? log : Logger.new(File::NULL))
|
33
|
+
end
|
34
|
+
|
35
|
+
# This reopens ALL logfiles in the process that have been rotated
|
36
|
+
# using logrotate(8) (without copytruncate) or similar tools.
|
37
|
+
# A +File+ object is considered for reopening if it is:
|
38
|
+
# 1) opened with the O_APPEND and O_WRONLY flags
|
39
|
+
# 2) the current open file handle does not match its original open path
|
40
|
+
# 3) unbuffered (as far as userspace buffering goes, not O_SYNC)
|
41
|
+
# Returns the number of files reopened
|
42
|
+
def self.reopen_logs
|
43
|
+
to_reopen = []
|
44
|
+
append_flags = File::WRONLY | File::APPEND
|
45
|
+
|
46
|
+
ObjectSpace.each_object(File) do |fp|
|
47
|
+
begin
|
48
|
+
if !fp.closed? && fp.stat.file? && fp.sync && (fp.fcntl(Fcntl::F_GETFL) & append_flags) == append_flags
|
49
|
+
to_reopen << fp
|
50
|
+
end
|
51
|
+
rescue IOError, Errno::EBADF
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
nr = 0
|
56
|
+
to_reopen.each do |fp|
|
57
|
+
orig_st = begin
|
58
|
+
fp.stat
|
59
|
+
rescue IOError, Errno::EBADF
|
60
|
+
next
|
61
|
+
end
|
62
|
+
|
63
|
+
begin
|
64
|
+
b = File.stat(fp.path)
|
65
|
+
next if orig_st.ino == b.ino && orig_st.dev == b.dev
|
66
|
+
rescue Errno::ENOENT
|
67
|
+
end
|
68
|
+
|
69
|
+
begin
|
70
|
+
File.open(fp.path, 'a') { |tmpfp| fp.reopen(tmpfp) }
|
71
|
+
fp.sync = true
|
72
|
+
nr += 1
|
73
|
+
rescue IOError, Errno::EBADF
|
74
|
+
# not much we can do...
|
75
|
+
end
|
76
|
+
end
|
77
|
+
nr
|
78
|
+
rescue RuntimeError => ex
|
79
|
+
# RuntimeError: ObjectSpace is disabled; each_object will only work with Class, pass -X+O to enable
|
80
|
+
puts "Unable to reopen logs: #{ex.message}"
|
81
|
+
end
|
82
|
+
|
83
|
+
def logger
|
84
|
+
Teplohod::Logging.logger
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'teplohod/stats'
|
2
|
+
|
3
|
+
module Teplohod
|
4
|
+
class Manager
|
5
|
+
class << self
|
6
|
+
def providers
|
7
|
+
@providers ||= []
|
8
|
+
end
|
9
|
+
|
10
|
+
def register_provider(provider)
|
11
|
+
providers << provider
|
12
|
+
end
|
13
|
+
|
14
|
+
def reload_providers!
|
15
|
+
@providers = []
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'teplohod/utils'
|
2
|
+
require 'teplohod/processor/equality'
|
3
|
+
|
4
|
+
module Teplohod
|
5
|
+
module Pipeline
|
6
|
+
class Base
|
7
|
+
include Utils
|
8
|
+
attr_reader :key, :timeout
|
9
|
+
attr_accessor :provider
|
10
|
+
|
11
|
+
def initialize(key: self.class.to_s.underscore, timeout: 5, loop: true)
|
12
|
+
@key = key
|
13
|
+
@timeout = timeout
|
14
|
+
@loop = loop
|
15
|
+
end
|
16
|
+
|
17
|
+
def parent
|
18
|
+
@parent ||= self.class.parent
|
19
|
+
end
|
20
|
+
|
21
|
+
def queue
|
22
|
+
@queue ||= self.class.queue
|
23
|
+
end
|
24
|
+
|
25
|
+
def processor
|
26
|
+
@processor = @processor || self.class.processor || Teplohod::Processor::Equality.new
|
27
|
+
end
|
28
|
+
|
29
|
+
def run
|
30
|
+
on_empty_load if queue.empty?
|
31
|
+
|
32
|
+
return if parent.nil?
|
33
|
+
threads thread_name do
|
34
|
+
log { logger.info { 'loop started' } }
|
35
|
+
loop do
|
36
|
+
while element = parent.queue.pop
|
37
|
+
queue.push process(element)
|
38
|
+
Teplohod::Stats.increment(:processed_jobs, self)
|
39
|
+
end
|
40
|
+
log { logger.warn { "parent queue is clear, sleep #{timeout} sec"} }
|
41
|
+
parent.on_finish
|
42
|
+
break unless @loop
|
43
|
+
sleep timeout
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def name
|
49
|
+
"#{self.class}#name is not implemented"
|
50
|
+
end
|
51
|
+
|
52
|
+
def description
|
53
|
+
"#{self.class}#description is not implemented"
|
54
|
+
end
|
55
|
+
|
56
|
+
protected
|
57
|
+
|
58
|
+
def process(data)
|
59
|
+
result = processor.process data
|
60
|
+
log { logger.info { "processed #{data}" } }
|
61
|
+
result
|
62
|
+
end
|
63
|
+
|
64
|
+
def on_finish
|
65
|
+
log { logger.warn { '#on_finish does nothing' } }
|
66
|
+
end
|
67
|
+
|
68
|
+
def on_empty_load
|
69
|
+
log { logger.warn { '#on_empty_load does nothing' } }
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def thread_name
|
75
|
+
[provider&.key, key].join('/')
|
76
|
+
end
|
77
|
+
|
78
|
+
class << self
|
79
|
+
attr_accessor :parent, :queue, :processor
|
80
|
+
end
|
81
|
+
|
82
|
+
def log
|
83
|
+
logger.tagged provider&.key, key do
|
84
|
+
yield
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|