clean-architecture 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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