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,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
+ """