clean-architecture 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.reek.yml +32 -0
- data/.rubocop.yml +3 -11
- data/CHANGELOG.md +6 -0
- data/Gemfile.lock +33 -9
- data/README.md +94 -4
- data/clean-architecture.gemspec +2 -0
- data/lib/clean_architecture/all.rb +2 -0
- data/lib/clean_architecture/entities/all.rb +1 -0
- data/lib/clean_architecture/entities/failure_details.rb +21 -0
- data/lib/clean_architecture/entities/use_case_history_entry.rb +3 -2
- data/lib/clean_architecture/matchers/all.rb +5 -0
- data/lib/clean_architecture/matchers/use_case_result.rb +45 -0
- data/lib/clean_architecture/queries/all.rb +1 -0
- data/lib/clean_architecture/queries/http_failure_code.rb +30 -0
- data/lib/clean_architecture/queries/http_success_code.rb +2 -1
- data/lib/clean_architecture/serializers/html_response_from_result.rb +17 -6
- data/lib/clean_architecture/serializers/json_response_from_result.rb +10 -22
- data/lib/clean_architecture/strategies/actor_gets_authorized_then_does_work.rb +8 -1
- data/lib/clean_architecture/types.rb +7 -0
- data/lib/clean_architecture/version.rb +1 -1
- metadata +36 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5c49173a8914ea9260202535efe792a4a678619d
|
4
|
+
data.tar.gz: 508d35b34541f7b631cf1ea8fd994b4b20e08927
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9cf655b8ae3fddb3071fb12b817b4b3e08863a34e0007a06d15c814c3cbde3d43057f62dca01ade1bfe02aed5634850e5b2852d6c9647a83055f598494cb6ec4
|
7
|
+
data.tar.gz: b92ebb0350cd0d79ed012c0318560bfef0148fa5669a6e4837f1c7f48a87611e44dafea1b6cfa75dc228946e28c34e42b44572f3a4c9c238b9e626d372c3203f
|
data/.reek.yml
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
---
|
2
|
+
detectors:
|
3
|
+
IrresponsibleModule:
|
4
|
+
enabled: false
|
5
|
+
TooManyStatements:
|
6
|
+
enabled: true
|
7
|
+
max_statements: 10
|
8
|
+
NilCheck:
|
9
|
+
enabled: false
|
10
|
+
directories:
|
11
|
+
app/controllers:
|
12
|
+
NestedIterators:
|
13
|
+
max_allowed_nesting: 2
|
14
|
+
UnusedPrivateMethod:
|
15
|
+
enabled: false
|
16
|
+
app/helpers:
|
17
|
+
UtilityFunction:
|
18
|
+
enabled: false
|
19
|
+
exclude_paths:
|
20
|
+
- app/assets
|
21
|
+
- bin
|
22
|
+
- client/node_modules
|
23
|
+
- config
|
24
|
+
- coverage
|
25
|
+
- data
|
26
|
+
- db
|
27
|
+
- dw
|
28
|
+
- log
|
29
|
+
- phrase
|
30
|
+
- public
|
31
|
+
- tmp
|
32
|
+
- vendor
|
data/.rubocop.yml
CHANGED
@@ -1,29 +1,19 @@
|
|
1
1
|
---
|
2
2
|
Documentation:
|
3
3
|
Enabled: false
|
4
|
-
Rails:
|
5
|
-
Enabled: true
|
6
4
|
AllCops:
|
7
5
|
Include:
|
8
|
-
- app/**/*.rb
|
9
6
|
- lib/**/*.rb
|
10
7
|
- spec/**/*.rb
|
11
8
|
Exclude:
|
12
|
-
- app/assets/**/*
|
13
9
|
- bin/**/*
|
14
|
-
- client/node_modules/**/*
|
15
10
|
- config/**/*
|
16
11
|
- coverage/**/*
|
17
|
-
- data/**/*
|
18
|
-
- db/**/*
|
19
|
-
- db_*/**/*
|
20
|
-
- dw/**/*
|
21
12
|
- log/**/*
|
22
|
-
- phrase/**/*
|
23
13
|
- public/**/*
|
24
14
|
- tmp/**/*
|
25
15
|
- vendor/**/*
|
26
|
-
TargetRubyVersion: 2.
|
16
|
+
TargetRubyVersion: 2.4
|
27
17
|
Metrics/LineLength:
|
28
18
|
Max: 100
|
29
19
|
Layout/MultilineMethodCallIndentation:
|
@@ -42,6 +32,8 @@ RSpec/NestedGroups:
|
|
42
32
|
Max: 10
|
43
33
|
RSpec/MessageExpectation:
|
44
34
|
Enabled: false
|
35
|
+
RSpec/MissingExampleGroupArgument:
|
36
|
+
Enabled: false
|
45
37
|
require:
|
46
38
|
- rubocop-rspec
|
47
39
|
- test_prof/rubocop
|
data/CHANGELOG.md
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,9 +1,11 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
clean-architecture (0.
|
4
|
+
clean-architecture (0.2.0)
|
5
5
|
dry-matcher (~> 0.0)
|
6
6
|
dry-monads (~> 1.0)
|
7
|
+
dry-struct (~> 0.0)
|
8
|
+
dry-types (~> 0.0)
|
7
9
|
duckface-interfaces (~> 0.0)
|
8
10
|
|
9
11
|
GEM
|
@@ -25,14 +27,36 @@ GEM
|
|
25
27
|
thread_safe (~> 0.3, >= 0.3.1)
|
26
28
|
diff-lcs (1.3)
|
27
29
|
docile (1.3.1)
|
30
|
+
dry-configurable (0.7.0)
|
31
|
+
concurrent-ruby (~> 1.0)
|
32
|
+
dry-container (0.6.0)
|
33
|
+
concurrent-ruby (~> 1.0)
|
34
|
+
dry-configurable (~> 0.1, >= 0.1.3)
|
28
35
|
dry-core (0.4.7)
|
29
36
|
concurrent-ruby (~> 1.0)
|
30
37
|
dry-equalizer (0.2.1)
|
38
|
+
dry-inflector (0.1.2)
|
39
|
+
dry-logic (0.4.2)
|
40
|
+
dry-container (~> 0.2, >= 0.2.6)
|
41
|
+
dry-core (~> 0.2)
|
42
|
+
dry-equalizer (~> 0.2)
|
31
43
|
dry-matcher (0.7.0)
|
32
|
-
dry-monads (1.0
|
44
|
+
dry-monads (1.1.0)
|
33
45
|
concurrent-ruby (~> 1.0)
|
34
46
|
dry-core (~> 0.4, >= 0.4.4)
|
35
47
|
dry-equalizer
|
48
|
+
dry-struct (0.5.1)
|
49
|
+
dry-core (~> 0.4, >= 0.4.3)
|
50
|
+
dry-equalizer (~> 0.2)
|
51
|
+
dry-types (~> 0.13)
|
52
|
+
ice_nine (~> 0.11)
|
53
|
+
dry-types (0.13.2)
|
54
|
+
concurrent-ruby (~> 1.0)
|
55
|
+
dry-container (~> 0.3)
|
56
|
+
dry-core (~> 0.4, >= 0.4.4)
|
57
|
+
dry-equalizer (~> 0.2)
|
58
|
+
dry-inflector (~> 0.1, >= 0.1.2)
|
59
|
+
dry-logic (~> 0.4, >= 0.4.2)
|
36
60
|
duckface-interfaces (0.0.2)
|
37
61
|
em-websocket (0.5.1)
|
38
62
|
eventmachine (>= 0.12.9)
|
@@ -92,7 +116,7 @@ GEM
|
|
92
116
|
rb-inotify (0.9.10)
|
93
117
|
ffi (>= 0.5.0, < 2)
|
94
118
|
rb-readline (0.5.5)
|
95
|
-
reek (5.0
|
119
|
+
reek (5.2.0)
|
96
120
|
codeclimate-engine-rb (~> 0.4.0)
|
97
121
|
kwalify (~> 0.7.0)
|
98
122
|
parser (>= 2.5.0.0, < 2.6, != 2.5.1.1)
|
@@ -103,14 +127,14 @@ GEM
|
|
103
127
|
rspec-mocks (~> 3.8.0)
|
104
128
|
rspec-core (3.8.0)
|
105
129
|
rspec-support (~> 3.8.0)
|
106
|
-
rspec-expectations (3.8.
|
130
|
+
rspec-expectations (3.8.2)
|
107
131
|
diff-lcs (>= 1.2.0, < 2.0)
|
108
132
|
rspec-support (~> 3.8.0)
|
109
133
|
rspec-mocks (3.8.0)
|
110
134
|
diff-lcs (>= 1.2.0, < 2.0)
|
111
135
|
rspec-support (~> 3.8.0)
|
112
136
|
rspec-support (3.8.0)
|
113
|
-
rubocop (0.
|
137
|
+
rubocop (0.59.2)
|
114
138
|
jaro_winkler (~> 1.5.1)
|
115
139
|
parallel (~> 1.10)
|
116
140
|
parser (>= 2.5, != 2.5.1.1)
|
@@ -118,7 +142,7 @@ GEM
|
|
118
142
|
rainbow (>= 2.2.2, < 4.0)
|
119
143
|
ruby-progressbar (~> 1.7)
|
120
144
|
unicode-display_width (~> 1.0, >= 1.0.1)
|
121
|
-
rubocop-rspec (1.
|
145
|
+
rubocop-rspec (1.30.0)
|
122
146
|
rubocop (>= 0.58.0)
|
123
147
|
ruby-progressbar (1.10.0)
|
124
148
|
ruby_dep (1.5.0)
|
@@ -143,12 +167,12 @@ PLATFORMS
|
|
143
167
|
ruby
|
144
168
|
|
145
169
|
DEPENDENCIES
|
146
|
-
bundler (
|
170
|
+
bundler (~> 1.13)
|
147
171
|
clean-architecture!
|
148
172
|
guard-livereload
|
149
173
|
guard-rspec
|
150
174
|
pry-byebug
|
151
|
-
rake (
|
175
|
+
rake (~> 12.0)
|
152
176
|
rb-fsevent
|
153
177
|
rb-readline
|
154
178
|
reek
|
@@ -160,4 +184,4 @@ DEPENDENCIES
|
|
160
184
|
timecop
|
161
185
|
|
162
186
|
BUNDLED WITH
|
163
|
-
1.16.
|
187
|
+
1.16.4
|
data/README.md
CHANGED
@@ -23,10 +23,11 @@ and decisions about I/O can be deferred until the last possible moment. It relie
|
|
23
23
|
[duckface-interfaces](https://github.com/samuelgiles/duckface) gem to enforce interface
|
24
24
|
implementation.
|
25
25
|
|
26
|
-
|
26
|
+
## Clean architecture principles
|
27
27
|
|
28
|
-
|
29
|
-
|
28
|
+
### Screaming architecture - use cases as an organisational principle
|
29
|
+
|
30
|
+
Uncle Bob suggests that your source code organisation should allow developers to easily find a listing of all use cases your application provides. Here's an example of how this might look in a
|
30
31
|
Rails application.
|
31
32
|
|
32
33
|
```
|
@@ -38,7 +39,96 @@ Rails application.
|
|
38
39
|
- ...
|
39
40
|
```
|
40
41
|
|
41
|
-
|
42
|
+
Note that the use case name contains:
|
43
|
+
|
44
|
+
- the user role
|
45
|
+
- the action
|
46
|
+
- the (sometimes implied) subject
|
47
|
+
|
48
|
+
### Design principles
|
49
|
+
|
50
|
+
#### SRP - The Single Responsibility principle
|
51
|
+
|
52
|
+
> A function should do one, and only one, thing
|
53
|
+
|
54
|
+
We satisfy the SRP by following these rules:
|
55
|
+
|
56
|
+
- An **adapter** is solely responsible for presenting the properties of a business object, or a small number of business objects, in a known interface
|
57
|
+
- A **command** is solely responsible for completing an atomic I/O operation
|
58
|
+
- An **entity** is solely responsible for representing, in memory, a business object whos properties do not come from a single source
|
59
|
+
- An **interface** is a module that represents a contract between two classes
|
60
|
+
- A **serializer** is solely responsible for taking a business object and turning it into a representation made up of purely primitive values
|
61
|
+
- A **strategy** is an algorithm used by commands to compose atomic I/O operations
|
62
|
+
- A **use case** is solely responsible for checking whether an actor has permissions to perform a command, and executing that command if so
|
63
|
+
- A **validator** is solely responsible for validating a business object and returning a validation result
|
64
|
+
|
65
|
+
#### OCP - The Open/Closed Principle, LSP - The Liskov Substitution Principle and DIP - The Dependency Inversion Principle
|
66
|
+
|
67
|
+
> A software artefact should be open for extension but closed for modification
|
68
|
+
|
69
|
+
> A caller should not have to know the type of an object to interact with it
|
70
|
+
|
71
|
+
> Always depend on or derive from a stable abstraction, rather than a volatile concrete class
|
72
|
+
|
73
|
+
We satisfy the OCP, LSP & DIP by following these rules:
|
74
|
+
|
75
|
+
- We create a clean boundary between our business logic, our persistence layer and our application-specific classes using interfaces
|
76
|
+
- We use interfaces wherever possible, allowing concrete implementations of those interfaces to be extended without breaking the contract
|
77
|
+
- We write unit tests against interfaces, never against concrete implementations (unless interfaces don't exist)
|
78
|
+
|
79
|
+
#### ISP - The Interface Segregation Principle
|
80
|
+
|
81
|
+
> Where some actors only use a subset of methods available from an interface, the interface should be split into sub-interfaces supporting each type of caller
|
82
|
+
|
83
|
+
We satisfy the ISP by following these rules:
|
84
|
+
|
85
|
+
- Each functional area of our code is split into folders (under `lib` in Rails projects)
|
86
|
+
- Each functional area defines its own interfaces
|
87
|
+
- Interfaces are not shared between functional areas
|
88
|
+
|
89
|
+
### Component cohesion
|
90
|
+
|
91
|
+
#### REP - The Reuse/Release Equivalence Principle, CCP - The Common Closure Principle & CRP - The Common Reuse Principle
|
92
|
+
|
93
|
+
> Classes and modules that are grouped together into a component should be releasable together
|
94
|
+
|
95
|
+
> Gather into components those changes the change for the same reasons and at the same times.
|
96
|
+
|
97
|
+
> Classes and modules that tend to be reused together should be placed in the same component
|
98
|
+
|
99
|
+
We satisfy the REP, CCP and CRP by:
|
100
|
+
|
101
|
+
- Having team discussions whenever we make decisions about what a new functional area should be called and what it should contain
|
102
|
+
- Ensuring that none of our functional areas make direct reference back to the parent application
|
103
|
+
- Splitting functional areas out into gems when those functional areas change at a different rate than the rest of the codebase
|
104
|
+
- Splitting functional areas out into standalone applications when it makes sense to do so
|
105
|
+
|
106
|
+
### Component coupling
|
107
|
+
|
108
|
+
#### ADP - The Acyclic Dependencies Principle
|
109
|
+
|
110
|
+
> Don't create circular dependencies
|
111
|
+
|
112
|
+
I don't think I need to explain this. Just don't do it. I like explicitly including dependencies using `require` because it actually prevents you from doing this. Rails, in so many ways, makes one lazy.
|
113
|
+
|
114
|
+
#### SDP - The Stable Dependencies Principle
|
115
|
+
|
116
|
+
> A component always have less things depending on it than it depends on
|
117
|
+
|
118
|
+
We satisfy the SDP by:
|
119
|
+
|
120
|
+
- Putting sensible abstractions in place that adhere to the Single Responsibility principle
|
121
|
+
- Not sharing abstractions and entities between multiple functional areas
|
122
|
+
|
123
|
+
#### SAP - The Stable Abstractions Principle
|
124
|
+
|
125
|
+
> A component should be as abstract as it is stable
|
126
|
+
|
127
|
+
We satisfy the SAP by:
|
128
|
+
|
129
|
+
- Thinking hard about the methods and parameters we specify in our interfaces. Are they solving for a general problem? Are we likely to have to change them when requirements change, and how we can avoid that?
|
130
|
+
|
131
|
+
## Practical suggestions for implementation
|
42
132
|
|
43
133
|
* The code that manages your inputs (e.g. a Rails controller) instantiates a persistence layer
|
44
134
|
object
|
data/clean-architecture.gemspec
CHANGED
@@ -21,6 +21,8 @@ Gem::Specification.new do |spec|
|
|
21
21
|
|
22
22
|
spec.add_dependency 'dry-matcher', '~> 0.0'
|
23
23
|
spec.add_dependency 'dry-monads', '~> 1.0'
|
24
|
+
spec.add_dependency 'dry-struct', '~> 0.0'
|
25
|
+
spec.add_dependency 'dry-types', '~> 0.0'
|
24
26
|
spec.add_dependency 'duckface-interfaces', '~> 0.0'
|
25
27
|
|
26
28
|
spec.add_development_dependency 'bundler', '~> 1.13'
|
@@ -5,8 +5,10 @@
|
|
5
5
|
require 'clean_architecture/adapters/all'
|
6
6
|
require 'clean_architecture/entities/all'
|
7
7
|
require 'clean_architecture/interfaces/all'
|
8
|
+
require 'clean_architecture/matchers/all'
|
8
9
|
require 'clean_architecture/queries/all'
|
9
10
|
require 'clean_architecture/serializers/all'
|
10
11
|
require 'clean_architecture/strategies/all'
|
11
12
|
|
13
|
+
require 'clean_architecture/types'
|
12
14
|
require 'clean_architecture/version'
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
# THIS FILE IS AUTOGENERATED AND SHOULD NOT BE MANUALLY MODIFIED
|
4
4
|
|
5
|
+
require 'clean_architecture/entities/failure_details'
|
5
6
|
require 'clean_architecture/entities/targeted_parameters'
|
6
7
|
require 'clean_architecture/entities/untargeted_parameters'
|
7
8
|
require 'clean_architecture/entities/use_case_history_entry'
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'clean_architecture/types'
|
4
|
+
require 'dry/struct'
|
5
|
+
|
6
|
+
module CleanArchitecture
|
7
|
+
module Entities
|
8
|
+
class FailureDetails < Dry::Struct
|
9
|
+
FailureTypes = Types::Strict::String.enum(
|
10
|
+
'error',
|
11
|
+
'expectation_failed',
|
12
|
+
'not_found',
|
13
|
+
'unauthorized'
|
14
|
+
)
|
15
|
+
|
16
|
+
attribute :type, FailureTypes
|
17
|
+
attribute :message, Types::Strict::String
|
18
|
+
attribute :other_properties, Types::Strict::Hash
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -4,6 +4,7 @@ require 'dry-monads'
|
|
4
4
|
require 'dry/matcher/result_matcher'
|
5
5
|
require 'duckface'
|
6
6
|
require 'clean_architecture/interfaces/use_case_history_entry'
|
7
|
+
require 'clean_architecture/matchers/use_case_result'
|
7
8
|
|
8
9
|
module CleanArchitecture
|
9
10
|
module Entities
|
@@ -23,9 +24,9 @@ module CleanArchitecture
|
|
23
24
|
end
|
24
25
|
|
25
26
|
def failure_messages
|
26
|
-
|
27
|
+
Matchers::UseCaseResult.call(@use_case_result) do |matcher|
|
27
28
|
matcher.success { nil }
|
28
|
-
matcher.failure
|
29
|
+
matcher.failure(&:message)
|
29
30
|
end
|
30
31
|
end
|
31
32
|
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'dry-matcher'
|
4
|
+
|
5
|
+
module CleanArchitecture
|
6
|
+
module Matchers
|
7
|
+
class UseCaseResult
|
8
|
+
def self.call(result, &block)
|
9
|
+
new.matcher.call(result, &block)
|
10
|
+
end
|
11
|
+
|
12
|
+
def matcher
|
13
|
+
Dry::Matcher.new(success: success_case, failure: failure_case)
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def success_case
|
19
|
+
Dry::Matcher::Case.new(
|
20
|
+
match: ->(value) { value.is_a?(Dry::Monads::Success) },
|
21
|
+
resolve: ->(value) { value.value! }
|
22
|
+
)
|
23
|
+
end
|
24
|
+
|
25
|
+
def failure_case
|
26
|
+
Dry::Matcher::Case.new(
|
27
|
+
match: ->(value) { value.is_a?(Dry::Monads::Failure) },
|
28
|
+
resolve: ->(value) { resolve_failure_value(value) }
|
29
|
+
)
|
30
|
+
end
|
31
|
+
|
32
|
+
def resolve_failure_value(value)
|
33
|
+
failure = value.failure
|
34
|
+
case failure
|
35
|
+
when String
|
36
|
+
Entities::FailureDetails.new(message: failure, other_properties: {}, type: 'error')
|
37
|
+
when Entities::FailureDetails
|
38
|
+
failure
|
39
|
+
else
|
40
|
+
raise 'Unexpected failure value - must be String or Entities::FailureDetails'
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CleanArchitecture
|
4
|
+
module Queries
|
5
|
+
class HttpFailureCode
|
6
|
+
def initialize(failure_details_type)
|
7
|
+
@failure_details_type = failure_details_type
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_sym
|
11
|
+
code = FAILURE_DETAILS_TYPE_TO_STATUS_CODE[@failure_details_type.to_s.downcase]
|
12
|
+
if code.nil?
|
13
|
+
raise NotImplementedError,
|
14
|
+
"cannot determine failure code for failure details type #{@failure_details_type}"
|
15
|
+
end
|
16
|
+
|
17
|
+
code
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
FAILURE_DETAILS_TYPE_TO_STATUS_CODE = {
|
23
|
+
'error' => :internal_server_error,
|
24
|
+
'expectation_failed' => :expectation_failed,
|
25
|
+
'not_found' => :not_found,
|
26
|
+
'unauthorized' => :unauthorized
|
27
|
+
}.freeze
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -8,10 +8,11 @@ module CleanArchitecture
|
|
8
8
|
end
|
9
9
|
|
10
10
|
def to_sym
|
11
|
-
code = HTTP_METHOD_TO_SUCCESS_CODE[@http_method]
|
11
|
+
code = HTTP_METHOD_TO_SUCCESS_CODE[@http_method.to_s.upcase]
|
12
12
|
if code.nil?
|
13
13
|
raise NotImplementedError, "cannot determine success code for HTTP method #{@http_method}"
|
14
14
|
end
|
15
|
+
|
15
16
|
code
|
16
17
|
end
|
17
18
|
|
@@ -1,7 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require '
|
3
|
+
require 'clean_architecture/entities/failure_details'
|
4
|
+
require 'clean_architecture/matchers/use_case_result'
|
4
5
|
require 'clean_architecture/queries/http_success_code'
|
6
|
+
require 'clean_architecture/queries/http_failure_code'
|
5
7
|
|
6
8
|
module CleanArchitecture
|
7
9
|
module Serializers
|
@@ -12,13 +14,22 @@ module CleanArchitecture
|
|
12
14
|
end
|
13
15
|
|
14
16
|
def to_h
|
15
|
-
|
16
|
-
matcher.success
|
17
|
-
|
18
|
-
end
|
19
|
-
matcher.failure { |error_message| { status: :error, error: error_message } }
|
17
|
+
Matchers::UseCaseResult.call(@result) do |matcher|
|
18
|
+
matcher.success { |data| success_html_response(data) }
|
19
|
+
matcher.failure { |failure_details| failure_html_response(failure_details) }
|
20
20
|
end
|
21
21
|
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def success_html_response(data)
|
26
|
+
{ status: Queries::HttpSuccessCode.new(@http_method).to_sym, data: data }
|
27
|
+
end
|
28
|
+
|
29
|
+
def failure_html_response(failure_details)
|
30
|
+
status = Queries::HttpFailureCode.new(failure_details.type).to_sym
|
31
|
+
{ status: status, error: failure_details.message }
|
32
|
+
end
|
22
33
|
end
|
23
34
|
end
|
24
35
|
end
|
@@ -1,7 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require '
|
4
|
-
require '
|
3
|
+
require 'clean_architecture/entities/failure_details'
|
4
|
+
require 'clean_architecture/matchers/use_case_result'
|
5
|
+
require 'clean_architecture/queries/http_failure_code'
|
5
6
|
require 'clean_architecture/queries/http_success_code'
|
6
7
|
|
7
8
|
module CleanArchitecture
|
@@ -23,22 +24,20 @@ module CleanArchitecture
|
|
23
24
|
private
|
24
25
|
|
25
26
|
def http_status_code
|
26
|
-
|
27
|
+
Matchers::UseCaseResult.call(@result) do |matcher|
|
27
28
|
matcher.success { Queries::HttpSuccessCode.new(@http_method).to_sym }
|
28
|
-
matcher.failure do
|
29
|
-
|
30
|
-
:unauthorized
|
31
|
-
else
|
32
|
-
:expectation_failed
|
33
|
-
end
|
29
|
+
matcher.failure do |failure_value|
|
30
|
+
Queries::HttpFailureCode.new(failure_value.type).to_sym
|
34
31
|
end
|
35
32
|
end
|
36
33
|
end
|
37
34
|
|
38
35
|
def json
|
39
|
-
|
36
|
+
Matchers::UseCaseResult.call(@result) do |matcher|
|
40
37
|
matcher.success { success_payload }
|
41
|
-
matcher.failure
|
38
|
+
matcher.failure do |failure_details|
|
39
|
+
{ jsonapi: json_api_version_hash, errors: [failure_details.message] }
|
40
|
+
end
|
42
41
|
end
|
43
42
|
end
|
44
43
|
|
@@ -49,20 +48,9 @@ module CleanArchitecture
|
|
49
48
|
}
|
50
49
|
end
|
51
50
|
|
52
|
-
def error_payload(failure_message)
|
53
|
-
{
|
54
|
-
jsonapi: json_api_version_hash,
|
55
|
-
errors: [failure_message]
|
56
|
-
}
|
57
|
-
end
|
58
|
-
|
59
51
|
def json_api_version_hash
|
60
52
|
{ version: @success_payload.version }
|
61
53
|
end
|
62
|
-
|
63
|
-
def result_is_authorization_failure?
|
64
|
-
!@result.failure.index('Unauthorized: ').nil?
|
65
|
-
end
|
66
54
|
end
|
67
55
|
end
|
68
56
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'dry/monads/all'
|
4
|
+
require 'clean_architecture/entities/failure_details'
|
4
5
|
require 'clean_architecture/interfaces/strategy'
|
5
6
|
|
6
7
|
module CleanArchitecture
|
@@ -17,12 +18,18 @@ module CleanArchitecture
|
|
17
18
|
|
18
19
|
def_delegator :@sub_strategy, :parameters
|
19
20
|
|
21
|
+
UNAUTHORIZED_FAILURE_DETAILS = Entities::FailureDetails.new(
|
22
|
+
message: 'Unauthorized',
|
23
|
+
other_properties: {},
|
24
|
+
type: 'unauthorized'
|
25
|
+
)
|
26
|
+
|
20
27
|
def result
|
21
28
|
@result ||= begin
|
22
29
|
if @authorization_check.authorized?
|
23
30
|
@sub_strategy.result
|
24
31
|
else
|
25
|
-
Dry::Monads::Failure(
|
32
|
+
Dry::Monads::Failure(UNAUTHORIZED_FAILURE_DETAILS)
|
26
33
|
end
|
27
34
|
end
|
28
35
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: clean-architecture
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Bellroy Tech Team
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-10-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: dry-matcher
|
@@ -38,6 +38,34 @@ dependencies:
|
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '1.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: dry-struct
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0.0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: dry-types
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0.0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0.0'
|
41
69
|
- !ruby/object:Gem::Dependency
|
42
70
|
name: duckface-interfaces
|
43
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -104,6 +132,7 @@ files:
|
|
104
132
|
- ".gitignore"
|
105
133
|
- ".overcommit.yml"
|
106
134
|
- ".reek"
|
135
|
+
- ".reek.yml"
|
107
136
|
- ".rspec"
|
108
137
|
- ".rubocop.yml"
|
109
138
|
- ".ruby-version"
|
@@ -120,6 +149,7 @@ files:
|
|
120
149
|
- lib/clean_architecture/adapters/attribute_hash_base.rb
|
121
150
|
- lib/clean_architecture/all.rb
|
122
151
|
- lib/clean_architecture/entities/all.rb
|
152
|
+
- lib/clean_architecture/entities/failure_details.rb
|
123
153
|
- lib/clean_architecture/entities/targeted_parameters.rb
|
124
154
|
- lib/clean_architecture/entities/untargeted_parameters.rb
|
125
155
|
- lib/clean_architecture/entities/use_case_history_entry.rb
|
@@ -137,7 +167,10 @@ files:
|
|
137
167
|
- lib/clean_architecture/interfaces/use_case_actor.rb
|
138
168
|
- lib/clean_architecture/interfaces/use_case_history_entry.rb
|
139
169
|
- lib/clean_architecture/interfaces/use_case_target.rb
|
170
|
+
- lib/clean_architecture/matchers/all.rb
|
171
|
+
- lib/clean_architecture/matchers/use_case_result.rb
|
140
172
|
- lib/clean_architecture/queries/all.rb
|
173
|
+
- lib/clean_architecture/queries/http_failure_code.rb
|
141
174
|
- lib/clean_architecture/queries/http_success_code.rb
|
142
175
|
- lib/clean_architecture/serializers/all.rb
|
143
176
|
- lib/clean_architecture/serializers/html_response_from_result.rb
|
@@ -146,6 +179,7 @@ files:
|
|
146
179
|
- lib/clean_architecture/strategies/actor_gets_authorized_then_does_work.rb
|
147
180
|
- lib/clean_architecture/strategies/all.rb
|
148
181
|
- lib/clean_architecture/strategies/with_audit_trail.rb
|
182
|
+
- lib/clean_architecture/types.rb
|
149
183
|
- lib/clean_architecture/version.rb
|
150
184
|
homepage: https://bellroy.com
|
151
185
|
licenses: []
|