teplohod 0.0.1.alpha1

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.
@@ -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
@@ -0,0 +1,3 @@
1
+ module Teplohod
2
+ VERSION = '0.0.1.alpha1'.freeze
3
+ end
@@ -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