use_cases 0.2.5
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 +7 -0
- data/.github/workflows/main.yml +23 -0
- data/.gitignore +12 -0
- data/.rspec +3 -0
- data/.rubocop.yml +19 -0
- data/.rubocop_todo.yml +28 -0
- data/CHANGELOG.md +20 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/Gemfile +20 -0
- data/Gemfile.lock +129 -0
- data/LICENSE.txt +21 -0
- data/README.md +265 -0
- data/Rakefile +22 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/lib/use_case.rb +57 -0
- data/lib/use_cases/authorize.rb +20 -0
- data/lib/use_cases/base.rb +8 -0
- data/lib/use_cases/dsl.rb +35 -0
- data/lib/use_cases/errors.rb +9 -0
- data/lib/use_cases/module_optins.rb +43 -0
- data/lib/use_cases/notifications.rb +51 -0
- data/lib/use_cases/params.rb +15 -0
- data/lib/use_cases/prepare.rb +19 -0
- data/lib/use_cases/rspec/matchers.rb +31 -0
- data/lib/use_cases/stack.rb +51 -0
- data/lib/use_cases/stack_runner.rb +60 -0
- data/lib/use_cases/step_active_job_adapter.rb +34 -0
- data/lib/use_cases/step_adapters/abstract.rb +99 -0
- data/lib/use_cases/step_adapters/authorize.rb +22 -0
- data/lib/use_cases/step_adapters/check.rb +22 -0
- data/lib/use_cases/step_adapters/enqueue.rb +18 -0
- data/lib/use_cases/step_adapters/map.rb +18 -0
- data/lib/use_cases/step_adapters/step.rb +18 -0
- data/lib/use_cases/step_adapters/tee.rb +20 -0
- data/lib/use_cases/step_adapters/try.rb +20 -0
- data/lib/use_cases/step_adapters.rb +25 -0
- data/lib/use_cases/step_result.rb +55 -0
- data/lib/use_cases/transaction.rb +25 -0
- data/lib/use_cases/validate.rb +104 -0
- data/lib/use_cases/version.rb +5 -0
- data/lib/use_cases.rb +7 -0
- data/use_cases.gemspec +42 -0
- metadata +200 -0
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "use_cases/step_adapters/step"
|
4
|
+
require "use_cases/step_adapters/map"
|
5
|
+
require "use_cases/step_adapters/tee"
|
6
|
+
require "use_cases/step_adapters/try"
|
7
|
+
require "use_cases/step_adapters/check"
|
8
|
+
require "use_cases/step_adapters/enqueue"
|
9
|
+
require "use_cases/step_active_job_adapter"
|
10
|
+
|
11
|
+
module UseCases
|
12
|
+
module StepAdapters
|
13
|
+
def self.included(base)
|
14
|
+
base.class_eval do
|
15
|
+
register_adapter StepAdapters::Step
|
16
|
+
register_adapter StepAdapters::Tee
|
17
|
+
register_adapter StepAdapters::Try
|
18
|
+
register_adapter StepAdapters::Map
|
19
|
+
register_adapter StepAdapters::Check
|
20
|
+
|
21
|
+
register_adapter StepAdapters::Enqueue if defined? ActiveJob
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module UseCases
|
4
|
+
class StepResult < Dry::Monads::Result
|
5
|
+
attr_reader :step, :result
|
6
|
+
|
7
|
+
def initialize(step, result)
|
8
|
+
super()
|
9
|
+
@step = step
|
10
|
+
@result = result
|
11
|
+
end
|
12
|
+
|
13
|
+
def value
|
14
|
+
return result if result_not_monad?
|
15
|
+
return nil if result_empty?
|
16
|
+
|
17
|
+
result.success? ? result.value! : result
|
18
|
+
end
|
19
|
+
alias value! value
|
20
|
+
|
21
|
+
def success?
|
22
|
+
!failure?
|
23
|
+
end
|
24
|
+
|
25
|
+
def failure?
|
26
|
+
value.is_a?(Dry::Monads::Result::Failure)
|
27
|
+
end
|
28
|
+
|
29
|
+
def failure
|
30
|
+
failure? && value.failure
|
31
|
+
end
|
32
|
+
|
33
|
+
def success
|
34
|
+
success? && value
|
35
|
+
end
|
36
|
+
|
37
|
+
def nil?
|
38
|
+
value.nil?
|
39
|
+
end
|
40
|
+
|
41
|
+
def to_result
|
42
|
+
value.to_result if failure?
|
43
|
+
|
44
|
+
result
|
45
|
+
end
|
46
|
+
|
47
|
+
def result_empty?
|
48
|
+
result.success? && result.value! == Dry::Monads::Unit
|
49
|
+
end
|
50
|
+
|
51
|
+
def result_not_monad?
|
52
|
+
!result.is_a?(Dry::Monads::Result)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module UseCases
|
4
|
+
module Transaction
|
5
|
+
class TransactionHandlerUndefined < StandardError; end
|
6
|
+
|
7
|
+
class TransactionHandlerInvalid < StandardError; end
|
8
|
+
|
9
|
+
def self.included(base)
|
10
|
+
base.prepend DoCallPatch
|
11
|
+
end
|
12
|
+
|
13
|
+
module DoCallPatch
|
14
|
+
def do_call(*args)
|
15
|
+
unless respond_to?(:transaction_handler)
|
16
|
+
raise TransactionHandlerUndefined, "when using *transactional*, make sure to include a transaction handler in your dependencies."
|
17
|
+
end
|
18
|
+
|
19
|
+
raise TransactionHandlerInvalid, "Make sure your transaction_handler implements #transaction." unless transaction_handler.respond_to?(:transaction)
|
20
|
+
|
21
|
+
transaction_handler.transaction { super }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/validation/contract"
|
4
|
+
|
5
|
+
module UseCases
|
6
|
+
module Validate
|
7
|
+
class NoValidationError < StandardError; end
|
8
|
+
|
9
|
+
def self.included(base)
|
10
|
+
base.class_eval do
|
11
|
+
extend DSL
|
12
|
+
extend ClassMethods
|
13
|
+
prepend CallPatch
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
module CallPatch
|
18
|
+
def call(*args)
|
19
|
+
unless stack.include_step?(:validate)
|
20
|
+
raise NoValidationError,
|
21
|
+
"Make sure to define params validations by using *params*" \
|
22
|
+
"*schema*, *json*, *rule* or *option* macros in your use case."
|
23
|
+
end
|
24
|
+
|
25
|
+
super
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
module DSL
|
30
|
+
def params(*args, &blk)
|
31
|
+
_setup_validation
|
32
|
+
|
33
|
+
_contract_class.params(*args, &blk)
|
34
|
+
end
|
35
|
+
|
36
|
+
def schema(*args, &blk)
|
37
|
+
_setup_validation
|
38
|
+
|
39
|
+
_contract_class.schema(*args, &blk)
|
40
|
+
end
|
41
|
+
|
42
|
+
def rule(*args, &blk)
|
43
|
+
_setup_validation
|
44
|
+
|
45
|
+
_contract_class.rule(*args, &blk)
|
46
|
+
end
|
47
|
+
|
48
|
+
def json(*args, &blk)
|
49
|
+
_setup_validation
|
50
|
+
|
51
|
+
_contract_class.json(*args, &blk)
|
52
|
+
end
|
53
|
+
|
54
|
+
def option(*args, &blk)
|
55
|
+
_contract_class.option(*args, &blk)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def validate(params, current_user)
|
62
|
+
return Failure([:validation_error, "*params* must be a hash."]) unless params.respond_to?(:merge)
|
63
|
+
|
64
|
+
validation = contract.call(params.merge(current_user: current_user))
|
65
|
+
|
66
|
+
validation.success? ? Success(validation.to_h) : Failure([:validation_error, validation.errors.to_h])
|
67
|
+
end
|
68
|
+
|
69
|
+
def contract
|
70
|
+
return self.class._contract_class.new if self.class._contract_class_defined?
|
71
|
+
end
|
72
|
+
|
73
|
+
module ClassMethods
|
74
|
+
def _setup_validation
|
75
|
+
_define_contract_class unless _contract_class_defined?
|
76
|
+
_define_validation_step unless _validation_step_defined?
|
77
|
+
end
|
78
|
+
|
79
|
+
def _define_validation_step
|
80
|
+
step :validate
|
81
|
+
end
|
82
|
+
|
83
|
+
def _contract_class
|
84
|
+
self::Contract
|
85
|
+
end
|
86
|
+
|
87
|
+
def _define_contract_class
|
88
|
+
const_set(:Contract, Class.new(Dry::Validation::Contract))
|
89
|
+
end
|
90
|
+
|
91
|
+
def _contract_class_name
|
92
|
+
"#{name}::Contract"
|
93
|
+
end
|
94
|
+
|
95
|
+
def _contract_class_defined?
|
96
|
+
Object.const_defined? _contract_class_name
|
97
|
+
end
|
98
|
+
|
99
|
+
def _validation_step_defined?
|
100
|
+
__steps__.map(&:name).include?(:validate)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
data/lib/use_cases.rb
ADDED
data/use_cases.gemspec
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "lib/use_cases/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "use_cases"
|
7
|
+
spec.version = UseCases::VERSION
|
8
|
+
spec.authors = ["Ring Twice"]
|
9
|
+
spec.email = ["guilherme@listminut.com"]
|
10
|
+
|
11
|
+
spec.summary = "Use Cases"
|
12
|
+
spec.description = "A DSL to encapsulate your domain logic."
|
13
|
+
spec.homepage = "https://github.com/listminut/use_cases"
|
14
|
+
spec.license = "MIT"
|
15
|
+
spec.required_ruby_version = ">= 2.6.8"
|
16
|
+
|
17
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
18
|
+
spec.metadata["source_code_uri"] = "https://github.com/listminut/use_cases"
|
19
|
+
spec.metadata["changelog_uri"] = "https://github.com/listminut/use_cases/CHANGELOG.md"
|
20
|
+
|
21
|
+
# Specify which files should be added to the gem when it is released.
|
22
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
23
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
24
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
|
25
|
+
end
|
26
|
+
spec.bindir = "exe"
|
27
|
+
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
28
|
+
spec.require_paths = ["lib"]
|
29
|
+
|
30
|
+
# For more information and examples about making a new gem, checkout our
|
31
|
+
# guide at: https://bundler.io/guides/creating_gem.html
|
32
|
+
|
33
|
+
spec.add_dependency "activesupport"
|
34
|
+
spec.add_dependency "dry-events"
|
35
|
+
spec.add_dependency "dry-matcher"
|
36
|
+
spec.add_dependency "dry-monads"
|
37
|
+
spec.add_dependency "dry-validation"
|
38
|
+
|
39
|
+
spec.add_development_dependency "rake"
|
40
|
+
spec.add_development_dependency "rspec"
|
41
|
+
spec.add_development_dependency "rubocop"
|
42
|
+
end
|
metadata
ADDED
@@ -0,0 +1,200 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: use_cases
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.5
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Ring Twice
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2021-10-20 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activesupport
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: dry-events
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: dry-matcher
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: dry-monads
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: dry-validation
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rake
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rspec
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: rubocop
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
description: A DSL to encapsulate your domain logic.
|
126
|
+
email:
|
127
|
+
- guilherme@listminut.com
|
128
|
+
executables: []
|
129
|
+
extensions: []
|
130
|
+
extra_rdoc_files: []
|
131
|
+
files:
|
132
|
+
- ".github/workflows/main.yml"
|
133
|
+
- ".gitignore"
|
134
|
+
- ".rspec"
|
135
|
+
- ".rubocop.yml"
|
136
|
+
- ".rubocop_todo.yml"
|
137
|
+
- CHANGELOG.md
|
138
|
+
- CODE_OF_CONDUCT.md
|
139
|
+
- Gemfile
|
140
|
+
- Gemfile.lock
|
141
|
+
- LICENSE.txt
|
142
|
+
- README.md
|
143
|
+
- Rakefile
|
144
|
+
- bin/console
|
145
|
+
- bin/setup
|
146
|
+
- lib/use_case.rb
|
147
|
+
- lib/use_cases.rb
|
148
|
+
- lib/use_cases/authorize.rb
|
149
|
+
- lib/use_cases/base.rb
|
150
|
+
- lib/use_cases/dsl.rb
|
151
|
+
- lib/use_cases/errors.rb
|
152
|
+
- lib/use_cases/module_optins.rb
|
153
|
+
- lib/use_cases/notifications.rb
|
154
|
+
- lib/use_cases/params.rb
|
155
|
+
- lib/use_cases/prepare.rb
|
156
|
+
- lib/use_cases/rspec/matchers.rb
|
157
|
+
- lib/use_cases/stack.rb
|
158
|
+
- lib/use_cases/stack_runner.rb
|
159
|
+
- lib/use_cases/step_active_job_adapter.rb
|
160
|
+
- lib/use_cases/step_adapters.rb
|
161
|
+
- lib/use_cases/step_adapters/abstract.rb
|
162
|
+
- lib/use_cases/step_adapters/authorize.rb
|
163
|
+
- lib/use_cases/step_adapters/check.rb
|
164
|
+
- lib/use_cases/step_adapters/enqueue.rb
|
165
|
+
- lib/use_cases/step_adapters/map.rb
|
166
|
+
- lib/use_cases/step_adapters/step.rb
|
167
|
+
- lib/use_cases/step_adapters/tee.rb
|
168
|
+
- lib/use_cases/step_adapters/try.rb
|
169
|
+
- lib/use_cases/step_result.rb
|
170
|
+
- lib/use_cases/transaction.rb
|
171
|
+
- lib/use_cases/validate.rb
|
172
|
+
- lib/use_cases/version.rb
|
173
|
+
- use_cases.gemspec
|
174
|
+
homepage: https://github.com/listminut/use_cases
|
175
|
+
licenses:
|
176
|
+
- MIT
|
177
|
+
metadata:
|
178
|
+
homepage_uri: https://github.com/listminut/use_cases
|
179
|
+
source_code_uri: https://github.com/listminut/use_cases
|
180
|
+
changelog_uri: https://github.com/listminut/use_cases/CHANGELOG.md
|
181
|
+
post_install_message:
|
182
|
+
rdoc_options: []
|
183
|
+
require_paths:
|
184
|
+
- lib
|
185
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
186
|
+
requirements:
|
187
|
+
- - ">="
|
188
|
+
- !ruby/object:Gem::Version
|
189
|
+
version: 2.6.8
|
190
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
191
|
+
requirements:
|
192
|
+
- - ">="
|
193
|
+
- !ruby/object:Gem::Version
|
194
|
+
version: '0'
|
195
|
+
requirements: []
|
196
|
+
rubygems_version: 3.0.3.1
|
197
|
+
signing_key:
|
198
|
+
specification_version: 4
|
199
|
+
summary: Use Cases
|
200
|
+
test_files: []
|