composable_operations 0.7.0 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/composable_operations/composed_operation.rb +1 -1
- data/lib/composable_operations/operation.rb +28 -3
- data/lib/composable_operations/version.rb +1 -1
- data/spec/composable_operations/exception_handling_spec.rb +146 -0
- data/spec/composable_operations/operation_input_forwarding_spec.rb +29 -0
- data/spec/composable_operations/operation_spec.rb +0 -75
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: af1c4603f14d42656d794709d5db02bc69014f7b
|
4
|
+
data.tar.gz: c3b7bcd54b0747b0173f1e01050cb0f1a307247c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7cd11e937b6183a4b628d02d9b9ade6400a6dd9c831410a6905ab21e06d88db3b17117e8a06261e6064119f092b760f66053db87d8d2c8294eb5e6af31915e64
|
7
|
+
data.tar.gz: 6647e6ba14819d2d6fe07d339ac5290d449038ed2096f33f8c1ea64903202379adb1acd707586332b6c2c809c2d8bdbfe05cff3bbf9f33cbdde232dba05027a8
|
@@ -15,7 +15,7 @@ module ComposableOperations
|
|
15
15
|
operation = new(*args)
|
16
16
|
operation.perform
|
17
17
|
|
18
|
-
raise
|
18
|
+
raise operation.exception if operation.failed?
|
19
19
|
|
20
20
|
operation.result
|
21
21
|
end
|
@@ -86,6 +86,7 @@ module ComposableOperations
|
|
86
86
|
attr_reader :result
|
87
87
|
attr_reader :message
|
88
88
|
attr_reader :backtrace
|
89
|
+
attr_reader :exception
|
89
90
|
|
90
91
|
def initialize(*args)
|
91
92
|
named_input_parameters = args.shift(self.class.arity)
|
@@ -136,18 +137,42 @@ module ComposableOperations
|
|
136
137
|
attr_writer :message
|
137
138
|
attr_writer :result
|
138
139
|
attr_writer :backtrace
|
140
|
+
attr_writer :exception
|
139
141
|
|
140
142
|
def execute
|
141
143
|
raise NotImplementedError, "#{name}#execute not implemented"
|
142
144
|
end
|
143
145
|
|
144
|
-
def fail(
|
146
|
+
def fail(*args)
|
145
147
|
raise "Operation execution has already been aborted" if halted? or failed?
|
148
|
+
exception, message, backtrace = nil, nil, nil
|
149
|
+
|
150
|
+
case args.length
|
151
|
+
when 1
|
152
|
+
value = args[0]
|
153
|
+
if Exception === value
|
154
|
+
exception = value
|
155
|
+
elsif Class === value && Exception > value
|
156
|
+
exception = value
|
157
|
+
message = value.message
|
158
|
+
backtrace = value.backtrace
|
159
|
+
else
|
160
|
+
message = value
|
161
|
+
end
|
162
|
+
when 2, 3
|
163
|
+
exception, message, backtrace = args[0], args[1], args[2]
|
164
|
+
end
|
165
|
+
|
166
|
+
backtrace ||= caller
|
167
|
+
exception ||= self.class.exception
|
168
|
+
exception = Class === exception ? exception.new(message) : exception
|
169
|
+
exception.set_backtrace(backtrace)
|
146
170
|
|
147
171
|
self.state = :failed
|
148
172
|
self.backtrace = backtrace
|
149
173
|
self.message = message
|
150
|
-
|
174
|
+
self.exception = exception
|
175
|
+
throw :halt, nil
|
151
176
|
end
|
152
177
|
|
153
178
|
def halt(message = nil, return_value = nil)
|
@@ -0,0 +1,146 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ComposableOperations::Operation, "that always fails:" do
|
4
|
+
let(:failing_operation) do
|
5
|
+
Class.new(described_class) do
|
6
|
+
def execute
|
7
|
+
fail "Operation failed"
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
context "When no default exception has been specified" do
|
13
|
+
it "should raise an error when executed" do
|
14
|
+
expect { failing_operation.perform }.to raise_error(ComposableOperations::OperationError, "Operation failed")
|
15
|
+
end
|
16
|
+
|
17
|
+
context "when manually instantiated and executed" do
|
18
|
+
subject(:failing_operation_instance) do
|
19
|
+
operation = failing_operation.new
|
20
|
+
operation.perform
|
21
|
+
operation
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should have nil as result" do
|
25
|
+
failing_operation_instance.result.should be_nil
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should have failed" do
|
29
|
+
failing_operation_instance.should be_failed
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should have a message" do
|
33
|
+
failing_operation_instance.message.should_not be_nil
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should have an exception of type OperationError whose message is 'Operation failed'" do
|
37
|
+
failing_operation_instance.exception.should be_kind_of(ComposableOperations::OperationError)
|
38
|
+
failing_operation_instance.exception.message.should be == 'Operation failed'
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
context "when extended with a finalizer" do
|
43
|
+
let(:supervisor) { mock("Supervisor") }
|
44
|
+
|
45
|
+
let(:failing_operation_with_finalizer) do
|
46
|
+
supervisor = supervisor()
|
47
|
+
Class.new(failing_operation) do
|
48
|
+
after { supervisor.notify }
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
subject(:failing_operation_with_finalizer_instance) do
|
53
|
+
failing_operation_with_finalizer.new
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should execute the finalizers" do
|
57
|
+
supervisor.should_receive(:notify)
|
58
|
+
failing_operation_with_finalizer_instance.perform
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
context "When a default exception has been specified" do
|
64
|
+
let(:custom_exception) { Class.new(RuntimeError) }
|
65
|
+
|
66
|
+
subject(:failing_operation_with_custom_default_exception) do
|
67
|
+
custom_exception = custom_exception()
|
68
|
+
Class.new(failing_operation) do
|
69
|
+
raises custom_exception
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should raise the custom exeception when executed" do
|
74
|
+
expect { failing_operation_with_custom_default_exception.perform }.
|
75
|
+
to raise_error(custom_exception, "Operation failed")
|
76
|
+
end
|
77
|
+
|
78
|
+
context "when manually instantiated and executed" do
|
79
|
+
subject(:failing_operation_with_custom_default_exception_instance) do
|
80
|
+
operation = failing_operation_with_custom_default_exception.new
|
81
|
+
operation.perform
|
82
|
+
operation
|
83
|
+
end
|
84
|
+
|
85
|
+
it "should have an exception of the custom exception type whose message is 'Operation failed'" do
|
86
|
+
failing_operation_with_custom_default_exception_instance.exception.should be_kind_of(custom_exception)
|
87
|
+
failing_operation_with_custom_default_exception_instance.exception.message.should be == 'Operation failed'
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
context "When a custom exception has been specified" do
|
93
|
+
let(:custom_exception) { Class.new(RuntimeError) }
|
94
|
+
subject(:failing_operation_with_custom_exception) do
|
95
|
+
custom_exception = self.custom_exception
|
96
|
+
Class.new(described_class) do
|
97
|
+
define_method(:execute) do
|
98
|
+
fail custom_exception, "Operation failed"
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
it "should raise the custom exeception when executed" do
|
104
|
+
expect { failing_operation_with_custom_exception.perform }.
|
105
|
+
to raise_error(custom_exception, "Operation failed")
|
106
|
+
end
|
107
|
+
|
108
|
+
context "when manually instantiated and executed" do
|
109
|
+
subject(:failing_operation_with_custom_exception_instance) do
|
110
|
+
operation = failing_operation_with_custom_exception.new
|
111
|
+
operation.perform
|
112
|
+
operation
|
113
|
+
end
|
114
|
+
|
115
|
+
it "should have an exception of the custom exception type whose message is 'Operation failed'" do
|
116
|
+
failing_operation_with_custom_exception_instance.exception.should be_kind_of(custom_exception)
|
117
|
+
failing_operation_with_custom_exception_instance.exception.message.should be == 'Operation failed'
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
context "when used in a composed operation" do
|
122
|
+
let(:failing_composed_operation) do
|
123
|
+
ComposableOperations::ComposedOperation.compose(failing_operation_with_custom_exception)
|
124
|
+
end
|
125
|
+
|
126
|
+
it "should raise the custom exception that is thrown by the inner operation when executed" do
|
127
|
+
expect { failing_composed_operation.perform }.
|
128
|
+
to raise_error(custom_exception, "Operation failed")
|
129
|
+
end
|
130
|
+
|
131
|
+
context "when this composed operation is manually instantiated and executed" do
|
132
|
+
subject(:failing_composed_operation_instance) do
|
133
|
+
operation = failing_composed_operation.new
|
134
|
+
operation.perform
|
135
|
+
operation
|
136
|
+
end
|
137
|
+
|
138
|
+
it "should have an exception of the custom exception type whose message is 'Operation failed'" do
|
139
|
+
failing_composed_operation_instance.exception.should be_kind_of(custom_exception)
|
140
|
+
failing_composed_operation_instance.exception.message.should be == 'Operation failed'
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
@@ -2,6 +2,35 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe ComposableOperations::ComposedOperation, "input forwarding:" do
|
4
4
|
|
5
|
+
describe "An operation pipeline that first constructs an array with two elements and then passes it to an operation that accepts two input parameters and returns the second one" do
|
6
|
+
|
7
|
+
let(:array_generator) do
|
8
|
+
Class.new(ComposableOperations::Operation) do
|
9
|
+
def execute
|
10
|
+
[:first_element, :second_element]
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
let(:extractor) do
|
16
|
+
Class.new(ComposableOperations::Operation) do
|
17
|
+
processes :first_parameter, :second_parameter
|
18
|
+
def execute
|
19
|
+
second_parameter
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
subject(:pipeline) do
|
25
|
+
ComposableOperations::ComposedOperation.compose(array_generator, extractor)
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should return the correct element" do
|
29
|
+
result = pipeline.perform
|
30
|
+
result.should == :second_element
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
5
34
|
describe "An operation pipeline that first constructs an enumerator, then passes it from operation to operation and finally returns it as the result" do
|
6
35
|
|
7
36
|
let(:enum_generator) do
|
@@ -46,81 +46,6 @@ describe ComposableOperations::Operation do
|
|
46
46
|
|
47
47
|
end
|
48
48
|
|
49
|
-
context "that always fails" do
|
50
|
-
|
51
|
-
let(:failing_operation) do
|
52
|
-
Class.new(described_class) do
|
53
|
-
def execute
|
54
|
-
fail "Operation failed"
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
subject(:failing_operation_instance) do
|
60
|
-
failing_operation.new
|
61
|
-
end
|
62
|
-
|
63
|
-
before(:each) do
|
64
|
-
failing_operation_instance.perform
|
65
|
-
end
|
66
|
-
|
67
|
-
it "should have nil as result" do
|
68
|
-
failing_operation_instance.result.should be_nil
|
69
|
-
end
|
70
|
-
|
71
|
-
it "should have failed" do
|
72
|
-
failing_operation_instance.should be_failed
|
73
|
-
end
|
74
|
-
|
75
|
-
it "should have a message" do
|
76
|
-
failing_operation_instance.message.should_not be_nil
|
77
|
-
end
|
78
|
-
|
79
|
-
it "should raise an error when executed using the class method perform" do
|
80
|
-
expect { failing_operation.perform }.to raise_error("Operation failed")
|
81
|
-
end
|
82
|
-
|
83
|
-
context "when extended with a finalizer" do
|
84
|
-
|
85
|
-
let(:supervisor) { mock("Supervisor") }
|
86
|
-
|
87
|
-
let(:failing_operation_instance_with_finalizer) do
|
88
|
-
supervisor = supervisor()
|
89
|
-
Class.new(failing_operation) do
|
90
|
-
after { supervisor.notify }
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
subject(:failing_operation_instance_with_finalizer_instance) do
|
95
|
-
failing_operation_instance_with_finalizer.new
|
96
|
-
end
|
97
|
-
|
98
|
-
it "should execute the finalizers" do
|
99
|
-
supervisor.should_receive(:notify)
|
100
|
-
failing_operation_instance_with_finalizer_instance.perform
|
101
|
-
end
|
102
|
-
|
103
|
-
end
|
104
|
-
|
105
|
-
context "when configured to raise a custom exception" do
|
106
|
-
|
107
|
-
let(:custom_exception) { Class.new(RuntimeError) }
|
108
|
-
|
109
|
-
subject(:failing_operation_with_custom_exception) do
|
110
|
-
custom_exception = custom_exception()
|
111
|
-
Class.new(failing_operation) do
|
112
|
-
raises custom_exception
|
113
|
-
end
|
114
|
-
end
|
115
|
-
|
116
|
-
it "should raise the custom exeception when executed using the class method perform" do
|
117
|
-
expect { failing_operation_with_custom_exception.perform }.to raise_error(custom_exception, "Operation failed")
|
118
|
-
end
|
119
|
-
|
120
|
-
end
|
121
|
-
|
122
|
-
end
|
123
|
-
|
124
49
|
context "that always returns something when executed" do
|
125
50
|
|
126
51
|
let(:simple_operation) do
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: composable_operations
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.8.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Konstantin Tennhard
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-
|
11
|
+
date: 2013-10-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: smart_properties
|
@@ -105,6 +105,7 @@ files:
|
|
105
105
|
- lib/composable_operations/version.rb
|
106
106
|
- spec/composable_operations/composed_operation_spec.rb
|
107
107
|
- spec/composable_operations/control_flow_spec.rb
|
108
|
+
- spec/composable_operations/exception_handling_spec.rb
|
108
109
|
- spec/composable_operations/operation_input_forwarding_spec.rb
|
109
110
|
- spec/composable_operations/operation_input_processing_spec.rb
|
110
111
|
- spec/composable_operations/operation_spec.rb
|
@@ -136,6 +137,7 @@ summary: Tool set for operation pipelines.
|
|
136
137
|
test_files:
|
137
138
|
- spec/composable_operations/composed_operation_spec.rb
|
138
139
|
- spec/composable_operations/control_flow_spec.rb
|
140
|
+
- spec/composable_operations/exception_handling_spec.rb
|
139
141
|
- spec/composable_operations/operation_input_forwarding_spec.rb
|
140
142
|
- spec/composable_operations/operation_input_processing_spec.rb
|
141
143
|
- spec/composable_operations/operation_spec.rb
|