processor 1.0.0 → 2.0.0
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.
- data/.coveralls.yml +1 -0
- data/.gitignore +1 -0
- data/README.md +68 -41
- data/example/migrator.rb +2 -0
- data/lib/processor/data/array_processor.rb +4 -0
- data/lib/processor/data/null_processor.rb +7 -10
- data/lib/processor/environment.rb +3 -0
- data/lib/processor/event_processor.rb +25 -0
- data/lib/processor/logger_messages.rb +56 -0
- data/lib/processor/messenger.rb +16 -3
- data/lib/processor/observer/logger.rb +43 -34
- data/lib/processor/observer/null_observer.rb +8 -3
- data/lib/processor/process_runner/threads.rb +3 -8
- data/lib/processor/runner.rb +8 -23
- data/lib/processor/subroutine/counter.rb +25 -0
- data/lib/processor/subroutine/name.rb +28 -0
- data/lib/processor/subroutine/recursion.rb +29 -0
- data/lib/processor/thread.rb +5 -1
- data/lib/processor/version.rb +1 -1
- data/lib/processor.rb +2 -1
- data/processor.gemspec +6 -4
- data/spec/processor/data/array_processor_spec.rb +1 -2
- data/spec/processor/data/null_processor_spec.rb +27 -2
- data/spec/processor/event_processor_spec.rb +58 -0
- data/spec/processor/logger_messages_spec.rb +69 -0
- data/spec/processor/messenger_spec.rb +15 -0
- data/spec/processor/observer/logger_spec.rb +47 -16
- data/spec/processor/observer/null_observer_spec.rb +33 -0
- data/spec/processor/process_runner/specs.rb +12 -23
- data/spec/processor/process_runner/threads_spec.rb +1 -3
- data/spec/processor/runner_spec.rb +28 -54
- data/spec/processor/subroutine/counter_spec.rb +44 -0
- data/spec/processor/subroutine/name_spec.rb +23 -0
- data/spec/processor/subroutine/recursion_spec.rb +23 -0
- data/spec/processor/thread_spec.rb +1 -1
- data/spec/spec_helper_lite.rb +7 -2
- data/spec/support/dummy_file +0 -0
- metadata +47 -12
- data/lib/processor/data/solr_processor.rb +0 -23
- data/lib/processor/events_registrator.rb +0 -16
- data/spec/processor/events_registrator_spec.rb +0 -15
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'delegate'
|
2
|
+
|
3
|
+
module Processor
|
4
|
+
module Subroutine
|
5
|
+
class Name < ::SimpleDelegator
|
6
|
+
def name
|
7
|
+
return super if __getobj__.respond_to? :name
|
8
|
+
|
9
|
+
# underscore a class name
|
10
|
+
@name ||= real_object.class.name.to_s.
|
11
|
+
gsub(/::/, '_').
|
12
|
+
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
13
|
+
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
14
|
+
downcase
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
def real_object
|
19
|
+
object = __getobj__
|
20
|
+
while object.is_a? SimpleDelegator
|
21
|
+
object = object.__getobj__
|
22
|
+
end
|
23
|
+
|
24
|
+
object
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require_relative 'counter'
|
2
|
+
require 'delegate'
|
3
|
+
|
4
|
+
module Processor
|
5
|
+
module Subroutine
|
6
|
+
class Recursion < ::SimpleDelegator
|
7
|
+
def initialize(processor)
|
8
|
+
# recursion depends on counter subroutine
|
9
|
+
processor = Counter.new(processor) unless processor.respond_to? :processed_records_count
|
10
|
+
|
11
|
+
super processor
|
12
|
+
end
|
13
|
+
|
14
|
+
def process(*)
|
15
|
+
recursion_preventer
|
16
|
+
super
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
def recursion_preventer
|
21
|
+
raise Exception, "Processing fall into recursion. Check logs." if processed_records_count >= max_records_to_process
|
22
|
+
end
|
23
|
+
|
24
|
+
def max_records_to_process
|
25
|
+
@max_records_to_process ||= (total_records * 1.1).round + 10
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/processor/thread.rb
CHANGED
@@ -1,11 +1,15 @@
|
|
1
1
|
require 'processor/runner'
|
2
|
+
require 'processor/event_processor'
|
2
3
|
require 'processor/process_runner/successive'
|
3
4
|
require 'processor/process_runner/threads'
|
5
|
+
require 'processor/subroutine/recursion'
|
4
6
|
|
5
7
|
module Processor
|
6
8
|
class Thread
|
7
9
|
def initialize(data_processor, *observers)
|
8
|
-
|
10
|
+
data_processor = Subroutine::Recursion.new(data_processor)
|
11
|
+
|
12
|
+
@runner = Runner.new EventProcessor.new(data_processor, observers)
|
9
13
|
end
|
10
14
|
|
11
15
|
def run_as(&process_runner)
|
data/lib/processor/version.rb
CHANGED
data/lib/processor.rb
CHANGED
@@ -1,10 +1,11 @@
|
|
1
|
+
require "processor/environment"
|
1
2
|
require "processor/version"
|
2
3
|
|
3
4
|
require "processor/data/array_processor"
|
4
5
|
require "processor/data/batch_processor"
|
5
6
|
require "processor/data/null_processor"
|
6
|
-
require "processor/data/solr_processor"
|
7
7
|
require "processor/data/solr_pages_processor"
|
8
|
+
require "processor/data/csv_processor"
|
8
9
|
|
9
10
|
require "processor/observer/logger"
|
10
11
|
require "processor/observer/null_observer"
|
data/processor.gemspec
CHANGED
@@ -2,15 +2,15 @@
|
|
2
2
|
lib = File.expand_path('../lib', __FILE__)
|
3
3
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
4
|
require 'processor/version'
|
5
|
+
require 'processor/environment'
|
5
6
|
|
6
7
|
Gem::Specification.new do |gem|
|
7
8
|
gem.name = "processor"
|
8
9
|
gem.version = Processor::VERSION
|
9
10
|
gem.authors = ["Alexander Paramonov"]
|
10
11
|
gem.email = ["alexander.n.paramonov@gmail.com"]
|
11
|
-
gem.summary = %q{Universal processor for
|
12
|
-
gem.description = %q{Processor
|
13
|
-
You may add own observers for monitoring background tasks on even send email to bussiness with generated report.}
|
12
|
+
gem.summary = %q{Universal processor for iteration over a collection with threads, logging and post processing}
|
13
|
+
gem.description = %q{Processor is a tool that helps to iterate over collection and perform complex actions on a result. It is extremely useful in data migrations, report generation, etc. }
|
14
14
|
gem.homepage = "http://github.com/AlexParamonov/processor"
|
15
15
|
gem.license = "MIT"
|
16
16
|
|
@@ -21,6 +21,8 @@ Gem::Specification.new do |gem|
|
|
21
21
|
|
22
22
|
gem.add_development_dependency "rake"
|
23
23
|
gem.add_development_dependency "rspec", ">= 2.6"
|
24
|
-
gem.add_development_dependency "pry"
|
24
|
+
gem.add_development_dependency "pry" unless Processor::RUNNING_ON_CI
|
25
|
+
gem.add_development_dependency "pry-plus" if "ruby" == RUBY_ENGINE && false == Processor::RUNNING_ON_CI
|
26
|
+
gem.add_development_dependency "coveralls" if Processor::RUNNING_ON_CI
|
25
27
|
end
|
26
28
|
|
@@ -3,8 +3,7 @@ require 'processor/data/array_processor'
|
|
3
3
|
|
4
4
|
describe Processor::Data::ArrayProcessor do
|
5
5
|
it "should have total records count equals to count of records" do
|
6
|
-
records
|
7
|
-
subject.stub(records: records)
|
6
|
+
subject.stub(records: 1..5)
|
8
7
|
subject.total_records.should eq 5
|
9
8
|
end
|
10
9
|
end
|
@@ -1,9 +1,34 @@
|
|
1
1
|
require 'spec_helper_lite'
|
2
2
|
require 'processor/data/null_processor'
|
3
|
+
require 'processor/runner'
|
4
|
+
require 'processor/process_runner/successive'
|
3
5
|
|
4
6
|
describe Processor::Data::NullProcessor do
|
5
|
-
it "should have
|
6
|
-
subject.
|
7
|
+
it "should have zero records" do
|
8
|
+
subject.records.should be_empty
|
9
|
+
subject.total_records.should be_zero
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "required methods" do
|
13
|
+
it "should respond to all happy path methods" do
|
14
|
+
collector = Class.new do
|
15
|
+
def records; [1] end
|
16
|
+
def method_missing(method, *)
|
17
|
+
@methods = @methods.to_a << method
|
18
|
+
end
|
19
|
+
attr_reader :methods
|
20
|
+
end.new
|
21
|
+
|
22
|
+
runner = Processor::Runner.new collector
|
23
|
+
runner.run Processor::ProcessRunner::Successive.new
|
24
|
+
collector.methods.each do |method|
|
25
|
+
subject.should respond_to method
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
%w[record_error error total_records].each do |method|
|
30
|
+
it { subject.should respond_to method }
|
31
|
+
end
|
7
32
|
end
|
8
33
|
end
|
9
34
|
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'spec_helper_lite'
|
2
|
+
require 'processor/event_processor'
|
3
|
+
|
4
|
+
describe Processor::EventProcessor do
|
5
|
+
let(:processor) { stub.as_null_object }
|
6
|
+
let(:observer) { stub.as_null_object }
|
7
|
+
let(:observers) { [observer] }
|
8
|
+
subject { Processor::EventProcessor.new(processor, observers) }
|
9
|
+
|
10
|
+
|
11
|
+
it "should delegate methods to processor" do
|
12
|
+
processor.should_receive(:method_name).and_return("result")
|
13
|
+
subject.method_name.should eq "result"
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should send a processor by update method" do
|
17
|
+
observer.should_receive(:update).with(/method_name/, processor)
|
18
|
+
subject.method_name
|
19
|
+
end
|
20
|
+
|
21
|
+
describe "before and after events" do
|
22
|
+
it "should fire before_method_name event before calling processor.method_name" do
|
23
|
+
observer.should_receive(:update).ordered do |event|
|
24
|
+
event.should eq :before_method_name
|
25
|
+
end
|
26
|
+
processor.should_receive(:method_name).ordered
|
27
|
+
subject.method_name
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should fire after_method_name event after calling processor.method_name" do
|
31
|
+
observer.should_receive(:update)
|
32
|
+
processor.should_receive(:method_name, processor).ordered
|
33
|
+
observer.should_receive(:update).ordered do |event|
|
34
|
+
event.should eq :after_method_name
|
35
|
+
end
|
36
|
+
subject.method_name
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should send result of processing to after_ event" do
|
40
|
+
result = stub
|
41
|
+
processor.stub(method_name: result)
|
42
|
+
observer.should_receive(:update).with(:after_method_name, processor, result).ordered
|
43
|
+
subject.method_name
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe "broadcasting of events" do
|
48
|
+
let(:observers) do
|
49
|
+
3.times.map do
|
50
|
+
stub(:observer).tap { |observer| observer.should_receive(:update).with(:test_event, processor).once }
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should broadcast events to all observers" do
|
55
|
+
subject.register :test_event
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require "spec_helper_lite"
|
2
|
+
require "logger"
|
3
|
+
require "processor/logger_messages"
|
4
|
+
|
5
|
+
describe Processor::LoggerMessages do
|
6
|
+
let(:messages) { Processor::LoggerMessages.new logger }
|
7
|
+
shared_examples_for "a null logger" do
|
8
|
+
it "should not produce any output" do
|
9
|
+
messages.finished.should eq ""
|
10
|
+
messages.initialized.should eq ""
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "Ruby Logger" do
|
15
|
+
describe "file" do
|
16
|
+
let(:filename) { "spec/support/dummy_file" }
|
17
|
+
let(:logger) { Logger.new File.new(filename) }
|
18
|
+
|
19
|
+
it "should include file name into message" do
|
20
|
+
messages.finished.should include filename
|
21
|
+
messages.initialized.should include filename
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe "filename" do
|
26
|
+
let(:filename) { "spec/support/dummy_file" }
|
27
|
+
let(:logger) { Logger.new filename }
|
28
|
+
|
29
|
+
it "should include file name into message" do
|
30
|
+
messages.finished.should include filename
|
31
|
+
messages.initialized.should include filename
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe "null" do
|
36
|
+
let(:logger) { ::Logger.new(File::NULL) }
|
37
|
+
it_behaves_like "a null logger"
|
38
|
+
end
|
39
|
+
|
40
|
+
describe "STDOUT" do
|
41
|
+
let(:logger) { Logger.new(STDOUT) }
|
42
|
+
|
43
|
+
it "should include IO in the message" do
|
44
|
+
messages.initialized.should include "IO"
|
45
|
+
messages.finished.should eq ""
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe "STDERR" do
|
50
|
+
let(:logger) { Logger.new(STDERR) }
|
51
|
+
|
52
|
+
it "should include IO in the message" do
|
53
|
+
messages.initialized.should include "IO"
|
54
|
+
messages.finished.should eq ""
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe "nil" do
|
60
|
+
let(:logger) { nil }
|
61
|
+
it_behaves_like "a null logger"
|
62
|
+
end
|
63
|
+
|
64
|
+
describe "unknown object" do
|
65
|
+
let(:logger) { Object.new }
|
66
|
+
it_behaves_like "a null logger"
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
@@ -21,6 +21,21 @@ describe Processor::Messenger do
|
|
21
21
|
messenger.fatal "fatal error"
|
22
22
|
end
|
23
23
|
|
24
|
+
it "should not send empty messages" do
|
25
|
+
io.should_not_receive(:write)
|
26
|
+
messenger = Processor::Messenger.new :info, io
|
27
|
+
[nil, ""].each do |empty_message|
|
28
|
+
messenger.message empty_message
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should send messages from a name of sender" do
|
33
|
+
sender = "File Logger"
|
34
|
+
io.should_receive(:write).with /#{sender}/
|
35
|
+
messenger = Processor::Messenger.new :info, io, sender
|
36
|
+
messenger.error "failed to create log file"
|
37
|
+
end
|
38
|
+
|
24
39
|
describe "messages" do
|
25
40
|
let(:messenger) { Processor::Messenger.new :debug, io }
|
26
41
|
%w[debug info error fatal].each do |message_level|
|
@@ -2,31 +2,62 @@ require 'spec_helper_lite'
|
|
2
2
|
require 'processor/observer/logger'
|
3
3
|
|
4
4
|
describe Processor::Observer::Logger do
|
5
|
+
subject { Processor::Observer::Logger.new logger, messenger: messenger, messages: messages, processor: processor }
|
6
|
+
|
5
7
|
let(:processor) { stub.as_null_object }
|
6
|
-
let(:
|
8
|
+
let(:messages) { stub.as_null_object }
|
9
|
+
let(:messenger) { ::Logger.new("/dev/null") }
|
10
|
+
let(:logger) { ::Logger.new("/dev/null") }
|
11
|
+
|
12
|
+
describe "logger" do
|
13
|
+
describe "as proc" do
|
14
|
+
let(:external_logger) { stub }
|
15
|
+
let(:logger) { -> name { external_logger } }
|
16
|
+
|
17
|
+
it "should be assepted" do
|
18
|
+
subject.logger.should eq external_logger
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "as object" do
|
23
|
+
let(:external_logger) { stub }
|
24
|
+
let(:logger) { external_logger }
|
25
|
+
|
26
|
+
it "should be assepted" do
|
27
|
+
subject.logger.should eq external_logger
|
28
|
+
end
|
29
|
+
end
|
7
30
|
|
8
|
-
|
9
|
-
|
10
|
-
external_logger = mock
|
11
|
-
logger_observer = subject.new -> name { external_logger }, messenger: no_messages
|
31
|
+
describe "nil" do
|
32
|
+
let(:logger) { nil }
|
12
33
|
|
13
|
-
|
14
|
-
|
34
|
+
it "uses ruby Logger" do
|
35
|
+
ruby_logger = stub.as_null_object
|
36
|
+
observer = subject
|
37
|
+
::Logger.should_receive(:new).and_return(ruby_logger)
|
38
|
+
observer.logger.should eq ruby_logger
|
39
|
+
end
|
15
40
|
end
|
41
|
+
end
|
16
42
|
|
17
|
-
|
18
|
-
|
19
|
-
|
43
|
+
describe "messages" do
|
44
|
+
describe "as object" do
|
45
|
+
let(:messages) { stub }
|
20
46
|
|
21
|
-
|
22
|
-
|
47
|
+
it "should be assepted" do
|
48
|
+
messages.should_receive(:initialized)
|
49
|
+
subject.after_start nil
|
50
|
+
end
|
23
51
|
end
|
24
52
|
|
25
|
-
|
26
|
-
|
53
|
+
describe "as hash" do
|
54
|
+
let(:messages) { { initialized: "INIT" } }
|
27
55
|
|
28
|
-
|
29
|
-
|
56
|
+
it "should be assepted" do
|
57
|
+
messenger.should_receive(:info).with("INIT")
|
58
|
+
subject.after_start nil
|
59
|
+
end
|
30
60
|
end
|
61
|
+
end
|
31
62
|
end
|
32
63
|
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'spec_helper_lite'
|
2
|
+
require 'processor/observer/null_observer'
|
3
|
+
|
4
|
+
describe Processor::Observer::NullObserver do
|
5
|
+
|
6
|
+
it "should send update message to itself if know how to respond to this message" do
|
7
|
+
subject.should_receive(:know_how)
|
8
|
+
subject.update :know_how
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should ignore update message if dont know how to respond to this message" do
|
12
|
+
subject.stub(:respond_to?).with(:unknown_method).and_return(false)
|
13
|
+
subject.should_not_receive(:unknown_method)
|
14
|
+
subject.update :unknown_method
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should blow up if got undefined method call" do
|
18
|
+
expect { subject.undefined_method }.to raise_error(NoMethodError)
|
19
|
+
end
|
20
|
+
|
21
|
+
describe "processor" do
|
22
|
+
let(:processor) { stub }
|
23
|
+
it "should set a processor" do
|
24
|
+
observer = Processor::Observer::NullObserver.new processor: processor
|
25
|
+
observer.processor.should eq processor
|
26
|
+
end
|
27
|
+
|
28
|
+
specify "update method should set a processor" do
|
29
|
+
subject.update :some_method, processor
|
30
|
+
subject.processor.should eq processor
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -1,49 +1,38 @@
|
|
1
1
|
shared_examples_for "a records processor" do
|
2
2
|
let(:process_runner) { described_class.new }
|
3
3
|
let(:records) { 1..2 }
|
4
|
-
let(:processor) { stub.tap { |p| p.stub(records: records) } }
|
5
|
-
|
6
|
-
let(:no_recursion_preventer) { Proc.new{} }
|
7
|
-
let(:no_events) { stub.as_null_object }
|
4
|
+
let(:processor) { stub.tap { |p| p.stub(records: records) }.as_null_object }
|
8
5
|
|
9
6
|
it "should fetch records from processor" do
|
10
7
|
processor.should_receive(:records).and_return([])
|
11
|
-
process_runner.call processor
|
8
|
+
process_runner.call processor
|
12
9
|
end
|
13
10
|
|
14
11
|
it "should send each found record to processor" do
|
15
12
|
records.each { |record| processor.should_receive(:process).with(record) }
|
16
|
-
process_runner.call processor
|
13
|
+
process_runner.call processor
|
17
14
|
end
|
18
15
|
|
19
16
|
describe "exception handling" do
|
20
17
|
describe "processing a record raised StandardError" do
|
18
|
+
let(:records) { 1..3 }
|
19
|
+
|
21
20
|
it "should continue processing" do
|
22
|
-
processor.should_receive(:process).
|
23
|
-
expect { process_runner.call processor
|
21
|
+
processor.should_receive(:process).exactly(3).times.and_raise(StandardError)
|
22
|
+
expect { process_runner.call processor }.to_not raise_error
|
24
23
|
end
|
25
24
|
|
26
|
-
it "should
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
events_registrator.should_receive(:register) do |event_name, failed_record, exception|
|
31
|
-
next if event_name != :record_processing_error
|
32
|
-
event_name.should eq :record_processing_error
|
33
|
-
exception.should be_a StandardError
|
34
|
-
event.trigger
|
35
|
-
end.any_number_of_times
|
36
|
-
|
37
|
-
processor.stub(:process).and_raise(StandardError)
|
38
|
-
|
39
|
-
process_runner.call processor, events_registrator, no_recursion_preventer rescue nil
|
25
|
+
it "should call record_error" do
|
26
|
+
processor.should_receive(:process).at_least(1).and_raise(StandardError)
|
27
|
+
processor.should_receive(:record_error).at_least(1)
|
28
|
+
process_runner.call processor
|
40
29
|
end
|
41
30
|
end
|
42
31
|
|
43
32
|
describe "processing a record raised Exception" do
|
44
33
|
it "should break processing" do
|
45
34
|
processor.should_receive(:process).once.and_raise(Exception)
|
46
|
-
expect { process_runner.call processor
|
35
|
+
expect { process_runner.call processor }.to raise_error(Exception)
|
47
36
|
end
|
48
37
|
end
|
49
38
|
end
|
@@ -3,8 +3,6 @@ require_relative 'specs'
|
|
3
3
|
require 'processor/process_runner/threads'
|
4
4
|
|
5
5
|
describe Processor::ProcessRunner::Threads do
|
6
|
-
let(:no_recursion_preventer) { Proc.new{} }
|
7
|
-
let(:no_events) { stub.as_null_object }
|
8
6
|
it_behaves_like "a records processor"
|
9
7
|
|
10
8
|
it "should run in defined number of threads" do
|
@@ -20,6 +18,6 @@ describe Processor::ProcessRunner::Threads do
|
|
20
18
|
processor.stub(records: 1..9)
|
21
19
|
processor.should_receive(:process).exactly(9).times
|
22
20
|
|
23
|
-
process_runner.call
|
21
|
+
process_runner.call processor
|
24
22
|
end
|
25
23
|
end
|
@@ -2,70 +2,44 @@ require 'spec_helper_lite'
|
|
2
2
|
require 'processor/runner'
|
3
3
|
|
4
4
|
describe Processor::Runner do
|
5
|
-
let(:runner) { Processor::Runner.new(processor
|
6
|
-
let(:processor) { stub }
|
7
|
-
let(:events_registrator) { stub.as_null_object }
|
5
|
+
let(:runner) { Processor::Runner.new(processor) }
|
6
|
+
let(:processor) { stub.as_null_object }
|
8
7
|
let(:no_process_runner) { Proc.new{} }
|
9
8
|
|
10
|
-
|
9
|
+
it "should start processing" do
|
10
|
+
processor.should_receive(:start)
|
11
|
+
runner.run no_process_runner
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should finish processing" do
|
15
|
+
processor.should_receive(:finish)
|
16
|
+
runner.run no_process_runner
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should finalize processing" do
|
20
|
+
processor.should_receive(:finalize)
|
21
|
+
runner.run no_process_runner
|
22
|
+
end
|
23
|
+
|
24
|
+
describe "processing error" do
|
11
25
|
describe "processing records raised" do
|
26
|
+
let(:process_runner) { Proc.new { raise RuntimeError } }
|
27
|
+
|
12
28
|
it "should break processing and rerise" do
|
13
29
|
expect do
|
14
|
-
runner.run
|
30
|
+
runner.run process_runner
|
15
31
|
end.to raise_error(RuntimeError)
|
16
32
|
end
|
17
33
|
|
18
|
-
it "should
|
19
|
-
|
20
|
-
runner.run
|
34
|
+
it "should send error to processor" do
|
35
|
+
processor.should_receive(:error)
|
36
|
+
runner.run process_runner rescue nil
|
21
37
|
end
|
22
|
-
end
|
23
|
-
|
24
|
-
private
|
25
|
-
def register_processing_error_event(event)
|
26
|
-
# Check that processing_error event was register
|
27
|
-
events_registrator.should_receive(:register) do |event_name, current_processor, exception|
|
28
|
-
next if event_name != :processing_error
|
29
|
-
event_name.should eq :processing_error
|
30
|
-
current_processor.should eq processor
|
31
|
-
exception.should be_a RuntimeError
|
32
|
-
event.trigger
|
33
|
-
end.any_number_of_times
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
describe "recursion" do
|
38
|
-
before(:each) do
|
39
|
-
processor.stub(total_records: 100)
|
40
|
-
processor.stub(records: 1..Float::INFINITY)
|
41
|
-
end
|
42
|
-
|
43
|
-
it "should not fall into recursion" do
|
44
|
-
processor.should_receive(:process).at_most(1000).times
|
45
38
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
data_processor.process record
|
51
|
-
end
|
52
|
-
end
|
53
|
-
runner.run process_runner
|
54
|
-
end.to raise_error(Exception, /Processing fall into recursion/)
|
55
|
-
end
|
56
|
-
|
57
|
-
it "should have 10% + 10 rerurns window" do
|
58
|
-
processor.should_receive(:process).exactly(120).times
|
59
|
-
|
60
|
-
expect do
|
61
|
-
process_runner = Proc.new do |data_processor, events, recursion_preventer|
|
62
|
-
data_processor.records.each do |record|
|
63
|
-
recursion_preventer.call
|
64
|
-
data_processor.process record
|
65
|
-
end
|
66
|
-
end
|
67
|
-
runner.run process_runner
|
68
|
-
end.to raise_error(Exception, /Processing fall into recursion/)
|
39
|
+
it "should finalize processing" do
|
40
|
+
processor.should_receive(:finalize)
|
41
|
+
runner.run process_runner rescue nil
|
42
|
+
end
|
69
43
|
end
|
70
44
|
end
|
71
45
|
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'spec_helper_lite'
|
2
|
+
require 'processor/subroutine/counter'
|
3
|
+
|
4
|
+
describe Processor::Subroutine::Counter do
|
5
|
+
let(:records) { 1..10 }
|
6
|
+
let(:processor) { stub(total_records: 10, process: true) }
|
7
|
+
let(:subroutine) { Processor::Subroutine::Counter.new processor }
|
8
|
+
|
9
|
+
it "should count remaining records" do
|
10
|
+
subroutine.remaining_records_count.should eq 10
|
11
|
+
records.each do
|
12
|
+
expect { subroutine.process }.to change { subroutine.remaining_records_count }.by -1
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should count processed records" do
|
17
|
+
subroutine.processed_records_count.should eq 0
|
18
|
+
records.each do
|
19
|
+
expect { subroutine.process }.to change { subroutine.processed_records_count }.by 1
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe "over process" do
|
24
|
+
before(:each) do
|
25
|
+
10.times { subroutine.process }
|
26
|
+
end
|
27
|
+
|
28
|
+
specify "remaining_records_count should be zero" do
|
29
|
+
subroutine.remaining_records_count.should eq 0
|
30
|
+
5.times do
|
31
|
+
expect { subroutine.process }.to_not change { subroutine.remaining_records_count }.from 0
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
specify "processed_records_count should keep counting" do
|
36
|
+
subroutine.processed_records_count.should eq 10
|
37
|
+
5.times do
|
38
|
+
expect { subroutine.process }.to change { subroutine.processed_records_count }.by 1
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|