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.
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