solid-adapters 1.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 (39) hide show
  1. checksums.yaml +7 -0
  2. data/.rubocop.yml +3 -0
  3. data/.standard.yml +5 -0
  4. data/CHANGELOG.md +10 -0
  5. data/CODE_OF_CONDUCT.md +132 -0
  6. data/LICENSE.txt +21 -0
  7. data/README.md +418 -0
  8. data/Rakefile +14 -0
  9. data/examples/README.md +15 -0
  10. data/examples/anti_corruption_layer/README.md +217 -0
  11. data/examples/anti_corruption_layer/Rakefile +30 -0
  12. data/examples/anti_corruption_layer/app/models/payment/charge_credit_card.rb +36 -0
  13. data/examples/anti_corruption_layer/config.rb +19 -0
  14. data/examples/anti_corruption_layer/lib/payment_gateways/adapters/circle_up.rb +19 -0
  15. data/examples/anti_corruption_layer/lib/payment_gateways/adapters/pay_friend.rb +19 -0
  16. data/examples/anti_corruption_layer/lib/payment_gateways/contract.rb +15 -0
  17. data/examples/anti_corruption_layer/lib/payment_gateways/response.rb +5 -0
  18. data/examples/anti_corruption_layer/lib/payment_gateways.rb +11 -0
  19. data/examples/anti_corruption_layer/vendor/circle_up/client.rb +11 -0
  20. data/examples/anti_corruption_layer/vendor/pay_friend/client.rb +11 -0
  21. data/examples/ports_and_adapters/README.md +157 -0
  22. data/examples/ports_and_adapters/Rakefile +66 -0
  23. data/examples/ports_and_adapters/app/models/user/record/repository.rb +13 -0
  24. data/examples/ports_and_adapters/app/models/user/record.rb +7 -0
  25. data/examples/ports_and_adapters/config.rb +32 -0
  26. data/examples/ports_and_adapters/db/setup.rb +16 -0
  27. data/examples/ports_and_adapters/lib/user/creation.rb +19 -0
  28. data/examples/ports_and_adapters/lib/user/data.rb +5 -0
  29. data/examples/ports_and_adapters/lib/user/repository.rb +14 -0
  30. data/examples/ports_and_adapters/test/user_test/repository.rb +21 -0
  31. data/lib/solid/adapters/configurable/options.rb +44 -0
  32. data/lib/solid/adapters/configurable.rb +19 -0
  33. data/lib/solid/adapters/core/config.rb +35 -0
  34. data/lib/solid/adapters/core/proxy.rb +25 -0
  35. data/lib/solid/adapters/interface.rb +57 -0
  36. data/lib/solid/adapters/proxy.rb +11 -0
  37. data/lib/solid/adapters/version.rb +7 -0
  38. data/lib/solid/adapters.rb +28 -0
  39. metadata +85 -0
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/all'
4
+
5
+ ActiveRecord::Base.establish_connection(
6
+ host: 'localhost',
7
+ adapter: 'sqlite3',
8
+ database: ':memory:'
9
+ )
10
+
11
+ ActiveRecord::Schema.define do
12
+ create_table :users do |t|
13
+ t.column :name, :string
14
+ t.column :email, :string
15
+ end
16
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module User
4
+ class Creation
5
+ def initialize(repository:)
6
+ repository => Repository
7
+
8
+ @repository = repository
9
+ end
10
+
11
+ def call(name:, email:)
12
+ user_data = @repository.create(name:, email:)
13
+
14
+ puts "Created user: #{user_data.inspect}"
15
+
16
+ user_data
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module User
4
+ Data = ::Struct.new(:id, :name, :email, keyword_init: true)
5
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module User::Repository
4
+ include Solid::Adapters::Interface
5
+
6
+ module Methods
7
+ def create(name:, email:)
8
+ name => String
9
+ email => String
10
+
11
+ super.tap { _1 => ::User::Data[id: Integer, name: String, email: String] }
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module UserTest
4
+ class Repository
5
+ include ::User::Repository
6
+
7
+ attr_reader :records
8
+
9
+ def initialize
10
+ @records = []
11
+ end
12
+
13
+ def create(name:, email:)
14
+ id = @records.size + 1
15
+
16
+ @records[id] = { id:, name:, email: }
17
+
18
+ ::User::Data.new(id:, name:, email:)
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Solid::Adapters::Configurable::Options
4
+ MapOption = ->(key) { key.end_with?("=") ? key[0..-2].to_sym : key }
5
+ MapValue = ->(value) { value.is_a?(::Proc) ? value.call : value }
6
+
7
+ def initialize(**options)
8
+ @options = options
9
+ end
10
+
11
+ def to_h
12
+ @options.dup
13
+ end
14
+
15
+ def key?(name)
16
+ @options.key?(name)
17
+ end
18
+
19
+ def [](name)
20
+ @options[name].then(&MapValue)
21
+ end
22
+
23
+ def fetch(name, &block)
24
+ @options.fetch(name, &block).then(&MapValue)
25
+ end
26
+
27
+ def method_missing(method_name, value = nil, &block)
28
+ return fetch(method_name) { super } if !method_name.end_with?("=") && !block
29
+
30
+ option_name = MapOption[method_name]
31
+
32
+ @options[option_name] = block || value
33
+ end
34
+
35
+ def respond_to_missing?(method_name, include_private = false)
36
+ (method_name.end_with?("=") || key?(method_name)) || super
37
+ end
38
+
39
+ def freeze
40
+ @options.freeze
41
+
42
+ super
43
+ end
44
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solid::Adapters
4
+ module Configurable
5
+ require_relative "configurable/options"
6
+
7
+ def config
8
+ @config ||= Options.new
9
+ end
10
+
11
+ def configuration(freeze: true)
12
+ yield(config)
13
+
14
+ config.tap { _1.freeze if freeze }
15
+ end
16
+
17
+ alias_method :configure, :configuration
18
+ end
19
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solid::Adapters::Core
4
+ class Config
5
+ attr_accessor :proxy_enabled, :interface_enabled
6
+
7
+ def initialize(proxy_enabled: true, interface_enabled: true)
8
+ self.proxy_enabled = proxy_enabled
9
+ self.interface_enabled = interface_enabled
10
+ end
11
+
12
+ def proxy_enabled?
13
+ proxy_enabled
14
+ end
15
+
16
+ def interface_enabled?
17
+ interface_enabled
18
+ end
19
+
20
+ def options
21
+ {
22
+ proxy_enabled: proxy_enabled,
23
+ interface_enabled: interface_enabled
24
+ }
25
+ end
26
+
27
+ def inspect
28
+ "#<#{self.class.name} proxy_enabled=#{proxy_enabled}, interface_enabled=#{interface_enabled}>"
29
+ end
30
+
31
+ @instance = new
32
+
33
+ singleton_class.send(:attr_reader, :instance)
34
+ end
35
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solid::Adapters::Core
4
+ module Proxy
5
+ module ClassMethods
6
+ def [](object)
7
+ new(object)
8
+ end
9
+
10
+ def to_proc
11
+ ->(object) { new(object) }
12
+ end
13
+ end
14
+
15
+ class Base
16
+ extend ClassMethods
17
+
18
+ attr_reader :object
19
+
20
+ def initialize(object)
21
+ @object = object
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "delegate"
4
+
5
+ module Solid::Adapters
6
+ module Interface
7
+ module Callbacks
8
+ def extended(impl)
9
+ impl.singleton_class.prepend(self::Methods)
10
+ end
11
+
12
+ def included(impl)
13
+ impl.prepend(self::Methods)
14
+ end
15
+ end
16
+
17
+ module ClassMethods
18
+ def [](object)
19
+ const_get(:Proxy, false).new(object).extend(self)
20
+ end
21
+ end
22
+
23
+ module ProxyDisabled
24
+ extend Core::Proxy::ClassMethods
25
+
26
+ def self.new(object)
27
+ object
28
+ end
29
+ end
30
+
31
+ DEFINE = lambda do |interface, enabled:|
32
+ proxy = ProxyDisabled
33
+
34
+ if enabled
35
+ proxy = ::Class.new(::SimpleDelegator)
36
+ proxy.extend(Core::Proxy::ClassMethods)
37
+
38
+ interface.extend(Callbacks)
39
+ end
40
+
41
+ interface.const_set(:Proxy, proxy)
42
+ interface.extend(ClassMethods)
43
+ end
44
+
45
+ def self.included(interface)
46
+ DEFINE[interface, enabled: Core::Config.instance.interface_enabled]
47
+ end
48
+
49
+ module AlwaysEnabled
50
+ def self.included(interface)
51
+ DEFINE[interface, enabled: true]
52
+ end
53
+ end
54
+
55
+ private_constant :Callbacks, :ClassMethods, :ProxyDisabled, :DEFINE
56
+ end
57
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solid::Adapters
4
+ class Proxy < Core::Proxy::Base
5
+ AlwaysEnabled = ::Class.new(Core::Proxy::Base)
6
+
7
+ def self.new(object)
8
+ Core::Config.instance.proxy_enabled ? super : object
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solid
4
+ module Adapters
5
+ VERSION = "1.0.0"
6
+ end
7
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "adapters/version"
4
+
5
+ module Solid
6
+ module Adapters
7
+ module Core
8
+ require_relative "adapters/core/config"
9
+ require_relative "adapters/core/proxy"
10
+ end
11
+
12
+ require_relative "adapters/configurable"
13
+ require_relative "adapters/interface"
14
+ require_relative "adapters/proxy"
15
+
16
+ def self.config
17
+ Core::Config.instance
18
+ end
19
+
20
+ def self.configuration(freeze: true)
21
+ yield(config)
22
+
23
+ config.tap { _1.freeze if freeze }
24
+ end
25
+
26
+ singleton_class.send(:alias_method, :configure, :configuration)
27
+ end
28
+ end
metadata ADDED
@@ -0,0 +1,85 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: solid-adapters
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Rodrigo Serradura
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2024-06-24 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Write interface contracts using pure Ruby.
14
+ email:
15
+ - rodrigo.serradura@gmail.com
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - ".rubocop.yml"
21
+ - ".standard.yml"
22
+ - CHANGELOG.md
23
+ - CODE_OF_CONDUCT.md
24
+ - LICENSE.txt
25
+ - README.md
26
+ - Rakefile
27
+ - examples/README.md
28
+ - examples/anti_corruption_layer/README.md
29
+ - examples/anti_corruption_layer/Rakefile
30
+ - examples/anti_corruption_layer/app/models/payment/charge_credit_card.rb
31
+ - examples/anti_corruption_layer/config.rb
32
+ - examples/anti_corruption_layer/lib/payment_gateways.rb
33
+ - examples/anti_corruption_layer/lib/payment_gateways/adapters/circle_up.rb
34
+ - examples/anti_corruption_layer/lib/payment_gateways/adapters/pay_friend.rb
35
+ - examples/anti_corruption_layer/lib/payment_gateways/contract.rb
36
+ - examples/anti_corruption_layer/lib/payment_gateways/response.rb
37
+ - examples/anti_corruption_layer/vendor/circle_up/client.rb
38
+ - examples/anti_corruption_layer/vendor/pay_friend/client.rb
39
+ - examples/ports_and_adapters/README.md
40
+ - examples/ports_and_adapters/Rakefile
41
+ - examples/ports_and_adapters/app/models/user/record.rb
42
+ - examples/ports_and_adapters/app/models/user/record/repository.rb
43
+ - examples/ports_and_adapters/config.rb
44
+ - examples/ports_and_adapters/db/setup.rb
45
+ - examples/ports_and_adapters/lib/user/creation.rb
46
+ - examples/ports_and_adapters/lib/user/data.rb
47
+ - examples/ports_and_adapters/lib/user/repository.rb
48
+ - examples/ports_and_adapters/test/user_test/repository.rb
49
+ - lib/solid/adapters.rb
50
+ - lib/solid/adapters/configurable.rb
51
+ - lib/solid/adapters/configurable/options.rb
52
+ - lib/solid/adapters/core/config.rb
53
+ - lib/solid/adapters/core/proxy.rb
54
+ - lib/solid/adapters/interface.rb
55
+ - lib/solid/adapters/proxy.rb
56
+ - lib/solid/adapters/version.rb
57
+ homepage: https://github.com/serradura/solid-adapters
58
+ licenses:
59
+ - MIT
60
+ metadata:
61
+ allowed_push_host: https://rubygems.org
62
+ homepage_uri: https://github.com/serradura/solid-adapters
63
+ source_code_uri: https://github.com/serradura/solid-adapters
64
+ changelog_uri: https://github.com/solid-process/solid-adapters/blob/main/CHANGELOG.md
65
+ rubygems_mfa_required: 'true'
66
+ post_install_message:
67
+ rdoc_options: []
68
+ require_paths:
69
+ - lib
70
+ required_ruby_version: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: 2.7.0
75
+ required_rubygems_version: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ version: '0'
80
+ requirements: []
81
+ rubygems_version: 3.5.14
82
+ signing_key:
83
+ specification_version: 4
84
+ summary: Write interface contracts using pure Ruby.
85
+ test_files: []