clean-architecture 0.0.1

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 (45) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +1 -0
  3. data/.overcommit.yml +41 -0
  4. data/.reek +16 -0
  5. data/.rspec +2 -0
  6. data/.rubocop.yml +57 -0
  7. data/.ruby-version +1 -0
  8. data/CHANGELOG.md +3 -0
  9. data/Gemfile +24 -0
  10. data/Gemfile.lock +160 -0
  11. data/Guardfile +111 -0
  12. data/README.md +17 -0
  13. data/Rakefile +8 -0
  14. data/clean-architecture.gemspec +29 -0
  15. data/generate_require_files.rb +49 -0
  16. data/lib/clean-architecture.rb +3 -0
  17. data/lib/clean_architecture/all.rb +11 -0
  18. data/lib/clean_architecture/entities/all.rb +7 -0
  19. data/lib/clean_architecture/entities/targeted_parameters.rb +20 -0
  20. data/lib/clean_architecture/entities/untargeted_parameters.rb +18 -0
  21. data/lib/clean_architecture/entities/use_case_history_entry.rb +52 -0
  22. data/lib/clean_architecture/interfaces/all.rb +17 -0
  23. data/lib/clean_architecture/interfaces/authorization_check.rb +15 -0
  24. data/lib/clean_architecture/interfaces/authorization_parameters.rb +18 -0
  25. data/lib/clean_architecture/interfaces/base_parameters.rb +23 -0
  26. data/lib/clean_architecture/interfaces/command.rb +23 -0
  27. data/lib/clean_architecture/interfaces/jsonable.rb +15 -0
  28. data/lib/clean_architecture/interfaces/persistence.rb +15 -0
  29. data/lib/clean_architecture/interfaces/strategy.rb +17 -0
  30. data/lib/clean_architecture/interfaces/success_payload.rb +19 -0
  31. data/lib/clean_architecture/interfaces/targeted_parameters.rb +18 -0
  32. data/lib/clean_architecture/interfaces/use_case.rb +23 -0
  33. data/lib/clean_architecture/interfaces/use_case_actor.rb +19 -0
  34. data/lib/clean_architecture/interfaces/use_case_history_entry.rb +39 -0
  35. data/lib/clean_architecture/interfaces/use_case_target.rb +23 -0
  36. data/lib/clean_architecture/queries/all.rb +5 -0
  37. data/lib/clean_architecture/queries/http_success_code.rb +28 -0
  38. data/lib/clean_architecture/serializers/all.rb +6 -0
  39. data/lib/clean_architecture/serializers/html_response_from_result.rb +23 -0
  40. data/lib/clean_architecture/serializers/json_response_from_result.rb +67 -0
  41. data/lib/clean_architecture/strategies/actor_gets_authorized_then_does_work.rb +30 -0
  42. data/lib/clean_architecture/strategies/all.rb +6 -0
  43. data/lib/clean_architecture/strategies/with_audit_trail.rb +40 -0
  44. data/lib/clean_architecture/version.rb +5 -0
  45. metadata +170 -0
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ task default: :spec
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path('../lib', __FILE__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require 'clean_architecture/version'
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = 'clean-architecture'
9
+ spec.version = CleanArchitecture::VERSION
10
+ spec.authors = ['Bellroy Tech Team']
11
+ spec.email = ['tech@bellroy.com']
12
+
13
+ spec.summary = 'Bellroy Clean Architecture Framework'
14
+ spec.description = 'An attempt at building a reusable Clean Architecture framework for Ruby'
15
+ spec.homepage = 'https://bellroy.com'
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
18
+ f.match(%r{^(test|spec|features)/})
19
+ end
20
+ spec.require_paths = ['lib']
21
+
22
+ spec.add_dependency 'dry-matcher'
23
+ spec.add_dependency 'dry-monads'
24
+ spec.add_dependency 'duckface-interfaces'
25
+
26
+ spec.add_development_dependency 'bundler', '>= 1.13'
27
+ spec.add_development_dependency 'rake', '>= 10.0'
28
+ spec.add_development_dependency 'rspec', '>= 3.0'
29
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ def directories_in(root)
4
+ Dir.entries(root).sort.select do |entry|
5
+ fully_qualified_entry = File.join(root, entry)
6
+ File.directory?(fully_qualified_entry) && !['.', '..'].include?(entry.to_s)
7
+ end
8
+ end
9
+
10
+ def files_in(root)
11
+ Dir.entries(root).sort.reject do |entry|
12
+ fully_qualified_entry = File.join(root, entry)
13
+ File.directory?(fully_qualified_entry) || entry.to_s == 'all.rb' || entry[-3..-1] != '.rb'
14
+ end
15
+ end
16
+
17
+ def recursive_files_in(root)
18
+ full_paths = Dir.glob(File.join(root, '**', '*')).sort.reject do |entry|
19
+ fully_qualified_entry = File.join(root, entry)
20
+ File.directory?(fully_qualified_entry) || entry[-7..-1] == '/all.rb' || entry[-3..-1] != '.rb'
21
+ end
22
+
23
+ remove_path = root.split('/')[0..-2].join('/') + '/'
24
+ full_paths.map { |full_path| full_path.gsub(remove_path, '') }
25
+ end
26
+
27
+ def write_require_file(root, require_prefix, require_directories, require_files)
28
+ require_file_path = File.join(root, 'all.rb')
29
+ File.open(require_file_path, 'w') do |file|
30
+ file.write("# frozen_string_literal: true\n\n")
31
+ file.write("# THIS FILE IS AUTOGENERATED AND SHOULD NOT BE MANUALLY MODIFIED\n\n")
32
+ require_directories.each do |require_directory|
33
+ file.write("require '#{require_prefix}/#{require_directory}/all'\n")
34
+ end
35
+ file.write("\n") unless require_directories.empty?
36
+ require_files.each { |require_file| file.write("require '#{require_prefix}/#{require_file[0..-4]}'\n") }
37
+ end
38
+ end
39
+
40
+ lib_directories = ['clean_architecture']
41
+ lib_directories.each do |lib_directory|
42
+ root = File.join(__dir__, 'lib', lib_directory)
43
+ subdirectories = directories_in(root)
44
+ write_require_file(root, lib_directory, subdirectories, files_in(root))
45
+ subdirectories.each do |subdirectory|
46
+ fully_qualified_directory = File.join(root, subdirectory)
47
+ write_require_file(fully_qualified_directory, lib_directory, [], recursive_files_in(fully_qualified_directory))
48
+ end
49
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'clean_architecture/all'
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ # THIS FILE IS AUTOGENERATED AND SHOULD NOT BE MANUALLY MODIFIED
4
+
5
+ require 'clean_architecture/interfaces/all'
6
+ require 'clean_architecture/entities/all'
7
+ require 'clean_architecture/queries/all'
8
+ require 'clean_architecture/serializers/all'
9
+ require 'clean_architecture/strategies/all'
10
+
11
+ require 'clean_architecture/version'
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ # THIS FILE IS AUTOGENERATED AND SHOULD NOT BE MANUALLY MODIFIED
4
+
5
+ require 'clean_architecture/entities/targeted_parameters'
6
+ require 'clean_architecture/entities/untargeted_parameters'
7
+ require 'clean_architecture/entities/use_case_history_entry'
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CleanArchitecture
4
+ module Entities
5
+ class TargetedParameters
6
+ attr_reader :actor, :extra_parameters_hash, :persistence, :target, :settings
7
+
8
+ implements_interface Interfaces::TargetedParameters
9
+ implements_interface Interfaces::AuthorizationParameters
10
+
11
+ def initialize(actor, target, extra_parameters_hash, persistence, settings)
12
+ @actor = actor
13
+ @target = target
14
+ @extra_parameters_hash = extra_parameters_hash
15
+ @persistence = persistence
16
+ @settings = settings
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CleanArchitecture
4
+ module Entities
5
+ class UntargetedParameters
6
+ attr_reader :actor, :extra_parameters_hash, :persistence, :settings
7
+
8
+ implements_interface Interfaces::BaseParameters
9
+
10
+ def initialize(actor, extra_parameters_hash, persistence, settings)
11
+ @actor = actor
12
+ @extra_parameters_hash = extra_parameters_hash
13
+ @persistence = persistence
14
+ @settings = settings
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'dry-monads'
4
+ require 'dry/matcher/result_matcher'
5
+ require 'duckface'
6
+
7
+ module CleanArchitecture
8
+ module Entities
9
+ class UseCaseHistoryEntry
10
+ implements_interface Interfaces::UseCaseHistoryEntry
11
+ extend Forwardable
12
+
13
+ def initialize(use_case_class, use_case_parameters, use_case_result, use_case_target)
14
+ @use_case_class = use_case_class
15
+ @use_case_parameters = use_case_parameters
16
+ @use_case_result = use_case_result
17
+ @use_case_target = use_case_target
18
+ end
19
+
20
+ def extra_parameters_hash
21
+ @use_case_parameters.extra_parameters_hash
22
+ end
23
+
24
+ def failure_messages
25
+ Dry::Matcher::ResultMatcher.call(@use_case_result) do |matcher|
26
+ matcher.success { nil }
27
+ matcher.failure { |value| value }
28
+ end
29
+ end
30
+
31
+ def prior_target_state
32
+ @use_case_target.attribute_hash
33
+ end
34
+
35
+ def succeeded?
36
+ @use_case_result.success?
37
+ end
38
+
39
+ def target_identifier
40
+ @use_case_target.identifier
41
+ end
42
+
43
+ def use_case_class_name
44
+ @use_case_class.name
45
+ end
46
+
47
+ def user_identifier
48
+ @use_case_parameters.actor.user_identifier
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ # THIS FILE IS AUTOGENERATED AND SHOULD NOT BE MANUALLY MODIFIED
4
+
5
+ require 'clean_architecture/interfaces/authorization_check'
6
+ require 'clean_architecture/interfaces/authorization_parameters'
7
+ require 'clean_architecture/interfaces/base_parameters'
8
+ require 'clean_architecture/interfaces/command'
9
+ require 'clean_architecture/interfaces/jsonable'
10
+ require 'clean_architecture/interfaces/persistence'
11
+ require 'clean_architecture/interfaces/strategy'
12
+ require 'clean_architecture/interfaces/success_payload'
13
+ require 'clean_architecture/interfaces/targeted_parameters'
14
+ require 'clean_architecture/interfaces/use_case'
15
+ require 'clean_architecture/interfaces/use_case_actor'
16
+ require 'clean_architecture/interfaces/use_case_history_entry'
17
+ require 'clean_architecture/interfaces/use_case_target'
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'duckface'
4
+
5
+ module CleanArchitecture
6
+ module Interfaces
7
+ module AuthorizationCheck
8
+ extend Duckface::ActsAsInterface
9
+
10
+ def authorized?
11
+ raise NotImplementedError
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'clean_architecture/interfaces/base_parameters'
4
+ require 'duckface'
5
+
6
+ module CleanArchitecture
7
+ module Interfaces
8
+ module AuthorizationParameters
9
+ extend Duckface::ActsAsInterface
10
+
11
+ include BaseParameters
12
+
13
+ def actor
14
+ raise NotImplementedError
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'duckface'
4
+
5
+ module CleanArchitecture
6
+ module Interfaces
7
+ module BaseParameters
8
+ extend Duckface::ActsAsInterface
9
+
10
+ def extra_parameters_hash
11
+ raise NotImplementedError
12
+ end
13
+
14
+ def persistence
15
+ raise NotImplementedError
16
+ end
17
+
18
+ def settings
19
+ raise NotImplementedError
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'duckface'
4
+
5
+ module CleanArchitecture
6
+ module Interfaces
7
+ module Command
8
+ extend Duckface::ActsAsInterface
9
+
10
+ def initialize(_parameters)
11
+ raise NotImplementedError
12
+ end
13
+
14
+ def parameters
15
+ raise NotImplementedError
16
+ end
17
+
18
+ def result
19
+ raise NotImplementedError
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'duckface'
4
+
5
+ module CleanArchitecture
6
+ module Interfaces
7
+ module Jsonable
8
+ extend Duckface::ActsAsInterface
9
+
10
+ def to_json
11
+ raise NotImplementedError
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'duckface'
4
+
5
+ module CleanArchitecture
6
+ module Interfaces
7
+ module Persistence
8
+ extend Duckface::ActsAsInterface
9
+
10
+ def create_use_case_history_entry(_use_case_history_entry)
11
+ raise NotImplementedError
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CleanArchitecture
4
+ module Interfaces
5
+ module Strategy
6
+ extend Duckface::ActsAsInterface
7
+
8
+ def parameters
9
+ raise NotImplementedError
10
+ end
11
+
12
+ def result
13
+ raise NotImplementedError
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'duckface'
4
+
5
+ module CleanArchitecture
6
+ module Interfaces
7
+ module SuccessPayload
8
+ extend Duckface::ActsAsInterface
9
+
10
+ def data_hash
11
+ raise NotImplementedError
12
+ end
13
+
14
+ def version
15
+ raise NotImplementedError
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'clean_architecture/interfaces/base_parameters'
4
+ require 'duckface'
5
+
6
+ module CleanArchitecture
7
+ module Interfaces
8
+ module TargetedParameters
9
+ extend Duckface::ActsAsInterface
10
+
11
+ include BaseParameters
12
+
13
+ def target
14
+ raise NotImplementedError
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'duckface'
4
+
5
+ module CleanArchitecture
6
+ module Interfaces
7
+ module UseCase
8
+ extend Duckface::ActsAsInterface
9
+
10
+ def initialize(_parameters)
11
+ raise NotImplementedError
12
+ end
13
+
14
+ def parameters
15
+ raise NotImplementedError
16
+ end
17
+
18
+ def result
19
+ raise NotImplementedError
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'duckface'
4
+
5
+ module CleanArchitecture
6
+ module Interfaces
7
+ module UseCaseActor
8
+ extend Duckface::ActsAsInterface
9
+
10
+ def authorized?(_action, _target)
11
+ raise NotImplementedError
12
+ end
13
+
14
+ def user_identifier
15
+ raise NotImplementedError
16
+ end
17
+ end
18
+ end
19
+ end