use_cases 0.2.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/main.yml +23 -0
  3. data/.gitignore +12 -0
  4. data/.rspec +3 -0
  5. data/.rubocop.yml +19 -0
  6. data/.rubocop_todo.yml +28 -0
  7. data/CHANGELOG.md +20 -0
  8. data/CODE_OF_CONDUCT.md +84 -0
  9. data/Gemfile +20 -0
  10. data/Gemfile.lock +129 -0
  11. data/LICENSE.txt +21 -0
  12. data/README.md +265 -0
  13. data/Rakefile +22 -0
  14. data/bin/console +15 -0
  15. data/bin/setup +8 -0
  16. data/lib/use_case.rb +57 -0
  17. data/lib/use_cases/authorize.rb +20 -0
  18. data/lib/use_cases/base.rb +8 -0
  19. data/lib/use_cases/dsl.rb +35 -0
  20. data/lib/use_cases/errors.rb +9 -0
  21. data/lib/use_cases/module_optins.rb +43 -0
  22. data/lib/use_cases/notifications.rb +51 -0
  23. data/lib/use_cases/params.rb +15 -0
  24. data/lib/use_cases/prepare.rb +19 -0
  25. data/lib/use_cases/rspec/matchers.rb +31 -0
  26. data/lib/use_cases/stack.rb +51 -0
  27. data/lib/use_cases/stack_runner.rb +60 -0
  28. data/lib/use_cases/step_active_job_adapter.rb +34 -0
  29. data/lib/use_cases/step_adapters/abstract.rb +99 -0
  30. data/lib/use_cases/step_adapters/authorize.rb +22 -0
  31. data/lib/use_cases/step_adapters/check.rb +22 -0
  32. data/lib/use_cases/step_adapters/enqueue.rb +18 -0
  33. data/lib/use_cases/step_adapters/map.rb +18 -0
  34. data/lib/use_cases/step_adapters/step.rb +18 -0
  35. data/lib/use_cases/step_adapters/tee.rb +20 -0
  36. data/lib/use_cases/step_adapters/try.rb +20 -0
  37. data/lib/use_cases/step_adapters.rb +25 -0
  38. data/lib/use_cases/step_result.rb +55 -0
  39. data/lib/use_cases/transaction.rb +25 -0
  40. data/lib/use_cases/validate.rb +104 -0
  41. data/lib/use_cases/version.rb +5 -0
  42. data/lib/use_cases.rb +7 -0
  43. data/use_cases.gemspec +42 -0
  44. 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
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module UseCases
4
+ VERSION = "0.2.5"
5
+ end
data/lib/use_cases.rb ADDED
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "use_case"
4
+ require_relative "use_cases/version"
5
+ require_relative "use_cases/base"
6
+
7
+ module UseCases; end
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: []