rung 0.0.1.pre.alpha → 0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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,41 @@
|
|
1
|
+
# order: 70
|
2
|
+
Feature: Operation wrappers
|
3
|
+
:!hardbreaks:
|
4
|
+
It is possible to define global wrappers on the Operation level using `around` call.
|
5
|
+
|
6
|
+
Scenario: Operation can have multiple global wrappers
|
7
|
+
Wrappers are called in the order they are defined.
|
8
|
+
Given definition
|
9
|
+
"""ruby
|
10
|
+
class Operation < Rung::Operation
|
11
|
+
class Wrapper
|
12
|
+
def initialize(name)
|
13
|
+
@name = name
|
14
|
+
end
|
15
|
+
|
16
|
+
def call
|
17
|
+
print_to_output "#{@name} start\n"
|
18
|
+
yield
|
19
|
+
print_to_output "#{@name} done\n"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
around Wrapper.new("1")
|
24
|
+
around Wrapper.new("2")
|
25
|
+
|
26
|
+
step { print_to_output "Hello World!\n" }
|
27
|
+
end
|
28
|
+
"""
|
29
|
+
When I run
|
30
|
+
"""
|
31
|
+
Operation.new.call(variable: true)
|
32
|
+
"""
|
33
|
+
Then I see output
|
34
|
+
"""
|
35
|
+
1 start
|
36
|
+
2 start
|
37
|
+
Hello World!
|
38
|
+
2 done
|
39
|
+
1 done
|
40
|
+
|
41
|
+
"""
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# order: 80
|
2
|
+
Feature: Exceptions handling
|
3
|
+
Rung doesn't provide any built-in exception handling.
|
4
|
+
If you need to catch any exceptions you can implement error catching
|
5
|
+
using a wrapper on a desired execution level.
|
6
|
+
|
7
|
+
Scenario: All exceptions are raised
|
8
|
+
Given definition
|
9
|
+
"""ruby
|
10
|
+
class Operation < Rung::Operation
|
11
|
+
step { raise "Oh no!" }
|
12
|
+
end
|
13
|
+
"""
|
14
|
+
When I run
|
15
|
+
"""
|
16
|
+
begin
|
17
|
+
Operation.new.call(output_text: "Hello ")
|
18
|
+
rescue => e
|
19
|
+
print_to_output e.message
|
20
|
+
end
|
21
|
+
"""
|
22
|
+
Then I see output
|
23
|
+
"""
|
24
|
+
Oh no!
|
25
|
+
"""
|
26
|
+
|
27
|
+
Scenario: Exception can be caught in a wrapper
|
28
|
+
Given definition
|
29
|
+
"""ruby
|
30
|
+
class Operation < Rung::Operation
|
31
|
+
class Wrapper
|
32
|
+
def self.call(state)
|
33
|
+
yield
|
34
|
+
rescue
|
35
|
+
print_to_output "Exception handled"
|
36
|
+
state[:exception_handled] = true
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
around Wrapper
|
41
|
+
|
42
|
+
step { print_to_output "Hello World!\n"; raise "oops!" }
|
43
|
+
end
|
44
|
+
"""
|
45
|
+
When I run
|
46
|
+
"""
|
47
|
+
@result = Operation.new.call
|
48
|
+
"""
|
49
|
+
Then I see output
|
50
|
+
"""
|
51
|
+
Hello World!
|
52
|
+
Exception handled
|
53
|
+
"""
|
54
|
+
And I can assure that
|
55
|
+
"""
|
56
|
+
@result.to_h == { exception_handled: true }
|
57
|
+
"""
|
@@ -0,0 +1,130 @@
|
|
1
|
+
# order: 90
|
2
|
+
Feature: Around step wrapper
|
3
|
+
:!hardbreaks:
|
4
|
+
|
5
|
+
Scenario: Step wrapper is called for every step
|
6
|
+
Given definition
|
7
|
+
"""ruby
|
8
|
+
class Operation < Rung::Operation
|
9
|
+
class Counter
|
10
|
+
def initialize
|
11
|
+
@count = 0
|
12
|
+
end
|
13
|
+
|
14
|
+
def call
|
15
|
+
@count += 1
|
16
|
+
print_to_output "Step #{@count}\n"
|
17
|
+
yield
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
around_each Counter.new
|
22
|
+
|
23
|
+
step do
|
24
|
+
print_to_output "Hello\n"
|
25
|
+
end
|
26
|
+
|
27
|
+
step do
|
28
|
+
print_to_output "World\n"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
"""
|
32
|
+
When I run
|
33
|
+
"""
|
34
|
+
Operation.new.call
|
35
|
+
"""
|
36
|
+
Then I see output
|
37
|
+
"""
|
38
|
+
Step 1
|
39
|
+
Hello
|
40
|
+
Step 2
|
41
|
+
World
|
42
|
+
|
43
|
+
"""
|
44
|
+
|
45
|
+
Scenario: Step wrapper receives state and current step
|
46
|
+
Given definition
|
47
|
+
"""ruby
|
48
|
+
class Operation < Rung::Operation
|
49
|
+
class Logger
|
50
|
+
def self.call(state, step)
|
51
|
+
result = yield
|
52
|
+
|
53
|
+
print_to_output "State: #{state.to_h}, success: #{state.success?}," \
|
54
|
+
"ignores result: #{step.ignore_result?}, nested: #{step.nested?}, result: #{result}\n"
|
55
|
+
|
56
|
+
result
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
noop_wrapper = -> (&block) { block.call }
|
61
|
+
|
62
|
+
around_each Logger
|
63
|
+
|
64
|
+
step do |state|
|
65
|
+
print_to_output "Hello\n"
|
66
|
+
state[:test] = 42
|
67
|
+
end
|
68
|
+
|
69
|
+
step noop_wrapper do
|
70
|
+
tee do |state|
|
71
|
+
state[:test] = 5
|
72
|
+
print_to_output "World\n"
|
73
|
+
true
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
"""
|
78
|
+
When I run
|
79
|
+
"""
|
80
|
+
Operation.new.call
|
81
|
+
"""
|
82
|
+
Then I see output
|
83
|
+
"""
|
84
|
+
Hello
|
85
|
+
State: {:test=>42}, success: true,ignores result: false, nested: false, result: 42
|
86
|
+
World
|
87
|
+
State: {:test=>5}, success: true,ignores result: true, nested: false, result: true
|
88
|
+
State: {:test=>5}, success: true,ignores result: false, nested: true, result: true
|
89
|
+
|
90
|
+
"""
|
91
|
+
|
92
|
+
Scenario: Step wrapper returned result is treated as the step result
|
93
|
+
Given definition
|
94
|
+
"""ruby
|
95
|
+
class Operation < Rung::Operation
|
96
|
+
class Boom
|
97
|
+
def self.call(state)
|
98
|
+
yield
|
99
|
+
print_to_output "Boom!\n"
|
100
|
+
false # always return false
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
around_each Boom
|
105
|
+
|
106
|
+
tee do |state| # tee step ignores result, so Boom doesn't affect it
|
107
|
+
print_to_output "Hello\n"
|
108
|
+
end
|
109
|
+
|
110
|
+
step { print_to_output "Beautiful\n" }
|
111
|
+
step { print_to_output "World\n" }
|
112
|
+
end
|
113
|
+
"""
|
114
|
+
When I run
|
115
|
+
"""
|
116
|
+
@result = Operation.new.call
|
117
|
+
"""
|
118
|
+
Then I see output
|
119
|
+
"""
|
120
|
+
Hello
|
121
|
+
Boom!
|
122
|
+
Beautiful
|
123
|
+
Boom!
|
124
|
+
|
125
|
+
"""
|
126
|
+
And I can assure that
|
127
|
+
"""
|
128
|
+
@result.success? == false
|
129
|
+
"""
|
130
|
+
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# order: 200
|
2
|
+
Feature: Misc
|
3
|
+
Scenario: Operation defines .call shorthand class method
|
4
|
+
Given definition
|
5
|
+
"""ruby
|
6
|
+
class Operation < Rung::Operation
|
7
|
+
step {|state| state[:test] = 42 + state[:input] }
|
8
|
+
end
|
9
|
+
"""
|
10
|
+
When I run
|
11
|
+
"""
|
12
|
+
@result1 = Operation.call(input: 1)
|
13
|
+
@result2 = Operation.new.call(input: 1)
|
14
|
+
"""
|
15
|
+
And I can assure that
|
16
|
+
"""
|
17
|
+
@result1.to_h == @result2.to_h
|
18
|
+
"""
|
@@ -1,10 +1,15 @@
|
|
1
1
|
Before do
|
2
|
-
allow_any_instance_of(Object)
|
3
|
-
|
2
|
+
allow_any_instance_of(Object)
|
3
|
+
.to receive(:print_to_output) do |_receiver, output|
|
4
|
+
@stored_output ||= ''
|
4
5
|
@stored_output << output
|
5
6
|
end
|
6
7
|
end
|
7
8
|
|
8
|
-
Then(
|
9
|
+
Then('I see output') do |string|
|
9
10
|
expect(@stored_output).to eq string
|
10
11
|
end
|
12
|
+
|
13
|
+
Then('I clear output') do
|
14
|
+
@stored_output = ''
|
15
|
+
end
|
@@ -1,16 +1,17 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
Given("definition") do |content|
|
5
|
-
@temporary_scope ||= TemporaryScope.new
|
1
|
+
Given('definition') do |content|
|
2
|
+
@temporary_scope ||= Object.new
|
6
3
|
|
7
4
|
@temporary_scope.instance_eval(content)
|
8
5
|
end
|
9
6
|
|
10
|
-
When(
|
7
|
+
When('I run') do |content|
|
11
8
|
@temporary_scope.instance_eval(content)
|
12
9
|
end
|
13
10
|
|
14
|
-
Then(
|
11
|
+
Then(/^I can assure that/) do |content|
|
15
12
|
expect(@temporary_scope.instance_eval(content)).to eq true
|
16
13
|
end
|
14
|
+
|
15
|
+
Then('I can test that') do |content|
|
16
|
+
@temporary_scope.instance_eval(content)
|
17
|
+
end
|
data/lib/rung.rb
CHANGED
@@ -2,17 +2,18 @@ require 'forwardable'
|
|
2
2
|
|
3
3
|
require 'rung/version'
|
4
4
|
require 'rung/error'
|
5
|
+
require 'rung/value_object'
|
5
6
|
|
6
|
-
require 'rung/definition/
|
7
|
-
require 'rung/definition/
|
7
|
+
require 'rung/definition/step'
|
8
|
+
require 'rung/definition/callback'
|
9
|
+
require 'rung/definition/nested_step'
|
8
10
|
|
9
|
-
require 'rung/definition/
|
11
|
+
require 'rung/definition/operation_dsl'
|
10
12
|
require 'rung/definition/steps_dsl'
|
11
13
|
|
12
|
-
require 'rung/
|
14
|
+
require 'rung/state'
|
13
15
|
require 'rung/runner/run_context'
|
14
|
-
require 'rung/runner/run_state'
|
15
16
|
require 'rung/runner/call_helper'
|
16
17
|
require 'rung/runner/runner'
|
17
18
|
|
18
|
-
require 'rung/
|
19
|
+
require 'rung/operation'
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Rung
|
2
|
+
module Definition
|
3
|
+
class NestedStep < Step
|
4
|
+
def initialize(action, nested_steps, options = {})
|
5
|
+
super(action, options)
|
6
|
+
@nested_steps = nested_steps
|
7
|
+
end
|
8
|
+
|
9
|
+
attr_reader :nested_steps
|
10
|
+
|
11
|
+
def nested?
|
12
|
+
true
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Rung
|
2
|
+
module Definition
|
3
|
+
module OperationDSL
|
4
|
+
def around_callbacks
|
5
|
+
@around_callbacks ||= []
|
6
|
+
end
|
7
|
+
|
8
|
+
def around_each_callbacks
|
9
|
+
@around_each_callbacks ||= []
|
10
|
+
end
|
11
|
+
|
12
|
+
def around(action = nil, &block)
|
13
|
+
around_callbacks.push callback_from_definition(action, &block)
|
14
|
+
end
|
15
|
+
|
16
|
+
def around_each(action = nil, &block)
|
17
|
+
around_each_callbacks.push callback_from_definition(action, &block)
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def callback_from_definition(action, &block)
|
23
|
+
if block
|
24
|
+
Callback.new block, from_block: true
|
25
|
+
else
|
26
|
+
Callback.new action
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Rung
|
2
|
+
module Definition
|
3
|
+
class Step
|
4
|
+
include ValueObject
|
5
|
+
# rubocop:disable Metrics/LineLength
|
6
|
+
def initialize(action, from_block: false, run_on: :success, ignore_result: false, fail_fast: false)
|
7
|
+
# rubocop:enable Metrics/LineLength
|
8
|
+
@action = action
|
9
|
+
@from_block = from_block
|
10
|
+
@run_on = run_on
|
11
|
+
@ignore_result = ignore_result
|
12
|
+
@fail_fast = fail_fast
|
13
|
+
end
|
14
|
+
|
15
|
+
attr_reader :action, :from_block
|
16
|
+
|
17
|
+
def run?(success)
|
18
|
+
case @run_on
|
19
|
+
when :success
|
20
|
+
success
|
21
|
+
when :failure
|
22
|
+
!success
|
23
|
+
when :any
|
24
|
+
true
|
25
|
+
else
|
26
|
+
false
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def nested?
|
31
|
+
false
|
32
|
+
end
|
33
|
+
|
34
|
+
def ignore_result?
|
35
|
+
@ignore_result
|
36
|
+
end
|
37
|
+
|
38
|
+
def fail_fast?
|
39
|
+
@fail_fast
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -2,35 +2,50 @@ module Rung
|
|
2
2
|
module Definition
|
3
3
|
module StepsDSL
|
4
4
|
def steps_definition
|
5
|
-
@steps_definition ||=
|
5
|
+
@steps_definition ||= []
|
6
6
|
end
|
7
7
|
|
8
|
-
def
|
9
|
-
|
8
|
+
def add_generic_step(action, options = {}, &block)
|
9
|
+
step = step_from_definition action, **options, &block
|
10
|
+
steps_definition.push step
|
10
11
|
end
|
11
12
|
|
12
|
-
def
|
13
|
-
|
13
|
+
def step(*args, &block)
|
14
|
+
add_step_from_args args, &block
|
14
15
|
end
|
15
16
|
|
16
|
-
def
|
17
|
-
|
18
|
-
|
17
|
+
def tee(*args, &block)
|
18
|
+
add_step_from_args args, ignore_result: true, &block
|
19
|
+
end
|
20
|
+
|
21
|
+
def failure(*args, &block)
|
22
|
+
add_step_from_args args, run_on: :failure, ignore_result: true, &block
|
23
|
+
end
|
24
|
+
|
25
|
+
def always(*args, &block)
|
26
|
+
add_step_from_args args, run_on: :any, ignore_result: true, &block
|
19
27
|
end
|
20
28
|
|
21
29
|
private
|
22
30
|
|
23
|
-
def
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
31
|
+
def add_step_from_args(args, options = {}, &block)
|
32
|
+
action, action_options = extract_action_and_options!(args)
|
33
|
+
add_generic_step action, **options, **action_options, &block
|
34
|
+
end
|
35
|
+
|
36
|
+
def extract_action_and_options!(args)
|
37
|
+
options = args.last.is_a?(::Hash) ? args.pop : {}
|
38
|
+
[args.first, options]
|
30
39
|
end
|
31
40
|
|
32
|
-
def
|
33
|
-
|
41
|
+
def step_from_definition(action, options, &block)
|
42
|
+
if action && block
|
43
|
+
NestedStep.new(action, calculate_block_nested_steps(&block), options)
|
44
|
+
elsif block
|
45
|
+
Step.new(block, **options, from_block: true)
|
46
|
+
else
|
47
|
+
Step.new(action, options)
|
48
|
+
end
|
34
49
|
end
|
35
50
|
|
36
51
|
def calculate_block_nested_steps(&block)
|
@@ -42,7 +57,7 @@ module Rung
|
|
42
57
|
|
43
58
|
def with_new_steps_definition
|
44
59
|
old_steps_definition = @steps_definition
|
45
|
-
@steps_definition =
|
60
|
+
@steps_definition = []
|
46
61
|
yield
|
47
62
|
ensure
|
48
63
|
@steps_definition = old_steps_definition
|