clean-architecture 5.0.2 → 6.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +15 -19
- data/.gitignore +4 -0
- data/CHANGELOG.md +4 -0
- data/Gemfile +1 -0
- data/README.md +1 -585
- data/bin/tapioca +29 -0
- data/clean-architecture.gemspec +4 -4
- data/lib/clean-architecture.rb +4 -1
- data/lib/clean_architecture/adapters/all.rb +1 -1
- data/lib/clean_architecture/adapters/attribute_hash_base.rb +47 -20
- data/lib/clean_architecture/all.rb +1 -4
- data/lib/clean_architecture/builders/abstract_active_record_entity_builder.rb +43 -13
- data/lib/clean_architecture/builders/all.rb +1 -1
- data/lib/clean_architecture/checks/all.rb +1 -1
- data/lib/clean_architecture/checks/authorization.rb +11 -9
- data/lib/clean_architecture/entities/all.rb +1 -3
- data/lib/clean_architecture/entities/failure_details.rb +27 -17
- data/lib/clean_architecture/matchers/all.rb +1 -1
- data/lib/clean_architecture/matchers/use_case_result.rb +9 -3
- data/lib/clean_architecture/queries/all.rb +1 -1
- data/lib/clean_architecture/queries/http_failure_code.rb +8 -20
- data/lib/clean_architecture/queries/http_success_code.rb +14 -7
- data/lib/clean_architecture/serializers/all.rb +1 -1
- data/lib/clean_architecture/serializers/html_response_from_result.rb +7 -1
- data/lib/clean_architecture/serializers/json_response_from_result.rb +4 -4
- data/lib/clean_architecture/version.rb +1 -1
- data/nix/sources.json +14 -0
- data/nix/sources.nix +174 -0
- data/run_ci.sh +7 -0
- data/shell.nix +17 -12
- data/sorbet/config +4 -0
- data/sorbet/{rbi/gems → dry-monads-sorbet}/dry-monads.rbi +176 -94
- data/sorbet/rbi/gems/activemodel@6.1.4.1.rbi +1292 -0
- data/sorbet/rbi/gems/activerecord@6.1.4.1.rbi +8092 -0
- data/sorbet/rbi/gems/activesupport@6.1.4.1.rbi +3531 -0
- data/sorbet/rbi/gems/{ast.rbi → ast@2.4.2.rbi} +28 -22
- data/sorbet/rbi/gems/byebug@11.1.3.rbi +1568 -0
- data/sorbet/rbi/gems/coderay@1.1.3.rbi +1005 -0
- data/sorbet/rbi/gems/concurrent-ruby@1.1.9.rbi +915 -0
- data/sorbet/rbi/gems/diff-lcs@1.4.4.rbi +8 -0
- data/sorbet/rbi/gems/docile@1.4.0.rbi +54 -0
- data/sorbet/rbi/gems/dry-core@0.7.1.rbi +92 -0
- data/sorbet/rbi/gems/dry-equalizer@0.3.0.rbi +28 -0
- data/sorbet/rbi/gems/dry-matcher@0.9.0.rbi +56 -0
- data/sorbet/rbi/gems/dry-monads-sorbet@1.1.7.rbi +41 -0
- data/sorbet/rbi/gems/dry-monads@1.4.0.rbi +697 -0
- data/sorbet/rbi/gems/em-websocket@0.5.2.rbi +8 -0
- data/sorbet/rbi/gems/eventmachine@1.2.7.rbi +45 -0
- data/sorbet/rbi/gems/ffi@1.15.4.rbi +8 -0
- data/sorbet/rbi/gems/formatador@0.3.0.rbi +8 -0
- data/sorbet/rbi/gems/guard-compat@1.2.1.rbi +31 -0
- data/sorbet/rbi/gems/guard-livereload@2.5.2.rbi +8 -0
- data/sorbet/rbi/gems/guard-rspec@4.7.3.rbi +211 -0
- data/sorbet/rbi/gems/guard@2.18.0.rbi +8 -0
- data/sorbet/rbi/gems/http_parser.rb@0.6.0.rbi +8 -0
- data/sorbet/rbi/gems/i18n@1.8.10.rbi +8 -0
- data/sorbet/rbi/gems/listen@3.7.0.rbi +8 -0
- data/sorbet/rbi/gems/lumberjack@1.2.8.rbi +8 -0
- data/sorbet/rbi/gems/method_source@1.0.0.rbi +72 -0
- data/sorbet/rbi/gems/minitest@5.14.4.rbi +344 -0
- data/sorbet/rbi/gems/multi_json@1.15.0.rbi +8 -0
- data/sorbet/rbi/gems/nenv@0.3.0.rbi +8 -0
- data/sorbet/rbi/gems/notiffany@0.1.3.rbi +8 -0
- data/sorbet/rbi/gems/parallel@1.21.0.rbi +113 -0
- data/sorbet/rbi/gems/{parser.rbi → parser@3.0.2.0.rbi} +966 -699
- data/sorbet/rbi/gems/pry-byebug@3.9.0.rbi +461 -0
- data/sorbet/rbi/gems/{pry.rbi → pry@0.13.1.rbi} +2191 -1605
- data/sorbet/rbi/gems/{rainbow.rbi → rainbow@3.0.0.rbi} +90 -55
- data/sorbet/rbi/gems/{rake.rbi → rake@13.0.6.rbi} +578 -427
- data/sorbet/rbi/gems/rb-fsevent@0.11.0.rbi +8 -0
- data/sorbet/rbi/gems/rb-inotify@0.10.1.rbi +8 -0
- data/sorbet/rbi/gems/rb-readline@0.5.5.rbi +884 -0
- data/sorbet/rbi/gems/rbi@0.0.6.rbi +1405 -0
- data/sorbet/rbi/gems/regexp_parser@2.1.1.rbi +1120 -0
- data/sorbet/rbi/gems/{rexml.rbi → rexml@3.2.5.rbi} +562 -479
- data/sorbet/rbi/gems/{rspec-core.rbi → rspec-core@3.10.1.rbi} +2317 -1533
- data/sorbet/rbi/gems/rspec-expectations@3.10.1.rbi +1574 -0
- data/sorbet/rbi/gems/rspec-mocks@3.10.2.rbi +1462 -0
- data/sorbet/rbi/gems/rspec-support@3.10.2.rbi +509 -0
- data/sorbet/rbi/gems/rspec@3.10.0.rbi +38 -0
- data/sorbet/rbi/gems/rubocop-ast@1.12.0.rbi +1938 -0
- data/sorbet/rbi/gems/rubocop-rspec@2.5.0.rbi +1786 -0
- data/sorbet/rbi/gems/rubocop@1.22.1.rbi +13252 -0
- data/sorbet/rbi/gems/ruby-progressbar@1.11.0.rbi +405 -0
- data/sorbet/rbi/gems/shellany@0.0.1.rbi +8 -0
- data/sorbet/rbi/gems/simplecov-html@0.12.3.rbi +89 -0
- data/sorbet/rbi/gems/simplecov@0.21.2.rbi +577 -0
- data/sorbet/rbi/gems/simplecov_json_formatter@0.1.3.rbi +8 -0
- data/sorbet/rbi/gems/sorbet-struct-comparable@1.1.0.rbi +17 -0
- data/sorbet/rbi/gems/spoom@1.1.5.rbi +1241 -0
- data/sorbet/rbi/gems/stackprof@0.2.17.rbi +98 -0
- data/sorbet/rbi/gems/tapioca@0.5.2.rbi +949 -0
- data/sorbet/rbi/gems/thor@1.1.0.rbi +839 -0
- data/sorbet/rbi/gems/tzinfo@2.0.4.rbi +8 -0
- data/sorbet/rbi/gems/unicode-display_width@2.1.0.rbi +26 -0
- data/sorbet/rbi/gems/unparser@0.6.0.rbi +8 -0
- data/sorbet/rbi/gems/zeitwerk@2.4.2.rbi +8 -0
- data/sorbet/tapioca/require.rb +9 -0
- metadata +88 -91
- data/.ruby-version +0 -1
- data/lib/clean_architecture/entities/targeted_parameters.rb +0 -24
- data/lib/clean_architecture/entities/untargeted_parameters.rb +0 -21
- data/lib/clean_architecture/interfaces/all.rb +0 -12
- data/lib/clean_architecture/interfaces/authorization_parameters.rb +0 -19
- data/lib/clean_architecture/interfaces/base_parameters.rb +0 -24
- data/lib/clean_architecture/interfaces/jsonable.rb +0 -16
- data/lib/clean_architecture/interfaces/targeted_parameters.rb +0 -19
- data/lib/clean_architecture/interfaces/use_case.rb +0 -20
- data/lib/clean_architecture/interfaces/use_case_actor.rb +0 -20
- data/lib/clean_architecture/interfaces/use_case_target.rb +0 -24
- data/lib/clean_architecture/types.rb +0 -8
- data/lib/clean_architecture/use_cases/abstract_use_case.rb +0 -63
- data/lib/clean_architecture/use_cases/all.rb +0 -10
- data/lib/clean_architecture/use_cases/contract.rb +0 -9
- data/lib/clean_architecture/use_cases/errors.rb +0 -58
- data/lib/clean_architecture/use_cases/form.rb +0 -116
- data/lib/clean_architecture/use_cases/parameters.rb +0 -43
- data/sorbet/rbi/gems/activemodel.rbi +0 -75
- data/sorbet/rbi/gems/activesupport.rbi +0 -440
- data/sorbet/rbi/gems/byebug.rbi +0 -1040
- data/sorbet/rbi/gems/coderay.rbi +0 -92
- data/sorbet/rbi/gems/concurrent-ruby.rbi +0 -1586
- data/sorbet/rbi/gems/docile.rbi +0 -32
- data/sorbet/rbi/gems/dry-configurable.rbi +0 -139
- data/sorbet/rbi/gems/dry-container.rbi +0 -89
- data/sorbet/rbi/gems/dry-core.rbi +0 -80
- data/sorbet/rbi/gems/dry-equalizer.rbi +0 -26
- data/sorbet/rbi/gems/dry-inflector.rbi +0 -73
- data/sorbet/rbi/gems/dry-initializer.rbi +0 -209
- data/sorbet/rbi/gems/dry-logic.rbi +0 -305
- data/sorbet/rbi/gems/dry-matcher.rbi +0 -34
- data/sorbet/rbi/gems/dry-schema.rbi +0 -786
- data/sorbet/rbi/gems/dry-struct.rbi +0 -137
- data/sorbet/rbi/gems/dry-types.rbi +0 -709
- data/sorbet/rbi/gems/dry-validation.rbi +0 -288
- data/sorbet/rbi/gems/duckface-interfaces.rbi +0 -94
- data/sorbet/rbi/gems/i18n.rbi +0 -133
- data/sorbet/rbi/gems/jaro_winkler.rbi +0 -15
- data/sorbet/rbi/gems/method_source.rbi +0 -64
- data/sorbet/rbi/gems/parallel.rbi +0 -82
- data/sorbet/rbi/gems/pry-byebug.rbi +0 -155
- data/sorbet/rbi/gems/rb-readline.rbi +0 -767
- data/sorbet/rbi/gems/rspec-expectations.rbi +0 -398
- data/sorbet/rbi/gems/rspec-mocks.rbi +0 -816
- data/sorbet/rbi/gems/rspec-support.rbi +0 -271
- data/sorbet/rbi/gems/rspec.rbi +0 -15
- data/sorbet/rbi/gems/rubocop-rspec.rbi +0 -922
- data/sorbet/rbi/gems/rubocop.rbi +0 -7319
- data/sorbet/rbi/gems/ruby-progressbar.rbi +0 -305
- data/sorbet/rbi/gems/simplecov-html.rbi +0 -35
- data/sorbet/rbi/gems/simplecov.rbi +0 -361
- data/sorbet/rbi/gems/stackprof.rbi +0 -52
- data/sorbet/rbi/gems/unicode-display_width.rbi +0 -17
- data/sorbet/rbi/hidden-definitions/errors.txt +0 -8580
- data/sorbet/rbi/hidden-definitions/hidden.rbi +0 -17036
- data/sorbet/rbi/sorbet-typed/lib/activemodel/all/activemodel.rbi +0 -452
- data/sorbet/rbi/sorbet-typed/lib/activesupport/>=6.0.0.rc1/activesupport.rbi +0 -23
- data/sorbet/rbi/sorbet-typed/lib/activesupport/all/activesupport.rbi +0 -979
- data/sorbet/rbi/sorbet-typed/lib/bundler/all/bundler.rbi +0 -8684
- data/sorbet/rbi/sorbet-typed/lib/minitest/all/minitest.rbi +0 -108
- data/sorbet/rbi/sorbet-typed/lib/rainbow/all/rainbow.rbi +0 -276
- data/sorbet/rbi/sorbet-typed/lib/ruby/all/gem.rbi +0 -4222
- data/sorbet/rbi/sorbet-typed/lib/ruby/all/open3.rbi +0 -111
- data/sorbet/rbi/sorbet-typed/lib/ruby/all/resolv.rbi +0 -543
- data/sorbet/rbi/todo.rbi +0 -10
data/bin/tapioca
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
#
|
5
|
+
# This file was generated by Bundler.
|
6
|
+
#
|
7
|
+
# The application 'tapioca' is installed as part of a gem, and
|
8
|
+
# this file is here to facilitate running it.
|
9
|
+
#
|
10
|
+
|
11
|
+
require "pathname"
|
12
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
13
|
+
Pathname.new(__FILE__).realpath)
|
14
|
+
|
15
|
+
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
16
|
+
|
17
|
+
if File.file?(bundle_binstub)
|
18
|
+
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
19
|
+
load(bundle_binstub)
|
20
|
+
else
|
21
|
+
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
22
|
+
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
require "rubygems"
|
27
|
+
require "bundler/setup"
|
28
|
+
|
29
|
+
load Gem.bin_path("tapioca", "tapioca")
|
data/clean-architecture.gemspec
CHANGED
@@ -28,13 +28,13 @@ Gem::Specification.new do |spec|
|
|
28
28
|
|
29
29
|
spec.add_dependency 'activemodel', '>= 5'
|
30
30
|
spec.add_dependency 'activesupport', '>= 5'
|
31
|
+
spec.add_dependency 'activerecord', '>= 5'
|
31
32
|
spec.add_dependency 'dry-matcher'
|
32
33
|
spec.add_dependency 'dry-monads'
|
33
|
-
spec.add_dependency 'dry-
|
34
|
-
spec.add_dependency '
|
35
|
-
spec.add_dependency 'dry-validation', '>= 1.0.0'
|
36
|
-
spec.add_dependency 'duckface-interfaces'
|
34
|
+
spec.add_dependency 'dry-monads-sorbet'
|
35
|
+
spec.add_dependency 'sorbet'
|
37
36
|
spec.add_dependency 'sorbet-runtime'
|
37
|
+
spec.add_dependency 'sorbet-struct-comparable'
|
38
38
|
|
39
39
|
spec.add_development_dependency 'bundler'
|
40
40
|
spec.add_development_dependency 'rake', '>= 12.0'
|
data/lib/clean-architecture.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require 'time'
|
@@ -12,114 +12,141 @@ module CleanArchitecture
|
|
12
12
|
# Error raised if an expected value isn't present or isn't the type we expect
|
13
13
|
class EmptyOrIncorrectAttributeTypeError < StandardError; end
|
14
14
|
|
15
|
+
extend T::Sig
|
16
|
+
|
17
|
+
sig { params(attribute_hash: T::Hash[Object, Object]).void }
|
15
18
|
def initialize(attribute_hash)
|
16
19
|
@attribute_hash = attribute_hash
|
17
20
|
end
|
18
21
|
|
19
22
|
protected
|
20
23
|
|
24
|
+
sig { returns(T::Hash[Object, Object]) }
|
21
25
|
attr_reader :attribute_hash
|
22
26
|
|
23
27
|
private
|
24
28
|
|
25
|
-
|
26
|
-
|
29
|
+
sig { params(default: Object, block: T.proc.returns(T.nilable(Object))).returns(Object) }
|
30
|
+
def ensured_value(default:, &block)
|
31
|
+
block.call || default
|
27
32
|
rescue EmptyOrIncorrectAttributeTypeError, TypeError, ArgumentError
|
28
33
|
default
|
29
34
|
end
|
30
35
|
|
31
|
-
|
32
|
-
|
36
|
+
sig { params(block: T.proc.returns(T.nilable(Object))).returns(T.nilable(Object)) }
|
37
|
+
def maybe_value(&block)
|
38
|
+
ensured_value(default: nil) { block.call }
|
33
39
|
end
|
34
40
|
|
41
|
+
sig { params(key: Object).returns(T::Boolean) }
|
35
42
|
def boolean_value(key)
|
36
43
|
raw_value(key).to_s == 'true'
|
37
44
|
end
|
38
45
|
|
46
|
+
sig { params(key: Object, adapter_class: Class).returns(Object) }
|
39
47
|
def complex_object_value(key, adapter_class)
|
40
|
-
adapter_class.new(raw_value(key))
|
48
|
+
T.unsafe(adapter_class).new(raw_value(key))
|
41
49
|
end
|
42
50
|
|
51
|
+
sig { params(key: Object, adapter_class: Class, additional_arguments: Object).returns(T::Array[Object]) }
|
43
52
|
def complex_object_list_value(key, adapter_class, additional_arguments = [])
|
44
53
|
array = maybe_raw_value(key) || []
|
45
54
|
unless array.is_a?(Array)
|
46
55
|
raise EmptyOrIncorrectAttributeTypeError, "value at key #{key} is not an array!"
|
47
56
|
end
|
48
|
-
array.map { |array_entry| adapter_class.new(array_entry, *additional_arguments) }
|
57
|
+
array.map { |array_entry| T.unsafe(adapter_class).new(array_entry, *additional_arguments) }
|
49
58
|
end
|
50
59
|
|
60
|
+
sig { params(key: Object).returns(Float) }
|
51
61
|
def currency_value(key)
|
52
|
-
float_value(key).round(2)
|
62
|
+
float_value(key).round(2).to_f
|
53
63
|
end
|
54
64
|
|
65
|
+
sig { params(key: Object).returns(Date) }
|
55
66
|
def date_value(key)
|
56
|
-
Date.parse(raw_value(key))
|
67
|
+
Date.parse(raw_value(key).to_s)
|
57
68
|
end
|
58
69
|
|
70
|
+
sig { params(key: Object).returns(Time) }
|
59
71
|
def time_value(key)
|
60
|
-
Time.parse(raw_value(key))
|
72
|
+
Time.parse(raw_value(key).to_s)
|
61
73
|
end
|
62
74
|
|
75
|
+
sig { params(key: Object).returns(Time) }
|
63
76
|
def date_time_value(key)
|
64
|
-
Time.strptime(raw_value(key), '%Y-%m-%dT%H:%M:%S.%L%z')
|
77
|
+
Time.strptime(raw_value(key).to_s, '%Y-%m-%dT%H:%M:%S.%L%z')
|
65
78
|
end
|
66
79
|
|
80
|
+
sig { params(key: Object).returns(Float) }
|
67
81
|
def float_value(key)
|
68
|
-
raw_value(key).to_f
|
82
|
+
raw_value(key).to_s.to_f
|
69
83
|
end
|
70
84
|
|
85
|
+
sig { params(key: Object).returns(Integer) }
|
71
86
|
def int_value(key)
|
72
|
-
raw_value(key).to_i
|
87
|
+
raw_value(key).to_s.to_i
|
73
88
|
end
|
74
89
|
|
90
|
+
sig { params(key: Object).returns(Regexp) }
|
75
91
|
def regex_value(key)
|
76
92
|
raw_pattern = raw_value(key)
|
77
93
|
return raw_pattern if raw_pattern.is_a?(Regexp)
|
78
94
|
/#{raw_pattern}/
|
79
95
|
end
|
80
96
|
|
97
|
+
sig { params(key: Object, adapter_class: Class).returns(T.nilable(Object)) }
|
81
98
|
def maybe_complex_object_value(key, adapter_class)
|
82
99
|
maybe_value { complex_object_value(key, adapter_class) }
|
83
100
|
end
|
84
101
|
|
102
|
+
sig { params(key: Object).returns(T.nilable(Date)) }
|
85
103
|
def maybe_date_value(key)
|
86
|
-
maybe_value { date_value(key) }
|
104
|
+
T.cast(maybe_value { date_value(key) }, T.nilable(Date))
|
87
105
|
end
|
88
106
|
|
107
|
+
sig { params(key: Object).returns(T.nilable(Time)) }
|
89
108
|
def maybe_time_value(key)
|
90
|
-
maybe_value { time_value(key) }
|
109
|
+
T.cast(maybe_value { time_value(key) }, T.nilable(Time))
|
91
110
|
end
|
92
111
|
|
112
|
+
sig { params(key: Object).returns(T.nilable(Time)) }
|
93
113
|
def maybe_date_time_value(key)
|
94
|
-
maybe_value { date_time_value(key) }
|
114
|
+
T.cast(maybe_value { date_time_value(key) }, T.nilable(Time))
|
95
115
|
end
|
96
116
|
|
117
|
+
sig { params(key: Object).returns(T.nilable(Integer)) }
|
97
118
|
def maybe_int_value(key)
|
98
|
-
maybe_value { int_value(key) }
|
119
|
+
T.cast(maybe_value { int_value(key) }, T.nilable(Integer))
|
99
120
|
end
|
100
121
|
|
122
|
+
sig { params(key: Object).returns(T.nilable(Float)) }
|
101
123
|
def maybe_float_value(key)
|
102
|
-
maybe_value { float_value(key) }
|
124
|
+
T.cast(maybe_value { float_value(key) }, T.nilable(Float))
|
103
125
|
end
|
104
126
|
|
127
|
+
sig { params(key: Object).returns(T.nilable(String)) }
|
105
128
|
def maybe_string_value(key)
|
106
|
-
maybe_value { string_value(key) }
|
129
|
+
T.cast(maybe_value { string_value(key) }, T.nilable(String))
|
107
130
|
end
|
108
131
|
|
132
|
+
sig { params(key: Object).returns(String) }
|
109
133
|
def string_value(key)
|
110
134
|
raw_value(key).to_s
|
111
135
|
end
|
112
136
|
|
137
|
+
sig { params(key: Object).returns(Symbol) }
|
113
138
|
def symbol_value(key)
|
114
139
|
string_value(key).to_sym
|
115
140
|
end
|
116
141
|
|
142
|
+
sig { params(key: Object).returns(T.nilable(Object)) }
|
117
143
|
def maybe_raw_value(key)
|
118
144
|
maybe_value { raw_value(key) }
|
119
145
|
end
|
120
146
|
|
121
|
-
EMPTY_VALUES = [nil, ''].freeze
|
147
|
+
EMPTY_VALUES = T.let([nil, ''].freeze, T::Array[Object])
|
122
148
|
|
149
|
+
sig { params(key: Object).returns(Object) }
|
123
150
|
def raw_value(key)
|
124
151
|
value = @attribute_hash[key]
|
125
152
|
if EMPTY_VALUES.include?(value)
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
# THIS FILE IS AUTOGENERATED AND SHOULD NOT BE MANUALLY MODIFIED
|
@@ -7,11 +7,8 @@ require 'clean_architecture/adapters/all'
|
|
7
7
|
require 'clean_architecture/builders/all'
|
8
8
|
require 'clean_architecture/checks/all'
|
9
9
|
require 'clean_architecture/entities/all'
|
10
|
-
require 'clean_architecture/interfaces/all'
|
11
10
|
require 'clean_architecture/matchers/all'
|
12
11
|
require 'clean_architecture/queries/all'
|
13
12
|
require 'clean_architecture/serializers/all'
|
14
|
-
require 'clean_architecture/use_cases/all'
|
15
13
|
|
16
|
-
require 'clean_architecture/types'
|
17
14
|
require 'clean_architecture/version'
|
@@ -1,13 +1,17 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
+
require 'active_record'
|
5
|
+
|
4
6
|
module CleanArchitecture
|
5
7
|
module Builders
|
6
8
|
# Helps to take an instance of an AR model and wrap it up in the given Entity
|
7
9
|
# Any columns from the AR model that do not directly map to an attribute on the Entity
|
8
10
|
# can be specified by overriding #attributes_for_entity.
|
9
11
|
class AbstractActiveRecordEntityBuilder
|
10
|
-
|
12
|
+
extend T::Sig
|
13
|
+
|
14
|
+
sig { params(entity_class: T.class_of(T::Struct)).void }
|
11
15
|
def self.acts_as_builder_for_entity(entity_class)
|
12
16
|
@has_many_builders = []
|
13
17
|
@belongs_to_builders = []
|
@@ -27,33 +31,44 @@ module CleanArchitecture
|
|
27
31
|
private :entity_class
|
28
32
|
end
|
29
33
|
|
34
|
+
sig { params(relation_name: Symbol, use: Class).void }
|
30
35
|
def self.has_many(relation_name, use:)
|
31
|
-
@has_many_builders
|
36
|
+
@has_many_builders = T.let(@has_many_builders, T.nilable(T::Array[Object]))
|
37
|
+
T.must(@has_many_builders) << [relation_name, use]
|
32
38
|
end
|
33
39
|
|
40
|
+
sig { params(relation_name: Symbol, use: Class).void }
|
34
41
|
def self.belongs_to(relation_name, use:)
|
35
|
-
@belongs_to_builders
|
42
|
+
@belongs_to_builders = T.let(@belongs_to_builders, T.nilable(T::Array[Object]))
|
43
|
+
T.must(@belongs_to_builders) << [relation_name, use]
|
36
44
|
end
|
37
45
|
|
38
|
-
|
46
|
+
sig { params(ar_model_instance: ActiveRecord::Base).void }
|
39
47
|
def initialize(ar_model_instance)
|
40
48
|
@ar_model_instance = ar_model_instance
|
41
49
|
end
|
42
50
|
|
51
|
+
sig { returns(T::Struct) }
|
43
52
|
def build
|
44
|
-
|
53
|
+
specified_entity_class.new(all_attributes_for_entity)
|
54
|
+
end
|
55
|
+
|
56
|
+
sig { returns(T.class_of(T::Struct)) }
|
57
|
+
def specified_entity_class
|
58
|
+
send(:entity_class)
|
45
59
|
end
|
46
60
|
|
47
61
|
private
|
48
62
|
|
63
|
+
sig { returns(ActiveRecord::Base) }
|
49
64
|
attr_reader :ar_model_instance
|
50
65
|
|
66
|
+
sig { returns(T::Array[Symbol]) }
|
51
67
|
def entity_attribute_names
|
68
|
+
@entity_attributes = T.let(@entity_attributes, T.nilable(T::Array[Symbol]))
|
52
69
|
@entity_attributes ||= begin
|
53
|
-
if
|
54
|
-
schema_keys =
|
55
|
-
elsif entity_class.respond_to?(:decorator) # T::Struct
|
56
|
-
schema_keys = entity_class.decorator.props.keys
|
70
|
+
if specified_entity_class.respond_to?(:decorator) # T::Struct
|
71
|
+
schema_keys = specified_entity_class.decorator.props.keys
|
57
72
|
else
|
58
73
|
raise 'Cannot determine schema format'
|
59
74
|
end
|
@@ -68,22 +83,34 @@ module CleanArchitecture
|
|
68
83
|
end
|
69
84
|
end
|
70
85
|
|
86
|
+
sig { returns(T::Hash[Symbol, Object]) }
|
71
87
|
def ar_model_instance_attributes
|
88
|
+
@ar_model_instance_attributes = T.let(
|
89
|
+
@ar_model_instance_attributes,
|
90
|
+
T.nilable(T::Hash[Symbol, Object])
|
91
|
+
)
|
72
92
|
@ar_model_instance_attributes ||= @ar_model_instance.attributes
|
73
93
|
end
|
74
94
|
|
95
|
+
sig { returns(T::Hash[Symbol, Object]) }
|
75
96
|
def symbolized_ar_model_instance_attributes
|
97
|
+
@symbolized_ar_model_instance_attributes = T.let(
|
98
|
+
@symbolized_ar_model_instance_attributes,
|
99
|
+
T.nilable(T::Hash[Symbol, Object])
|
100
|
+
)
|
76
101
|
@symbolized_ar_model_instance_attributes ||= Hash[
|
77
102
|
ar_model_instance_attributes.map{|(key, value)| [key.to_sym, value]}
|
78
103
|
]
|
79
104
|
end
|
80
105
|
|
106
|
+
sig { returns(T::Hash[Symbol, Object]) }
|
81
107
|
def ar_attributes_for_entity
|
82
|
-
symbolized_ar_model_instance_attributes.slice(*entity_attribute_names)
|
108
|
+
T.unsafe(symbolized_ar_model_instance_attributes).slice(*entity_attribute_names)
|
83
109
|
end
|
84
110
|
|
111
|
+
sig { returns(T::Hash[Symbol, Object]) }
|
85
112
|
def attributes_for_belongs_to_relations
|
86
|
-
self.class.belongs_to_builders.map do |belongs_to_builder_config|
|
113
|
+
T.unsafe(self.class).belongs_to_builders.map do |belongs_to_builder_config|
|
87
114
|
relation_name, builder_class = belongs_to_builder_config
|
88
115
|
relation = @ar_model_instance.public_send(relation_name)
|
89
116
|
|
@@ -94,8 +121,9 @@ module CleanArchitecture
|
|
94
121
|
end.to_h
|
95
122
|
end
|
96
123
|
|
124
|
+
sig { returns(T::Hash[Symbol, Object]) }
|
97
125
|
def attributes_for_has_many_relations
|
98
|
-
self.class.has_many_builders.map do |has_many_builder_config|
|
126
|
+
T.unsafe(self.class).has_many_builders.map do |has_many_builder_config|
|
99
127
|
relation_name, builder_class = has_many_builder_config
|
100
128
|
relations = @ar_model_instance.public_send(relation_name)
|
101
129
|
built_relations = relations.map do |relation|
|
@@ -109,10 +137,12 @@ module CleanArchitecture
|
|
109
137
|
end.to_h
|
110
138
|
end
|
111
139
|
|
140
|
+
sig { returns(T::Hash[Symbol, Object]) }
|
112
141
|
def attributes_for_entity
|
113
142
|
{}
|
114
143
|
end
|
115
144
|
|
145
|
+
sig { returns(T::Hash[Symbol, Object]) }
|
116
146
|
def all_attributes_for_entity
|
117
147
|
ar_attributes_for_entity
|
118
148
|
.merge(attributes_for_belongs_to_relations)
|
@@ -1,23 +1,25 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require 'dry/monads/all'
|
5
5
|
require 'clean_architecture/entities/failure_details'
|
6
|
-
require 'forwardable'
|
7
6
|
|
8
7
|
module CleanArchitecture
|
9
8
|
module Checks
|
10
9
|
class Authorization
|
11
|
-
extend
|
10
|
+
extend T::Sig
|
11
|
+
extend T::Helpers
|
12
12
|
|
13
|
+
abstract!
|
14
|
+
|
15
|
+
sig { returns(Dry::Monads::Result[Entities::FailureDetails, NilClass]) }
|
13
16
|
def result
|
14
17
|
if authorized?
|
15
|
-
Dry::Monads::Success(
|
18
|
+
Dry::Monads::Success(nil)
|
16
19
|
else
|
17
20
|
failure_details = Entities::FailureDetails.new(
|
18
21
|
message: failure_message,
|
19
|
-
|
20
|
-
type: 'unauthorized'
|
22
|
+
type: Entities::FailureType::Unauthorized
|
21
23
|
)
|
22
24
|
Dry::Monads::Failure(failure_details)
|
23
25
|
end
|
@@ -25,13 +27,13 @@ module CleanArchitecture
|
|
25
27
|
|
26
28
|
protected
|
27
29
|
|
30
|
+
sig { returns(String) }
|
28
31
|
def failure_message
|
29
32
|
'Unauthorized'
|
30
33
|
end
|
31
34
|
|
32
|
-
|
33
|
-
|
34
|
-
end
|
35
|
+
sig { abstract.returns(T::Boolean) }
|
36
|
+
def authorized?; end
|
35
37
|
end
|
36
38
|
end
|
37
39
|
end
|
@@ -1,8 +1,6 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
# THIS FILE IS AUTOGENERATED AND SHOULD NOT BE MANUALLY MODIFIED
|
5
5
|
|
6
6
|
require 'clean_architecture/entities/failure_details'
|
7
|
-
require 'clean_architecture/entities/targeted_parameters'
|
8
|
-
require 'clean_architecture/entities/untargeted_parameters'
|
@@ -1,30 +1,40 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
require 'clean_architecture/types'
|
5
|
-
require 'dry/struct'
|
6
|
-
|
7
4
|
module CleanArchitecture
|
8
5
|
module Entities
|
9
|
-
class
|
10
|
-
|
11
|
-
'
|
12
|
-
'expectation_failed'
|
13
|
-
'not_found'
|
14
|
-
'unauthorized'
|
15
|
-
'unprocessable_entity'
|
16
|
-
|
6
|
+
class FailureType < T::Enum
|
7
|
+
enums do
|
8
|
+
Error = new('internal_server_error')
|
9
|
+
ExpectationFailed = new('expectation_failed')
|
10
|
+
NotFound = new('not_found')
|
11
|
+
Unauthorized = new('unauthorized')
|
12
|
+
UnprocessableEntity = new('unprocessable_entity')
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class FailureDetails < T::Struct
|
17
|
+
extend T::Sig
|
18
|
+
|
19
|
+
include T::Struct::ActsAsComparable
|
17
20
|
|
18
|
-
|
19
|
-
|
20
|
-
attribute :other_properties, Types::Strict::Hash.default({}.freeze)
|
21
|
+
const :type, FailureType
|
22
|
+
const :message, String
|
21
23
|
|
24
|
+
sig { params(array: T::Array[Object]).returns(FailureDetails) }
|
22
25
|
def self.from_array(array)
|
23
|
-
new(
|
26
|
+
new(
|
27
|
+
message: array.map(&:to_s).join(', '),
|
28
|
+
type: FailureType::Error
|
29
|
+
)
|
24
30
|
end
|
25
31
|
|
32
|
+
sig { params(string: String).returns(FailureDetails) }
|
26
33
|
def self.from_string(string)
|
27
|
-
new(
|
34
|
+
new(
|
35
|
+
message: string,
|
36
|
+
type: FailureType::Error
|
37
|
+
)
|
28
38
|
end
|
29
39
|
end
|
30
40
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require 'clean_architecture/entities/failure_details'
|
@@ -8,16 +8,21 @@ require 'dry/monads/all'
|
|
8
8
|
module CleanArchitecture
|
9
9
|
module Matchers
|
10
10
|
class UseCaseResult
|
11
|
+
extend T::Sig
|
12
|
+
|
13
|
+
sig { params(result: Dry::Monads::Result[T.any(T::Array[Object], String, Entities::FailureDetails), T.untyped], block: T.untyped).returns(T.untyped) }
|
11
14
|
def self.call(result, &block)
|
12
15
|
new.matcher.call(result, &block)
|
13
16
|
end
|
14
17
|
|
18
|
+
sig {returns(Dry::Matcher)}
|
15
19
|
def matcher
|
16
20
|
Dry::Matcher.new(success: success_case, failure: failure_case)
|
17
21
|
end
|
18
22
|
|
19
23
|
private
|
20
24
|
|
25
|
+
sig { returns(Dry::Matcher::Case) }
|
21
26
|
def success_case
|
22
27
|
Dry::Matcher::Case.new(
|
23
28
|
match: ->(value) { value.is_a?(Dry::Monads::Success) },
|
@@ -25,6 +30,7 @@ module CleanArchitecture
|
|
25
30
|
)
|
26
31
|
end
|
27
32
|
|
33
|
+
sig { returns(Dry::Matcher::Case) }
|
28
34
|
def failure_case
|
29
35
|
Dry::Matcher::Case.new(
|
30
36
|
match: ->(value) { value.is_a?(Dry::Monads::Failure) },
|
@@ -32,6 +38,7 @@ module CleanArchitecture
|
|
32
38
|
)
|
33
39
|
end
|
34
40
|
|
41
|
+
sig { params(value: Dry::Monads::Result[T.any(T::Array[Object], String, Entities::FailureDetails), T.untyped]).returns(Entities::FailureDetails) }
|
35
42
|
def resolve_failure_value(value)
|
36
43
|
failure = value.failure
|
37
44
|
case failure
|
@@ -42,8 +49,7 @@ module CleanArchitecture
|
|
42
49
|
when Entities::FailureDetails
|
43
50
|
failure
|
44
51
|
else
|
45
|
-
|
46
|
-
raise ArgumentError, "Unexpected failure value - must be #{type_list}"
|
52
|
+
T.absurd(failure)
|
47
53
|
end
|
48
54
|
end
|
49
55
|
end
|
@@ -1,32 +1,20 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
module CleanArchitecture
|
5
5
|
module Queries
|
6
6
|
class HttpFailureCode
|
7
|
-
|
8
|
-
|
7
|
+
extend T::Sig
|
8
|
+
|
9
|
+
sig { params(failure_type: Entities::FailureType).void }
|
10
|
+
def initialize(failure_type)
|
11
|
+
@failure_type = failure_type
|
9
12
|
end
|
10
13
|
|
14
|
+
sig { returns(Symbol) }
|
11
15
|
def to_sym
|
12
|
-
|
13
|
-
if code.nil?
|
14
|
-
raise NotImplementedError,
|
15
|
-
"cannot determine failure code for failure details type #{@failure_details_type}"
|
16
|
-
end
|
17
|
-
|
18
|
-
code
|
16
|
+
@failure_type.serialize.to_sym
|
19
17
|
end
|
20
|
-
|
21
|
-
private
|
22
|
-
|
23
|
-
FAILURE_DETAILS_TYPE_TO_STATUS_CODE = {
|
24
|
-
'error' => :internal_server_error,
|
25
|
-
'expectation_failed' => :expectation_failed,
|
26
|
-
'not_found' => :not_found,
|
27
|
-
'unauthorized' => :unauthorized,
|
28
|
-
'unprocessable_entity' => :unprocessable_entity
|
29
|
-
}.freeze
|
30
18
|
end
|
31
19
|
end
|
32
20
|
end
|
@@ -1,13 +1,17 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
module CleanArchitecture
|
5
5
|
module Queries
|
6
6
|
class HttpSuccessCode
|
7
|
+
extend T::Sig
|
8
|
+
|
9
|
+
sig { params(http_method: String).void }
|
7
10
|
def initialize(http_method)
|
8
11
|
@http_method = http_method
|
9
12
|
end
|
10
13
|
|
14
|
+
sig { returns(Symbol) }
|
11
15
|
def to_sym
|
12
16
|
code = HTTP_METHOD_TO_SUCCESS_CODE[@http_method.to_s.upcase]
|
13
17
|
if code.nil?
|
@@ -19,12 +23,15 @@ module CleanArchitecture
|
|
19
23
|
|
20
24
|
private
|
21
25
|
|
22
|
-
HTTP_METHOD_TO_SUCCESS_CODE =
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
26
|
+
HTTP_METHOD_TO_SUCCESS_CODE = T.let(
|
27
|
+
{
|
28
|
+
'GET' => :ok,
|
29
|
+
'POST' => :created,
|
30
|
+
'PUT' => :accepted,
|
31
|
+
'DELETE' => :ok
|
32
|
+
}.freeze,
|
33
|
+
T::Hash[String, Symbol]
|
34
|
+
)
|
28
35
|
end
|
29
36
|
end
|
30
37
|
end
|