interactor-contracts 0.2.0 → 0.3.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 +4 -4
- data/CHANGELOG.md +23 -0
- data/README.md +62 -8
- data/interactor-contracts.gemspec +27 -5
- data/lib/interactor/contracts/breach_set.rb +1 -1
- data/lib/interactor/contracts/contract.rb +24 -14
- data/lib/interactor/contracts/dsl.rb +44 -19
- data/lib/interactor/contracts/outcome.rb +2 -2
- data/lib/interactor/contracts/terms.rb +34 -5
- data/lib/interactor/contracts/version.rb +1 -1
- metadata +27 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6209758247e17ba8e6ef497cf6266e864cdd0a3cc33d9755f1cce473c6ed2c29
|
4
|
+
data.tar.gz: dac680662651cdfdb9aa193158165978a03766ca88f05ac95e5768c85c7d18d4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 45047bfb79bb8233b12d8f91dfe730633a7cdea37e1c3e643bdd6300a5befefc2bea2f2808f4ff7321d5e37e0b16989a8e0bd5aa92a5e12be3fdc95f3b239ac8
|
7
|
+
data.tar.gz: 77e09b1dbcb20c9294c6af0c2dfac4fd84e982063c875907f46038b17e55aa0ea25f5ba134f49fc1c043fdcdc835333c87c9d8c89f5dc1e58e407a49fd4d66d7
|
data/CHANGELOG.md
CHANGED
@@ -4,6 +4,27 @@ All notable changes to this project will be documented in this file. This projec
|
|
4
4
|
|
5
5
|
[semver]: http://semver.org/spec/v2.0.0.html
|
6
6
|
|
7
|
+
## [0.3.0] - 2019-10-09
|
8
|
+
|
9
|
+
### Added
|
10
|
+
|
11
|
+
* [#27](https://github.com/michaelherold/interactor-contracts/pull/27): Upgrade dry-validation to 1.0 - [@vaihtovirta](https://github.com/vaihtovirta).
|
12
|
+
* [#30](https://github.com/michaelherold/interactor-contracts/pull/30): Allow setting a custom I18n backend for contract messages - [@michaelherold](https://github.com/michaelherold).
|
13
|
+
|
14
|
+
### Changed
|
15
|
+
|
16
|
+
* [#29](https://github.com/michaelherold/interactor-contracts/pull/29): Renamed the `assures` method to `promises` and all of the internals to reflect the new naming. This is a backward-compatible change due to an alias of `assures` to `promises` - [@michaelherold](https://github.com/michaelherold), suggested by [@KelseyDH](https://github.com/KelseyDH).
|
17
|
+
|
18
|
+
### Fixed
|
19
|
+
|
20
|
+
* [#26](https://github.com/michaelherold/interactor-contracts/pull/26): Fix bug with inheritance - [@raykov](https://github.com/raykov).
|
21
|
+
|
22
|
+
### Miscellaneous
|
23
|
+
|
24
|
+
* [#24](https://github.com/michaelherold/interactor-contracts/pull/24): Fixed a typo in the gem specification - [@bittersweet](https://github.com/bittersweet).
|
25
|
+
* [#28](https://github.com/michaelherold/interactor-contracts/pull/28): Fixed
|
26
|
+
the Travis configuration to stop failing on JRuby builds - [@michaelherold](https://github.com/michaelherold).
|
27
|
+
|
7
28
|
## [0.2.0] - 2019-05-27
|
8
29
|
|
9
30
|
### Added
|
@@ -35,5 +56,7 @@ All notable changes to this project will be documented in this file. This projec
|
|
35
56
|
* [#4](https://github.com/michaelherold/interactor-contracts/pull/4): Added Danger as a collaboration tool - [@michaelherold](https://github.com/michaelherold).
|
36
57
|
* [#6](https://github.com/michaelherold/interactor-contracts/pull/6): Updated the README - [@michaelherold](https://github.com/michaelherold).
|
37
58
|
|
59
|
+
[unreleased]: https://github.com/michaelherold/interactor-contracts/compare/v0.2.0...master
|
60
|
+
[0.3.0]: https://github.com/michaelherold/interactor-contracts/compare/v0.2.0...v0.3.0
|
38
61
|
[0.2.0]: https://github.com/michaelherold/interactor-contracts/compare/v0.1.0...v0.2.0
|
39
62
|
[0.1.0]: https://github.com/michaelherold/interactor-contracts/tree/v0.1.0
|
data/README.md
CHANGED
@@ -9,7 +9,7 @@
|
|
9
9
|
[travis]: https://travis-ci.org/michaelherold/interactor-contracts
|
10
10
|
|
11
11
|
Interactor::Contracts is an extension to the [interactor] gem that gives you
|
12
|
-
the ability to specify the expectations (expected inputs) and
|
12
|
+
the ability to specify the expectations (expected inputs) and promises
|
13
13
|
(expected outputs) of your interactors.
|
14
14
|
|
15
15
|
[interactor]: https://github.com/collectiveidea/interactor
|
@@ -33,7 +33,7 @@ Or install it yourself as:
|
|
33
33
|
## Usage
|
34
34
|
|
35
35
|
Let's extend the sample `AuthenticateUser` from the Interactor examples with a
|
36
|
-
contract that specifies its expectations and
|
36
|
+
contract that specifies its expectations and promises.
|
37
37
|
|
38
38
|
```ruby
|
39
39
|
class AuthenticateUser
|
@@ -45,7 +45,7 @@ class AuthenticateUser
|
|
45
45
|
required(:password).filled
|
46
46
|
end
|
47
47
|
|
48
|
-
|
48
|
+
promises do
|
49
49
|
required(:user).filled
|
50
50
|
required(:token).filled
|
51
51
|
end
|
@@ -65,23 +65,38 @@ class AuthenticateUser
|
|
65
65
|
end
|
66
66
|
```
|
67
67
|
|
68
|
+
### Inputs: expectations
|
69
|
+
|
68
70
|
The `expects` block defines the expectations: the expected attributes of the
|
69
71
|
context prior to the interactor running, along with any predicates that further
|
70
72
|
constrain the input.
|
71
73
|
|
72
|
-
|
74
|
+
### Outputs: promises
|
75
|
+
|
76
|
+
The `promises` block defines the promises: the expected attributes of the
|
73
77
|
context after the interactor runs and successfully completes, along with any
|
74
|
-
predicates the further constrain the output.
|
78
|
+
predicates the further constrain the output. Note, for backward-compatibility,
|
79
|
+
this is also available via the `assures` method.
|
75
80
|
|
76
81
|
Because interactors can have transitive dependencies through the use of
|
77
82
|
organizers, any other inputs or outputs are ignored from the perspective of
|
78
83
|
the contract and are passed along to the outgoing (successful) context.
|
79
84
|
|
80
|
-
Both `expects` and `
|
85
|
+
Both `expects` and `promises` wrap [dry-validation], so you can use any
|
81
86
|
predicates defined in it to describe the expected inputs and outputs of your
|
82
87
|
interactor.
|
83
88
|
|
84
|
-
|
89
|
+
### Contract failures: breaches
|
90
|
+
|
91
|
+
By default, contract failures, or breaches, behave like the example above:
|
92
|
+
breached keys are set on the failed context with their contract failures as
|
93
|
+
values. Note that you do not have to define an `on_breach` for the behavior, but
|
94
|
+
if you do this behavior will not occur.
|
95
|
+
|
96
|
+
If there are more complicated things you wish to do with contract failures, you
|
97
|
+
can define one or more breach handlers.
|
98
|
+
|
99
|
+
To hook into a failed expectation or promise, you can use the `on_breach`
|
85
100
|
method to defined a breach handler. It should take a 1-arity block that expects
|
86
101
|
an array of `Breach` objects. These objects have a `property` attribute that
|
87
102
|
will give you the key that's in breach of its contract. Breaches also have a
|
@@ -100,8 +115,47 @@ result = AuthenticateUser.call({})
|
|
100
115
|
result.failure? #=> true
|
101
116
|
```
|
102
117
|
|
118
|
+
If you would rather have your contract breaches be aggregated into, for example,
|
119
|
+
an `errors` property, you could use a breach handler like the following:
|
120
|
+
|
121
|
+
```ruby
|
122
|
+
on_breach do |breaches|
|
123
|
+
context.fail!(errors: breaches)
|
124
|
+
end
|
125
|
+
```
|
126
|
+
|
103
127
|
[dry-validation]: https://github.com/dryrb/dry-validation
|
104
128
|
|
129
|
+
### I18n support
|
130
|
+
|
131
|
+
You can [configure the underlying `dry-validation` contract][config] by passing
|
132
|
+
a block to the `config` method in your contract. This block will be evaluated on
|
133
|
+
the underlying configuration for the contract. For example, if you want to set
|
134
|
+
up the contract to use I18n in your Rails app, you might do something like this:
|
135
|
+
|
136
|
+
```ruby
|
137
|
+
class MyInteractor
|
138
|
+
include Interactor
|
139
|
+
include Interactor::Contracts
|
140
|
+
|
141
|
+
config do
|
142
|
+
messages.backend = :i18n
|
143
|
+
messages.load_paths << Rails.root / 'config' / 'locales' / 'errors.yml'
|
144
|
+
messages.top_namespace = :interactor_contracts
|
145
|
+
end
|
146
|
+
end
|
147
|
+
```
|
148
|
+
|
149
|
+
This sets up the I18n system (assuming the delicate load-order has been done in
|
150
|
+
the right way - you have to require `i18n` prior to requiring
|
151
|
+
`interactor-contracts` since we load `dry-validation` immediately) to use your
|
152
|
+
custom file. All lookups for error messages happen starting at the
|
153
|
+
`interactor_contracts` key in this example.
|
154
|
+
|
155
|
+
See [the documentation for `dry-validation`][config] for more information.
|
156
|
+
|
157
|
+
[config]: https://dry-rb.org/gems/dry-validation/1.0/configuration/
|
158
|
+
|
105
159
|
## Development
|
106
160
|
|
107
161
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run
|
@@ -140,7 +194,7 @@ versions:
|
|
140
194
|
* Ruby 2.4
|
141
195
|
* Ruby 2.5
|
142
196
|
* Ruby 2.6
|
143
|
-
* JRuby 9.
|
197
|
+
* JRuby 9.2
|
144
198
|
|
145
199
|
If something doesn't work on one of these versions, it's a bug.
|
146
200
|
|
@@ -1,15 +1,30 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require File.expand_path('
|
3
|
+
require File.expand_path('lib/interactor/contracts/version', __dir__)
|
4
4
|
|
5
5
|
Gem::Specification.new do |spec|
|
6
6
|
spec.name = 'interactor-contracts'
|
7
7
|
spec.version = Interactor::Contracts::VERSION
|
8
8
|
spec.authors = ['Michael Herold']
|
9
|
-
spec.email = ['
|
9
|
+
spec.email = ['opensource@michaeljherold.com']
|
10
10
|
|
11
|
-
spec.summary = 'Add
|
12
|
-
spec.description =
|
11
|
+
spec.summary = 'Add contracts to your interactors'
|
12
|
+
spec.description = <<~DESC
|
13
|
+
Interactors are a pattern for structuring your business logic into units.
|
14
|
+
They have a flexible context that they pass between them, which makes them
|
15
|
+
easy-to-write, but hard-to-understand after you've written them. Much of
|
16
|
+
this confusion comes from not knowing what the interactor is supposed to
|
17
|
+
take as input and what it's expected to produce.
|
18
|
+
|
19
|
+
Enter contracts. Contracts allow you define, up front, a contract both for
|
20
|
+
the input of an interactor, known as expectations, and the output of it,
|
21
|
+
known as promises. Additionally, you can define a handler for what happens
|
22
|
+
when an interactor violates its contracts, known as a breach.
|
23
|
+
|
24
|
+
Declaring these contracts can help define your interface and make it easier
|
25
|
+
to understand how to use an interactor. They form both documentation and
|
26
|
+
validation for your business logic.
|
27
|
+
DESC
|
13
28
|
spec.homepage = 'https://github.com/michaelherold/interactor-contracts'
|
14
29
|
spec.license = 'MIT'
|
15
30
|
|
@@ -18,7 +33,14 @@ Gem::Specification.new do |spec|
|
|
18
33
|
spec.files += Dir['lib/**/*.rb']
|
19
34
|
spec.require_paths = ['lib']
|
20
35
|
|
21
|
-
spec.
|
36
|
+
spec.metadata = {
|
37
|
+
'bug_tracker_uri' => 'https://github.com/michaelherold/interactor-contracts/issues',
|
38
|
+
'changelog_uri' => 'https://github.com/michaelherold/interactor-contracts/blob/master/CHANGELOG.md',
|
39
|
+
'documentation_uri' => 'https://www.rubydoc.info/gems/interactor-contracts',
|
40
|
+
'source_code_uri' => 'https://github.com/michaelherold/interactor-contracts'
|
41
|
+
}
|
42
|
+
|
43
|
+
spec.add_dependency 'dry-validation', '~> 1.0'
|
22
44
|
spec.add_dependency 'interactor', '~> 3'
|
23
45
|
|
24
46
|
spec.add_development_dependency 'bundler', '> 1.11'
|
@@ -4,35 +4,35 @@ require 'interactor/contracts/terms'
|
|
4
4
|
|
5
5
|
module Interactor
|
6
6
|
module Contracts
|
7
|
-
# Contains the
|
7
|
+
# Contains the promises, expectations, and consequences of an
|
8
8
|
# interactor's contract.
|
9
9
|
class Contract
|
10
|
-
# Instantiates a new Contract with the given
|
10
|
+
# Instantiates a new Contract with the given constraints
|
11
11
|
#
|
12
12
|
# @example
|
13
13
|
# Interactor::Contracts::Contract.new
|
14
14
|
#
|
15
15
|
# @api semipublic
|
16
|
-
# @param [Terms]
|
16
|
+
# @param [Terms] promises the Contract's promises
|
17
17
|
# @param [Terms] expectations the Contract's expectations
|
18
18
|
# @param [Array<#call>] consequences the Contract's consequences
|
19
19
|
# rubocop:disable Metrics/LineLength
|
20
|
-
def initialize(
|
21
|
-
@
|
20
|
+
def initialize(promises: Terms.new, expectations: Terms.new, consequences: [])
|
21
|
+
@promises = promises
|
22
22
|
@consequences = consequences
|
23
23
|
@expectations = expectations
|
24
24
|
end
|
25
25
|
# rubocop:enable Metrics/LineLength
|
26
26
|
|
27
|
-
# The
|
27
|
+
# The promises the Contract will fulfill
|
28
28
|
#
|
29
29
|
# @example
|
30
30
|
# contract = Interactor::Contracts::Contract.new
|
31
|
-
# contract.
|
31
|
+
# contract.promises #=> <#Interactor::Contracts::Terms>
|
32
32
|
#
|
33
33
|
# @api semipublic
|
34
|
-
# @return [Terms] the terms for the
|
35
|
-
attr_reader :
|
34
|
+
# @return [Terms] the terms for the promises
|
35
|
+
attr_reader :promises
|
36
36
|
|
37
37
|
# The expectations for arguments passed into the Interactor
|
38
38
|
#
|
@@ -44,19 +44,19 @@ module Interactor
|
|
44
44
|
# @return [Terms] the terms for the expectations
|
45
45
|
attr_reader :expectations
|
46
46
|
|
47
|
-
# Adds an
|
47
|
+
# Adds an promise to the Contract's set of promises
|
48
48
|
#
|
49
49
|
# @example
|
50
50
|
# contract = Interactor::Contracts::Contract.new
|
51
|
-
# contract.
|
51
|
+
# contract.add_promise do
|
52
52
|
# required(:name).filled
|
53
53
|
# end
|
54
54
|
#
|
55
55
|
# @api semipublic
|
56
|
-
# @param [Block] term the
|
56
|
+
# @param [Block] term the promise as a block of arity 0
|
57
57
|
# @return [void]
|
58
|
-
def
|
59
|
-
|
58
|
+
def add_promise(&term)
|
59
|
+
promises.add(&term)
|
60
60
|
end
|
61
61
|
|
62
62
|
# Adds a consequence to the Contract's set of consequences
|
@@ -89,6 +89,16 @@ module Interactor
|
|
89
89
|
expectations.add(&term)
|
90
90
|
end
|
91
91
|
|
92
|
+
# Configures the underlying contracts for the validation schemata
|
93
|
+
#
|
94
|
+
# @api private
|
95
|
+
# @private
|
96
|
+
# @return [void]
|
97
|
+
def config(&block)
|
98
|
+
promises.config(&block)
|
99
|
+
expectations.config(&block)
|
100
|
+
end
|
101
|
+
|
92
102
|
# The consequences for the Contract
|
93
103
|
#
|
94
104
|
# @example
|
@@ -6,14 +6,14 @@ module Interactor
|
|
6
6
|
module Contracts
|
7
7
|
# Defines the class-level DSL that enables Interactor contracts.
|
8
8
|
module DSL
|
9
|
-
# Defines the
|
9
|
+
# Defines the promises of an Interactor and creates an after hook
|
10
10
|
#
|
11
11
|
# @example
|
12
12
|
# class CreatePerson
|
13
13
|
# include Interactor
|
14
14
|
# include Interactor::Contracts
|
15
15
|
#
|
16
|
-
#
|
16
|
+
# promises do
|
17
17
|
# required(:person).filled
|
18
18
|
# end
|
19
19
|
#
|
@@ -23,11 +23,33 @@ module Interactor
|
|
23
23
|
# end
|
24
24
|
#
|
25
25
|
# @api public
|
26
|
-
# @param [Block] block the block defining the
|
26
|
+
# @param [Block] block the block defining the promises
|
27
27
|
# @return [void]
|
28
|
-
def
|
29
|
-
contract.
|
30
|
-
|
28
|
+
def promises(&block)
|
29
|
+
contract.add_promise(&block)
|
30
|
+
define_promises_hook
|
31
|
+
end
|
32
|
+
alias assures promises
|
33
|
+
|
34
|
+
# Sends configuration set up to the underlying contracts in the terms
|
35
|
+
#
|
36
|
+
# @example
|
37
|
+
# class CreatePerson
|
38
|
+
# include Interactor
|
39
|
+
# include Interactor::Contracts
|
40
|
+
#
|
41
|
+
# config do
|
42
|
+
# messages.backend = :i18n
|
43
|
+
# messages.top_namespace = :my_app
|
44
|
+
# messages.load_paths << File.join(__dir__, '..', 'errors.yml')
|
45
|
+
# end
|
46
|
+
# end
|
47
|
+
#
|
48
|
+
# @api public
|
49
|
+
# @param [Block] block the block to execute for the underlying contracts
|
50
|
+
# @return [void]
|
51
|
+
def config(&block)
|
52
|
+
contract.config(&block)
|
31
53
|
end
|
32
54
|
|
33
55
|
# The Contract to enforce on calls to the Interactor
|
@@ -37,7 +59,7 @@ module Interactor
|
|
37
59
|
# include Interactor
|
38
60
|
# include Interactor::Contracts
|
39
61
|
#
|
40
|
-
#
|
62
|
+
# promises do
|
41
63
|
# required(:person).filled
|
42
64
|
# end
|
43
65
|
#
|
@@ -77,15 +99,18 @@ module Interactor
|
|
77
99
|
define_expectations_hook
|
78
100
|
end
|
79
101
|
|
80
|
-
#
|
81
|
-
# define_assurances_hook and define_expectations_hook
|
102
|
+
# Allows for the inheritance of contracts in subclasses
|
82
103
|
#
|
83
|
-
# @api
|
104
|
+
# @api private
|
84
105
|
# @param [Contract] contract
|
85
106
|
# @return [void]
|
86
107
|
def inherit_contract(contract)
|
87
|
-
@contract =
|
88
|
-
|
108
|
+
@contract = Contract.new(
|
109
|
+
promises: contract.promises.clone,
|
110
|
+
expectations: contract.expectations.clone,
|
111
|
+
consequences: contract.consequences.clone
|
112
|
+
)
|
113
|
+
define_promises_hook
|
89
114
|
define_expectations_hook
|
90
115
|
end
|
91
116
|
|
@@ -120,12 +145,12 @@ module Interactor
|
|
120
145
|
|
121
146
|
private
|
122
147
|
|
123
|
-
# Flags whether the
|
148
|
+
# Flags whether the promises hook has been defined
|
124
149
|
#
|
125
150
|
# @api private
|
126
151
|
# @return [TrueClass, FalseClass] true if the hook is defined
|
127
|
-
attr_reader :
|
128
|
-
alias
|
152
|
+
attr_reader :defined_promises_hook
|
153
|
+
alias defined_promises_hook? defined_promises_hook
|
129
154
|
|
130
155
|
# Flags whether the expectations hook has been defined
|
131
156
|
#
|
@@ -139,12 +164,12 @@ module Interactor
|
|
139
164
|
# @api private
|
140
165
|
# @raise [Interactor::Failure] if the input fails to meet its contract.
|
141
166
|
# @return [void]
|
142
|
-
def
|
143
|
-
return if
|
167
|
+
def define_promises_hook
|
168
|
+
return if defined_promises_hook?
|
144
169
|
|
145
|
-
after { enforce_contracts(contract.
|
170
|
+
after { enforce_contracts(contract.promises) }
|
146
171
|
|
147
|
-
@
|
172
|
+
@defined_promises_hook = true
|
148
173
|
end
|
149
174
|
|
150
175
|
# Defines a before hook that validates the Interactor's input
|
@@ -10,7 +10,7 @@ module Interactor
|
|
10
10
|
class Outcome
|
11
11
|
extend Forwardable
|
12
12
|
|
13
|
-
#
|
13
|
+
# Instantiates a new Outcome based on the results of a Terms enforcement
|
14
14
|
#
|
15
15
|
# @api private
|
16
16
|
# @param [Dry::Validation::Result] result
|
@@ -48,7 +48,7 @@ module Interactor
|
|
48
48
|
# @api semipublic
|
49
49
|
# @return [Array<Breach>] the breaches of the Terms' constraints
|
50
50
|
def breaches
|
51
|
-
BreachSet.new(result.
|
51
|
+
BreachSet.new(result.errors(full: true).to_h.map do |property, messages|
|
52
52
|
Breach.new(property, messages)
|
53
53
|
end)
|
54
54
|
end
|
@@ -1,11 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'dry-validation'
|
4
3
|
require 'interactor/contracts/outcome'
|
5
4
|
|
6
5
|
module Interactor
|
7
6
|
module Contracts
|
8
|
-
# The terms of a contract, either for
|
7
|
+
# The terms of a contract, either for promises or expectations
|
9
8
|
class Terms
|
10
9
|
# Instantiates a new set of terms
|
11
10
|
#
|
@@ -13,8 +12,8 @@ module Interactor
|
|
13
12
|
# terms = Interactor::Contracts::Terms.new
|
14
13
|
#
|
15
14
|
# @api public
|
16
|
-
# @param [Dry::Validation::
|
17
|
-
def initialize(terms = Class.new(Dry::Validation::
|
15
|
+
# @param [Dry::Validation::Contract] terms the terms to start with
|
16
|
+
def initialize(terms = Class.new(Dry::Validation::Contract))
|
18
17
|
@terms = terms
|
19
18
|
end
|
20
19
|
|
@@ -30,7 +29,13 @@ module Interactor
|
|
30
29
|
# @param [Block] term the term to add to the terms
|
31
30
|
# @return [void]
|
32
31
|
def add(&term)
|
33
|
-
@terms = Dry::Validation.
|
32
|
+
@terms = Class.new(Dry::Validation::Contract).tap do |new_terms|
|
33
|
+
new_terms.instance_variable_set(
|
34
|
+
:@config,
|
35
|
+
@terms.instance_variable_get(:@config).dup
|
36
|
+
)
|
37
|
+
new_terms.params(@terms.schema, &term)
|
38
|
+
end
|
34
39
|
end
|
35
40
|
|
36
41
|
# Validates the terms against a given context
|
@@ -46,8 +51,32 @@ module Interactor
|
|
46
51
|
# @param [#to_h] context the context to validate the terms against
|
47
52
|
# @return [Outcome]
|
48
53
|
def call(context)
|
54
|
+
define_empty_schema
|
55
|
+
|
49
56
|
Outcome.new(@terms.new.call(context.to_h))
|
50
57
|
end
|
58
|
+
|
59
|
+
# Configures the underlying contracts within the terms
|
60
|
+
#
|
61
|
+
# @api private
|
62
|
+
# @private
|
63
|
+
# @return [void]
|
64
|
+
def config(&block)
|
65
|
+
@terms.config.instance_exec(&block)
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
# Defines a no-op schema as a base to extend upon
|
71
|
+
#
|
72
|
+
# This prevents the raising of a `Dry::Validation::SchemaMissingError`
|
73
|
+
# exception.
|
74
|
+
#
|
75
|
+
# @api private
|
76
|
+
# @return [void]
|
77
|
+
def define_empty_schema
|
78
|
+
@terms.params.nil? && @terms.params {}
|
79
|
+
end
|
51
80
|
end
|
52
81
|
end
|
53
82
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: interactor-contracts
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Michael Herold
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-10-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: dry-validation
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '0
|
19
|
+
version: '1.0'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '0
|
26
|
+
version: '1.0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: interactor
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -52,9 +52,23 @@ dependencies:
|
|
52
52
|
- - ">"
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '1.11'
|
55
|
-
description:
|
55
|
+
description: |
|
56
|
+
Interactors are a pattern for structuring your business logic into units.
|
57
|
+
They have a flexible context that they pass between them, which makes them
|
58
|
+
easy-to-write, but hard-to-understand after you've written them. Much of
|
59
|
+
this confusion comes from not knowing what the interactor is supposed to
|
60
|
+
take as input and what it's expected to produce.
|
61
|
+
|
62
|
+
Enter contracts. Contracts allow you define, up front, a contract both for
|
63
|
+
the input of an interactor, known as expectations, and the output of it,
|
64
|
+
known as promises. Additionally, you can define a handler for what happens
|
65
|
+
when an interactor violates its contracts, known as a breach.
|
66
|
+
|
67
|
+
Declaring these contracts can help define your interface and make it easier
|
68
|
+
to understand how to use an interactor. They form both documentation and
|
69
|
+
validation for your business logic.
|
56
70
|
email:
|
57
|
-
-
|
71
|
+
- opensource@michaeljherold.com
|
58
72
|
executables: []
|
59
73
|
extensions: []
|
60
74
|
extra_rdoc_files: []
|
@@ -77,7 +91,11 @@ files:
|
|
77
91
|
homepage: https://github.com/michaelherold/interactor-contracts
|
78
92
|
licenses:
|
79
93
|
- MIT
|
80
|
-
metadata:
|
94
|
+
metadata:
|
95
|
+
bug_tracker_uri: https://github.com/michaelherold/interactor-contracts/issues
|
96
|
+
changelog_uri: https://github.com/michaelherold/interactor-contracts/blob/master/CHANGELOG.md
|
97
|
+
documentation_uri: https://www.rubydoc.info/gems/interactor-contracts
|
98
|
+
source_code_uri: https://github.com/michaelherold/interactor-contracts
|
81
99
|
post_install_message:
|
82
100
|
rdoc_options: []
|
83
101
|
require_paths:
|
@@ -93,8 +111,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
93
111
|
- !ruby/object:Gem::Version
|
94
112
|
version: '0'
|
95
113
|
requirements: []
|
96
|
-
rubygems_version: 3.0.
|
114
|
+
rubygems_version: 3.0.6
|
97
115
|
signing_key:
|
98
116
|
specification_version: 4
|
99
|
-
summary: Add
|
117
|
+
summary: Add contracts to your interactors
|
100
118
|
test_files: []
|