tzu 0.1.2.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/README.md +1 -1
- data/lib/tzu/failure.rb +1 -1
- data/lib/tzu/hooks.rb +0 -1
- data/lib/tzu/match.rb +13 -11
- data/lib/tzu/outcome.rb +3 -3
- data/lib/tzu/run_methods.rb +1 -1
- data/lib/tzu/sequence.rb +2 -2
- data/lib/tzu/step.rb +8 -4
- data/lib/tzu/validation.rb +2 -2
- data/lib/tzu/validation_result.rb +1 -1
- data/lib/tzu/version.rb +5 -0
- data/lib/tzu.rb +12 -13
- metadata +15 -108
- data/lib/tzu/core_extensions/string.rb +0 -17
- data/spec/hooks_spec.rb +0 -169
- data/spec/outcome_spec.rb +0 -48
- data/spec/sequence_spec.rb +0 -232
- data/spec/spec_helper.rb +0 -9
- data/spec/step_spec.rb +0 -56
- data/spec/tzu_spec.rb +0 -214
- data/spec/validation_spec.rb +0 -80
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 3461c8cde94996a9366831443f7bc018fea3ecc7bbbf39b051a8186dbc83941c
|
4
|
+
data.tar.gz: a68fedad1cc1379833332982bb56a683a3f5b0d68675cf4e21587bb10dacbaee
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1f7a7b835a3f3340e8af96f4d733523b054cd82ee87b69024c1bc90843fa2452125f18aa12141b9bbcff9ab8adda1f89ff2c6d26a38acc457dbf367873834b2c
|
7
|
+
data.tar.gz: da137e33a5883acd558ff3fbf070055697aeeacc60766f8a71a8b78c4ab43e1e18b59c383309261e9abbf9324fc399529d3d454b77bfb228f84656cb474250a2
|
data/README.md
CHANGED
@@ -4,7 +4,7 @@ Tzu provides a simple interface for writing classes that encapsulate a single co
|
|
4
4
|
|
5
5
|
**Commands should:**
|
6
6
|
|
7
|
-
- Do exactly one thing (Single Responsibility
|
7
|
+
- Do exactly one thing (Single Responsibility Principle)
|
8
8
|
- Be self-documenting
|
9
9
|
- Be testable
|
10
10
|
- Be easy to mock and stub
|
data/lib/tzu/failure.rb
CHANGED
data/lib/tzu/hooks.rb
CHANGED
data/lib/tzu/match.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
module Tzu
|
2
|
-
|
3
2
|
# with thanks to https://github.com/pzol/deterministic
|
4
3
|
class Match
|
5
4
|
def initialize(outcome, context)
|
@@ -12,15 +11,15 @@ module Tzu
|
|
12
11
|
@context.instance_exec(@outcome.result, &matcher.block)
|
13
12
|
end
|
14
13
|
|
15
|
-
%w
|
16
|
-
define_method type.to_sym do |condition=nil, &result_block|
|
14
|
+
%w[success failure].each do |type|
|
15
|
+
define_method type.to_sym do |condition = nil, &result_block|
|
17
16
|
push(type, condition, result_block)
|
18
17
|
end
|
19
18
|
end
|
20
19
|
|
21
20
|
# todo: hash and define_method if more overrides identified
|
22
21
|
def invalid(&result_block)
|
23
|
-
push(
|
22
|
+
push("failure", :validation, result_block)
|
24
23
|
end
|
25
24
|
|
26
25
|
private
|
@@ -32,12 +31,15 @@ module Tzu
|
|
32
31
|
end
|
33
32
|
|
34
33
|
def push(type, condition, result_block)
|
35
|
-
condition_pred =
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
34
|
+
condition_pred = if condition.nil?
|
35
|
+
->(v) { true }
|
36
|
+
elsif condition.is_a?(Proc)
|
37
|
+
condition
|
38
|
+
elsif condition.is_a?(Class)
|
39
|
+
->(v) { condition === @outcome.type }
|
40
|
+
else
|
41
|
+
->(v) { @outcome.type == condition }
|
42
|
+
end
|
41
43
|
|
42
44
|
matcher_pred = compose_predicates(type_pred[type], condition_pred)
|
43
45
|
@collection << Matcher.new(matcher_pred, result_block)
|
@@ -49,7 +51,7 @@ module Tzu
|
|
49
51
|
|
50
52
|
# return a partial function for matching a matcher's type
|
51
53
|
def type_pred
|
52
|
-
|
54
|
+
->(type, x) { @outcome.send(:"#{type}?") }.curry
|
53
55
|
end
|
54
56
|
end
|
55
57
|
end
|
data/lib/tzu/outcome.rb
CHANGED
@@ -17,10 +17,10 @@ module Tzu
|
|
17
17
|
!@success
|
18
18
|
end
|
19
19
|
|
20
|
-
def handle(context=nil, &block)
|
21
|
-
context ||= block.binding.eval(
|
20
|
+
def handle(context = nil, &block)
|
21
|
+
context ||= block.binding.eval("self")
|
22
22
|
match = Match.new(self, context)
|
23
|
-
match.instance_eval
|
23
|
+
match.instance_eval(&block)
|
24
24
|
match.result
|
25
25
|
end
|
26
26
|
end
|
data/lib/tzu/run_methods.rb
CHANGED
data/lib/tzu/sequence.rb
CHANGED
@@ -21,7 +21,7 @@ module Tzu
|
|
21
21
|
end
|
22
22
|
|
23
23
|
def step(klass, &block)
|
24
|
-
@steps
|
24
|
+
@steps ||= []
|
25
25
|
|
26
26
|
step = Step.new(klass)
|
27
27
|
step.instance_eval(&block) if block
|
@@ -56,7 +56,7 @@ module Tzu
|
|
56
56
|
private
|
57
57
|
|
58
58
|
def last_outcome_is_failure?
|
59
|
-
return true if
|
59
|
+
return true if @last_outcome.respond_to?(:failure?) && @last_outcome.failure?
|
60
60
|
false
|
61
61
|
end
|
62
62
|
|
data/lib/tzu/step.rb
CHANGED
@@ -1,8 +1,11 @@
|
|
1
|
+
require "active_support"
|
2
|
+
require "active_support/core_ext/object/blank"
|
3
|
+
require "active_support/core_ext/string/inflections"
|
4
|
+
|
1
5
|
module Tzu
|
2
6
|
class Step
|
3
|
-
DOUBLE_MUTATOR =
|
7
|
+
DOUBLE_MUTATOR = "You cannot define both receives and receives_many"
|
4
8
|
|
5
|
-
String.send(:include, ::Tzu::CoreExtensions::String)
|
6
9
|
attr_reader :klass, :single_mutator, :splat_mutator
|
7
10
|
|
8
11
|
def initialize(klass)
|
@@ -21,8 +24,9 @@ module Tzu
|
|
21
24
|
end
|
22
25
|
|
23
26
|
def name
|
24
|
-
return @name if @name
|
25
|
-
|
27
|
+
return @name if @name&.is_a?(Symbol)
|
28
|
+
|
29
|
+
@klass.to_s.split("::").last.underscore.to_sym
|
26
30
|
end
|
27
31
|
|
28
32
|
def receives(&block)
|
data/lib/tzu/validation.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
module Tzu
|
2
2
|
module Validation
|
3
|
-
|
4
3
|
def self.included(base)
|
5
4
|
base.class_eval do
|
6
5
|
# registers validation as a before hook
|
@@ -16,12 +15,13 @@ module Tzu
|
|
16
15
|
|
17
16
|
def validate(params)
|
18
17
|
return ValidationResult.new(params.valid?, params.errors) if params.respond_to?(:valid?)
|
18
|
+
|
19
19
|
ValidationResult.new(true)
|
20
20
|
end
|
21
21
|
|
22
22
|
def invalid!(obj)
|
23
23
|
output = [:errors, :messages, :message].reduce(obj) do |result, m|
|
24
|
-
result
|
24
|
+
result.respond_to?(m) ? result.send(m) : result
|
25
25
|
end
|
26
26
|
|
27
27
|
raise Invalid.new(output)
|
data/lib/tzu/version.rb
ADDED
data/lib/tzu.rb
CHANGED
@@ -1,15 +1,14 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
8
|
-
require
|
9
|
-
require
|
10
|
-
require
|
11
|
-
require
|
12
|
-
require 'tzu/validation_result'
|
1
|
+
require "tzu/errors"
|
2
|
+
require "tzu/run_methods"
|
3
|
+
require "tzu/failure"
|
4
|
+
require "tzu/hooks"
|
5
|
+
require "tzu/invalid"
|
6
|
+
require "tzu/match"
|
7
|
+
require "tzu/sequence"
|
8
|
+
require "tzu/step"
|
9
|
+
require "tzu/outcome"
|
10
|
+
require "tzu/validation"
|
11
|
+
require "tzu/validation_result"
|
13
12
|
|
14
13
|
module Tzu
|
15
14
|
def self.included(base)
|
@@ -32,7 +31,7 @@ module Tzu
|
|
32
31
|
outcome.is_a?(Tzu::Outcome) ? outcome : Outcome.new(true, outcome)
|
33
32
|
end
|
34
33
|
rescue
|
35
|
-
rollback! if
|
34
|
+
rollback! if respond_to?(:rollback!)
|
36
35
|
raise
|
37
36
|
end
|
38
37
|
|
metadata
CHANGED
@@ -1,114 +1,36 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tzu
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Morgan Bruce
|
8
8
|
- Blake Turner
|
9
|
-
autorequire:
|
9
|
+
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2024-01-08 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
|
-
- !ruby/object:Gem::Dependency
|
15
|
-
name: bundler
|
16
|
-
requirement: !ruby/object:Gem::Requirement
|
17
|
-
requirements:
|
18
|
-
- - ">="
|
19
|
-
- !ruby/object:Gem::Version
|
20
|
-
version: 1.0.0
|
21
|
-
type: :development
|
22
|
-
prerelease: false
|
23
|
-
version_requirements: !ruby/object:Gem::Requirement
|
24
|
-
requirements:
|
25
|
-
- - ">="
|
26
|
-
- !ruby/object:Gem::Version
|
27
|
-
version: 1.0.0
|
28
|
-
- !ruby/object:Gem::Dependency
|
29
|
-
name: activerecord
|
30
|
-
requirement: !ruby/object:Gem::Requirement
|
31
|
-
requirements:
|
32
|
-
- - ">="
|
33
|
-
- !ruby/object:Gem::Version
|
34
|
-
version: 3.2.15
|
35
|
-
type: :development
|
36
|
-
prerelease: false
|
37
|
-
version_requirements: !ruby/object:Gem::Requirement
|
38
|
-
requirements:
|
39
|
-
- - ">="
|
40
|
-
- !ruby/object:Gem::Version
|
41
|
-
version: 3.2.15
|
42
14
|
- !ruby/object:Gem::Dependency
|
43
15
|
name: activesupport
|
44
16
|
requirement: !ruby/object:Gem::Requirement
|
45
17
|
requirements:
|
46
18
|
- - ">="
|
47
19
|
- !ruby/object:Gem::Version
|
48
|
-
version:
|
49
|
-
|
50
|
-
prerelease: false
|
51
|
-
version_requirements: !ruby/object:Gem::Requirement
|
52
|
-
requirements:
|
53
|
-
- - ">="
|
54
|
-
- !ruby/object:Gem::Version
|
55
|
-
version: 3.2.15
|
56
|
-
- !ruby/object:Gem::Dependency
|
57
|
-
name: rspec
|
58
|
-
requirement: !ruby/object:Gem::Requirement
|
59
|
-
requirements:
|
60
|
-
- - ">="
|
61
|
-
- !ruby/object:Gem::Version
|
62
|
-
version: 2.4.0
|
63
|
-
type: :development
|
64
|
-
prerelease: false
|
65
|
-
version_requirements: !ruby/object:Gem::Requirement
|
66
|
-
requirements:
|
67
|
-
- - ">="
|
20
|
+
version: '4.2'
|
21
|
+
- - "<"
|
68
22
|
- !ruby/object:Gem::Version
|
69
|
-
version:
|
70
|
-
|
71
|
-
name: sqlite3
|
72
|
-
requirement: !ruby/object:Gem::Requirement
|
73
|
-
requirements:
|
74
|
-
- - ">="
|
75
|
-
- !ruby/object:Gem::Version
|
76
|
-
version: '0'
|
77
|
-
type: :development
|
23
|
+
version: '8'
|
24
|
+
type: :runtime
|
78
25
|
prerelease: false
|
79
26
|
version_requirements: !ruby/object:Gem::Requirement
|
80
27
|
requirements:
|
81
28
|
- - ">="
|
82
29
|
- !ruby/object:Gem::Version
|
83
|
-
version: '
|
84
|
-
-
|
85
|
-
name: byebug
|
86
|
-
requirement: !ruby/object:Gem::Requirement
|
87
|
-
requirements:
|
88
|
-
- - ">="
|
89
|
-
- !ruby/object:Gem::Version
|
90
|
-
version: '0'
|
91
|
-
type: :development
|
92
|
-
prerelease: false
|
93
|
-
version_requirements: !ruby/object:Gem::Requirement
|
94
|
-
requirements:
|
95
|
-
- - ">="
|
96
|
-
- !ruby/object:Gem::Version
|
97
|
-
version: '0'
|
98
|
-
- !ruby/object:Gem::Dependency
|
99
|
-
name: virtus
|
100
|
-
requirement: !ruby/object:Gem::Requirement
|
101
|
-
requirements:
|
102
|
-
- - ">="
|
103
|
-
- !ruby/object:Gem::Version
|
104
|
-
version: '0'
|
105
|
-
type: :development
|
106
|
-
prerelease: false
|
107
|
-
version_requirements: !ruby/object:Gem::Requirement
|
108
|
-
requirements:
|
109
|
-
- - ">="
|
30
|
+
version: '4.2'
|
31
|
+
- - "<"
|
110
32
|
- !ruby/object:Gem::Version
|
111
|
-
version: '
|
33
|
+
version: '8'
|
112
34
|
description: Tzu is a library for issuing commands in Ruby
|
113
35
|
email: morgan@onfido.com
|
114
36
|
executables: []
|
@@ -118,7 +40,6 @@ files:
|
|
118
40
|
- LICENSE.txt
|
119
41
|
- README.md
|
120
42
|
- lib/tzu.rb
|
121
|
-
- lib/tzu/core_extensions/string.rb
|
122
43
|
- lib/tzu/errors.rb
|
123
44
|
- lib/tzu/failure.rb
|
124
45
|
- lib/tzu/hooks.rb
|
@@ -130,18 +51,12 @@ files:
|
|
130
51
|
- lib/tzu/step.rb
|
131
52
|
- lib/tzu/validation.rb
|
132
53
|
- lib/tzu/validation_result.rb
|
133
|
-
-
|
134
|
-
- spec/outcome_spec.rb
|
135
|
-
- spec/sequence_spec.rb
|
136
|
-
- spec/spec_helper.rb
|
137
|
-
- spec/step_spec.rb
|
138
|
-
- spec/tzu_spec.rb
|
139
|
-
- spec/validation_spec.rb
|
54
|
+
- lib/tzu/version.rb
|
140
55
|
homepage: https://github.com/onfido/tzu
|
141
56
|
licenses:
|
142
57
|
- MIT
|
143
58
|
metadata: {}
|
144
|
-
post_install_message:
|
59
|
+
post_install_message:
|
145
60
|
rdoc_options: []
|
146
61
|
require_paths:
|
147
62
|
- lib
|
@@ -156,16 +71,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
156
71
|
- !ruby/object:Gem::Version
|
157
72
|
version: '0'
|
158
73
|
requirements: []
|
159
|
-
|
160
|
-
|
161
|
-
signing_key:
|
74
|
+
rubygems_version: 3.4.22
|
75
|
+
signing_key:
|
162
76
|
specification_version: 4
|
163
77
|
summary: Standardise and encapsulate your application's actions
|
164
|
-
test_files:
|
165
|
-
- spec/hooks_spec.rb
|
166
|
-
- spec/outcome_spec.rb
|
167
|
-
- spec/sequence_spec.rb
|
168
|
-
- spec/spec_helper.rb
|
169
|
-
- spec/step_spec.rb
|
170
|
-
- spec/tzu_spec.rb
|
171
|
-
- spec/validation_spec.rb
|
78
|
+
test_files: []
|
@@ -1,17 +0,0 @@
|
|
1
|
-
module Tzu
|
2
|
-
module CoreExtensions
|
3
|
-
module String
|
4
|
-
def symbolize
|
5
|
-
underscore.to_sym
|
6
|
-
end
|
7
|
-
|
8
|
-
def underscore
|
9
|
-
gsub(/::/, '/')
|
10
|
-
.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
|
11
|
-
.gsub(/([a-z\d])([A-Z])/, '\1_\2')
|
12
|
-
.tr('-', '_')
|
13
|
-
.downcase
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
data/spec/hooks_spec.rb
DELETED
@@ -1,169 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe Tzu::Hooks do
|
4
|
-
describe '#with_hooks' do
|
5
|
-
def build_hooked(&block)
|
6
|
-
hooked = Class.new.send(:include, Tzu::Hooks)
|
7
|
-
|
8
|
-
hooked.class_eval do
|
9
|
-
attr_reader :steps
|
10
|
-
|
11
|
-
def self.process
|
12
|
-
new.tap(&:process).steps
|
13
|
-
end
|
14
|
-
|
15
|
-
def initialize
|
16
|
-
@steps = []
|
17
|
-
end
|
18
|
-
|
19
|
-
def process
|
20
|
-
with_hooks({}) { steps << :process }
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
hooked.class_eval(&block) if block
|
25
|
-
hooked
|
26
|
-
end
|
27
|
-
|
28
|
-
context 'when before hook is a method' do
|
29
|
-
let(:hooked) do
|
30
|
-
build_hooked do
|
31
|
-
before :add_before
|
32
|
-
|
33
|
-
def add_before(p)
|
34
|
-
steps << :before
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
it 'runs the before hook method' do
|
40
|
-
expect(hooked.process).to eq([:before, :process])
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
context 'when before hook is a block' do
|
45
|
-
let(:hooked) do
|
46
|
-
build_hooked do
|
47
|
-
before do
|
48
|
-
steps << :before
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
it 'runs the before hook block' do
|
54
|
-
expect(hooked.process).to eq([:before, :process])
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
context 'when after hook is a method' do
|
59
|
-
let(:hooked) do
|
60
|
-
build_hooked do
|
61
|
-
after :add_after
|
62
|
-
|
63
|
-
def add_after(p)
|
64
|
-
steps << :after
|
65
|
-
end
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
it 'runs the after hook method' do
|
70
|
-
expect(hooked.process).to eq([:process, :after])
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
context 'when after hook is a block' do
|
75
|
-
let(:hooked) do
|
76
|
-
build_hooked do
|
77
|
-
after do
|
78
|
-
steps << :after
|
79
|
-
end
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
it 'runs the after hook block' do
|
84
|
-
expect(hooked.process).to eq([:process, :after])
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
context 'when both before and after blocks are defined' do
|
89
|
-
let(:hooked) do
|
90
|
-
build_hooked do
|
91
|
-
before do
|
92
|
-
steps << :before
|
93
|
-
end
|
94
|
-
|
95
|
-
after do
|
96
|
-
steps << :after
|
97
|
-
end
|
98
|
-
end
|
99
|
-
end
|
100
|
-
|
101
|
-
it 'runs hooks in the expected order' do
|
102
|
-
expect(hooked.process).to eq([:before, :process, :after])
|
103
|
-
end
|
104
|
-
end
|
105
|
-
|
106
|
-
|
107
|
-
context 'when both before and after methods are defined' do
|
108
|
-
let(:hooked) do
|
109
|
-
build_hooked do
|
110
|
-
before :add_before
|
111
|
-
after :add_after
|
112
|
-
|
113
|
-
def add_before(p)
|
114
|
-
steps << :before
|
115
|
-
end
|
116
|
-
|
117
|
-
def add_after(p)
|
118
|
-
steps << :after
|
119
|
-
end
|
120
|
-
end
|
121
|
-
end
|
122
|
-
|
123
|
-
it 'runs hooks in the expected order' do
|
124
|
-
expect(hooked.process).to eq([:before, :process, :after])
|
125
|
-
end
|
126
|
-
end
|
127
|
-
|
128
|
-
context 'when multiple before methods are defined' do
|
129
|
-
let(:hooked) do
|
130
|
-
build_hooked do
|
131
|
-
before :add_before1, :add_before2
|
132
|
-
|
133
|
-
def add_before1(p)
|
134
|
-
steps << :before1
|
135
|
-
end
|
136
|
-
|
137
|
-
def add_before2(p)
|
138
|
-
steps << :before2
|
139
|
-
end
|
140
|
-
end
|
141
|
-
end
|
142
|
-
|
143
|
-
it 'runs hooks in the expected order' do
|
144
|
-
expect(hooked.process).to eq([:before1, :before2, :process])
|
145
|
-
end
|
146
|
-
end
|
147
|
-
|
148
|
-
context 'when multiple after methods are defined' do
|
149
|
-
let(:hooked) do
|
150
|
-
build_hooked do
|
151
|
-
after :add_after1, :add_after2
|
152
|
-
|
153
|
-
def add_after1(p)
|
154
|
-
steps << :after1
|
155
|
-
end
|
156
|
-
|
157
|
-
def add_after2(p)
|
158
|
-
steps << :after2
|
159
|
-
end
|
160
|
-
end
|
161
|
-
end
|
162
|
-
|
163
|
-
it 'runs hooks in the expected order' do
|
164
|
-
expect(hooked.process).to eq([:process, :after1, :after2])
|
165
|
-
end
|
166
|
-
end
|
167
|
-
|
168
|
-
end
|
169
|
-
end
|
data/spec/outcome_spec.rb
DELETED
@@ -1,48 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe Tzu::Outcome do
|
4
|
-
|
5
|
-
context 'outcome is failed with specified type' do
|
6
|
-
subject { Tzu::Outcome.new(false, 'abc', :validation) }
|
7
|
-
|
8
|
-
it 'calls appropriate handler' do
|
9
|
-
matched = false
|
10
|
-
subject.handle do
|
11
|
-
success { raise }
|
12
|
-
failure(:something) { raise }
|
13
|
-
failure(:validation) { matched = true }
|
14
|
-
end
|
15
|
-
expect(matched).to eq true
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
context 'outcome is failed with unspecified type' do
|
20
|
-
subject { Tzu::Outcome.new(false, 'abc', :validation) }
|
21
|
-
|
22
|
-
it 'calls appropriate handler' do
|
23
|
-
matched = false
|
24
|
-
subject.handle do
|
25
|
-
success { raise }
|
26
|
-
failure { matched = true }
|
27
|
-
failure(:validation) { raise }
|
28
|
-
end
|
29
|
-
expect(matched).to eq true
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
context 'outcome is successful' do
|
34
|
-
subject { Tzu::Outcome.new(true, 'abc') }
|
35
|
-
|
36
|
-
it 'calls success handler' do
|
37
|
-
matched = false
|
38
|
-
subject.handle do
|
39
|
-
success { matched = true }
|
40
|
-
failure(:something) { raise }
|
41
|
-
end
|
42
|
-
expect(matched).to eq true
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
|
48
|
-
|
data/spec/sequence_spec.rb
DELETED
@@ -1,232 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
class SayMyName
|
4
|
-
include Tzu
|
5
|
-
|
6
|
-
def call(params)
|
7
|
-
"Hello, #{params[:name]}"
|
8
|
-
end
|
9
|
-
end
|
10
|
-
|
11
|
-
class MakeMeSoundImportant
|
12
|
-
include Tzu
|
13
|
-
|
14
|
-
def call(params)
|
15
|
-
"#{params[:boring_message]}! You are the most important citizen of #{params[:country]}!"
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
class ThrowInvalidError
|
20
|
-
include Tzu
|
21
|
-
|
22
|
-
def call(params)
|
23
|
-
invalid!('Who am I? Why am I here?')
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
class ConstructGreeting
|
28
|
-
class << self
|
29
|
-
def go(greeting, name)
|
30
|
-
"#{greeting}, #{name}"
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
class MultiStepSimple
|
36
|
-
include Tzu::Sequence
|
37
|
-
|
38
|
-
step SayMyName do
|
39
|
-
receives do |params|
|
40
|
-
{ name: params[:name] }
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
step MakeMeSoundImportant do
|
45
|
-
receives do |params, prior_results|
|
46
|
-
{
|
47
|
-
boring_message: prior_results[:say_my_name],
|
48
|
-
country: params[:country]
|
49
|
-
}
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
class MultiStepNonTzu
|
55
|
-
include Tzu::Sequence
|
56
|
-
|
57
|
-
step ConstructGreeting do
|
58
|
-
as :say_my_name
|
59
|
-
invoke_with :go
|
60
|
-
|
61
|
-
receives_many do |greeting, name, country|
|
62
|
-
[greeting, name]
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
step MakeMeSoundImportant do
|
67
|
-
receives do |greeting, name, country, prior_results|
|
68
|
-
{
|
69
|
-
boring_message: prior_results[:say_my_name],
|
70
|
-
country: country
|
71
|
-
}
|
72
|
-
end
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
class MultiStepComplex
|
77
|
-
include Tzu::Sequence
|
78
|
-
|
79
|
-
step SayMyName do
|
80
|
-
as :first_command
|
81
|
-
end
|
82
|
-
|
83
|
-
step MakeMeSoundImportant do
|
84
|
-
as :final_command
|
85
|
-
receives do |params, prior_results|
|
86
|
-
{
|
87
|
-
boring_message: prior_results[:first_command],
|
88
|
-
country: params[:country]
|
89
|
-
}
|
90
|
-
end
|
91
|
-
end
|
92
|
-
|
93
|
-
result :take_all
|
94
|
-
end
|
95
|
-
|
96
|
-
class MultiStepProcessResults
|
97
|
-
include Tzu::Sequence
|
98
|
-
|
99
|
-
step SayMyName do
|
100
|
-
as :first_command
|
101
|
-
end
|
102
|
-
|
103
|
-
step MakeMeSoundImportant do
|
104
|
-
as :final_command
|
105
|
-
receives do |params, prior_results|
|
106
|
-
{
|
107
|
-
boring_message: prior_results[:first_command],
|
108
|
-
country: params[:country]
|
109
|
-
}
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
|
-
result do |params, prior_results|
|
114
|
-
{
|
115
|
-
status: :important,
|
116
|
-
message: "BULLETIN: #{prior_results[:final_command]}"
|
117
|
-
}
|
118
|
-
end
|
119
|
-
end
|
120
|
-
|
121
|
-
class MultiStepInvalid
|
122
|
-
include Tzu::Sequence
|
123
|
-
|
124
|
-
step SayMyName
|
125
|
-
|
126
|
-
step ThrowInvalidError do
|
127
|
-
receives do |params, prior_results|
|
128
|
-
{ answer: "#{params[:name]}!!! #{prior_results[:say_my_name]}" }
|
129
|
-
end
|
130
|
-
end
|
131
|
-
|
132
|
-
step MakeMeSoundImportant do
|
133
|
-
receives do |params, prior_results|
|
134
|
-
{
|
135
|
-
boring_message: prior_results[:say_my_name],
|
136
|
-
country: params[:country]
|
137
|
-
}
|
138
|
-
end
|
139
|
-
end
|
140
|
-
end
|
141
|
-
|
142
|
-
describe Tzu::Sequence do
|
143
|
-
context '#steps' do
|
144
|
-
context MultiStepSimple do
|
145
|
-
let(:steps) { MultiStepSimple.steps }
|
146
|
-
|
147
|
-
it 'returns array of Steps' do
|
148
|
-
steps.each { |step| expect(step.is_a? Tzu::Step).to be true }
|
149
|
-
end
|
150
|
-
|
151
|
-
it 'passes the appropriate klass, name, and param_mutators to each step' do
|
152
|
-
say_my_name = steps.first
|
153
|
-
expect(say_my_name.klass).to eq SayMyName
|
154
|
-
expect(say_my_name.name).to eq :say_my_name
|
155
|
-
expect(say_my_name.single_mutator.is_a? Proc).to be true
|
156
|
-
|
157
|
-
make_me_sound_important = steps.last
|
158
|
-
expect(make_me_sound_important.klass).to eq MakeMeSoundImportant
|
159
|
-
expect(make_me_sound_important.name).to eq :make_me_sound_important
|
160
|
-
expect(make_me_sound_important.single_mutator.is_a? Proc).to be true
|
161
|
-
end
|
162
|
-
end
|
163
|
-
|
164
|
-
context MultiStepComplex do
|
165
|
-
let(:steps) { MultiStepComplex.steps }
|
166
|
-
|
167
|
-
it 'returns array of Steps' do
|
168
|
-
steps.each { |step| expect(step.is_a? Tzu::Step).to be true }
|
169
|
-
end
|
170
|
-
|
171
|
-
it 'passes the appropriate klass, name, and param_mutators to each step' do
|
172
|
-
say_my_name = steps.first
|
173
|
-
expect(say_my_name.klass).to eq SayMyName
|
174
|
-
expect(say_my_name.name).to eq :first_command
|
175
|
-
|
176
|
-
make_me_sound_important = steps.last
|
177
|
-
expect(make_me_sound_important.klass).to eq MakeMeSoundImportant
|
178
|
-
expect(make_me_sound_important.name).to eq :final_command
|
179
|
-
expect(make_me_sound_important.single_mutator.is_a? Proc).to be true
|
180
|
-
end
|
181
|
-
end
|
182
|
-
end
|
183
|
-
|
184
|
-
context '#run' do
|
185
|
-
let(:params) do
|
186
|
-
{
|
187
|
-
name: 'Jessica',
|
188
|
-
country: 'Azerbaijan'
|
189
|
-
}
|
190
|
-
end
|
191
|
-
|
192
|
-
context MultiStepSimple do
|
193
|
-
it 'returns the outcome of the last command' do
|
194
|
-
outcome = MultiStepSimple.run(params)
|
195
|
-
expect(outcome.result).to eq 'Hello, Jessica! You are the most important citizen of Azerbaijan!'
|
196
|
-
end
|
197
|
-
end
|
198
|
-
|
199
|
-
context MultiStepNonTzu do
|
200
|
-
it 'returns the outcome of the last command' do
|
201
|
-
outcome = MultiStepNonTzu.run('Greetings', 'Christopher', 'Canada')
|
202
|
-
expect(outcome.result).to eq 'Greetings, Christopher! You are the most important citizen of Canada!'
|
203
|
-
end
|
204
|
-
end
|
205
|
-
|
206
|
-
context MultiStepComplex do
|
207
|
-
it 'returns the outcome of the last command' do
|
208
|
-
outcome = MultiStepComplex.run(params)
|
209
|
-
results = outcome.result
|
210
|
-
expect(results[:first_command]).to eq 'Hello, Jessica'
|
211
|
-
expect(results[:final_command]).to eq 'Hello, Jessica! You are the most important citizen of Azerbaijan!'
|
212
|
-
end
|
213
|
-
end
|
214
|
-
|
215
|
-
context MultiStepProcessResults do
|
216
|
-
it 'returns the outcome of the last command' do
|
217
|
-
outcome = MultiStepProcessResults.run(params)
|
218
|
-
result = outcome.result
|
219
|
-
expect(result[:status]).to eq :important
|
220
|
-
expect(result[:message]).to eq 'BULLETIN: Hello, Jessica! You are the most important citizen of Azerbaijan!'
|
221
|
-
end
|
222
|
-
end
|
223
|
-
|
224
|
-
context MultiStepInvalid do
|
225
|
-
it 'stops its execution at the invalid command, which it returns' do
|
226
|
-
outcome = MultiStepInvalid.run(params)
|
227
|
-
expect(outcome.success?).to be false
|
228
|
-
expect(outcome.result).to eq(errors: 'Who am I? Why am I here?')
|
229
|
-
end
|
230
|
-
end
|
231
|
-
end
|
232
|
-
end
|
data/spec/spec_helper.rb
DELETED
data/spec/step_spec.rb
DELETED
@@ -1,56 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe Tzu::Step do
|
4
|
-
let(:dummy_mutator) { proc { 'Dummy!' } }
|
5
|
-
|
6
|
-
context '#name' do
|
7
|
-
context 'when name is symbol' do
|
8
|
-
let(:step) { Tzu::Step.new(:step_name) }
|
9
|
-
|
10
|
-
it 'returns name' do
|
11
|
-
expect(step.name).to eq :step_name
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
context 'when name is a class' do
|
16
|
-
let(:step) { Tzu::Step.new(StandardError) }
|
17
|
-
let(:outcome) { ControlledOutcome.run(result: :success) }
|
18
|
-
|
19
|
-
it 'returns underscored name' do
|
20
|
-
expect(step.name).to eq :standard_error
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
context '#receives' do
|
26
|
-
context 'when splat_mutator is already defined' do
|
27
|
-
let(:step) { Tzu::Step.new(:step_name) }
|
28
|
-
|
29
|
-
before do
|
30
|
-
step.receives_many do |variable|
|
31
|
-
[1, 2, 3]
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
it 'throws error' do
|
36
|
-
expect { step.receives { |var| 'foo'} } .to raise_error(Tzu::InvalidSequence)
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
context '#receives_many' do
|
42
|
-
context 'when single_mutator is already defined' do
|
43
|
-
let(:step) { Tzu::Step.new(:step_name) }
|
44
|
-
|
45
|
-
before do
|
46
|
-
step.receives do |var|
|
47
|
-
'hello'
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
it 'throws error' do
|
52
|
-
expect { step.receives_many { |var| [1, 2, 3] } } .to raise_error(Tzu::InvalidSequence)
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
data/spec/tzu_spec.rb
DELETED
@@ -1,214 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
class ControlledOutcome
|
4
|
-
include Tzu
|
5
|
-
|
6
|
-
def call(params)
|
7
|
-
result = params[:result]
|
8
|
-
return 123 if result == :success
|
9
|
-
return invalid!('Invalid Message') if result == :invalid
|
10
|
-
return fail!(:falure_type, 'Failure Message') if result == :failure
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
class MyRequestObject
|
15
|
-
attr_reader :value, :errors
|
16
|
-
|
17
|
-
def initialize(params)
|
18
|
-
@params = params
|
19
|
-
@value = params[:value]
|
20
|
-
@errors = 'Error Message'
|
21
|
-
end
|
22
|
-
|
23
|
-
def valid?
|
24
|
-
@params[:valid]
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
class VirtusRequestObject
|
29
|
-
include Virtus.model
|
30
|
-
include ActiveModel::Validations
|
31
|
-
|
32
|
-
validates :name, :age, presence: :true
|
33
|
-
|
34
|
-
attribute :name, String
|
35
|
-
attribute :age, Integer
|
36
|
-
end
|
37
|
-
|
38
|
-
class ValidatedCommand
|
39
|
-
include Tzu
|
40
|
-
|
41
|
-
request_object MyRequestObject
|
42
|
-
|
43
|
-
def call(request)
|
44
|
-
request.value
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
class VirtusValidatedCommand
|
49
|
-
include Tzu
|
50
|
-
|
51
|
-
request_object VirtusRequestObject
|
52
|
-
|
53
|
-
def call(request)
|
54
|
-
"Name: #{request.name}, Age: #{request.age}"
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
describe Tzu do
|
59
|
-
context '#run' do
|
60
|
-
context 'when command succeeds' do
|
61
|
-
let(:outcome) { ControlledOutcome.run(result: :success) }
|
62
|
-
|
63
|
-
it 'correctly returns sets pass/fail flags' do
|
64
|
-
expect(outcome.success?).to be true
|
65
|
-
expect(outcome.failure?).to be false
|
66
|
-
end
|
67
|
-
|
68
|
-
it 'returns result' do
|
69
|
-
expect(outcome.result).to eq(123)
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
context 'when command is invalid' do
|
74
|
-
let(:outcome) { ControlledOutcome.run(result: :invalid) }
|
75
|
-
|
76
|
-
it 'correctly returns sets pass/fail flags' do
|
77
|
-
expect(outcome.success?).to be false
|
78
|
-
expect(outcome.failure?).to be true
|
79
|
-
end
|
80
|
-
|
81
|
-
it 'returns error string as errors hash' do
|
82
|
-
expect(outcome.result).to eq(errors: 'Invalid Message')
|
83
|
-
end
|
84
|
-
|
85
|
-
it 'sets type to validation' do
|
86
|
-
expect(outcome.type).to eq(:validation)
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
context 'when command fails' do
|
91
|
-
let(:outcome) { ControlledOutcome.run(result: :failure) }
|
92
|
-
|
93
|
-
it 'correctly returns sets pass/fail flags' do
|
94
|
-
expect(outcome.success?).to be false
|
95
|
-
expect(outcome.failure?).to be true
|
96
|
-
end
|
97
|
-
|
98
|
-
it 'returns error string as errors hash' do
|
99
|
-
expect(outcome.result).to eq(errors: 'Failure Message')
|
100
|
-
end
|
101
|
-
|
102
|
-
it 'sets type to falure_type' do
|
103
|
-
expect(outcome.type).to eq(:falure_type)
|
104
|
-
end
|
105
|
-
end
|
106
|
-
end
|
107
|
-
|
108
|
-
context '#run with block' do
|
109
|
-
let(:success_spy) { spy('success') }
|
110
|
-
let(:invalid_spy) { spy('invalid') }
|
111
|
-
let(:failure_spy) { spy('failure') }
|
112
|
-
|
113
|
-
before do
|
114
|
-
ControlledOutcome.run(result: result) do
|
115
|
-
success { |_| success_spy.call }
|
116
|
-
invalid { |_| invalid_spy.call }
|
117
|
-
failure { |_| failure_spy.call }
|
118
|
-
end
|
119
|
-
end
|
120
|
-
|
121
|
-
context 'when command succeeds' do
|
122
|
-
let(:result) { :success }
|
123
|
-
it 'executes success block' do
|
124
|
-
expect(success_spy).to have_received(:call)
|
125
|
-
end
|
126
|
-
end
|
127
|
-
|
128
|
-
context 'when command is invalid' do
|
129
|
-
let(:result) { :invalid }
|
130
|
-
it 'executes invalid block' do
|
131
|
-
expect(invalid_spy).to have_received(:call)
|
132
|
-
end
|
133
|
-
end
|
134
|
-
|
135
|
-
context 'when command fails' do
|
136
|
-
let(:result) { :failure }
|
137
|
-
it 'executes failure block' do
|
138
|
-
expect(failure_spy).to have_received(:call)
|
139
|
-
end
|
140
|
-
end
|
141
|
-
end
|
142
|
-
|
143
|
-
context '#run with request object' do
|
144
|
-
context 'when request is valid' do
|
145
|
-
let(:outcome) { ValidatedCommand.run(value: 1111, valid: true) }
|
146
|
-
|
147
|
-
it 'executes successfully' do
|
148
|
-
expect(outcome.success?).to be true
|
149
|
-
end
|
150
|
-
|
151
|
-
it 'returns value' do
|
152
|
-
expect(outcome.result).to eq 1111
|
153
|
-
end
|
154
|
-
end
|
155
|
-
|
156
|
-
context 'when request is invalid' do
|
157
|
-
let(:outcome) { ValidatedCommand.run(value: 2222, valid: false) }
|
158
|
-
|
159
|
-
it 'does not execute successfully' do
|
160
|
-
expect(outcome.failure?).to be true
|
161
|
-
end
|
162
|
-
|
163
|
-
it 'has outcome type :validation' do
|
164
|
-
expect(outcome.type).to eq :validation
|
165
|
-
end
|
166
|
-
|
167
|
-
it 'returns error string as errors hash' do
|
168
|
-
expect(outcome.result).to eq(errors: 'Error Message')
|
169
|
-
end
|
170
|
-
end
|
171
|
-
|
172
|
-
context 'with virtus/active_model request object' do
|
173
|
-
let(:outcome) { VirtusValidatedCommand.run(params) }
|
174
|
-
|
175
|
-
context 'when request is valid' do
|
176
|
-
let(:params) { { name: 'Young Tzu', age: '19' } }
|
177
|
-
|
178
|
-
it 'executes successfully' do
|
179
|
-
expect(outcome.success?).to be true
|
180
|
-
end
|
181
|
-
end
|
182
|
-
|
183
|
-
context 'when request is invalid' do
|
184
|
-
let(:params) { { name: 'My Name' } }
|
185
|
-
|
186
|
-
it 'does not execute successfully' do
|
187
|
-
expect(outcome.failure?).to be true
|
188
|
-
end
|
189
|
-
|
190
|
-
it 'returns ActiveModel error object' do
|
191
|
-
expect(outcome.result).to eq(age: ["can't be blank"])
|
192
|
-
end
|
193
|
-
end
|
194
|
-
end
|
195
|
-
end
|
196
|
-
|
197
|
-
context '#run!' do
|
198
|
-
context 'when command is invalid' do
|
199
|
-
let(:outcome) { ControlledOutcome.run!(result: :invalid) }
|
200
|
-
|
201
|
-
it 'raises Tzu::Invalid' do
|
202
|
-
expect { outcome }.to raise_error Tzu::Invalid, 'Invalid Message'
|
203
|
-
end
|
204
|
-
end
|
205
|
-
|
206
|
-
context 'when command fails' do
|
207
|
-
let(:outcome) { ControlledOutcome.run!(result: :failure) }
|
208
|
-
|
209
|
-
it 'raises Tzu::Failure' do
|
210
|
-
expect { outcome }.to raise_error Tzu::Failure, 'Failure Message'
|
211
|
-
end
|
212
|
-
end
|
213
|
-
end
|
214
|
-
end
|
data/spec/validation_spec.rb
DELETED
@@ -1,80 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe Tzu::Validation do
|
4
|
-
|
5
|
-
context 'params define valid? method' do
|
6
|
-
subject do
|
7
|
-
Class.new do
|
8
|
-
include Tzu
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
|
-
let(:errors) { [1, 2, 3] }
|
13
|
-
let(:params) { spy(errors: errors, valid?: false) }
|
14
|
-
|
15
|
-
it 'valid? method is called' do
|
16
|
-
subject.run(params)
|
17
|
-
expect(params).to have_received(:valid?)
|
18
|
-
end
|
19
|
-
|
20
|
-
it 'returns validation result' do
|
21
|
-
result = subject.run(params)
|
22
|
-
expect(result).to have_attributes(success: false, type: :validation, result: errors)
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
context 'invoked directly' do
|
27
|
-
context 'error message is string' do
|
28
|
-
let(:str) { 'error_message' }
|
29
|
-
subject { Tzu::Invalid.new(str) }
|
30
|
-
it 'has string as #message' do
|
31
|
-
expect(subject.message).to eq str
|
32
|
-
end
|
33
|
-
|
34
|
-
it '#errors converts sting to hash' do
|
35
|
-
expect(subject.errors).to eq(errors: str)
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
context 'rescued' do
|
41
|
-
subject do
|
42
|
-
Class.new do
|
43
|
-
include Tzu
|
44
|
-
|
45
|
-
def call(params)
|
46
|
-
raise StandardError.new(params[:message])
|
47
|
-
rescue StandardError => e
|
48
|
-
invalid! e
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
context 'error message is string' do
|
54
|
-
let(:str) { 'error_message' }
|
55
|
-
let(:params) { { message: str } }
|
56
|
-
|
57
|
-
describe '#run' do
|
58
|
-
it 'returns error hash as result' do
|
59
|
-
outcome = subject.run(params)
|
60
|
-
expect(outcome.result).to eq(errors: str)
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
describe '#run!' do
|
65
|
-
it 'has string as #message' do
|
66
|
-
expect { subject.run!(params) }.to raise_error Tzu::Invalid, str
|
67
|
-
end
|
68
|
-
|
69
|
-
it 'has string as #errors' do
|
70
|
-
begin
|
71
|
-
subject.run!(params)
|
72
|
-
expect(false).to be true # Should never reach this
|
73
|
-
rescue Tzu::Invalid => e
|
74
|
-
expect(e.errors).to eq(errors: str)
|
75
|
-
end
|
76
|
-
end
|
77
|
-
end
|
78
|
-
end
|
79
|
-
end
|
80
|
-
end
|