tzu 0.1.2.0 → 1.0.0
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.
- 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
|