processor 0.0.0.beta → 0.0.0.initial

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,3 @@
1
1
  module Processor
2
- VERSION = "0.0.0.beta"
2
+ VERSION = "0.0.0.initial"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: processor
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.0.beta
4
+ version: 0.0.0.initial
5
5
  prerelease: 6
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-05-02 00:00:00.000000000 Z
12
+ date: 2013-04-30 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake
@@ -67,29 +67,15 @@ extensions: []
67
67
  extra_rdoc_files: []
68
68
  files:
69
69
  - .gitignore
70
- - .rspec
71
70
  - .rvmrc
72
71
  - .travis.yml
73
72
  - Gemfile
74
73
  - LICENSE
75
74
  - README.md
76
75
  - Rakefile
77
- - example/example_runner.rb
78
- - example/migration.rb
79
- - example/observer/progress_bar.rb
80
- - example/simple_runner.rb
81
76
  - lib/processor.rb
82
- - lib/processor/data_processor.rb
83
- - lib/processor/events_registrator.rb
84
- - lib/processor/observer/logger.rb
85
- - lib/processor/observer/null_observer.rb
86
- - lib/processor/thread_runner.rb
87
77
  - lib/processor/version.rb
88
78
  - processor.gemspec
89
- - spec/example_spec.rb
90
- - spec/processor/events_registrator_spec.rb
91
- - spec/processor/observer/logger_spec.rb
92
- - spec/processor/thread_runner_spec.rb
93
79
  - spec/spec_helper_lite.rb
94
80
  homepage: http://github.com/AlexParamonov/processor
95
81
  licenses:
@@ -117,8 +103,4 @@ signing_key:
117
103
  specification_version: 3
118
104
  summary: Process records one by one
119
105
  test_files:
120
- - spec/example_spec.rb
121
- - spec/processor/events_registrator_spec.rb
122
- - spec/processor/observer/logger_spec.rb
123
- - spec/processor/thread_runner_spec.rb
124
106
  - spec/spec_helper_lite.rb
data/.rspec DELETED
@@ -1,2 +0,0 @@
1
- --colour
2
- --format documentation
@@ -1,43 +0,0 @@
1
- require 'processor/thread_runner'
2
- require 'processor/observer/logger'
3
-
4
- module Processor
5
- module Example
6
- class ExampleRunner < ThreadRunner
7
- def initialize
8
- # Logger could be a lambda
9
- # logger = -> name do
10
- # ::Logger.new("log/debug_#{name}_daily.log", "daily").tap do |logger|
11
- # logger.datetime_format = "%H:%M:%S"
12
- # logger.level = ::Logger::DEBUG
13
- # end
14
- # end
15
-
16
- # logger could be an instance of Ruby Logger
17
- logger = ::Logger.new(STDOUT).tap do |logger|
18
- logger.level = ::Logger::DEBUG
19
- logger.formatter = -> _, _, _, msg do
20
- "log < #{msg}\n"
21
- end
22
- end
23
-
24
- # logger could be an instance of Rails Logger
25
- # logger = Rails.logger
26
- # Or be nil
27
- # logger = nil
28
- # in this case logger will be initialized as Ruby Logger and write to log/name_of_processor_time_stamp.log
29
-
30
- # messenger could be an instance of Ruby Logger
31
- messenger = ::Logger.new(STDOUT).tap do |logger|
32
- logger.formatter = -> _, _, _, msg do
33
- "message > #{msg}\n"
34
- end
35
- logger.level = ::Logger::INFO
36
- end
37
-
38
- logger_observer = Processor::Observer::Logger.new(logger, messenger: messenger)
39
- super logger_observer
40
- end
41
- end
42
- end
43
- end
data/example/migration.rb DELETED
@@ -1,29 +0,0 @@
1
- require 'processor/data_processor'
2
-
3
- module Processor
4
- module Example
5
- class Migration < DataProcessor
6
- attr_reader :records
7
- def initialize(records)
8
- @records = records
9
- end
10
-
11
- def done?(records)
12
- records.count < 1
13
- end
14
-
15
- def process(record)
16
- record.do_something
17
- "OK"
18
- end
19
-
20
- def fetch_records
21
- records.shift(2)
22
- end
23
-
24
- def total_records
25
- records.count
26
- end
27
- end
28
- end
29
- end
@@ -1,26 +0,0 @@
1
- require 'processor/observer/null_observer'
2
- require 'progressbar'
3
-
4
- module Processor
5
- module Example
6
- module Observer
7
- class ProgressBar < Processor::Observer::NullObserver
8
- def processing_started
9
- @progress_bar = ::ProgressBar.new("Records", processor.total_records)
10
- messenger.debug "Initialized ProgressBar with #{processor.total_records} records"
11
- end
12
-
13
- def before_record_processing(record)
14
- progress_bar.inc
15
- end
16
-
17
- def processing_finished
18
- progress_bar.finish
19
- end
20
-
21
- private
22
- attr_reader :progress_bar
23
- end
24
- end
25
- end
26
- end
@@ -1,12 +0,0 @@
1
- require 'processor/thread_runner'
2
- require 'processor/observer/logger'
3
-
4
- module Processor
5
- module Example
6
- class SimpleRunner < ThreadRunner
7
- def initialize
8
- super Processor::Observer::Logger.new
9
- end
10
- end
11
- end
12
- end
@@ -1,28 +0,0 @@
1
- module Processor
2
- class DataProcessor
3
- def done?(records)
4
- records.count < 1
5
- end
6
-
7
- def process(record)
8
- raise NotImplementedError
9
- end
10
-
11
- def fetch_records
12
- raise NotImplementedError
13
- end
14
-
15
- def total_records
16
- raise NotImplementedError
17
- end
18
-
19
- def name
20
- # underscore a class name
21
- self.class.name.to_s.
22
- gsub(/::/, '_').
23
- gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
24
- gsub(/([a-z\d])([A-Z])/,'\1_\2').
25
- downcase
26
- end
27
- end
28
- end
@@ -1,16 +0,0 @@
1
- module Processor
2
- class EventsRegistrator
3
- def initialize(observers)
4
- @observers = observers
5
- end
6
-
7
- def register(event, *data)
8
- observers.each do |observer|
9
- observer.update event, *data
10
- end
11
- end
12
-
13
- private
14
- attr_reader :observers
15
- end
16
- end
@@ -1,98 +0,0 @@
1
- require_relative 'null_observer'
2
- require 'logger'
3
-
4
- module Processor
5
- module Observer
6
- class Logger < NullObserver
7
- def initialize(logger = nil, options = {})
8
- @logger_source = logger
9
- super options
10
- end
11
-
12
- def processing_started(processor)
13
- initialize_logger(processor)
14
-
15
- message = "Processing of #{processor.name} started."
16
- logger.info message
17
- messenger.info message
18
-
19
- message = <<-MESSAGE.gsub(/^\s+/, '')
20
- Proggress will be saved to the log file. Run
21
- tail -f #{log_file_name}
22
- to see log in realtime
23
- MESSAGE
24
- messenger.info message if use_log_file?
25
- end
26
-
27
- def before_record_processing(record)
28
- message = "Record #{id_for record} is going to be processed"
29
- logger.debug message
30
- messenger.debug message
31
- end
32
-
33
- def after_record_processing(record, result)
34
- message = "Successfully processed #{id_for record}: #{result}"
35
- logger.info message
36
- messenger.debug message
37
- end
38
-
39
- def processing_finished(processor)
40
- message = "Processing of #{processor.name} finished."
41
- logger.info message
42
- messenger.info message
43
- messenger.info "Log file saved to #{log_file_name}" if use_log_file?
44
- end
45
-
46
- def record_processing_error(record, exception)
47
- message = "Error processing #{id_for record}: #{exception}"
48
- logger.error message
49
- messenger.error message
50
- end
51
-
52
- def processing_error(processor, exception)
53
- message = "Processing #{processor.name} failed: #{exception}"
54
- logger.fatal message
55
- messenger.fatal message
56
- end
57
-
58
- private
59
- attr_reader :logger, :log_file_name
60
-
61
- def initialize_logger(processor)
62
- @logger =
63
- if @logger_source.is_a? Proc
64
- @logger_source.call processor.name
65
- else
66
- @logger_source or ::Logger.new(create_log_filename(processor.name)).tap do |logger|
67
- logger.level = ::Logger::INFO
68
- end
69
- end
70
- end
71
-
72
- def create_log_filename(processor_name)
73
- @log_file_name = "log/#{processor_name}_on_#{current_time_string}.log"
74
- end
75
-
76
- def use_log_file?
77
- not log_file_name.nil?
78
- end
79
-
80
- def current_time_string
81
- Time.now.gmtime.strftime "%Y-%m-%d_%H%M%S_UTC"
82
- end
83
-
84
- def id_for record
85
- [:uid, :id, :to_token, :token, :to_sym].each do |method|
86
- return record.public_send method if record.respond_to? method
87
- end
88
-
89
- [:uid, :id, :token, :sym, :UID, :ID, :TOKEN, :SYM].each do |method|
90
- return record[method] if record.key? method
91
- return record[method.to_s] if record.key? method.to_s
92
- end if record.respond_to?(:key?) && record.respond_to?(:[])
93
-
94
- record.to_s
95
- end
96
- end
97
- end
98
- end
@@ -1,23 +0,0 @@
1
- require 'logger'
2
-
3
- module Processor
4
- module Observer
5
- class NullObserver
6
- def initialize(options = {})
7
- @messenger = options.fetch :messenger do
8
- ::Logger.new(STDOUT).tap do |logger|
9
- logger.formatter = -> _, _, _, msg do
10
- "> #{msg}\n"
11
- end
12
- end
13
- end
14
- end
15
-
16
- def method_missing(*); end
17
- alias_method :update, :send
18
-
19
- private
20
- attr_reader :messenger
21
- end
22
- end
23
- end
@@ -1,59 +0,0 @@
1
- require_relative "events_registrator"
2
-
3
- module Processor
4
- class ThreadRunner
5
- def initialize(*observers)
6
- @observers = observers
7
- end
8
-
9
- # This method is thread-safe. But not observers.
10
- # Consider creating new runner for a thread or use thread safe observers
11
- def run(processor)
12
- events = events_registrator
13
- events.register :processing_started, processor
14
-
15
- records_ran = 0
16
- until processor.done?(records = processor.fetch_records)
17
- threads = []
18
- begin
19
- records.each do |record|
20
- recursion_preventer processor do
21
- records_ran += 1
22
- end
23
-
24
- threads << Thread.new(processor, record) do |thread_data_processor, thread_record|
25
- begin
26
- events.register :before_record_processing, thread_record
27
-
28
- result = thread_data_processor.process(thread_record)
29
-
30
- events.register :after_record_processing, thread_record, result
31
- rescue RuntimeError => exception
32
- events.register :record_processing_error, thread_record, exception
33
- end
34
- end
35
- end
36
- ensure # join already created threads even if recursion was detected
37
- threads.each(&:join)
38
- end
39
- end
40
-
41
- events.register :processing_finished, processor
42
- rescue Exception => exception
43
- events.register :processing_error, processor, exception
44
- raise exception
45
- end
46
-
47
- private
48
- attr_reader :observers
49
-
50
- def recursion_preventer(processor)
51
- counter = yield
52
- raise Exception, "Processing fall into recursion. Check logs." if counter > (processor.total_records * 1.1).round + 10
53
- end
54
-
55
- def events_registrator
56
- EventsRegistrator.new observers
57
- end
58
- end
59
- end
data/spec/example_spec.rb DELETED
@@ -1,24 +0,0 @@
1
- require 'spec_helper_lite'
2
- require_relative '../example/example_runner'
3
- require_relative '../example/migration'
4
- require 'fileutils'
5
-
6
- describe "Example" do
7
- before(:each) do
8
- records = %w[item1 item2 item3 item4 item5]
9
- records.each do |record|
10
- record.should_receive(:do_something).once
11
- end
12
- @migration = Processor::Example::Migration.new records
13
- end
14
-
15
- it "should use logger and messenger" do
16
- migration_runner = Processor::Example::ExampleRunner.new
17
- migration_runner.run @migration
18
- end
19
-
20
- it "should run without configuration" do
21
- runner = Processor::ThreadRunner.new
22
- runner.run @migration
23
- end
24
- end
@@ -1,15 +0,0 @@
1
- require 'spec_helper_lite'
2
- require 'processor/events_registrator'
3
-
4
- describe Processor::EventsRegistrator do
5
- subject { Processor::EventsRegistrator }
6
-
7
- it "should broadcast events to all observers" do
8
- observers = 3.times.map do
9
- stub(:observer).tap { |observer| observer.should_receive(:update).with(:test_event).once }
10
- end
11
-
12
- events = subject.new(observers)
13
- events.register :test_event
14
- end
15
- end
@@ -1,32 +0,0 @@
1
- require 'spec_helper_lite'
2
- require 'processor/observer/logger'
3
-
4
- describe Processor::Observer::Logger do
5
- let(:processor) { stub.as_null_object }
6
- let(:no_messages) { ::Logger.new("/dev/null") }
7
-
8
- subject { Processor::Observer::Logger }
9
- it "accepts logger builder as parameter" do
10
- external_logger = mock
11
- logger_observer = subject.new -> name { external_logger }, messenger: no_messages
12
-
13
- external_logger.should_receive(:info)
14
- logger_observer.processing_started processor
15
- end
16
-
17
- it "accepts logger as parameter" do
18
- external_logger = mock
19
- logger_observer = subject.new external_logger, messenger: no_messages
20
-
21
- external_logger.should_receive(:info)
22
- logger_observer.processing_started processor
23
- end
24
-
25
- it "use ruby Logger if no external logger provided" do
26
- logger_observer = subject.new nil, messenger: no_messages
27
-
28
- Logger.should_receive(:new).and_return(stub.as_null_object)
29
- logger_observer.processing_started processor
30
- end
31
- end
32
-
@@ -1,134 +0,0 @@
1
- require 'spec_helper_lite'
2
- require 'processor/thread_runner'
3
-
4
- processor = Class.new do
5
- def done?(records)
6
- records.count < 1
7
- end
8
-
9
- def total_records
10
- 10
11
- end
12
- end.new
13
-
14
- describe Processor::ThreadRunner do
15
- let(:runner) { Processor::ThreadRunner.new }
16
- let(:events_registrator) { stub.as_null_object }
17
- before(:each) do
18
- runner.stub(events_registrator: events_registrator)
19
- end
20
-
21
- it "should fetch records from processor till it'll be done" do
22
- processor.stub(:done?).and_return(false, false, true)
23
- processor.should_receive(:fetch_records).exactly(3).times.and_return([])
24
- runner.run processor
25
- end
26
-
27
- it "should send each found record to processor" do
28
- records = [mock(:one), mock(:two), mock(:three)]
29
- records.each { |record| processor.should_receive(:process).with(record) }
30
-
31
- processor.stub(:fetch_records).and_return(records, [])
32
- runner.run processor
33
- end
34
-
35
- describe "exception handling" do
36
- let(:record) { stub }
37
- before(:each) do
38
- processor.stub(:fetch_records).and_return([record], [record], [])
39
- end
40
-
41
- describe "processing a record raised RuntimeError" do
42
- it "should continue processing" do
43
- processor.should_receive(:process).twice.and_raise(RuntimeError)
44
- expect { runner.run processor }.to_not raise_error
45
- end
46
-
47
- it "should register a record_processing_error event" do
48
- event_registered = false
49
- events_registrator.should_receive(:register) do |event_name, failed_record, exception|
50
- next if event_name != :record_processing_error
51
- event_name.should eq :record_processing_error
52
- failed_record.should eq record
53
- exception.should be_a RuntimeError
54
- event_registered = true
55
- end.any_number_of_times
56
-
57
- processor.stub(:process).and_raise(RuntimeError)
58
-
59
- begin
60
- runner.run processor
61
- rescue Exception; end
62
- event_registered.should be_true
63
- end
64
- end
65
-
66
- describe "processing a record raised Exception" do
67
- it "should break processing and rerise Exception" do
68
- custom_exception = Class.new Exception
69
- processor.should_receive(:process).once.and_raise(custom_exception)
70
- expect { runner.run processor }.to raise_error(custom_exception)
71
- end
72
-
73
- it "should register a processing_error event" do
74
- event_registered = false
75
- events_registrator.should_receive(:register) do |event_name, current_processor, exception|
76
- next if event_name != :processing_error
77
- event_name.should eq :processing_error
78
- current_processor.should eq processor
79
- exception.should be_a Exception
80
- event_registered = true
81
- end.any_number_of_times
82
-
83
- processor.stub(:process).and_raise(Exception)
84
-
85
- begin
86
- runner.run processor
87
- rescue Exception; end
88
- event_registered.should be_true
89
- end
90
- end
91
-
92
- describe "fetching records raised" do
93
- it "should break processing and rerise Exception" do
94
- custom_exception = Class.new RuntimeError
95
- processor.stub(:fetch_records).and_raise(custom_exception)
96
- processor.should_not_receive(:process)
97
- expect { runner.run processor }.to raise_error(custom_exception)
98
- end
99
-
100
- it "should register a processing_error" do
101
- event_registered = false
102
- events_registrator.should_receive(:register) do |event_name, processor, exception|
103
- next if event_name != :processing_error
104
- event_name.should eq :processing_error
105
- exception.should be_a RuntimeError
106
- event_registered = true
107
- end.any_number_of_times
108
-
109
- processor.stub(:fetch_records).and_raise(RuntimeError)
110
-
111
- begin
112
- runner.run processor
113
- rescue Exception; end
114
- event_registered.should be_true
115
- end
116
- end
117
- end
118
-
119
- describe "recursion" do
120
- it "should not allow infinit recursion" do
121
- processor.stub(:fetch_records).and_return([:one, :two])
122
- processor.should_receive(:process).at_most(100).times
123
- expect { runner.run processor }.to raise_error(Exception, /Processing fall into recursion/)
124
- end
125
-
126
- it "should have 10% + 10 rerurns window" do
127
- processor.stub(total_records: 100)
128
- processor.stub(:fetch_records).and_return([:one, :two])
129
- processor.should_receive(:process).exactly(120).times
130
- expect { runner.run processor }.to raise_error(Exception, /Processing fall into recursion/)
131
- end
132
- end
133
- end
134
-