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.
Files changed (166) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +15 -19
  3. data/.gitignore +4 -0
  4. data/CHANGELOG.md +4 -0
  5. data/Gemfile +1 -0
  6. data/README.md +1 -585
  7. data/bin/tapioca +29 -0
  8. data/clean-architecture.gemspec +4 -4
  9. data/lib/clean-architecture.rb +4 -1
  10. data/lib/clean_architecture/adapters/all.rb +1 -1
  11. data/lib/clean_architecture/adapters/attribute_hash_base.rb +47 -20
  12. data/lib/clean_architecture/all.rb +1 -4
  13. data/lib/clean_architecture/builders/abstract_active_record_entity_builder.rb +43 -13
  14. data/lib/clean_architecture/builders/all.rb +1 -1
  15. data/lib/clean_architecture/checks/all.rb +1 -1
  16. data/lib/clean_architecture/checks/authorization.rb +11 -9
  17. data/lib/clean_architecture/entities/all.rb +1 -3
  18. data/lib/clean_architecture/entities/failure_details.rb +27 -17
  19. data/lib/clean_architecture/matchers/all.rb +1 -1
  20. data/lib/clean_architecture/matchers/use_case_result.rb +9 -3
  21. data/lib/clean_architecture/queries/all.rb +1 -1
  22. data/lib/clean_architecture/queries/http_failure_code.rb +8 -20
  23. data/lib/clean_architecture/queries/http_success_code.rb +14 -7
  24. data/lib/clean_architecture/serializers/all.rb +1 -1
  25. data/lib/clean_architecture/serializers/html_response_from_result.rb +7 -1
  26. data/lib/clean_architecture/serializers/json_response_from_result.rb +4 -4
  27. data/lib/clean_architecture/version.rb +1 -1
  28. data/nix/sources.json +14 -0
  29. data/nix/sources.nix +174 -0
  30. data/run_ci.sh +7 -0
  31. data/shell.nix +17 -12
  32. data/sorbet/config +4 -0
  33. data/sorbet/{rbi/gems → dry-monads-sorbet}/dry-monads.rbi +176 -94
  34. data/sorbet/rbi/gems/activemodel@6.1.4.1.rbi +1292 -0
  35. data/sorbet/rbi/gems/activerecord@6.1.4.1.rbi +8092 -0
  36. data/sorbet/rbi/gems/activesupport@6.1.4.1.rbi +3531 -0
  37. data/sorbet/rbi/gems/{ast.rbi → ast@2.4.2.rbi} +28 -22
  38. data/sorbet/rbi/gems/byebug@11.1.3.rbi +1568 -0
  39. data/sorbet/rbi/gems/coderay@1.1.3.rbi +1005 -0
  40. data/sorbet/rbi/gems/concurrent-ruby@1.1.9.rbi +915 -0
  41. data/sorbet/rbi/gems/diff-lcs@1.4.4.rbi +8 -0
  42. data/sorbet/rbi/gems/docile@1.4.0.rbi +54 -0
  43. data/sorbet/rbi/gems/dry-core@0.7.1.rbi +92 -0
  44. data/sorbet/rbi/gems/dry-equalizer@0.3.0.rbi +28 -0
  45. data/sorbet/rbi/gems/dry-matcher@0.9.0.rbi +56 -0
  46. data/sorbet/rbi/gems/dry-monads-sorbet@1.1.7.rbi +41 -0
  47. data/sorbet/rbi/gems/dry-monads@1.4.0.rbi +697 -0
  48. data/sorbet/rbi/gems/em-websocket@0.5.2.rbi +8 -0
  49. data/sorbet/rbi/gems/eventmachine@1.2.7.rbi +45 -0
  50. data/sorbet/rbi/gems/ffi@1.15.4.rbi +8 -0
  51. data/sorbet/rbi/gems/formatador@0.3.0.rbi +8 -0
  52. data/sorbet/rbi/gems/guard-compat@1.2.1.rbi +31 -0
  53. data/sorbet/rbi/gems/guard-livereload@2.5.2.rbi +8 -0
  54. data/sorbet/rbi/gems/guard-rspec@4.7.3.rbi +211 -0
  55. data/sorbet/rbi/gems/guard@2.18.0.rbi +8 -0
  56. data/sorbet/rbi/gems/http_parser.rb@0.6.0.rbi +8 -0
  57. data/sorbet/rbi/gems/i18n@1.8.10.rbi +8 -0
  58. data/sorbet/rbi/gems/listen@3.7.0.rbi +8 -0
  59. data/sorbet/rbi/gems/lumberjack@1.2.8.rbi +8 -0
  60. data/sorbet/rbi/gems/method_source@1.0.0.rbi +72 -0
  61. data/sorbet/rbi/gems/minitest@5.14.4.rbi +344 -0
  62. data/sorbet/rbi/gems/multi_json@1.15.0.rbi +8 -0
  63. data/sorbet/rbi/gems/nenv@0.3.0.rbi +8 -0
  64. data/sorbet/rbi/gems/notiffany@0.1.3.rbi +8 -0
  65. data/sorbet/rbi/gems/parallel@1.21.0.rbi +113 -0
  66. data/sorbet/rbi/gems/{parser.rbi → parser@3.0.2.0.rbi} +966 -699
  67. data/sorbet/rbi/gems/pry-byebug@3.9.0.rbi +461 -0
  68. data/sorbet/rbi/gems/{pry.rbi → pry@0.13.1.rbi} +2191 -1605
  69. data/sorbet/rbi/gems/{rainbow.rbi → rainbow@3.0.0.rbi} +90 -55
  70. data/sorbet/rbi/gems/{rake.rbi → rake@13.0.6.rbi} +578 -427
  71. data/sorbet/rbi/gems/rb-fsevent@0.11.0.rbi +8 -0
  72. data/sorbet/rbi/gems/rb-inotify@0.10.1.rbi +8 -0
  73. data/sorbet/rbi/gems/rb-readline@0.5.5.rbi +884 -0
  74. data/sorbet/rbi/gems/rbi@0.0.6.rbi +1405 -0
  75. data/sorbet/rbi/gems/regexp_parser@2.1.1.rbi +1120 -0
  76. data/sorbet/rbi/gems/{rexml.rbi → rexml@3.2.5.rbi} +562 -479
  77. data/sorbet/rbi/gems/{rspec-core.rbi → rspec-core@3.10.1.rbi} +2317 -1533
  78. data/sorbet/rbi/gems/rspec-expectations@3.10.1.rbi +1574 -0
  79. data/sorbet/rbi/gems/rspec-mocks@3.10.2.rbi +1462 -0
  80. data/sorbet/rbi/gems/rspec-support@3.10.2.rbi +509 -0
  81. data/sorbet/rbi/gems/rspec@3.10.0.rbi +38 -0
  82. data/sorbet/rbi/gems/rubocop-ast@1.12.0.rbi +1938 -0
  83. data/sorbet/rbi/gems/rubocop-rspec@2.5.0.rbi +1786 -0
  84. data/sorbet/rbi/gems/rubocop@1.22.1.rbi +13252 -0
  85. data/sorbet/rbi/gems/ruby-progressbar@1.11.0.rbi +405 -0
  86. data/sorbet/rbi/gems/shellany@0.0.1.rbi +8 -0
  87. data/sorbet/rbi/gems/simplecov-html@0.12.3.rbi +89 -0
  88. data/sorbet/rbi/gems/simplecov@0.21.2.rbi +577 -0
  89. data/sorbet/rbi/gems/simplecov_json_formatter@0.1.3.rbi +8 -0
  90. data/sorbet/rbi/gems/sorbet-struct-comparable@1.1.0.rbi +17 -0
  91. data/sorbet/rbi/gems/spoom@1.1.5.rbi +1241 -0
  92. data/sorbet/rbi/gems/stackprof@0.2.17.rbi +98 -0
  93. data/sorbet/rbi/gems/tapioca@0.5.2.rbi +949 -0
  94. data/sorbet/rbi/gems/thor@1.1.0.rbi +839 -0
  95. data/sorbet/rbi/gems/tzinfo@2.0.4.rbi +8 -0
  96. data/sorbet/rbi/gems/unicode-display_width@2.1.0.rbi +26 -0
  97. data/sorbet/rbi/gems/unparser@0.6.0.rbi +8 -0
  98. data/sorbet/rbi/gems/zeitwerk@2.4.2.rbi +8 -0
  99. data/sorbet/tapioca/require.rb +9 -0
  100. metadata +88 -91
  101. data/.ruby-version +0 -1
  102. data/lib/clean_architecture/entities/targeted_parameters.rb +0 -24
  103. data/lib/clean_architecture/entities/untargeted_parameters.rb +0 -21
  104. data/lib/clean_architecture/interfaces/all.rb +0 -12
  105. data/lib/clean_architecture/interfaces/authorization_parameters.rb +0 -19
  106. data/lib/clean_architecture/interfaces/base_parameters.rb +0 -24
  107. data/lib/clean_architecture/interfaces/jsonable.rb +0 -16
  108. data/lib/clean_architecture/interfaces/targeted_parameters.rb +0 -19
  109. data/lib/clean_architecture/interfaces/use_case.rb +0 -20
  110. data/lib/clean_architecture/interfaces/use_case_actor.rb +0 -20
  111. data/lib/clean_architecture/interfaces/use_case_target.rb +0 -24
  112. data/lib/clean_architecture/types.rb +0 -8
  113. data/lib/clean_architecture/use_cases/abstract_use_case.rb +0 -63
  114. data/lib/clean_architecture/use_cases/all.rb +0 -10
  115. data/lib/clean_architecture/use_cases/contract.rb +0 -9
  116. data/lib/clean_architecture/use_cases/errors.rb +0 -58
  117. data/lib/clean_architecture/use_cases/form.rb +0 -116
  118. data/lib/clean_architecture/use_cases/parameters.rb +0 -43
  119. data/sorbet/rbi/gems/activemodel.rbi +0 -75
  120. data/sorbet/rbi/gems/activesupport.rbi +0 -440
  121. data/sorbet/rbi/gems/byebug.rbi +0 -1040
  122. data/sorbet/rbi/gems/coderay.rbi +0 -92
  123. data/sorbet/rbi/gems/concurrent-ruby.rbi +0 -1586
  124. data/sorbet/rbi/gems/docile.rbi +0 -32
  125. data/sorbet/rbi/gems/dry-configurable.rbi +0 -139
  126. data/sorbet/rbi/gems/dry-container.rbi +0 -89
  127. data/sorbet/rbi/gems/dry-core.rbi +0 -80
  128. data/sorbet/rbi/gems/dry-equalizer.rbi +0 -26
  129. data/sorbet/rbi/gems/dry-inflector.rbi +0 -73
  130. data/sorbet/rbi/gems/dry-initializer.rbi +0 -209
  131. data/sorbet/rbi/gems/dry-logic.rbi +0 -305
  132. data/sorbet/rbi/gems/dry-matcher.rbi +0 -34
  133. data/sorbet/rbi/gems/dry-schema.rbi +0 -786
  134. data/sorbet/rbi/gems/dry-struct.rbi +0 -137
  135. data/sorbet/rbi/gems/dry-types.rbi +0 -709
  136. data/sorbet/rbi/gems/dry-validation.rbi +0 -288
  137. data/sorbet/rbi/gems/duckface-interfaces.rbi +0 -94
  138. data/sorbet/rbi/gems/i18n.rbi +0 -133
  139. data/sorbet/rbi/gems/jaro_winkler.rbi +0 -15
  140. data/sorbet/rbi/gems/method_source.rbi +0 -64
  141. data/sorbet/rbi/gems/parallel.rbi +0 -82
  142. data/sorbet/rbi/gems/pry-byebug.rbi +0 -155
  143. data/sorbet/rbi/gems/rb-readline.rbi +0 -767
  144. data/sorbet/rbi/gems/rspec-expectations.rbi +0 -398
  145. data/sorbet/rbi/gems/rspec-mocks.rbi +0 -816
  146. data/sorbet/rbi/gems/rspec-support.rbi +0 -271
  147. data/sorbet/rbi/gems/rspec.rbi +0 -15
  148. data/sorbet/rbi/gems/rubocop-rspec.rbi +0 -922
  149. data/sorbet/rbi/gems/rubocop.rbi +0 -7319
  150. data/sorbet/rbi/gems/ruby-progressbar.rbi +0 -305
  151. data/sorbet/rbi/gems/simplecov-html.rbi +0 -35
  152. data/sorbet/rbi/gems/simplecov.rbi +0 -361
  153. data/sorbet/rbi/gems/stackprof.rbi +0 -52
  154. data/sorbet/rbi/gems/unicode-display_width.rbi +0 -17
  155. data/sorbet/rbi/hidden-definitions/errors.txt +0 -8580
  156. data/sorbet/rbi/hidden-definitions/hidden.rbi +0 -17036
  157. data/sorbet/rbi/sorbet-typed/lib/activemodel/all/activemodel.rbi +0 -452
  158. data/sorbet/rbi/sorbet-typed/lib/activesupport/>=6.0.0.rc1/activesupport.rbi +0 -23
  159. data/sorbet/rbi/sorbet-typed/lib/activesupport/all/activesupport.rbi +0 -979
  160. data/sorbet/rbi/sorbet-typed/lib/bundler/all/bundler.rbi +0 -8684
  161. data/sorbet/rbi/sorbet-typed/lib/minitest/all/minitest.rbi +0 -108
  162. data/sorbet/rbi/sorbet-typed/lib/rainbow/all/rainbow.rbi +0 -276
  163. data/sorbet/rbi/sorbet-typed/lib/ruby/all/gem.rbi +0 -4222
  164. data/sorbet/rbi/sorbet-typed/lib/ruby/all/open3.rbi +0 -111
  165. data/sorbet/rbi/sorbet-typed/lib/ruby/all/resolv.rbi +0 -543
  166. 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")
@@ -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-struct'
34
- spec.add_dependency 'dry-types'
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'
@@ -1,4 +1,7 @@
1
- # typed: strong
1
+ # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
+ require 'sorbet-runtime'
5
+ require 'dry-monads-sorbet'
6
+ require 'sorbet-struct-comparable'
4
7
  require 'clean_architecture/all'
@@ -1,4 +1,4 @@
1
- # typed: strong
1
+ # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
4
  # THIS FILE IS AUTOGENERATED AND SHOULD NOT BE MANUALLY MODIFIED
@@ -1,4 +1,4 @@
1
- # typed: true
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
- def ensured_value(default:)
26
- yield
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
- def maybe_value
32
- ensured_value(default: nil) { yield }
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: strong
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: false
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
- # @param [Class] A Dry::Struct based entity that this builder will construct instances of
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 << [relation_name, use]
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 << [relation_name, use]
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
- # @param [ActiveRecord::Base] An ActiveRecord model to map to the entity
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
- entity_class.new(all_attributes_for_entity)
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 entity_class.respond_to?(:schema) # Dry::Struct
54
- schema_keys = entity_class.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,4 +1,4 @@
1
- # typed: strong
1
+ # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
4
  # THIS FILE IS AUTOGENERATED AND SHOULD NOT BE MANUALLY MODIFIED
@@ -1,4 +1,4 @@
1
- # typed: strong
1
+ # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
4
  # THIS FILE IS AUTOGENERATED AND SHOULD NOT BE MANUALLY MODIFIED
@@ -1,23 +1,25 @@
1
- # typed: true
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 Forwardable
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(true)
18
+ Dry::Monads::Success(nil)
16
19
  else
17
20
  failure_details = Entities::FailureDetails.new(
18
21
  message: failure_message,
19
- other_properties: {},
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
- def authorized?
33
- raise NotImplementedError
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: strong
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: false
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 FailureDetails < Dry::Struct
10
- FailureTypes = Types::Strict::String.enum(
11
- 'error',
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
- attribute :type, FailureTypes
19
- attribute :message, Types::Strict::String
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(message: array.map(&:to_s).join(', '), other_properties: {}, type: 'error')
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(message: string, other_properties: {}, type: 'error')
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: strong
1
+ # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
4
  # THIS FILE IS AUTOGENERATED AND SHOULD NOT BE MANUALLY MODIFIED
@@ -1,4 +1,4 @@
1
- # typed: true
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
- type_list = [Array, String, Entities::FailureDetails].map(&:to_s).join(' or ')
46
- raise ArgumentError, "Unexpected failure value - must be #{type_list}"
52
+ T.absurd(failure)
47
53
  end
48
54
  end
49
55
  end
@@ -1,4 +1,4 @@
1
- # typed: strong
1
+ # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
4
  # THIS FILE IS AUTOGENERATED AND SHOULD NOT BE MANUALLY MODIFIED
@@ -1,32 +1,20 @@
1
- # typed: true
1
+ # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module CleanArchitecture
5
5
  module Queries
6
6
  class HttpFailureCode
7
- def initialize(failure_details_type)
8
- @failure_details_type = failure_details_type
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
- code = FAILURE_DETAILS_TYPE_TO_STATUS_CODE[@failure_details_type.to_s.downcase]
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: true
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
- 'GET' => :ok,
24
- 'POST' => :created,
25
- 'PUT' => :accepted,
26
- 'DELETE' => :ok
27
- }.freeze
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
@@ -1,4 +1,4 @@
1
- # typed: strong
1
+ # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
4
  # THIS FILE IS AUTOGENERATED AND SHOULD NOT BE MANUALLY MODIFIED