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.
- checksums.yaml +4 -4
- data/.github/workflows/rspec.yml +21 -0
- data/.gitignore +1 -0
- data/.ruby-version +1 -1
- data/CHANGELOG.md +42 -0
- data/Gemfile +1 -0
- data/README.md +423 -4
- data/clean-architecture.gemspec +9 -5
- data/generate_require_files.rb +1 -0
- data/lib/clean-architecture.rb +1 -0
- data/lib/clean_architecture/adapters/all.rb +1 -0
- data/lib/clean_architecture/adapters/attribute_hash_base.rb +1 -0
- data/lib/clean_architecture/all.rb +3 -0
- data/lib/clean_architecture/builders/abstract_active_record_entity_builder.rb +124 -0
- data/lib/clean_architecture/builders/all.rb +6 -0
- data/lib/clean_architecture/checks/all.rb +1 -0
- data/lib/clean_architecture/checks/authorization.rb +1 -0
- data/lib/clean_architecture/entities/all.rb +1 -0
- data/lib/clean_architecture/entities/failure_details.rb +2 -1
- data/lib/clean_architecture/entities/targeted_parameters.rb +1 -0
- data/lib/clean_architecture/entities/untargeted_parameters.rb +1 -0
- data/lib/clean_architecture/interfaces/all.rb +1 -0
- data/lib/clean_architecture/interfaces/authorization_parameters.rb +1 -0
- data/lib/clean_architecture/interfaces/base_parameters.rb +1 -0
- data/lib/clean_architecture/interfaces/jsonable.rb +1 -0
- data/lib/clean_architecture/interfaces/success_payload.rb +1 -0
- data/lib/clean_architecture/interfaces/targeted_parameters.rb +1 -0
- data/lib/clean_architecture/interfaces/use_case.rb +1 -0
- data/lib/clean_architecture/interfaces/use_case_actor.rb +1 -0
- data/lib/clean_architecture/interfaces/use_case_target.rb +1 -0
- data/lib/clean_architecture/matchers/all.rb +1 -0
- data/lib/clean_architecture/matchers/use_case_result.rb +1 -0
- data/lib/clean_architecture/queries/all.rb +1 -0
- data/lib/clean_architecture/queries/http_failure_code.rb +1 -0
- data/lib/clean_architecture/queries/http_success_code.rb +1 -0
- data/lib/clean_architecture/serializers/all.rb +1 -0
- data/lib/clean_architecture/serializers/html_response_from_result.rb +1 -0
- data/lib/clean_architecture/serializers/json_response_from_result.rb +1 -0
- data/lib/clean_architecture/serializers/success_collection_payload.rb +1 -0
- data/lib/clean_architecture/serializers/success_payload.rb +1 -0
- data/lib/clean_architecture/types.rb +2 -1
- data/lib/clean_architecture/use_cases/abstract_use_case.rb +62 -0
- data/lib/clean_architecture/use_cases/all.rb +10 -0
- data/lib/clean_architecture/use_cases/contract.rb +9 -0
- data/lib/clean_architecture/use_cases/errors.rb +57 -0
- data/lib/clean_architecture/use_cases/form.rb +116 -0
- data/lib/clean_architecture/use_cases/parameters.rb +42 -0
- data/lib/clean_architecture/version.rb +2 -1
- data/sorbet/config +2 -0
- data/sorbet/rbi/gems/activemodel.rbi +74 -0
- data/sorbet/rbi/gems/activesupport.rbi +440 -0
- data/sorbet/rbi/gems/ast.rbi +47 -0
- data/sorbet/rbi/gems/axiom-types.rbi +159 -0
- data/sorbet/rbi/gems/byebug.rbi +1039 -0
- data/sorbet/rbi/gems/codeclimate-engine-rb.rbi +123 -0
- data/sorbet/rbi/gems/coderay.rbi +91 -0
- data/sorbet/rbi/gems/coercible.rbi +156 -0
- data/sorbet/rbi/gems/concurrent-ruby.rbi +1587 -0
- data/sorbet/rbi/gems/descendants_tracker.rbi +17 -0
- data/sorbet/rbi/gems/docile.rbi +31 -0
- data/sorbet/rbi/gems/dry-configurable.rbi +89 -0
- data/sorbet/rbi/gems/dry-container.rbi +88 -0
- data/sorbet/rbi/gems/dry-core.rbi +79 -0
- data/sorbet/rbi/gems/dry-equalizer.rbi +25 -0
- data/sorbet/rbi/gems/dry-inflector.rbi +72 -0
- data/sorbet/rbi/gems/dry-initializer.rbi +209 -0
- data/sorbet/rbi/gems/dry-logic.rbi +304 -0
- data/sorbet/rbi/gems/dry-matcher.rbi +33 -0
- data/sorbet/rbi/gems/dry-monads.rbi +508 -0
- data/sorbet/rbi/gems/dry-schema.rbi +790 -0
- data/sorbet/rbi/gems/dry-struct.rbi +165 -0
- data/sorbet/rbi/gems/dry-types.rbi +688 -0
- data/sorbet/rbi/gems/dry-validation.rbi +284 -0
- data/sorbet/rbi/gems/duckface-interfaces.rbi +93 -0
- data/sorbet/rbi/gems/equalizer.rbi +22 -0
- data/sorbet/rbi/gems/i18n.rbi +132 -0
- data/sorbet/rbi/gems/ice_nine.rbi +66 -0
- data/sorbet/rbi/gems/jaro_winkler.rbi +14 -0
- data/sorbet/rbi/gems/kwalify.rbi +339 -0
- data/sorbet/rbi/gems/method_source.rbi +63 -0
- data/sorbet/rbi/gems/parallel.rbi +81 -0
- data/sorbet/rbi/gems/parser.rbi +1293 -0
- data/sorbet/rbi/gems/pry-byebug.rbi +149 -0
- data/sorbet/rbi/gems/pry.rbi +1964 -0
- data/sorbet/rbi/gems/psych.rbi +462 -0
- data/sorbet/rbi/gems/rainbow.rbi +117 -0
- data/sorbet/rbi/gems/rake.rbi +634 -0
- data/sorbet/rbi/gems/rb-readline.rbi +766 -0
- data/sorbet/rbi/gems/reek.rbi +1066 -0
- data/sorbet/rbi/gems/rspec-core.rbi +1658 -0
- data/sorbet/rbi/gems/rspec-expectations.rbi +430 -0
- data/sorbet/rbi/gems/rspec-mocks.rbi +815 -0
- data/sorbet/rbi/gems/rspec-support.rbi +268 -0
- data/sorbet/rbi/gems/rspec.rbi +14 -0
- data/sorbet/rbi/gems/rubocop-rspec.rbi +875 -0
- data/sorbet/rbi/gems/rubocop.rbi +7014 -0
- data/sorbet/rbi/gems/ruby-progressbar.rbi +304 -0
- data/sorbet/rbi/gems/simplecov-html.rbi +30 -0
- data/sorbet/rbi/gems/simplecov.rbi +225 -0
- data/sorbet/rbi/gems/stackprof.rbi +51 -0
- data/sorbet/rbi/gems/thread_safe.rbi +81 -0
- data/sorbet/rbi/gems/timecop.rbi +97 -0
- data/sorbet/rbi/gems/unicode-display_width.rbi +16 -0
- data/sorbet/rbi/gems/virtus.rbi +421 -0
- data/sorbet/rbi/hidden-definitions/errors.txt +7332 -0
- data/sorbet/rbi/hidden-definitions/hidden.rbi +17521 -0
- data/sorbet/rbi/sorbet-typed/lib/activemodel/all/activemodel.rbi +422 -0
- data/sorbet/rbi/sorbet-typed/lib/activesupport/>=6.0.0.rc1/activesupport.rbi +23 -0
- data/sorbet/rbi/sorbet-typed/lib/activesupport/all/activesupport.rbi +625 -0
- data/sorbet/rbi/sorbet-typed/lib/bundler/all/bundler.rbi +8684 -0
- data/sorbet/rbi/sorbet-typed/lib/minitest/all/minitest.rbi +99 -0
- data/sorbet/rbi/sorbet-typed/lib/rainbow/all/rainbow.rbi +254 -0
- data/sorbet/rbi/sorbet-typed/lib/ruby/all/gem.rbi +4222 -0
- data/sorbet/rbi/sorbet-typed/lib/ruby/all/open3.rbi +111 -0
- data/sorbet/rbi/sorbet-typed/lib/ruby/all/resolv.rbi +543 -0
- data/sorbet/rbi/todo.rbi +12 -0
- metadata +156 -24
- data/Gemfile.lock +0 -187
data/clean-architecture.gemspec
CHANGED
@@ -19,13 +19,17 @@ Gem::Specification.new do |spec|
|
|
19
19
|
end
|
20
20
|
spec.require_paths = ['lib']
|
21
21
|
|
22
|
-
spec.add_dependency '
|
23
|
-
spec.add_dependency '
|
24
|
-
spec.add_dependency 'dry-
|
25
|
-
spec.add_dependency 'dry-
|
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'
|
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
|
data/generate_require_files.rb
CHANGED
data/lib/clean-architecture.rb
CHANGED
@@ -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
|
@@ -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')
|
@@ -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'
|