processor 1.0.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|