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
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'active_support/core_ext/string/inflections'
|
2
|
+
require 'teplohod/utils'
|
3
|
+
|
4
|
+
module Teplohod
|
5
|
+
module Provider
|
6
|
+
class Base
|
7
|
+
include Utils
|
8
|
+
|
9
|
+
attr_reader :key
|
10
|
+
|
11
|
+
def initialize(key: self.class.to_s.underscore)
|
12
|
+
@key = key
|
13
|
+
end
|
14
|
+
|
15
|
+
def tail
|
16
|
+
@tail ||= self.class.tail
|
17
|
+
end
|
18
|
+
|
19
|
+
def pipelines
|
20
|
+
@pipelines ||= []
|
21
|
+
if @pipelines.empty?
|
22
|
+
pipeline = tail
|
23
|
+
while pipeline
|
24
|
+
@pipelines << pipeline
|
25
|
+
pipeline = pipeline.parent
|
26
|
+
end
|
27
|
+
end
|
28
|
+
@pipelines
|
29
|
+
end
|
30
|
+
|
31
|
+
def run
|
32
|
+
logger.tagged key do
|
33
|
+
logger.info { 'starting...' }
|
34
|
+
end
|
35
|
+
pipeline = tail
|
36
|
+
while pipeline
|
37
|
+
pipeline.provider = self
|
38
|
+
pipeline.run
|
39
|
+
pipeline = pipeline.parent
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class << self
|
44
|
+
attr_accessor :tail
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Teplohod
|
2
|
+
module Queue
|
3
|
+
class Base
|
4
|
+
def pop
|
5
|
+
raise NotImplementedError
|
6
|
+
end
|
7
|
+
|
8
|
+
def push(_data)
|
9
|
+
raise NotImplementedError
|
10
|
+
end
|
11
|
+
|
12
|
+
def length
|
13
|
+
raise NotImplementedError
|
14
|
+
end
|
15
|
+
|
16
|
+
def empty?
|
17
|
+
length.zero?
|
18
|
+
end
|
19
|
+
|
20
|
+
def last(count)
|
21
|
+
raise NotImplementedError
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'teplohod/queue/base'
|
2
|
+
|
3
|
+
module Teplohod
|
4
|
+
module Queue
|
5
|
+
module Memory
|
6
|
+
class Base < ::Teplohod::Queue::Base
|
7
|
+
def initialize
|
8
|
+
@data = []
|
9
|
+
end
|
10
|
+
|
11
|
+
def all
|
12
|
+
@data
|
13
|
+
end
|
14
|
+
|
15
|
+
def push(value)
|
16
|
+
@data.push value
|
17
|
+
end
|
18
|
+
|
19
|
+
def pop
|
20
|
+
@data.pop
|
21
|
+
end
|
22
|
+
|
23
|
+
def length
|
24
|
+
@data.length
|
25
|
+
end
|
26
|
+
|
27
|
+
def last(count)
|
28
|
+
@data.take(count)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'teplohod/utils'
|
2
|
+
|
3
|
+
module Teplohod
|
4
|
+
module SignalHandler
|
5
|
+
class << self
|
6
|
+
include Utils
|
7
|
+
|
8
|
+
def handle(signal)
|
9
|
+
logger.debug "Got #{signal} signal"
|
10
|
+
case signal
|
11
|
+
when 'INT'
|
12
|
+
# Handle Ctrl-C in JRuby like MRI
|
13
|
+
# http://jira.codehaus.org/browse/JRUBY-4637
|
14
|
+
raise Interrupt
|
15
|
+
when 'TERM'
|
16
|
+
# Heroku sends TERM and then waits 10 seconds for process to exit.
|
17
|
+
raise Interrupt
|
18
|
+
when 'USR1'
|
19
|
+
logger.info 'Received USR1, no longer accepting new work'
|
20
|
+
launcher.quiet
|
21
|
+
when 'USR2'
|
22
|
+
if options[:logfile]
|
23
|
+
logger.info 'Received USR2, reopening log file'
|
24
|
+
Teplohod::Logging.reopen_logs
|
25
|
+
end
|
26
|
+
when 'TTIN'
|
27
|
+
Thread.list.each do |thread|
|
28
|
+
logger.warn "Thread TID-#{thread.object_id.to_s(36)} #{thread['label']}"
|
29
|
+
if thread.backtrace
|
30
|
+
logger.warn thread.backtrace.join("\n")
|
31
|
+
else
|
32
|
+
logger.warn '<no backtrace available>'
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'teplohod/utils'
|
2
|
+
|
3
|
+
# TODO: use statsd or something more flexible
|
4
|
+
module Teplohod
|
5
|
+
module Stats
|
6
|
+
include Utils
|
7
|
+
|
8
|
+
class << self
|
9
|
+
PROVIDERS = 'providers'.freeze
|
10
|
+
|
11
|
+
def all(target)
|
12
|
+
key = target.key
|
13
|
+
processed = redis.with { |conn| conn.get "#{key}:processed" } || 0
|
14
|
+
{
|
15
|
+
processed: processed
|
16
|
+
}
|
17
|
+
end
|
18
|
+
|
19
|
+
def increment(type, target)
|
20
|
+
send type, target.key
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def processed_jobs(key)
|
26
|
+
redis.with do |conn|
|
27
|
+
conn.incr "#{key}:processed"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def redis
|
32
|
+
@redis ||= Teplohod.redis
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Teplohod
|
2
|
+
module Utils
|
3
|
+
def threads(_name, count: 1)
|
4
|
+
threads = []
|
5
|
+
count.times do
|
6
|
+
thread = Thread.new do
|
7
|
+
yield
|
8
|
+
end
|
9
|
+
thread.abort_on_exception = true
|
10
|
+
threads << thread
|
11
|
+
end
|
12
|
+
threads
|
13
|
+
end
|
14
|
+
|
15
|
+
def environment
|
16
|
+
Teplohod.environment
|
17
|
+
end
|
18
|
+
|
19
|
+
def logger
|
20
|
+
Teplohod.logger
|
21
|
+
end
|
22
|
+
|
23
|
+
def options
|
24
|
+
Teplohod.options
|
25
|
+
end
|
26
|
+
|
27
|
+
def redis
|
28
|
+
Teplohod.redis
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/lib/teplohod/web.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'sinatra'
|
2
|
+
require 'erb'
|
3
|
+
|
4
|
+
require 'teplohod'
|
5
|
+
|
6
|
+
module Teplohod
|
7
|
+
class Web < Sinatra::Base
|
8
|
+
set :views, File.expand_path("#{File.dirname(__FILE__)}/../../web/views")
|
9
|
+
set :public_folder, File.expand_path("#{File.dirname(__FILE__)}/../../web/assets")
|
10
|
+
|
11
|
+
set :server, :thin
|
12
|
+
set :logging, false
|
13
|
+
|
14
|
+
get '/' do
|
15
|
+
providers = Teplohod::Manager.providers
|
16
|
+
erb :index, locals: { providers: providers }
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/lib/teplohod.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'active_support/core_ext/class/attribute_accessors'
|
2
|
+
require 'redis'
|
3
|
+
require 'connection_pool'
|
4
|
+
require 'redis-namespace'
|
5
|
+
|
6
|
+
require 'teplohod/version'
|
7
|
+
require 'teplohod/utils'
|
8
|
+
require 'teplohod/logging'
|
9
|
+
|
10
|
+
require 'teplohod/manager'
|
11
|
+
|
12
|
+
require 'teplohod/queue/base'
|
13
|
+
require 'teplohod/provider/base'
|
14
|
+
require 'teplohod/pipeline/base'
|
15
|
+
require 'teplohod/processor/base'
|
16
|
+
|
17
|
+
module Teplohod
|
18
|
+
NAME = 'Teplohod'.freeze
|
19
|
+
REDIS_NAMESPACE = 'teplohod'.freeze
|
20
|
+
|
21
|
+
class << self
|
22
|
+
DEFAULTS = {
|
23
|
+
environment: (ENV['RACK_ENV'] || ENV['RAILS_ENV'] || 'development'),
|
24
|
+
require: './parser.rb',
|
25
|
+
verbose: false,
|
26
|
+
daemonize: false
|
27
|
+
}.freeze
|
28
|
+
|
29
|
+
def options
|
30
|
+
@options ||= DEFAULTS.dup
|
31
|
+
end
|
32
|
+
|
33
|
+
attr_writer :options
|
34
|
+
attr_reader :redis
|
35
|
+
|
36
|
+
def reload_options!
|
37
|
+
@options = DEFAULTS.dup
|
38
|
+
end
|
39
|
+
|
40
|
+
def logger
|
41
|
+
Teplohod::Logging.logger
|
42
|
+
end
|
43
|
+
|
44
|
+
def logger=(log)
|
45
|
+
Teplohod::Logging.logger = log
|
46
|
+
end
|
47
|
+
|
48
|
+
# TODO add configuartion from options
|
49
|
+
def redis=(redis)
|
50
|
+
@redis ||= ConnectionPool.new(options) { Redis::Namespace.new(REDIS_NAMESPACE, redis: redis) }
|
51
|
+
end
|
52
|
+
|
53
|
+
def environment
|
54
|
+
options[:environment]
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
data/teplohod.gemspec
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'teplohod/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'teplohod'
|
8
|
+
spec.version = Teplohod::VERSION
|
9
|
+
spec.authors = ['Sergey Kuchmistov']
|
10
|
+
spec.email = ['sergey.kuchmistov@gmail.com']
|
11
|
+
|
12
|
+
spec.summary = 'Framework for persisted ongoing data processing'
|
13
|
+
spec.description = 'Framework for persisted ongoing data processing like scraping or analyzing.'
|
14
|
+
spec.homepage = 'http://raisintech.com/teplohod'
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
17
|
+
f.match(%r{^(test)/})
|
18
|
+
end
|
19
|
+
spec.executables = ['teplohod']
|
20
|
+
spec.require_paths = ['lib']
|
21
|
+
|
22
|
+
spec.add_dependency 'sinatra', '~> 2.0.0.beta2'
|
23
|
+
spec.add_dependency 'activesupport'
|
24
|
+
spec.add_dependency 'redis'
|
25
|
+
spec.add_dependency 'connection_pool'
|
26
|
+
spec.add_dependency 'redis-namespace'
|
27
|
+
spec.add_dependency 'thin'
|
28
|
+
|
29
|
+
spec.add_development_dependency 'bundler', '~> 1.13'
|
30
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
31
|
+
spec.add_development_dependency 'minitest', '~> 5.0'
|
32
|
+
spec.add_development_dependency 'simplecov'
|
33
|
+
spec.add_development_dependency 'simplecov-console'
|
34
|
+
spec.add_development_dependency 'minitest-reporters'
|
35
|
+
spec.add_development_dependency 'fakeredis'
|
36
|
+
spec.add_development_dependency 'rubocop'
|
37
|
+
spec.add_development_dependency 'yard'
|
38
|
+
end
|