clean-architecture 2.0.0 → 3.0.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.
Files changed (118) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/rspec.yml +21 -0
  3. data/.gitignore +1 -0
  4. data/.ruby-version +1 -1
  5. data/CHANGELOG.md +42 -0
  6. data/Gemfile +1 -0
  7. data/README.md +423 -4
  8. data/clean-architecture.gemspec +9 -5
  9. data/generate_require_files.rb +1 -0
  10. data/lib/clean-architecture.rb +1 -0
  11. data/lib/clean_architecture/adapters/all.rb +1 -0
  12. data/lib/clean_architecture/adapters/attribute_hash_base.rb +1 -0
  13. data/lib/clean_architecture/all.rb +3 -0
  14. data/lib/clean_architecture/builders/abstract_active_record_entity_builder.rb +124 -0
  15. data/lib/clean_architecture/builders/all.rb +6 -0
  16. data/lib/clean_architecture/checks/all.rb +1 -0
  17. data/lib/clean_architecture/checks/authorization.rb +1 -0
  18. data/lib/clean_architecture/entities/all.rb +1 -0
  19. data/lib/clean_architecture/entities/failure_details.rb +2 -1
  20. data/lib/clean_architecture/entities/targeted_parameters.rb +1 -0
  21. data/lib/clean_architecture/entities/untargeted_parameters.rb +1 -0
  22. data/lib/clean_architecture/interfaces/all.rb +1 -0
  23. data/lib/clean_architecture/interfaces/authorization_parameters.rb +1 -0
  24. data/lib/clean_architecture/interfaces/base_parameters.rb +1 -0
  25. data/lib/clean_architecture/interfaces/jsonable.rb +1 -0
  26. data/lib/clean_architecture/interfaces/success_payload.rb +1 -0
  27. data/lib/clean_architecture/interfaces/targeted_parameters.rb +1 -0
  28. data/lib/clean_architecture/interfaces/use_case.rb +1 -0
  29. data/lib/clean_architecture/interfaces/use_case_actor.rb +1 -0
  30. data/lib/clean_architecture/interfaces/use_case_target.rb +1 -0
  31. data/lib/clean_architecture/matchers/all.rb +1 -0
  32. data/lib/clean_architecture/matchers/use_case_result.rb +1 -0
  33. data/lib/clean_architecture/queries/all.rb +1 -0
  34. data/lib/clean_architecture/queries/http_failure_code.rb +1 -0
  35. data/lib/clean_architecture/queries/http_success_code.rb +1 -0
  36. data/lib/clean_architecture/serializers/all.rb +1 -0
  37. data/lib/clean_architecture/serializers/html_response_from_result.rb +1 -0
  38. data/lib/clean_architecture/serializers/json_response_from_result.rb +1 -0
  39. data/lib/clean_architecture/serializers/success_collection_payload.rb +1 -0
  40. data/lib/clean_architecture/serializers/success_payload.rb +1 -0
  41. data/lib/clean_architecture/types.rb +2 -1
  42. data/lib/clean_architecture/use_cases/abstract_use_case.rb +62 -0
  43. data/lib/clean_architecture/use_cases/all.rb +10 -0
  44. data/lib/clean_architecture/use_cases/contract.rb +9 -0
  45. data/lib/clean_architecture/use_cases/errors.rb +57 -0
  46. data/lib/clean_architecture/use_cases/form.rb +116 -0
  47. data/lib/clean_architecture/use_cases/parameters.rb +42 -0
  48. data/lib/clean_architecture/version.rb +2 -1
  49. data/sorbet/config +2 -0
  50. data/sorbet/rbi/gems/activemodel.rbi +74 -0
  51. data/sorbet/rbi/gems/activesupport.rbi +440 -0
  52. data/sorbet/rbi/gems/ast.rbi +47 -0
  53. data/sorbet/rbi/gems/axiom-types.rbi +159 -0
  54. data/sorbet/rbi/gems/byebug.rbi +1039 -0
  55. data/sorbet/rbi/gems/codeclimate-engine-rb.rbi +123 -0
  56. data/sorbet/rbi/gems/coderay.rbi +91 -0
  57. data/sorbet/rbi/gems/coercible.rbi +156 -0
  58. data/sorbet/rbi/gems/concurrent-ruby.rbi +1587 -0
  59. data/sorbet/rbi/gems/descendants_tracker.rbi +17 -0
  60. data/sorbet/rbi/gems/docile.rbi +31 -0
  61. data/sorbet/rbi/gems/dry-configurable.rbi +89 -0
  62. data/sorbet/rbi/gems/dry-container.rbi +88 -0
  63. data/sorbet/rbi/gems/dry-core.rbi +79 -0
  64. data/sorbet/rbi/gems/dry-equalizer.rbi +25 -0
  65. data/sorbet/rbi/gems/dry-inflector.rbi +72 -0
  66. data/sorbet/rbi/gems/dry-initializer.rbi +209 -0
  67. data/sorbet/rbi/gems/dry-logic.rbi +304 -0
  68. data/sorbet/rbi/gems/dry-matcher.rbi +33 -0
  69. data/sorbet/rbi/gems/dry-monads.rbi +508 -0
  70. data/sorbet/rbi/gems/dry-schema.rbi +790 -0
  71. data/sorbet/rbi/gems/dry-struct.rbi +165 -0
  72. data/sorbet/rbi/gems/dry-types.rbi +688 -0
  73. data/sorbet/rbi/gems/dry-validation.rbi +284 -0
  74. data/sorbet/rbi/gems/duckface-interfaces.rbi +93 -0
  75. data/sorbet/rbi/gems/equalizer.rbi +22 -0
  76. data/sorbet/rbi/gems/i18n.rbi +132 -0
  77. data/sorbet/rbi/gems/ice_nine.rbi +66 -0
  78. data/sorbet/rbi/gems/jaro_winkler.rbi +14 -0
  79. data/sorbet/rbi/gems/kwalify.rbi +339 -0
  80. data/sorbet/rbi/gems/method_source.rbi +63 -0
  81. data/sorbet/rbi/gems/parallel.rbi +81 -0
  82. data/sorbet/rbi/gems/parser.rbi +1293 -0
  83. data/sorbet/rbi/gems/pry-byebug.rbi +149 -0
  84. data/sorbet/rbi/gems/pry.rbi +1964 -0
  85. data/sorbet/rbi/gems/psych.rbi +462 -0
  86. data/sorbet/rbi/gems/rainbow.rbi +117 -0
  87. data/sorbet/rbi/gems/rake.rbi +634 -0
  88. data/sorbet/rbi/gems/rb-readline.rbi +766 -0
  89. data/sorbet/rbi/gems/reek.rbi +1066 -0
  90. data/sorbet/rbi/gems/rspec-core.rbi +1658 -0
  91. data/sorbet/rbi/gems/rspec-expectations.rbi +430 -0
  92. data/sorbet/rbi/gems/rspec-mocks.rbi +815 -0
  93. data/sorbet/rbi/gems/rspec-support.rbi +268 -0
  94. data/sorbet/rbi/gems/rspec.rbi +14 -0
  95. data/sorbet/rbi/gems/rubocop-rspec.rbi +875 -0
  96. data/sorbet/rbi/gems/rubocop.rbi +7014 -0
  97. data/sorbet/rbi/gems/ruby-progressbar.rbi +304 -0
  98. data/sorbet/rbi/gems/simplecov-html.rbi +30 -0
  99. data/sorbet/rbi/gems/simplecov.rbi +225 -0
  100. data/sorbet/rbi/gems/stackprof.rbi +51 -0
  101. data/sorbet/rbi/gems/thread_safe.rbi +81 -0
  102. data/sorbet/rbi/gems/timecop.rbi +97 -0
  103. data/sorbet/rbi/gems/unicode-display_width.rbi +16 -0
  104. data/sorbet/rbi/gems/virtus.rbi +421 -0
  105. data/sorbet/rbi/hidden-definitions/errors.txt +7332 -0
  106. data/sorbet/rbi/hidden-definitions/hidden.rbi +17521 -0
  107. data/sorbet/rbi/sorbet-typed/lib/activemodel/all/activemodel.rbi +422 -0
  108. data/sorbet/rbi/sorbet-typed/lib/activesupport/>=6.0.0.rc1/activesupport.rbi +23 -0
  109. data/sorbet/rbi/sorbet-typed/lib/activesupport/all/activesupport.rbi +625 -0
  110. data/sorbet/rbi/sorbet-typed/lib/bundler/all/bundler.rbi +8684 -0
  111. data/sorbet/rbi/sorbet-typed/lib/minitest/all/minitest.rbi +99 -0
  112. data/sorbet/rbi/sorbet-typed/lib/rainbow/all/rainbow.rbi +254 -0
  113. data/sorbet/rbi/sorbet-typed/lib/ruby/all/gem.rbi +4222 -0
  114. data/sorbet/rbi/sorbet-typed/lib/ruby/all/open3.rbi +111 -0
  115. data/sorbet/rbi/sorbet-typed/lib/ruby/all/resolv.rbi +543 -0
  116. data/sorbet/rbi/todo.rbi +12 -0
  117. metadata +156 -24
  118. data/Gemfile.lock +0 -187
@@ -19,13 +19,17 @@ Gem::Specification.new do |spec|
19
19
  end
20
20
  spec.require_paths = ['lib']
21
21
 
22
- spec.add_dependency 'dry-matcher', '~> 0.0'
23
- spec.add_dependency 'dry-monads', '~> 1.0'
24
- spec.add_dependency 'dry-struct', '~> 0.0'
25
- spec.add_dependency 'dry-types', '~> 0.0'
22
+ spec.add_dependency 'activemodel', '>= 5'
23
+ spec.add_dependency 'activesupport', '>= 5'
24
+ spec.add_dependency 'dry-matcher'
25
+ spec.add_dependency 'dry-monads'
26
+ spec.add_dependency 'dry-struct'
27
+ spec.add_dependency 'dry-types'
28
+ spec.add_dependency 'dry-validation', '>= 1.0.0'
26
29
  spec.add_dependency 'duckface-interfaces', '~> 0.0'
30
+ spec.add_dependency 'sorbet-runtime'
27
31
 
28
- spec.add_development_dependency 'bundler', '~> 1.13'
32
+ spec.add_development_dependency 'bundler'
29
33
  spec.add_development_dependency 'rake', '~> 12.0'
30
34
  spec.add_development_dependency 'rspec', '~> 3.0'
31
35
  end
@@ -1,3 +1,4 @@
1
+ # typed: true
1
2
  # frozen_string_literal: true
2
3
 
3
4
  def directories_in(root)
@@ -1,3 +1,4 @@
1
+ # typed: strong
1
2
  # frozen_string_literal: true
2
3
 
3
4
  require 'clean_architecture/all'
@@ -1,3 +1,4 @@
1
+ # typed: strong
1
2
  # frozen_string_literal: true
2
3
 
3
4
  # THIS FILE IS AUTOGENERATED AND SHOULD NOT BE MANUALLY MODIFIED
@@ -1,3 +1,4 @@
1
+ # typed: true
1
2
  # frozen_string_literal: true
2
3
 
3
4
  require 'time'
@@ -1,14 +1,17 @@
1
+ # typed: strong
1
2
  # frozen_string_literal: true
2
3
 
3
4
  # THIS FILE IS AUTOGENERATED AND SHOULD NOT BE MANUALLY MODIFIED
4
5
 
5
6
  require 'clean_architecture/adapters/all'
7
+ require 'clean_architecture/builders/all'
6
8
  require 'clean_architecture/checks/all'
7
9
  require 'clean_architecture/entities/all'
8
10
  require 'clean_architecture/interfaces/all'
9
11
  require 'clean_architecture/matchers/all'
10
12
  require 'clean_architecture/queries/all'
11
13
  require 'clean_architecture/serializers/all'
14
+ require 'clean_architecture/use_cases/all'
12
15
 
13
16
  require 'clean_architecture/types'
14
17
  require 'clean_architecture/version'
@@ -0,0 +1,124 @@
1
+ # typed: false
2
+ # frozen_string_literal: true
3
+
4
+ module CleanArchitecture
5
+ module Builders
6
+ # Helps to take an instance of an AR model and wrap it up in the given Entity
7
+ # Any columns from the AR model that do not directly map to an attribute on the Entity
8
+ # can be specified by overriding #attributes_for_entity.
9
+ class AbstractActiveRecordEntityBuilder
10
+ # @param [Class] A Dry::Struct based entity that this builder will construct instances of
11
+ def self.acts_as_builder_for_entity(entity_class)
12
+ @has_many_builders = []
13
+ @belongs_to_builders = []
14
+
15
+ define_singleton_method :has_many_builders do
16
+ @has_many_builders
17
+ end
18
+
19
+ define_singleton_method :belongs_to_builders do
20
+ @belongs_to_builders
21
+ end
22
+
23
+ define_method :entity_class do
24
+ entity_class
25
+ end
26
+
27
+ private :entity_class
28
+ end
29
+
30
+ def self.has_many(relation_name, use:)
31
+ @has_many_builders << [relation_name, use]
32
+ end
33
+
34
+ def self.belongs_to(relation_name, use:)
35
+ @belongs_to_builders << [relation_name, use]
36
+ end
37
+
38
+ # @param [ActiveRecord::Base] An ActiveRecord model to map to the entity
39
+ def initialize(ar_model_instance)
40
+ @ar_model_instance = ar_model_instance
41
+ end
42
+
43
+ def build
44
+ entity_class.new(all_attributes_for_entity)
45
+ end
46
+
47
+ private
48
+
49
+ attr_reader :ar_model_instance
50
+
51
+ def entity_attribute_names
52
+ @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
57
+ else
58
+ raise 'Cannot determine schema format'
59
+ end
60
+ first_key = schema_keys.first
61
+ if first_key.is_a?(Symbol)
62
+ schema_keys
63
+ elsif first_key.respond_to?(:name)
64
+ schema_keys.map(&:name)
65
+ else
66
+ raise 'Cannot determine schema format'
67
+ end
68
+ end
69
+ end
70
+
71
+ def ar_model_instance_attributes
72
+ @ar_model_instance_attributes ||= @ar_model_instance.attributes
73
+ end
74
+
75
+ def symbolized_ar_model_instance_attributes
76
+ @symbolized_ar_model_instance_attributes ||= Hash[
77
+ ar_model_instance_attributes.map{|(key, value)| [key.to_sym, value]}
78
+ ]
79
+ end
80
+
81
+ def ar_attributes_for_entity
82
+ symbolized_ar_model_instance_attributes.slice(*entity_attribute_names)
83
+ end
84
+
85
+ def attributes_for_belongs_to_relations
86
+ self.class.belongs_to_builders.map do |belongs_to_builder_config|
87
+ relation_name, builder_class = belongs_to_builder_config
88
+ relation = @ar_model_instance.public_send(relation_name)
89
+
90
+ [
91
+ relation_name,
92
+ relation ? builder_class.new(relation).build : nil
93
+ ]
94
+ end.to_h
95
+ end
96
+
97
+ def attributes_for_has_many_relations
98
+ self.class.has_many_builders.map do |has_many_builder_config|
99
+ relation_name, builder_class = has_many_builder_config
100
+ relations = @ar_model_instance.public_send(relation_name)
101
+ built_relations = relations.map do |relation|
102
+ builder_class.new(relation).build
103
+ end
104
+
105
+ [
106
+ relation_name,
107
+ built_relations
108
+ ]
109
+ end.to_h
110
+ end
111
+
112
+ def attributes_for_entity
113
+ {}
114
+ end
115
+
116
+ def all_attributes_for_entity
117
+ ar_attributes_for_entity
118
+ .merge(attributes_for_belongs_to_relations)
119
+ .merge(attributes_for_has_many_relations)
120
+ .merge(attributes_for_entity)
121
+ end
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,6 @@
1
+ # typed: strong
2
+ # frozen_string_literal: true
3
+
4
+ # THIS FILE IS AUTOGENERATED AND SHOULD NOT BE MANUALLY MODIFIED
5
+
6
+ require 'clean_architecture/builders/abstract_active_record_entity_builder'
@@ -1,3 +1,4 @@
1
+ # typed: strong
1
2
  # frozen_string_literal: true
2
3
 
3
4
  # THIS FILE IS AUTOGENERATED AND SHOULD NOT BE MANUALLY MODIFIED
@@ -1,3 +1,4 @@
1
+ # typed: true
1
2
  # frozen_string_literal: true
2
3
 
3
4
  require 'dry/monads/all'
@@ -1,3 +1,4 @@
1
+ # typed: strong
1
2
  # frozen_string_literal: true
2
3
 
3
4
  # THIS FILE IS AUTOGENERATED AND SHOULD NOT BE MANUALLY MODIFIED
@@ -1,3 +1,4 @@
1
+ # typed: false
1
2
  # frozen_string_literal: true
2
3
 
3
4
  require 'clean_architecture/types'
@@ -15,7 +16,7 @@ module CleanArchitecture
15
16
 
16
17
  attribute :type, FailureTypes
17
18
  attribute :message, Types::Strict::String
18
- attribute :other_properties, Types::Strict::Hash.default({})
19
+ attribute :other_properties, Types::Strict::Hash.default({}.freeze)
19
20
 
20
21
  def self.from_array(array)
21
22
  new(message: array.map(&:to_s).join(', '), other_properties: {}, type: 'error')
@@ -1,3 +1,4 @@
1
+ # typed: true
1
2
  # frozen_string_literal: true
2
3
 
3
4
  require 'clean_architecture/interfaces/targeted_parameters'
@@ -1,3 +1,4 @@
1
+ # typed: true
1
2
  # frozen_string_literal: true
2
3
 
3
4
  require 'clean_architecture/interfaces/base_parameters'
@@ -1,3 +1,4 @@
1
+ # typed: strong
1
2
  # frozen_string_literal: true
2
3
 
3
4
  # THIS FILE IS AUTOGENERATED AND SHOULD NOT BE MANUALLY MODIFIED
@@ -1,3 +1,4 @@
1
+ # typed: false
1
2
  # frozen_string_literal: true
2
3
 
3
4
  require 'clean_architecture/interfaces/base_parameters'
@@ -1,3 +1,4 @@
1
+ # typed: false
1
2
  # frozen_string_literal: true
2
3
 
3
4
  require 'duckface'
@@ -1,3 +1,4 @@
1
+ # typed: false
1
2
  # frozen_string_literal: true
2
3
 
3
4
  require 'duckface'
@@ -1,3 +1,4 @@
1
+ # typed: false
1
2
  # frozen_string_literal: true
2
3
 
3
4
  require 'duckface'
@@ -1,3 +1,4 @@
1
+ # typed: false
1
2
  # frozen_string_literal: true
2
3
 
3
4
  require 'clean_architecture/interfaces/base_parameters'
@@ -1,3 +1,4 @@
1
+ # typed: false
1
2
  # frozen_string_literal: true
2
3
 
3
4
  require 'duckface'
@@ -1,3 +1,4 @@
1
+ # typed: false
1
2
  # frozen_string_literal: true
2
3
 
3
4
  require 'duckface'
@@ -1,3 +1,4 @@
1
+ # typed: false
1
2
  # frozen_string_literal: true
2
3
 
3
4
  require 'duckface'
@@ -1,3 +1,4 @@
1
+ # typed: strong
1
2
  # frozen_string_literal: true
2
3
 
3
4
  # THIS FILE IS AUTOGENERATED AND SHOULD NOT BE MANUALLY MODIFIED
@@ -1,3 +1,4 @@
1
+ # typed: true
1
2
  # frozen_string_literal: true
2
3
 
3
4
  require 'clean_architecture/entities/failure_details'
@@ -1,3 +1,4 @@
1
+ # typed: strong
1
2
  # frozen_string_literal: true
2
3
 
3
4
  # THIS FILE IS AUTOGENERATED AND SHOULD NOT BE MANUALLY MODIFIED
@@ -1,3 +1,4 @@
1
+ # typed: true
1
2
  # frozen_string_literal: true
2
3
 
3
4
  module CleanArchitecture
@@ -1,3 +1,4 @@
1
+ # typed: true
1
2
  # frozen_string_literal: true
2
3
 
3
4
  module CleanArchitecture
@@ -1,3 +1,4 @@
1
+ # typed: strong
1
2
  # frozen_string_literal: true
2
3
 
3
4
  # THIS FILE IS AUTOGENERATED AND SHOULD NOT BE MANUALLY MODIFIED
@@ -1,3 +1,4 @@
1
+ # typed: true
1
2
  # frozen_string_literal: true
2
3
 
3
4
  require 'clean_architecture/entities/failure_details'
@@ -1,3 +1,4 @@
1
+ # typed: true
1
2
  # frozen_string_literal: true
2
3
 
3
4
  require 'clean_architecture/entities/failure_details'
@@ -1,3 +1,4 @@
1
+ # typed: true
1
2
  # frozen_string_literal: true
2
3
 
3
4
  require 'duckface'
@@ -1,3 +1,4 @@
1
+ # typed: true
1
2
  # frozen_string_literal: true
2
3
 
3
4
  require 'duckface'
@@ -1,7 +1,8 @@
1
+ # typed: ignore
1
2
  # frozen_string_literal: true
2
3
 
3
4
  require 'dry-types'
4
5
 
5
6
  module Types
6
- include Dry::Types.module
7
+ include Dry.Types()
7
8
  end
@@ -0,0 +1,62 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ require 'dry/monads/result'
5
+ require 'dry/validation'
6
+ require 'dry/matcher/result_matcher'
7
+ require 'clean_architecture/use_cases/errors'
8
+ require 'clean_architecture/use_cases/parameters'
9
+ require 'clean_architecture/use_cases/contract'
10
+ require 'clean_architecture/entities/failure_details'
11
+
12
+ module CleanArchitecture
13
+ module UseCases
14
+ class AbstractUseCase
15
+ extend Forwardable
16
+
17
+ @contract = nil
18
+
19
+ def self.contract(base_contract = Contract)
20
+ @contract ||= begin
21
+ Class.new(base_contract, &Proc.new)
22
+ end
23
+ end
24
+
25
+ def self.parameters(params)
26
+ raise 'You must define a contract first' if @contract.nil?
27
+
28
+ context = params.fetch(:context, {})
29
+ Parameters.new(
30
+ context,
31
+ contract.new(context).call(params)
32
+ )
33
+ end
34
+
35
+ def initialize(params)
36
+ @params = params
37
+ end
38
+
39
+ def result
40
+ raise NotImplementedError
41
+ end
42
+
43
+ private
44
+
45
+ DEFAULT_FAILURE_TYPE = 'error'
46
+
47
+ def fail_with_error_message(message, failure_type = DEFAULT_FAILURE_TYPE)
48
+ new_errors = Errors.new(nil, failure_type)
49
+ new_errors.add(:base, message)
50
+ Dry::Monads::Failure(new_errors)
51
+ end
52
+
53
+ def result_of_validating_params
54
+ @params.to_monad
55
+ end
56
+
57
+ def context(key)
58
+ @params.context(key)
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,10 @@
1
+ # typed: strong
2
+ # frozen_string_literal: true
3
+
4
+ # THIS FILE IS AUTOGENERATED AND SHOULD NOT BE MANUALLY MODIFIED
5
+
6
+ require 'clean_architecture/use_cases/abstract_use_case'
7
+ require 'clean_architecture/use_cases/contract'
8
+ require 'clean_architecture/use_cases/errors'
9
+ require 'clean_architecture/use_cases/form'
10
+ require 'clean_architecture/use_cases/parameters'