exel 1.1.0 → 1.2.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.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -0
  3. data/.rubocop_todo.yml +10 -16
  4. data/Gemfile +2 -0
  5. data/README.md +5 -2
  6. data/exel.gemspec +2 -1
  7. data/lib/exel/ast_node.rb +2 -1
  8. data/lib/exel/context.rb +45 -42
  9. data/lib/exel/deferred_context_value.rb +35 -0
  10. data/lib/exel/error/job_termination.rb +3 -5
  11. data/lib/exel/events.rb +26 -0
  12. data/lib/exel/instruction.rb +2 -4
  13. data/lib/exel/instruction_node.rb +1 -0
  14. data/lib/exel/job.rb +32 -10
  15. data/lib/exel/listen_instruction.rb +17 -0
  16. data/lib/exel/null_instruction.rb +1 -0
  17. data/lib/exel/old_context.rb +109 -0
  18. data/lib/exel/processor_helper.rb +1 -4
  19. data/lib/exel/processors/async_processor.rb +1 -0
  20. data/lib/exel/processors/run_processor.rb +3 -0
  21. data/lib/exel/processors/split_processor.rb +12 -2
  22. data/lib/exel/providers/local_file_provider.rb +3 -1
  23. data/lib/exel/providers/threaded_async_provider.rb +1 -0
  24. data/lib/exel/sequence_node.rb +1 -0
  25. data/lib/exel/value.rb +1 -0
  26. data/lib/exel/version.rb +1 -1
  27. data/lib/exel.rb +20 -1
  28. data/spec/exel/ast_node_spec.rb +4 -4
  29. data/spec/exel/context_spec.rb +35 -109
  30. data/spec/exel/deferred_context_value_spec.rb +51 -7
  31. data/spec/exel/events_spec.rb +89 -0
  32. data/spec/exel/instruction_node_spec.rb +3 -3
  33. data/spec/exel/instruction_spec.rb +9 -9
  34. data/spec/exel/job_spec.rb +23 -13
  35. data/spec/exel/listen_instruction_spec.rb +14 -0
  36. data/spec/exel/logging_spec.rb +3 -3
  37. data/spec/exel/processors/split_processor_spec.rb +14 -6
  38. data/spec/exel/sequence_node_spec.rb +1 -1
  39. data/spec/exel_spec.rb +7 -0
  40. data/spec/fixtures/sample.csv +501 -0
  41. data/spec/integration/integration_spec.rb +51 -0
  42. data/spec/spec_helper.rb +17 -1
  43. data/spec/support/integration_test_classes.rb +44 -0
  44. metadata +32 -5
@@ -4,18 +4,28 @@ require_relative '../processor_helper'
4
4
 
5
5
  module EXEL
6
6
  module Processors
7
+ # Implements the +split+ instruction. Used to concurrently process a large file by splitting it into small chunks to
8
+ # be separately processed.
9
+ #
10
+ # =Supported Context Options
11
+ # * +:delete_resource+ Defaults to true, can be set to false to preserve the original resource. Otherwise, it will
12
+ # be deleted when splitting is complete
13
+ # * +:chunk_size+ Set to specify the number of lines that each chunk should contain
7
14
  class SplitProcessor
8
15
  include EXEL::ProcessorHelper
9
16
 
10
17
  attr_accessor :file_name, :block
11
18
 
19
+ # Number of lines to include in each chunk. Can be overridden by setting :chunk_size in the context
12
20
  DEFAULT_CHUNK_SIZE = 1000
13
21
 
22
+ # The context must contain a CSV File object in context[:resource]
14
23
  def initialize(context)
15
24
  @buffer = []
16
25
  @tempfile_count = 0
17
26
  @context = context
18
27
  @file = context[:resource]
28
+ @context[:delete_resource] = true if @context[:delete_resource].nil?
19
29
 
20
30
  log_prefix_with '[SplitProcessor]'
21
31
  end
@@ -73,7 +83,7 @@ module EXEL
73
83
  end
74
84
 
75
85
  def chunk_size
76
- DEFAULT_CHUNK_SIZE
86
+ @context[:chunk_size] || DEFAULT_CHUNK_SIZE
77
87
  end
78
88
 
79
89
  def chunk_filename
@@ -87,7 +97,7 @@ module EXEL
87
97
 
88
98
  def finish(callback)
89
99
  process_line(:eof, callback)
90
- File.delete(@file.path)
100
+ File.delete(@file.path) if @context[:delete_resource]
91
101
  end
92
102
  end
93
103
  end
@@ -1,12 +1,14 @@
1
1
  module EXEL
2
2
  module Providers
3
+ # The default remote provider. Doesn't actually upload and download files to and from remote storage, but rather
4
+ # just works with local files.
3
5
  class LocalFileProvider
4
6
  def upload(file)
5
7
  "file://#{file.path}"
6
8
  end
7
9
 
8
10
  def download(uri)
9
- fail 'URI must begin with "file://"' unless uri.start_with? 'file://'
11
+ raise 'URI must begin with "file://"' unless uri.start_with? 'file://'
10
12
  File.open(uri.split('file://').last)
11
13
  end
12
14
 
@@ -1,5 +1,6 @@
1
1
  module EXEL
2
2
  module Providers
3
+ # The default remote provider. Provides async execution by running the given EXEL block in a new Thread
3
4
  class ThreadedAsyncProvider
4
5
  def initialize(context)
5
6
  @context = context
@@ -1,6 +1,7 @@
1
1
  require_relative './ast_node'
2
2
 
3
3
  module EXEL
4
+ # A node in the AST that has as its children a sequence of nodes to be run sequentially
4
5
  class SequenceNode < ASTNode
5
6
  def initialize(*children)
6
7
  @instruction = NullInstruction.new
data/lib/exel/value.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  module EXEL
2
+ # Contains methods to handle remote and local values. Used for {Context} serialization
2
3
  module Value
3
4
  def self.remotize(value)
4
5
  file?(value) ? upload(value) : value
data/lib/exel/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module EXEL
2
- VERSION = '1.1.0'
2
+ VERSION = '1.2.0'.freeze
3
3
  end
data/lib/exel.rb CHANGED
@@ -2,31 +2,50 @@ require 'exel/version'
2
2
  require 'exel/logging'
3
3
  require 'ostruct'
4
4
 
5
+ # Provides methods to configure EXEL
5
6
  module EXEL
7
+ # @return The currently set logger
6
8
  def self.logger
7
9
  EXEL::Logging.logger
8
10
  end
9
11
 
12
+ # Sets the logger to be used.
13
+ #
14
+ # @param [Logger] The logger to set. Must comply with the Ruby Logger interface
10
15
  def self.logger=(logger)
11
16
  EXEL::Logging.logger = logger
12
17
  end
13
18
 
19
+ # @return The current configuration
14
20
  def self.configuration
15
21
  @config ||= OpenStruct.new
16
22
  end
17
23
 
24
+ # Yields the configuration object to the given block. Configuration can include:
25
+ # * +async_provider+ Set an async provider. Defaults to EXEL::Providers::ThreadedAsyncProvider
26
+ # * +remote_provider+ Set a remote provider. Defaults to EXEL::Providers::LocalFileProvider
27
+ # * Any configuration required by the async/remote providers
28
+ #
29
+ # Typically, async_provider and remote_provider will be automatically set upon requiring those gems.
30
+ #
31
+ # Example:
32
+ # EXEL.configure do |config|
33
+ # config.s3_bucket = 'my_bucket'
34
+ # end
18
35
  def self.configure
19
36
  yield configuration
20
37
  end
21
38
 
39
+ # @return The currently configured async provider. Defaults to EXEL::Providers::ThreadedAsyncProvider
22
40
  def self.async_provider
23
41
  configuration.async_provider || Providers::ThreadedAsyncProvider
24
42
  end
25
43
 
44
+ # @return The currently configured remote provider. Defaults to EXEL::Providers::LocalFileProvider
26
45
  def self.remote_provider
27
46
  configuration.remote_provider || Providers::LocalFileProvider
28
47
  end
29
48
 
30
49
  root = File.expand_path('../..', __FILE__)
31
- Dir[File.join(root, 'lib/exel/**/*.rb')].each { |file| require file }
50
+ Dir[File.join(root, 'lib/exel/**/*.rb')].reject { |file| file.include?('old_context') }.each { |file| require file }
32
51
  end
@@ -10,23 +10,23 @@ module EXEL
10
10
 
11
11
  describe '#start' do
12
12
  context 'when an JobTermination error bubbles up' do
13
- it 'should ensure the process fails silently' do
13
+ it 'ensures the process fails silently' do
14
14
  node = TestNode.new(instruction)
15
15
  allow(node).to receive(:run).and_raise(EXEL::Error::JobTermination, 'Error')
16
16
  expect(EXEL.logger).to receive(:error).with('JobTerminationError: Error')
17
- expect { node.start(context) }.to_not raise_error
17
+ expect { node.start(context) }.not_to raise_error
18
18
  end
19
19
  end
20
20
  end
21
21
 
22
22
  describe '#run' do
23
- it 'should raise an error if not implemented' do
23
+ it 'raises an error if not implemented' do
24
24
  expect { TestNode.new(instruction).run(context) }.to raise_error 'EXEL::TestNode does not implement #process'
25
25
  end
26
26
  end
27
27
 
28
28
  describe '#add_child' do
29
- it 'should add the given node to its children' do
29
+ it 'adds the given node to its children' do
30
30
  root = ASTNode.new(instruction)
31
31
  child_node = ASTNode.new(instruction)
32
32
  child_node2 = ASTNode.new(instruction)
@@ -2,10 +2,13 @@ module EXEL
2
2
  describe Context do
3
3
  subject(:context) { EXEL::Context.new(key1: '1', key2: 2) }
4
4
 
5
+ it { is_expected.to be_a(Hash) }
6
+
5
7
  describe '#initialize' do
6
- it 'should be able to initialize with a hash' do
7
- expect(context.table[:key1]).to eq('1')
8
- expect(context.table[:key2]).to eq(2)
8
+ it 'initializes with a hash' do
9
+ expect(context[:key1]).to eq('1')
10
+ expect(context[:key2]).to eq(2)
11
+ expect(context[:key3]).to be_nil
9
12
  end
10
13
  end
11
14
 
@@ -15,7 +18,7 @@ module EXEL
15
18
 
16
19
  dup = context.deep_dup
17
20
  expect(context).to eq(dup)
18
- expect(context).to_not be_equal(dup)
21
+ expect(context).not_to be_equal(dup)
19
22
 
20
23
  dup[:a][:nested] << 1
21
24
  expect(context[:a][:nested]).to be_empty
@@ -25,7 +28,7 @@ module EXEL
25
28
  describe '#serialize' do
26
29
  before { allow(Value).to receive(:upload) }
27
30
 
28
- it 'should write the serialized context to a file and upload it' do
31
+ it 'writes the serialized context to a file and upload it' do
29
32
  expect(Value).to receive(:remotize).with(context[:key1]).and_return('remote_value1')
30
33
  expect(Value).to receive(:remotize).with(context[:key2]).and_return('remote_value2')
31
34
 
@@ -40,15 +43,16 @@ module EXEL
40
43
  expect(context.serialize).to eq('file_uri')
41
44
  end
42
45
 
43
- it 'should not mutate the current context' do
44
- original_table = context.table.dup
46
+ it 'does not mutate the current context' do
47
+ allow(Value).to receive(:remotize).and_return('remote_value')
48
+ original_table = context.dup
45
49
  context.serialize
46
- expect(context.table).to eq(original_table)
50
+ expect(context).to eq(original_table)
47
51
  end
48
52
  end
49
53
 
50
54
  describe '.deserialize' do
51
- it 'should deserialize a given uri' do
55
+ it 'deserializes a given uri' do
52
56
  file = StringIO.new(Marshal.dump(context))
53
57
  expect(Value).to receive(:localize).with('uri').and_return(file)
54
58
 
@@ -58,124 +62,46 @@ module EXEL
58
62
  end
59
63
  end
60
64
 
61
- describe '#[]' do
65
+ shared_examples 'a reader method' do
62
66
  subject(:context) { EXEL::Context.new(key: 'value') }
63
67
 
64
- it 'should return the value' do
65
- expect(context[:key]).to eq('value')
68
+ it 'returns the value' do
69
+ expect(context.send(method, :key)).to eq('value')
66
70
  end
67
71
 
68
- it 'should localize the returned value' do
72
+ it 'localizes the returned value' do
69
73
  expect(Value).to receive(:localize).with('value').and_return('localized')
70
- expect(context[:key]).to eq('localized')
74
+ expect(context.send(method, :key)).to eq('localized')
71
75
  end
72
76
 
73
- it 'should store the localized value' do
77
+ it 'stores the localized value' do
74
78
  allow(Value).to receive(:localize).with('value').and_return('localized')
75
- context[:key]
76
- expect(context.table[:key]).to eq('localized')
79
+ context.send(method, :key)
80
+ allow(Value).to receive(:localize).with('localized').and_return('localized')
81
+ context.send(method, :key)
77
82
  end
78
83
 
79
- context 'DeferredContextValue object' do
80
- context 'at the top level' do
81
- it 'should return the lookup value from the context' do
82
- deferred_context_value = DeferredContextValue.new[:key]
83
- context[:deferred_value] = deferred_context_value
84
- expect(context[:deferred_value]).to eq(context[:key])
85
- end
86
- end
87
-
88
- context 'in an array' do
89
- it 'should return the lookup value from the context' do
90
- deferred_context_value = DeferredContextValue.new[:key]
91
- context[:array] = [1, 2, deferred_context_value]
92
- expect(context[:array]).to eq([1, 2, context[:key]])
93
- end
94
- end
95
-
96
- context 'in a hash' do
97
- it 'should return the lookup value from the context' do
98
- deferred_context_value = DeferredContextValue.new[:key]
99
- context[:hash] = {hash_key: deferred_context_value}
100
- expect(context[:hash]).to eq(hash_key: context[:key])
101
- end
102
- end
103
-
104
- context 'in nested arrays and hashes' do
105
- it 'should lookup a deferred context value in a hash nested in an array' do
106
- deferred_context_value = DeferredContextValue.new[:key]
107
- context[:nested] = [{}, {hash_key: deferred_context_value}]
108
- expect(context[:nested]).to eq([{}, {hash_key: context[:key]}])
109
- end
110
-
111
- it 'should lookup a deferred context value in an array nested in a hash' do
112
- deferred_context_value = DeferredContextValue.new[:key]
113
- context[:nested] = {hash_key: [1, deferred_context_value]}
114
- expect(context[:nested]).to eq(hash_key: [1, context[:key]])
115
- end
116
- end
117
- end
118
- end
119
-
120
- describe '#[]=' do
121
- it 'should add the key/value pair to table' do
122
- context[:new_key] = 'new_value'
123
- expect(context.table[:new_key]).to eq('new_value')
124
- end
125
- end
126
-
127
- describe '#delete' do
128
- it 'should delete the key/value pair from the table' do
129
- context[:key] = 'value'
130
- context[:key2] = 'value2'
131
- context.delete(:key)
132
- expect(context.table.keys).to_not include(:key)
133
- expect(context.table.keys).to include(:key2)
84
+ it 'looks up deferred values' do
85
+ # eq(context) as an argument matcher is necessary to prevent RSpec from calling fetch on the context, leading to
86
+ # a stack overflow
87
+ expect(DeferredContextValue).to receive(:resolve).with('value', eq(context)).and_return('resolved')
88
+ expect(context.send(method, :key)).to eq('resolved')
134
89
  end
135
90
  end
136
91
 
137
- describe '#merge!' do
138
- it 'should merge given keys and values into the context' do
139
- context.table[:overwrite] = 'overwrite'
140
- context.table[:existing] = 'existing'
141
-
142
- context.merge!(overwrite: 'changed', new: 'new')
143
-
144
- expect(context.table[:overwrite]).to eq('changed')
145
- expect(context.table[:existing]).to eq('existing')
146
- expect(context.table[:new]).to eq('new')
147
- end
148
-
149
- it 'should return itself' do
150
- expect(context.merge!(key: 'value')).to eq(context)
92
+ describe '#[]' do
93
+ it_behaves_like 'a reader method' do
94
+ let(:method) { :[] }
151
95
  end
152
96
  end
153
97
 
154
- describe '#==' do
155
- it { is_expected.to_not eq(nil) }
156
-
157
- it { is_expected.to eq(context) }
158
-
159
- it { is_expected.to_not eq(42) }
160
-
161
- it { is_expected.to_not eq(Context.new(other_key: 'value')) }
162
-
163
- it { is_expected.to eq(context.dup) }
164
- end
165
-
166
- describe 'include?' do
167
- subject(:context) { EXEL::Context.new(key1: 1, key2: 2, key3: 3) }
168
-
169
- context 'context contains all key value pairs' do
170
- it 'should return true' do
171
- expect(context).to include(key1: 1, key2: 2)
172
- end
98
+ describe '#fetch' do
99
+ it 'raises an exception if the key is not found' do
100
+ expect { context.fetch(:unknown) }.to raise_error(KeyError)
173
101
  end
174
102
 
175
- context 'context does not contain all key value pairs' do
176
- it 'should return true' do
177
- expect(context).not_to include(foo: 'bar', key2: 2)
178
- end
103
+ it_behaves_like 'a reader method' do
104
+ let(:method) { :fetch }
179
105
  end
180
106
  end
181
107
  end
@@ -1,20 +1,64 @@
1
1
  module EXEL
2
2
  describe DeferredContextValue do
3
- subject(:deferred_context_value) { DeferredContextValue.new }
3
+ subject(:deferred_value) { DeferredContextValue.new }
4
+
5
+ describe '.resolve' do
6
+ subject(:deferred_value) { DeferredContextValue.new[:key] }
7
+
8
+ context 'at the top level' do
9
+ let(:context) { {key: 'value', deferred_value: deferred_value} }
10
+
11
+ it 'returns the lookup value from the context' do
12
+ expect(DeferredContextValue.resolve(context[:deferred_value], context)).to eq(context[:key])
13
+ end
14
+ end
15
+
16
+ context 'in an array' do
17
+ let(:context) { {key: 'value', array: [1, 2, deferred_value]} }
18
+
19
+ it 'returns the lookup value from the context' do
20
+ expect(DeferredContextValue.resolve(context[:array], context)).to eq([1, 2, context[:key]])
21
+ end
22
+ end
23
+
24
+ context 'in a hash' do
25
+ let(:context) { {key: 'value', hash: {hash_key: deferred_value}} }
26
+
27
+ it 'returns the lookup value from the context' do
28
+ expect(DeferredContextValue.resolve(context[:hash], context)).to eq(hash_key: context[:key])
29
+ end
30
+ end
31
+
32
+ context 'in a hash nested in an array' do
33
+ let(:context) { {key: 'value', nested: [{}, {hash_key: deferred_value}]} }
34
+
35
+ it 'looks up a deferred context value in a hash nested in an array' do
36
+ expect(DeferredContextValue.resolve(context[:nested], context)).to eq([{}, {hash_key: context[:key]}])
37
+ end
38
+ end
39
+
40
+ context 'in an array nested in a hash' do
41
+ let(:context) { {key: 'value', nested: {hash_key: [1, deferred_value]}} }
42
+
43
+ it 'looks up a deferred context value in an array nested in a hash' do
44
+ expect(DeferredContextValue.resolve(context[:nested], context)).to eq(hash_key: [1, context[:key]])
45
+ end
46
+ end
47
+ end
4
48
 
5
49
  describe '#[]' do
6
- it 'should store passed key in the keys attribute' do
7
- deferred_context_value[:top_level_key]['sub_key']
8
- expect(deferred_context_value.keys).to eq([:top_level_key, 'sub_key'])
50
+ it 'stores the given key in the keys attribute' do
51
+ deferred_value[:top_level_key]['sub_key']
52
+ expect(deferred_value.keys).to eq([:top_level_key, 'sub_key'])
9
53
  end
10
54
  end
11
55
 
12
56
  describe '#get' do
13
- it 'should look up the value of the keys attribute in the passed-in context' do
14
- allow(deferred_context_value).to receive(:keys).and_return([:top_level_key, 'sub_key'])
57
+ it 'looks up the value of the keys attribute in the passed-in context' do
58
+ allow(deferred_value).to receive(:keys).and_return([:top_level_key, 'sub_key'])
15
59
  value = 'example_value'
16
60
  context = Context.new(top_level_key: {'sub_key' => value})
17
- expect(deferred_context_value.get(context)).to eq(value)
61
+ expect(deferred_value.get(context)).to eq(value)
18
62
  end
19
63
  end
20
64
  end
@@ -0,0 +1,89 @@
1
+ module EXEL
2
+ describe Events do
3
+ class EventTest
4
+ include Events
5
+ end
6
+
7
+ subject(:events) { EventTest.new }
8
+ let(:event_listener) { double(:event_listener) }
9
+ let(:context) { EXEL::Context.new(Events::LISTENERS_KEY => {event: [event_listener]}) }
10
+
11
+ describe '#register_listener' do
12
+ context 'when no listeners have been defined' do
13
+ let(:context) { EXEL::Context.new }
14
+
15
+ it 'adds a new listener to the context' do
16
+ events.register_listener(context, :event, event_listener)
17
+ expect(context[Events::LISTENERS_KEY].fetch(:event)).to contain_exactly(event_listener)
18
+ end
19
+ end
20
+
21
+ it 'registers multiple listeners for the same event' do
22
+ new_listener = double(:event_listener2)
23
+ events.register_listener(context, :event, new_listener)
24
+ expect(context[Events::LISTENERS_KEY].fetch(:event)).to contain_exactly(event_listener, new_listener)
25
+ end
26
+ end
27
+
28
+ describe '#trigger' do
29
+ let(:context) { EXEL::Context.new }
30
+ let(:data) { {foo: 1} }
31
+
32
+ before { allow(events).to receive(:context).and_return(context) }
33
+
34
+ context 'when no events have been registered' do
35
+ it 'does not trigger anything' do
36
+ expect(event_listener).not_to receive(:event)
37
+
38
+ events.trigger(:event, data)
39
+ end
40
+ end
41
+
42
+ context 'with a single listener registered for the event' do
43
+ before do
44
+ events.register_listener(context, :event, event_listener)
45
+ end
46
+
47
+ it 'calls the listener with the context and event data' do
48
+ expect(event_listener).to receive(:event).with(context, data)
49
+
50
+ events.trigger(:event, data)
51
+ end
52
+
53
+ it 'passes an empty hash if no data was given' do
54
+ expect(event_listener).to receive(:event).with(context, {})
55
+
56
+ events.trigger(:event)
57
+ end
58
+ end
59
+
60
+ context 'with no listeners registered for the event' do
61
+ before do
62
+ events.register_listener(context, :other_event, event_listener)
63
+ end
64
+
65
+ it 'does not trigger anything' do
66
+ expect(event_listener).not_to receive(:event)
67
+
68
+ events.trigger(:event, data)
69
+ end
70
+ end
71
+
72
+ context 'with multiple listeners registered for the event' do
73
+ let(:event_listener2) { double(:event_listener2) }
74
+
75
+ before do
76
+ events.register_listener(context, :event, event_listener)
77
+ events.register_listener(context, :event, event_listener2)
78
+ end
79
+
80
+ it 'calls each listener with the context and event data' do
81
+ expect(event_listener).to receive(:event).with(context, data)
82
+ expect(event_listener2).to receive(:event).with(context, data)
83
+
84
+ events.trigger(:event, data)
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
@@ -8,13 +8,13 @@ module EXEL
8
8
  it { is_expected.to be_kind_of(ASTNode) }
9
9
 
10
10
  describe '#run' do
11
- it 'should only execute the instruction' do
11
+ it 'only executes the instruction' do
12
12
  expect(instruction).to receive(:execute).with(context).once
13
13
  node.run(context)
14
14
  end
15
15
 
16
- it 'should not run it`s children' do
17
- expect(child).to_not receive(:run)
16
+ it 'does not run it`s children' do
17
+ expect(child).not_to receive(:run)
18
18
  node.run(context)
19
19
  end
20
20
  end
@@ -1,20 +1,20 @@
1
1
  module EXEL
2
2
  describe Instruction do
3
- subject(:instruction) { EXEL::Instruction.new('ins_name', processor_class, args) }
3
+ subject(:instruction) { EXEL::Instruction.new(processor_class, args) }
4
4
  let(:processor_class) { double(:processor_class, new: processor_instance) }
5
5
  let(:processor_instance) { double(:processor_instance, process: nil) }
6
6
  let(:args) { {arg1: 'arg_value1', arg2: {}} }
7
7
  let(:context) { {context_key: 'context_value'} }
8
8
 
9
- describe '#run' do
10
- it 'should call process on an instance of the processor class' do
9
+ describe '#execute' do
10
+ it 'calls process on an instance of the processor class' do
11
11
  expect(processor_class).to receive(:new).and_return(processor_instance)
12
12
  expect(processor_instance).to receive(:process)
13
13
 
14
14
  instruction.execute(context)
15
15
  end
16
16
 
17
- it 'should not pass a copy of the context' do
17
+ it 'does not pass a copy of the context' do
18
18
  allow(processor_class).to receive(:new) do |context_arg|
19
19
  expect(context_arg).to be(context)
20
20
  processor_instance
@@ -23,13 +23,13 @@ module EXEL
23
23
  instruction.execute(context)
24
24
  end
25
25
 
26
- it 'should add args to the context' do
26
+ it 'adds args to the context' do
27
27
  instruction.execute(context)
28
28
  expect(context.keys).to include(*args.keys)
29
29
  end
30
30
 
31
31
  context 'with args' do
32
- it 'should pass the args to the processor' do
32
+ it 'passes the args to the processor' do
33
33
  expect(processor_class).to receive(:new).with(hash_including(args))
34
34
  instruction.execute(context)
35
35
  end
@@ -38,7 +38,7 @@ module EXEL
38
38
  context 'without args' do
39
39
  let(:args) { nil }
40
40
 
41
- it 'should just pass the context to the processor' do
41
+ it 'passes only the context to the processor' do
42
42
  expect(processor_class).to receive(:new).with(context)
43
43
  instruction.execute(context)
44
44
  end
@@ -46,9 +46,9 @@ module EXEL
46
46
 
47
47
  context 'with a subtree' do
48
48
  let(:subtree) { double(:subtree) }
49
- subject(:instruction) { EXEL::Instruction.new('ins_name', processor_class, args, subtree) }
49
+ subject(:instruction) { EXEL::Instruction.new(processor_class, args, subtree) }
50
50
 
51
- it 'should pass the subtree to the processor' do
51
+ it 'passes the subtree to the processor' do
52
52
  expect(processor_instance).to receive(:process).with(subtree)
53
53
  instruction.execute(context)
54
54
  end