rung 0.0.1.pre.alpha → 0.1
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.
- checksums.yaml +4 -4
- data/.circleci/config.yml +72 -0
- data/.config/cucumber.yml +1 -0
- data/.gitignore +3 -0
- data/.rspec +0 -1
- data/.rubocop.yml +22 -0
- data/Gemfile +6 -2
- data/Gemfile.lock +23 -1
- data/README.adoc +111 -0
- data/Rakefile +19 -4
- data/features/{steps_definition.feature → 010_operation.feature} +29 -6
- data/features/{state.feature → 020_state.feature} +17 -4
- data/features/{failure.feature → 030_failure.feature} +29 -6
- data/features/040_failure_step.feature +118 -0
- data/features/050_other_steps.feature +135 -0
- data/features/051_fail_fast.feature +66 -0
- data/features/060_step_wrappers.feature +170 -0
- data/features/070_operation_wrappers.feature +41 -0
- data/features/080_exceptions_handling.feature +57 -0
- data/features/090_around_step_wrapper.feature +130 -0
- data/features/200_misc.feature +18 -0
- data/features/step_definitions/output.rb +8 -3
- data/features/step_definitions/temporary_code_scope.rb +8 -7
- data/lib/rung.rb +7 -6
- data/lib/rung/definition/callback.rb +14 -0
- data/lib/rung/definition/nested_step.rb +16 -0
- data/lib/rung/definition/operation_dsl.rb +31 -0
- data/lib/rung/definition/step.rb +43 -0
- data/lib/rung/definition/steps_dsl.rb +33 -18
- data/lib/rung/{base.rb → operation.rb} +3 -2
- data/lib/rung/runner/call_helper.rb +30 -12
- data/lib/rung/runner/run_context.rb +23 -6
- data/lib/rung/runner/runner.rb +34 -14
- data/lib/rung/state.rb +35 -0
- data/lib/rung/value_object.rb +12 -0
- data/lib/rung/version.rb +1 -1
- data/rung.gemspec +15 -16
- data/target/cukedoctor-intro.adoc +1 -0
- data/target/cukedoctor.css +3 -0
- metadata +39 -23
- data/README.md +0 -79
- data/lib/rung/definition/steps/nested_step.rb +0 -20
- data/lib/rung/definition/steps/step.rb +0 -30
- data/lib/rung/definition/steps_definition.rb +0 -13
- data/lib/rung/runner/result.rb +0 -24
- data/lib/rung/runner/run_state.rb +0 -12
@@ -0,0 +1,118 @@
|
|
1
|
+
# order: 40
|
2
|
+
Feature: Failure Step
|
3
|
+
:!hardbreaks:
|
4
|
+
When operations fails next normal steps are no ignored and not executed.
|
5
|
+
|
6
|
+
There's a way to react to a failure with special failure steps.
|
7
|
+
|
8
|
+
Failure steps can be defined similarly to normal steps (as a block, method, or a callable object).
|
9
|
+
They are *only* executed when operation has failed.
|
10
|
+
|
11
|
+
Scenario: Failure step is executed when operation fails
|
12
|
+
Given definition
|
13
|
+
"""ruby
|
14
|
+
class Operation < Rung::Operation
|
15
|
+
step do
|
16
|
+
print_to_output "Working..."
|
17
|
+
# something went wrong...
|
18
|
+
false
|
19
|
+
end
|
20
|
+
|
21
|
+
failure do
|
22
|
+
print_to_output " Oops!"
|
23
|
+
end
|
24
|
+
|
25
|
+
step do
|
26
|
+
print_to_output "This won't be executed"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
"""
|
30
|
+
When I run
|
31
|
+
"""
|
32
|
+
Operation.new.call
|
33
|
+
"""
|
34
|
+
Then I see output
|
35
|
+
"""
|
36
|
+
Working... Oops!
|
37
|
+
"""
|
38
|
+
|
39
|
+
Scenario: Failure step is not executed when operation doesn't fail
|
40
|
+
Given definition
|
41
|
+
"""ruby
|
42
|
+
class Operation < Rung::Operation
|
43
|
+
step do
|
44
|
+
print_to_output "Working..."
|
45
|
+
# everything's fine
|
46
|
+
true
|
47
|
+
end
|
48
|
+
|
49
|
+
failure do
|
50
|
+
print_to_output " Oops!"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
"""
|
54
|
+
When I run
|
55
|
+
"""
|
56
|
+
Operation.new.call
|
57
|
+
"""
|
58
|
+
Then I see output
|
59
|
+
"""
|
60
|
+
Working...
|
61
|
+
"""
|
62
|
+
|
63
|
+
Scenario: It's possible to define multiple failure steps
|
64
|
+
Given definition
|
65
|
+
"""ruby
|
66
|
+
class Operation < Rung::Operation
|
67
|
+
step do |state|
|
68
|
+
print_to_output "Working..."
|
69
|
+
# something went wrong...
|
70
|
+
state[:error] = "404"
|
71
|
+
false
|
72
|
+
end
|
73
|
+
|
74
|
+
failure do
|
75
|
+
print_to_output " Error: "
|
76
|
+
end
|
77
|
+
|
78
|
+
failure do |state|
|
79
|
+
print_to_output state[:error]
|
80
|
+
end
|
81
|
+
end
|
82
|
+
"""
|
83
|
+
When I run
|
84
|
+
"""
|
85
|
+
Operation.new.call
|
86
|
+
"""
|
87
|
+
Then I see output
|
88
|
+
"""
|
89
|
+
Working... Error: 404
|
90
|
+
"""
|
91
|
+
|
92
|
+
Scenario: Failure step is executed only when it's defined after failed step
|
93
|
+
Given definition
|
94
|
+
"""ruby
|
95
|
+
class Operation < Rung::Operation
|
96
|
+
failure do
|
97
|
+
print_to_output "Something's wrong"
|
98
|
+
end
|
99
|
+
|
100
|
+
step do
|
101
|
+
print_to_output "Working..."
|
102
|
+
# something went wrong...
|
103
|
+
false
|
104
|
+
end
|
105
|
+
|
106
|
+
failure do
|
107
|
+
print_to_output " Oops!"
|
108
|
+
end
|
109
|
+
end
|
110
|
+
"""
|
111
|
+
When I run
|
112
|
+
"""
|
113
|
+
Operation.new.call
|
114
|
+
"""
|
115
|
+
Then I see output
|
116
|
+
"""
|
117
|
+
Working... Oops!
|
118
|
+
"""
|
@@ -0,0 +1,135 @@
|
|
1
|
+
# order: 50
|
2
|
+
Feature: Other steps
|
3
|
+
:!hardbreaks:
|
4
|
+
|
5
|
+
`step` and `failure` are the basic step types.
|
6
|
+
For the convenience there are also two additional step
|
7
|
+
types defined:
|
8
|
+
|
9
|
+
* `tee` - works the same way as `step` but ignores the return value
|
10
|
+
* `always` - executes on both success and failure. Return value is not checked
|
11
|
+
|
12
|
+
.Table Step behaviours
|
13
|
+
|===
|
14
|
+
|Step name|Execute on success|Execute on failure|Ignore return value
|
15
|
+
|
16
|
+
|step|✓|✗|✗
|
17
|
+
|failure|✗|✓|✓
|
18
|
+
|tee|✓|✗|✓
|
19
|
+
|always|✓|✓|✓
|
20
|
+
|
21
|
+
Scenario: `tee` step result is not checked
|
22
|
+
Given definition
|
23
|
+
"""ruby
|
24
|
+
class Operation < Rung::Operation
|
25
|
+
tee do
|
26
|
+
print_to_output "Hello "
|
27
|
+
false
|
28
|
+
end
|
29
|
+
|
30
|
+
step do
|
31
|
+
print_to_output "World!"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
"""
|
35
|
+
When I run
|
36
|
+
"""
|
37
|
+
Operation.new.call
|
38
|
+
"""
|
39
|
+
Then I see output
|
40
|
+
"""
|
41
|
+
Hello World!
|
42
|
+
"""
|
43
|
+
|
44
|
+
Scenario: `tee` is not executed when operation is failed
|
45
|
+
Given definition
|
46
|
+
"""ruby
|
47
|
+
class Operation < Rung::Operation
|
48
|
+
step do
|
49
|
+
print_to_output "Hello"
|
50
|
+
false # fail
|
51
|
+
end
|
52
|
+
|
53
|
+
tee do
|
54
|
+
print_to_output "World!"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
"""
|
58
|
+
When I run
|
59
|
+
"""
|
60
|
+
Operation.new.call
|
61
|
+
"""
|
62
|
+
Then I see output
|
63
|
+
"""
|
64
|
+
Hello
|
65
|
+
"""
|
66
|
+
|
67
|
+
|
68
|
+
Scenario: `always` is called when operation is successful
|
69
|
+
Given definition
|
70
|
+
"""ruby
|
71
|
+
class Operation < Rung::Operation
|
72
|
+
step do
|
73
|
+
print_to_output "Hello "
|
74
|
+
end
|
75
|
+
|
76
|
+
always do
|
77
|
+
print_to_output "World!"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
"""
|
81
|
+
When I run
|
82
|
+
"""
|
83
|
+
Operation.new.call
|
84
|
+
"""
|
85
|
+
Then I see output
|
86
|
+
"""
|
87
|
+
Hello World!
|
88
|
+
"""
|
89
|
+
|
90
|
+
Scenario: `always` is called when operation is failed
|
91
|
+
Given definition
|
92
|
+
"""ruby
|
93
|
+
class Operation < Rung::Operation
|
94
|
+
step do
|
95
|
+
print_to_output "Hello "
|
96
|
+
false # fail
|
97
|
+
end
|
98
|
+
|
99
|
+
always do
|
100
|
+
print_to_output "World!"
|
101
|
+
end
|
102
|
+
end
|
103
|
+
"""
|
104
|
+
When I run
|
105
|
+
"""
|
106
|
+
Operation.new.call
|
107
|
+
"""
|
108
|
+
Then I see output
|
109
|
+
"""
|
110
|
+
Hello World!
|
111
|
+
"""
|
112
|
+
|
113
|
+
Scenario: state object provides information about operation success with `success?` and `fail?`
|
114
|
+
Given definition
|
115
|
+
"""ruby
|
116
|
+
class Operation < Rung::Operation
|
117
|
+
step do
|
118
|
+
print_to_output "Hello"
|
119
|
+
false # fail
|
120
|
+
end
|
121
|
+
|
122
|
+
always do |state|
|
123
|
+
print_to_output " World!" if state.success?
|
124
|
+
print_to_output " There!" if state.fail?
|
125
|
+
end
|
126
|
+
end
|
127
|
+
"""
|
128
|
+
When I run
|
129
|
+
"""
|
130
|
+
Operation.new.call
|
131
|
+
"""
|
132
|
+
Then I see output
|
133
|
+
"""
|
134
|
+
Hello There!
|
135
|
+
"""
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# order: 51
|
2
|
+
Feature: Fail fast
|
3
|
+
:!hardbreaks:
|
4
|
+
When a fail-fast step is executed and the operation is a failure then
|
5
|
+
no next step is executed, including `always` and `failure` steps.
|
6
|
+
|
7
|
+
It doesn't matter if a failure is caused by the fail-fast step itself or if it
|
8
|
+
was caused by any previous step, it behaves the same way.
|
9
|
+
|
10
|
+
Scenario: When step with `fail_fast` fails the execution is immediately stopped
|
11
|
+
Given definition
|
12
|
+
"""ruby
|
13
|
+
class Operation < Rung::Operation
|
14
|
+
step(fail_fast: true) do
|
15
|
+
print_to_output "Hello"
|
16
|
+
false
|
17
|
+
end
|
18
|
+
|
19
|
+
step do
|
20
|
+
print_to_output " World!"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
"""
|
24
|
+
When I run
|
25
|
+
"""
|
26
|
+
@result = Operation.new.call
|
27
|
+
"""
|
28
|
+
Then I see output
|
29
|
+
"""
|
30
|
+
Hello
|
31
|
+
"""
|
32
|
+
And I can assure that
|
33
|
+
"""
|
34
|
+
@result.failure?
|
35
|
+
"""
|
36
|
+
|
37
|
+
Scenario: Any kind of step can use `fail_fast`
|
38
|
+
Given definition
|
39
|
+
"""ruby
|
40
|
+
class Operation < Rung::Operation
|
41
|
+
step do
|
42
|
+
print_to_output "Hello"
|
43
|
+
false
|
44
|
+
end
|
45
|
+
|
46
|
+
failure fail_fast: true do
|
47
|
+
print_to_output "...Goodbye!"
|
48
|
+
end
|
49
|
+
|
50
|
+
step do
|
51
|
+
print_to_output " World!"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
"""
|
55
|
+
When I run
|
56
|
+
"""
|
57
|
+
@result = Operation.new.call
|
58
|
+
"""
|
59
|
+
Then I see output
|
60
|
+
"""
|
61
|
+
Hello...Goodbye!
|
62
|
+
"""
|
63
|
+
And I can assure that
|
64
|
+
"""
|
65
|
+
@result.failure?
|
66
|
+
"""
|
@@ -0,0 +1,170 @@
|
|
1
|
+
# order: 60
|
2
|
+
Feature: Step wrappers
|
3
|
+
:!hardbreaks:
|
4
|
+
It's possible to step around a group of successive steps.
|
5
|
+
This is a common use case for surrounding multiple steps in
|
6
|
+
a database transaction and rolling back when steps inside fail.
|
7
|
+
|
8
|
+
Steps wrapper is defined as a step (of any type, e.g. `step` or `tee`)
|
9
|
+
with callable wrapper-action and additional
|
10
|
+
block defining nested steps.
|
11
|
+
|
12
|
+
Wrapper-action is a Callable or method name.
|
13
|
+
|
14
|
+
Yielding the block calls inner steps. Yield returns `false` if
|
15
|
+
any of the inner steps failed or `true` otherwise.
|
16
|
+
|
17
|
+
Example usage:
|
18
|
+
```ruby
|
19
|
+
class Operation < Rung::Operation
|
20
|
+
class TransactionWrapper
|
21
|
+
def self.call(state)
|
22
|
+
return if state.fail?
|
23
|
+
ActiveRecord::Base.transaction do
|
24
|
+
success = yield
|
25
|
+
raise ActiveRecord::Rollback unless success
|
26
|
+
true
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
step TransactionWrapper do
|
32
|
+
step :create_new_entity
|
33
|
+
step :update_counter
|
34
|
+
end
|
35
|
+
end
|
36
|
+
```
|
37
|
+
|
38
|
+
Scenario: Wrapper can yield to execute inner steps
|
39
|
+
Given definition
|
40
|
+
"""ruby
|
41
|
+
class Operation < Rung::Operation
|
42
|
+
class Wrapper
|
43
|
+
def self.call
|
44
|
+
print_to_output "Starting\n"
|
45
|
+
success = yield
|
46
|
+
if success
|
47
|
+
print_to_output "\nSuccess!"
|
48
|
+
else
|
49
|
+
print_to_output "\nFailure!"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
step Wrapper do
|
55
|
+
step { print_to_output "Hello " }
|
56
|
+
step do |state|
|
57
|
+
print_to_output "World!"
|
58
|
+
state[:variable] # return variable passed by the user
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
"""
|
63
|
+
When I run
|
64
|
+
"""
|
65
|
+
Operation.new.call(variable: true)
|
66
|
+
"""
|
67
|
+
Then I see output
|
68
|
+
"""
|
69
|
+
Starting
|
70
|
+
Hello World!
|
71
|
+
Success!
|
72
|
+
"""
|
73
|
+
Then I clear output
|
74
|
+
When I run
|
75
|
+
"""
|
76
|
+
Operation.new.call(variable: false)
|
77
|
+
"""
|
78
|
+
Then I see output
|
79
|
+
"""
|
80
|
+
Starting
|
81
|
+
Hello World!
|
82
|
+
Failure!
|
83
|
+
"""
|
84
|
+
|
85
|
+
Scenario: Wrappers can be nested
|
86
|
+
Given definition
|
87
|
+
"""ruby
|
88
|
+
class Operation < Rung::Operation
|
89
|
+
class Wrapper
|
90
|
+
def initialize(name)
|
91
|
+
@name = name
|
92
|
+
end
|
93
|
+
|
94
|
+
def call
|
95
|
+
print_to_output "Starting #{@name}\n"
|
96
|
+
yield
|
97
|
+
print_to_output "Finishing #{@name}\n"
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
step Wrapper.new("first") do
|
102
|
+
step { print_to_output "Hello\n" }
|
103
|
+
step Wrapper.new("second") do
|
104
|
+
step { print_to_output "Hi\n" }
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
"""
|
109
|
+
When I run
|
110
|
+
"""
|
111
|
+
Operation.new.call
|
112
|
+
"""
|
113
|
+
Then I see output
|
114
|
+
"""
|
115
|
+
Starting first
|
116
|
+
Hello
|
117
|
+
Starting second
|
118
|
+
Hi
|
119
|
+
Finishing second
|
120
|
+
Finishing first
|
121
|
+
|
122
|
+
"""
|
123
|
+
|
124
|
+
Scenario: step type is important when calling a wrapper
|
125
|
+
Given definition
|
126
|
+
"""ruby
|
127
|
+
class Operation < Rung::Operation
|
128
|
+
class Wrapper
|
129
|
+
def initialize(name)
|
130
|
+
@name = name
|
131
|
+
end
|
132
|
+
|
133
|
+
def call
|
134
|
+
print_to_output "from: #{@name}\n"
|
135
|
+
yield
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
step do
|
140
|
+
print_to_output "RED ALERT\n"
|
141
|
+
false
|
142
|
+
end
|
143
|
+
|
144
|
+
step Wrapper.new("OK") do
|
145
|
+
step { print_to_output "Hurray!\n" }
|
146
|
+
end
|
147
|
+
|
148
|
+
failure Wrapper.new("FAIL") do
|
149
|
+
failure { print_to_output "Oops!\n" }
|
150
|
+
end
|
151
|
+
|
152
|
+
always Wrapper.new("ALWAYS") do
|
153
|
+
step { print_to_output "We're done!\n" }
|
154
|
+
failure { print_to_output "We're done, but something went wrong!\n" }
|
155
|
+
end
|
156
|
+
end
|
157
|
+
"""
|
158
|
+
When I run
|
159
|
+
"""
|
160
|
+
Operation.new.call(variable: true)
|
161
|
+
"""
|
162
|
+
Then I see output
|
163
|
+
"""
|
164
|
+
RED ALERT
|
165
|
+
from: FAIL
|
166
|
+
Oops!
|
167
|
+
from: ALWAYS
|
168
|
+
We're done, but something went wrong!
|
169
|
+
|
170
|
+
"""
|