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.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +72 -0
  3. data/.config/cucumber.yml +1 -0
  4. data/.gitignore +3 -0
  5. data/.rspec +0 -1
  6. data/.rubocop.yml +22 -0
  7. data/Gemfile +6 -2
  8. data/Gemfile.lock +23 -1
  9. data/README.adoc +111 -0
  10. data/Rakefile +19 -4
  11. data/features/{steps_definition.feature → 010_operation.feature} +29 -6
  12. data/features/{state.feature → 020_state.feature} +17 -4
  13. data/features/{failure.feature → 030_failure.feature} +29 -6
  14. data/features/040_failure_step.feature +118 -0
  15. data/features/050_other_steps.feature +135 -0
  16. data/features/051_fail_fast.feature +66 -0
  17. data/features/060_step_wrappers.feature +170 -0
  18. data/features/070_operation_wrappers.feature +41 -0
  19. data/features/080_exceptions_handling.feature +57 -0
  20. data/features/090_around_step_wrapper.feature +130 -0
  21. data/features/200_misc.feature +18 -0
  22. data/features/step_definitions/output.rb +8 -3
  23. data/features/step_definitions/temporary_code_scope.rb +8 -7
  24. data/lib/rung.rb +7 -6
  25. data/lib/rung/definition/callback.rb +14 -0
  26. data/lib/rung/definition/nested_step.rb +16 -0
  27. data/lib/rung/definition/operation_dsl.rb +31 -0
  28. data/lib/rung/definition/step.rb +43 -0
  29. data/lib/rung/definition/steps_dsl.rb +33 -18
  30. data/lib/rung/{base.rb → operation.rb} +3 -2
  31. data/lib/rung/runner/call_helper.rb +30 -12
  32. data/lib/rung/runner/run_context.rb +23 -6
  33. data/lib/rung/runner/runner.rb +34 -14
  34. data/lib/rung/state.rb +35 -0
  35. data/lib/rung/value_object.rb +12 -0
  36. data/lib/rung/version.rb +1 -1
  37. data/rung.gemspec +15 -16
  38. data/target/cukedoctor-intro.adoc +1 -0
  39. data/target/cukedoctor.css +3 -0
  40. metadata +39 -23
  41. data/README.md +0 -79
  42. data/lib/rung/definition/steps/nested_step.rb +0 -20
  43. data/lib/rung/definition/steps/step.rb +0 -30
  44. data/lib/rung/definition/steps_definition.rb +0 -13
  45. data/lib/rung/runner/result.rb +0 -24
  46. 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).to receive(:print_to_output) do |_receiver, output|
3
- @stored_output ||= ""
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("I see output") do |string|
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
- class TemporaryScope
2
- end
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("I run") do |content|
7
+ When('I run') do |content|
11
8
  @temporary_scope.instance_eval(content)
12
9
  end
13
10
 
14
- Then("I can assure that") do |content|
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
@@ -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/steps/step'
7
- require 'rung/definition/steps/nested_step'
7
+ require 'rung/definition/step'
8
+ require 'rung/definition/callback'
9
+ require 'rung/definition/nested_step'
8
10
 
9
- require 'rung/definition/steps_definition'
11
+ require 'rung/definition/operation_dsl'
10
12
  require 'rung/definition/steps_dsl'
11
13
 
12
- require 'rung/runner/result'
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/base'
19
+ require 'rung/operation'
@@ -0,0 +1,14 @@
1
+ module Rung
2
+ module Definition
3
+ class Callback
4
+ include ValueObject
5
+
6
+ def initialize(action = nil, from_block: false)
7
+ @action = action
8
+ @from_block = from_block
9
+ end
10
+
11
+ attr_reader :action, :from_block
12
+ end
13
+ end
14
+ end
@@ -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 ||= StepsDefinition.new
5
+ @steps_definition ||= []
6
6
  end
7
7
 
8
- def step(reference = nil, &block)
9
- add_step(reference, &block)
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 failure(reference = nil, &block)
13
- add_step(reference, run_on: :failure, &block)
13
+ def step(*args, &block)
14
+ add_step_from_args args, &block
14
15
  end
15
16
 
16
- def wrap(wrapper, &block)
17
- nested_steps = calculate_block_nested_steps(&block)
18
- add_nested_step wrapper, nested_steps
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 add_step(reference, options = {}, &block)
24
- step = if block
25
- Step.new(block, *options, from_block: true)
26
- else
27
- Step.new(reference, options)
28
- end
29
- steps_definition.push(step)
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 add_nested_step(operation, nested_steps)
33
- steps_definition.push(NestedStep.new(operation, nested_steps))
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 = StepsDefinition.new
60
+ @steps_definition = []
46
61
  yield
47
62
  ensure
48
63
  @steps_definition = old_steps_definition