piko-lite-box 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 (99) hide show
  1. checksums.yaml +7 -0
  2. data/hanami-3.0.1/CHANGELOG.md +1701 -0
  3. data/hanami-3.0.1/LICENSE +20 -0
  4. data/hanami-3.0.1/README.md +89 -0
  5. data/hanami-3.0.1/hanami.gemspec +49 -0
  6. data/hanami-3.0.1/lib/hanami/app.rb +197 -0
  7. data/hanami-3.0.1/lib/hanami/boot.rb +5 -0
  8. data/hanami-3.0.1/lib/hanami/config/actions/content_security_policy.rb +140 -0
  9. data/hanami-3.0.1/lib/hanami/config/actions/cookies.rb +57 -0
  10. data/hanami-3.0.1/lib/hanami/config/actions/sessions.rb +83 -0
  11. data/hanami-3.0.1/lib/hanami/config/actions.rb +189 -0
  12. data/hanami-3.0.1/lib/hanami/config/assets.rb +84 -0
  13. data/hanami-3.0.1/lib/hanami/config/console.rb +79 -0
  14. data/hanami-3.0.1/lib/hanami/config/db.rb +35 -0
  15. data/hanami-3.0.1/lib/hanami/config/i18n.rb +138 -0
  16. data/hanami-3.0.1/lib/hanami/config/logger.rb +207 -0
  17. data/hanami-3.0.1/lib/hanami/config/null_config.rb +17 -0
  18. data/hanami-3.0.1/lib/hanami/config/router.rb +48 -0
  19. data/hanami-3.0.1/lib/hanami/config/views.rb +103 -0
  20. data/hanami-3.0.1/lib/hanami/config.rb +564 -0
  21. data/hanami-3.0.1/lib/hanami/constants.rb +62 -0
  22. data/hanami-3.0.1/lib/hanami/env.rb +52 -0
  23. data/hanami-3.0.1/lib/hanami/errors.rb +41 -0
  24. data/hanami-3.0.1/lib/hanami/extensions/action/i18n_helper.rb +64 -0
  25. data/hanami-3.0.1/lib/hanami/extensions/action/name_inferrer.rb +34 -0
  26. data/hanami-3.0.1/lib/hanami/extensions/action/slice_configured_action.rb +190 -0
  27. data/hanami-3.0.1/lib/hanami/extensions/action.rb +147 -0
  28. data/hanami-3.0.1/lib/hanami/extensions/db/repo.rb +108 -0
  29. data/hanami-3.0.1/lib/hanami/extensions/mailer/slice_configured_mailer.rb +134 -0
  30. data/hanami-3.0.1/lib/hanami/extensions/mailer.rb +28 -0
  31. data/hanami-3.0.1/lib/hanami/extensions/operation/slice_configured_db_operation.rb +90 -0
  32. data/hanami-3.0.1/lib/hanami/extensions/operation.rb +33 -0
  33. data/hanami-3.0.1/lib/hanami/extensions/router/errors.rb +58 -0
  34. data/hanami-3.0.1/lib/hanami/extensions/view/context.rb +298 -0
  35. data/hanami-3.0.1/lib/hanami/extensions/view/part.rb +92 -0
  36. data/hanami-3.0.1/lib/hanami/extensions/view/scope.rb +36 -0
  37. data/hanami-3.0.1/lib/hanami/extensions/view/slice_configured_context.rb +73 -0
  38. data/hanami-3.0.1/lib/hanami/extensions/view/slice_configured_helpers.rb +55 -0
  39. data/hanami-3.0.1/lib/hanami/extensions/view/slice_configured_part.rb +73 -0
  40. data/hanami-3.0.1/lib/hanami/extensions/view/slice_configured_view.rb +243 -0
  41. data/hanami-3.0.1/lib/hanami/extensions/view/standard_helpers.rb +26 -0
  42. data/hanami-3.0.1/lib/hanami/extensions/view.rb +38 -0
  43. data/hanami-3.0.1/lib/hanami/extensions.rb +30 -0
  44. data/hanami-3.0.1/lib/hanami/helpers/assets_helper.rb +804 -0
  45. data/hanami-3.0.1/lib/hanami/helpers/form_helper/form_builder.rb +1389 -0
  46. data/hanami-3.0.1/lib/hanami/helpers/form_helper/values.rb +75 -0
  47. data/hanami-3.0.1/lib/hanami/helpers/form_helper.rb +213 -0
  48. data/hanami-3.0.1/lib/hanami/helpers/i18n_helper.rb +241 -0
  49. data/hanami-3.0.1/lib/hanami/logger/rack_formatter.rb +73 -0
  50. data/hanami-3.0.1/lib/hanami/logger/sql_formatter.rb +80 -0
  51. data/hanami-3.0.1/lib/hanami/logger/sql_logger.rb +48 -0
  52. data/hanami-3.0.1/lib/hanami/middleware/assets.rb +21 -0
  53. data/hanami-3.0.1/lib/hanami/middleware/content_security_policy_nonce.rb +53 -0
  54. data/hanami-3.0.1/lib/hanami/middleware/public_errors_app.rb +75 -0
  55. data/hanami-3.0.1/lib/hanami/middleware/render_errors.rb +90 -0
  56. data/hanami-3.0.1/lib/hanami/port.rb +45 -0
  57. data/hanami-3.0.1/lib/hanami/prepare.rb +5 -0
  58. data/hanami-3.0.1/lib/hanami/provider/source.rb +16 -0
  59. data/hanami-3.0.1/lib/hanami/provider_registrar.rb +28 -0
  60. data/hanami-3.0.1/lib/hanami/providers/assets.rb +28 -0
  61. data/hanami-3.0.1/lib/hanami/providers/db/adapter.rb +75 -0
  62. data/hanami-3.0.1/lib/hanami/providers/db/adapters.rb +66 -0
  63. data/hanami-3.0.1/lib/hanami/providers/db/config.rb +44 -0
  64. data/hanami-3.0.1/lib/hanami/providers/db/gateway.rb +87 -0
  65. data/hanami-3.0.1/lib/hanami/providers/db/sql_adapter.rb +100 -0
  66. data/hanami-3.0.1/lib/hanami/providers/db.rb +325 -0
  67. data/hanami-3.0.1/lib/hanami/providers/db_logging.rb +19 -0
  68. data/hanami-3.0.1/lib/hanami/providers/i18n/backend.rb +373 -0
  69. data/hanami-3.0.1/lib/hanami/providers/i18n/locale/en.yml +57 -0
  70. data/hanami-3.0.1/lib/hanami/providers/i18n.rb +114 -0
  71. data/hanami-3.0.1/lib/hanami/providers/inflector.rb +17 -0
  72. data/hanami-3.0.1/lib/hanami/providers/logger.rb +41 -0
  73. data/hanami-3.0.1/lib/hanami/providers/mailers.rb +101 -0
  74. data/hanami-3.0.1/lib/hanami/providers/rack.rb +47 -0
  75. data/hanami-3.0.1/lib/hanami/providers/relations.rb +31 -0
  76. data/hanami-3.0.1/lib/hanami/providers/routes.rb +29 -0
  77. data/hanami-3.0.1/lib/hanami/rake_tasks.rb +54 -0
  78. data/hanami-3.0.1/lib/hanami/routes.rb +146 -0
  79. data/hanami-3.0.1/lib/hanami/settings/composite_store.rb +53 -0
  80. data/hanami-3.0.1/lib/hanami/settings/env_store.rb +32 -0
  81. data/hanami-3.0.1/lib/hanami/settings.rb +234 -0
  82. data/hanami-3.0.1/lib/hanami/setup.rb +6 -0
  83. data/hanami-3.0.1/lib/hanami/slice/router.rb +280 -0
  84. data/hanami-3.0.1/lib/hanami/slice/routes_helper.rb +37 -0
  85. data/hanami-3.0.1/lib/hanami/slice/routing/middleware/stack.rb +216 -0
  86. data/hanami-3.0.1/lib/hanami/slice/routing/resolver.rb +88 -0
  87. data/hanami-3.0.1/lib/hanami/slice/view_name_inferrer.rb +63 -0
  88. data/hanami-3.0.1/lib/hanami/slice.rb +1239 -0
  89. data/hanami-3.0.1/lib/hanami/slice_configurable.rb +72 -0
  90. data/hanami-3.0.1/lib/hanami/slice_name.rb +111 -0
  91. data/hanami-3.0.1/lib/hanami/slice_registrar.rb +215 -0
  92. data/hanami-3.0.1/lib/hanami/universal_logger.rb +250 -0
  93. data/hanami-3.0.1/lib/hanami/version.rb +45 -0
  94. data/hanami-3.0.1/lib/hanami/web/rack_logger.rb +126 -0
  95. data/hanami-3.0.1/lib/hanami/web/welcome.html.erb +584 -0
  96. data/hanami-3.0.1/lib/hanami/web/welcome.rb +46 -0
  97. data/hanami-3.0.1/lib/hanami.rb +276 -0
  98. data/piko-lite-box.gemspec +11 -0
  99. metadata +137 -0
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hanami
4
+ module Logger
5
+ # SQL query logger that integrates with the Hanami logger using structured, tagged logging.
6
+ #
7
+ # Subscribes to `:sql` notification events (emitted by ROM via the Hanami app's
8
+ # `"notifications"` component) and logs each query as a structured payload. The log entries are
9
+ # tagged as `:sql`, which allows Dry Logger backends to route them to a dedicated formatter (see
10
+ # {SQLFormatter}), in the same way that rack log entries are routed using the `:rack` tag.
11
+ #
12
+ # @see SQLFormatter
13
+ # @see Hanami::Providers::DBLogging
14
+ #
15
+ # @api private
16
+ class SQLLogger
17
+ attr_reader :logger, :level
18
+
19
+ # @param logger [#tagged, #info] a Hanami-compatible logger (typically a
20
+ # Dry::Logger::Dispatcher or a {Hanami::UniversalLogger}-wrapped logger)
21
+ def initialize(logger, level: :debug)
22
+ @logger = logger
23
+ @level = level
24
+ end
25
+
26
+ # Subscribes to `:sql` notification events.
27
+ #
28
+ # @param notifications [Dry::Monitor::Notifications] the notifications bus
29
+ # @return [void]
30
+ def subscribe(notifications)
31
+ notifications.subscribe(:sql) { |params| log_query(**params) }
32
+ end
33
+
34
+ # Log a SQL query with structured data.
35
+ #
36
+ # @param time [Numeric] elapsed time in milliseconds
37
+ # @param name [Symbol] database adapter name (e.g. `:sqlite`, `:postgres`)
38
+ # @param query [String] the SQL query string
39
+ def log_query(time:, name:, query:)
40
+ logger.tagged(:sql) do
41
+ logger.public_send(@level) do
42
+ {query:, db: name, elapsed: time.round(3), elapsed_unit: "ms"}
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rack/static"
4
+
5
+ module Hanami
6
+ module Middleware
7
+ class Assets < Rack::Static
8
+ def initialize(app, options = {}, config: Hanami.app.config)
9
+ root = config.actions.public_directory
10
+ urls = [config.assets.path_prefix]
11
+
12
+ defaults = {
13
+ root: root,
14
+ urls: urls
15
+ }
16
+
17
+ super(app, defaults.merge(options))
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rack"
4
+ require "securerandom"
5
+ require_relative "../constants"
6
+
7
+ module Hanami
8
+ module Middleware
9
+ # Generates a random per request nonce value like `mSMnSwfVZVe+LyQy1SPCGw==`, stores it as
10
+ # `"hanami.content_security_policy_nonce"` in the Rack environment, and replaces all occurrences
11
+ # of `'nonce'` in the `Content-Security-Policy header with the actual nonce value for the
12
+ # request, e.g. `'nonce-mSMnSwfVZVe+LyQy1SPCGw=='`.
13
+ #
14
+ # @see Hanami::Middleware::ContentSecurityPolicyNonce
15
+ #
16
+ # @api private
17
+ # @since 2.3.0
18
+ class ContentSecurityPolicyNonce
19
+ # @api private
20
+ # @since 2.3.0
21
+ def initialize(app)
22
+ @app = app
23
+ end
24
+
25
+ # @api private
26
+ # @since 2.3.0
27
+ def call(env)
28
+ return @app.call(env) unless Hanami.app.config.actions.content_security_policy?
29
+
30
+ args = nonce_generator.arity == 1 ? [Rack::Request.new(env)] : []
31
+ request_nonce = nonce_generator.call(*args)
32
+
33
+ env[CONTENT_SECURITY_POLICY_NONCE_REQUEST_KEY] = request_nonce
34
+
35
+ _, headers, _ = response = @app.call(env)
36
+
37
+ headers["Content-Security-Policy"] = sub_nonce(headers["Content-Security-Policy"], request_nonce)
38
+
39
+ response
40
+ end
41
+
42
+ private
43
+
44
+ def nonce_generator
45
+ Hanami.app.config.actions.content_security_policy_nonce_generator
46
+ end
47
+
48
+ def sub_nonce(string, nonce)
49
+ string&.gsub("'nonce'", "'nonce-#{nonce}'")
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rack"
4
+
5
+ module Hanami
6
+ module Middleware
7
+ # The errors app given to {Hanami::Middleware::RenderErrors}, which renders error responses
8
+ # from HTML pages kept in `public/` as simple JSON structures.
9
+ #
10
+ # @see Hanami::Middleware::RenderErrors
11
+ #
12
+ # @api private
13
+ # @since 2.1.0
14
+ class PublicErrorsApp
15
+ # @api private
16
+ # @since 2.1.0
17
+ attr_reader :public_path
18
+
19
+ # @api private
20
+ # @since 2.1.0
21
+ def initialize(public_path)
22
+ @public_path = public_path
23
+ end
24
+
25
+ # @api private
26
+ # @since 2.1.0
27
+ def call(env)
28
+ request = Rack::Request.new(env)
29
+ status = request.path_info[1..].to_i
30
+ content_type = request.get_header("HTTP_ACCEPT")
31
+
32
+ default_body = {
33
+ status: status,
34
+ error: Rack::Utils::HTTP_STATUS_CODES.fetch(status, Rack::Utils::HTTP_STATUS_CODES[500])
35
+ }
36
+
37
+ render(status, content_type, default_body)
38
+ end
39
+
40
+ private
41
+
42
+ def render(status, content_type, default_body)
43
+ body, rendered_content_type = render_content(status, content_type, default_body)
44
+
45
+ [
46
+ status,
47
+ {
48
+ "Content-Type" => "#{rendered_content_type}; charset=utf-8",
49
+ "Content-Length" => body.bytesize.to_s
50
+ },
51
+ [body]
52
+ ]
53
+ end
54
+
55
+ def render_content(status, content_type, default_body)
56
+ if content_type.to_s.start_with?("application/json")
57
+ require "json"
58
+ [JSON.generate(default_body), "application/json"]
59
+ else
60
+ [render_html_content(status, default_body), "text/html"]
61
+ end
62
+ end
63
+
64
+ def render_html_content(status, default_body)
65
+ path = "#{public_path}/#{status}.html"
66
+
67
+ if File.exist?(path)
68
+ File.read(path)
69
+ else
70
+ default_body[:error]
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rack"
4
+
5
+ # rubocop:disable Lint/RescueException
6
+
7
+ module Hanami
8
+ module Middleware
9
+ # Rack middleware that rescues errors raised by the app renders friendly error responses, via a
10
+ # given "errors app".
11
+ #
12
+ # By default, this is enabled only in production mode.
13
+ #
14
+ # @see Hanami::Config#render_errors
15
+ # @see Hanani::Middleware::PublicErrorsApp
16
+ #
17
+ # @api private
18
+ # @since 2.1.0
19
+ class RenderErrors
20
+ # @api private
21
+ # @since 2.1.0
22
+ class RenderableException
23
+ attr_reader :exception
24
+ attr_reader :responses
25
+
26
+ # @api private
27
+ # @since 2.1.0
28
+ def initialize(exception, responses:)
29
+ @exception = exception
30
+ @responses = responses
31
+ end
32
+
33
+ # @api private
34
+ # @since 2.1.0
35
+ def rescue_response?
36
+ responses.key?(exception.class.name)
37
+ end
38
+
39
+ # @api private
40
+ # @since 2.1.0
41
+ def status_code
42
+ Rack::Utils.status_code(responses[exception.class.name])
43
+ end
44
+ end
45
+
46
+ # @api private
47
+ # @since 2.1.0
48
+ def initialize(app, config, errors_app)
49
+ @app = app
50
+ @config = config
51
+ @errors_app = errors_app
52
+ end
53
+
54
+ # @api private
55
+ # @since 2.1.0
56
+ def call(env)
57
+ @app.call(env)
58
+ rescue Exception => exception
59
+ raise unless @config.render_errors
60
+
61
+ render_exception(env, exception)
62
+ end
63
+
64
+ private
65
+
66
+ def render_exception(env, exception)
67
+ request = Rack::Request.new(env)
68
+ renderable = RenderableException.new(exception, responses: @config.render_error_responses)
69
+
70
+ status = renderable.status_code
71
+ request.path_info = "/#{status}"
72
+ request.set_header(Rack::REQUEST_METHOD, "GET")
73
+
74
+ @errors_app.call(request.env)
75
+ rescue Exception => exception
76
+ # rubocop:disable Style/StderrPuts
77
+ $stderr.puts "Error during exception rendering: #{exception}\n #{exception.backtrace * "\n "}"
78
+ # rubocop:enable Style/StderrPuts
79
+
80
+ [
81
+ 500,
82
+ {"Content-Type" => "text/plain; charset=utf-8"},
83
+ ["Internal Server Error"]
84
+ ]
85
+ end
86
+ end
87
+ end
88
+ end
89
+
90
+ # rubocop:enable Lint/RescueException
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hanami
4
+ # @since 2.0.1
5
+ # @api private
6
+ module Port
7
+ # @since 2.0.1
8
+ # @api private
9
+ DEFAULT = 2300
10
+
11
+ # @since 2.0.1
12
+ # @api private
13
+ ENV_VAR = "HANAMI_PORT"
14
+
15
+ # @since 2.0.1
16
+ # @api private
17
+ def self.call(value, env = ENV.fetch(ENV_VAR, nil))
18
+ return Integer(value) if !value.nil? && !default?(value)
19
+ return Integer(env) unless env.nil?
20
+ return Integer(value) unless value.nil?
21
+
22
+ DEFAULT
23
+ end
24
+
25
+ # @since 2.0.1
26
+ # @api private
27
+ def self.call!(value)
28
+ return if default?(value)
29
+
30
+ ENV[ENV_VAR] = value.to_s
31
+ end
32
+
33
+ # @since 2.0.1
34
+ # @api private
35
+ def self.default?(value)
36
+ value.to_i == DEFAULT
37
+ end
38
+
39
+ class << self
40
+ # @since 2.0.1
41
+ # @api private
42
+ alias_method :[], :call
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "setup"
4
+
5
+ Hanami.prepare
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hanami
4
+ module Provider
5
+ class Source < Dry::System::Provider::Source
6
+ attr_reader :slice
7
+
8
+ def initialize(slice:, **options, &block)
9
+ @slice = slice
10
+ super(**options, &block)
11
+ end
12
+
13
+ def target_container = slice
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api private
4
+ # @since 2.2.0
5
+ module Hanami
6
+ class ProviderRegistrar < Dry::System::ProviderRegistrar
7
+ def self.for_slice(slice)
8
+ Class.new(self) do
9
+ define_singleton_method(:new) do |container|
10
+ super(container, slice)
11
+ end
12
+ end
13
+ end
14
+
15
+ attr_reader :slice
16
+
17
+ def initialize(container, slice)
18
+ super(container)
19
+ @slice = slice
20
+ end
21
+
22
+ def provider_source_class = Hanami::Provider::Source
23
+
24
+ def provider_source_options
25
+ {slice: slice}
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hanami
4
+ # @api private
5
+ module Providers
6
+ # Provider source to register routes helper component in Hanami slices.
7
+ #
8
+ # @see Hanami::Slice::RoutesHelper
9
+ #
10
+ # @api private
11
+ # @since 2.0.0
12
+ class Assets < Hanami::Provider::Source
13
+ # @api private
14
+ def prepare
15
+ require "hanami/assets"
16
+ end
17
+
18
+ # @api private
19
+ def start
20
+ root = slice.app.root.join("public", "assets", Hanami::Assets.public_assets_dir(target).to_s)
21
+
22
+ assets = Hanami::Assets.new(config: slice.config.assets, root: root)
23
+
24
+ register(:assets, assets)
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dry/configurable"
4
+
5
+ module Hanami
6
+ module Providers
7
+ class DB < Hanami::Provider::Source
8
+ # @api public
9
+ # @since 2.2.0
10
+ class Adapter
11
+ include Dry::Configurable
12
+
13
+ # @api public
14
+ # @since 2.2.0
15
+ setting :plugins, mutable: true
16
+
17
+ # @api private
18
+ def initialize(...)
19
+ @skip_defaults = Hash.new(false)
20
+ end
21
+
22
+ # @api public
23
+ # @since 2.2.0
24
+ def skip_defaults(setting_name = nil)
25
+ @skip_defaults[setting_name] = true
26
+ end
27
+
28
+ # @api private
29
+ private def skip_defaults?(setting_name = nil)
30
+ @skip_defaults[setting_name]
31
+ end
32
+
33
+ # @api private
34
+ def configure_from_adapter(other_adapter)
35
+ return if skip_defaults?
36
+
37
+ plugins.concat(other_adapter.plugins).uniq! unless skip_defaults?(:plugins)
38
+ end
39
+
40
+ # @api private
41
+ def configure_for_database(database_url)
42
+ end
43
+
44
+ # @api public
45
+ # @since 2.2.0
46
+ def plugin(**plugin_spec, &config_block)
47
+ plugins << [plugin_spec, config_block]
48
+ end
49
+
50
+ # @api public
51
+ # @since 2.2.0
52
+ def plugins
53
+ config.plugins ||= []
54
+ end
55
+
56
+ # @api private
57
+ def gateway_cache_keys
58
+ gateway_options
59
+ end
60
+
61
+ # @api private
62
+ def gateway_options
63
+ {}
64
+ end
65
+
66
+ # @api public
67
+ # @since 2.2.0
68
+ def clear
69
+ config.plugins = nil
70
+ self
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hanami
4
+ module Providers
5
+ class DB < Hanami::Provider::Source
6
+ # @api public
7
+ # @since 2.2.0
8
+ class Adapters
9
+ # @api private
10
+ # @since 2.2.0
11
+ ADAPTER_CLASSES = Hash.new(Adapter).update(
12
+ sql: SQLAdapter
13
+ ).freeze
14
+ private_constant :ADAPTER_CLASSES
15
+
16
+ extend Forwardable
17
+
18
+ def_delegators :adapters, :[], :[]=, :each, :to_h
19
+
20
+ # @api private
21
+ # @since 2.2.0
22
+ def self.new_adapter(name)
23
+ ADAPTER_CLASSES[name].new
24
+ end
25
+
26
+ # @api private
27
+ # @since 2.2.0
28
+ attr_reader :adapters
29
+
30
+ # @api private
31
+ # @since 2.2.0
32
+ def initialize
33
+ @adapters = {}
34
+ end
35
+
36
+ # @api private
37
+ # @since 2.2.0
38
+ def initialize_copy(source)
39
+ @adapters = source.adapters.dup
40
+
41
+ source.adapters.each do |key, val|
42
+ @adapters[key] = val.dup
43
+ end
44
+ end
45
+
46
+ # @api private
47
+ # @since 2.2.0
48
+ def adapter(key)
49
+ adapters[key] ||= new(key)
50
+ end
51
+
52
+ # @api private
53
+ # @since 2.2.0
54
+ def find(key)
55
+ adapters.fetch(key) { new(key) }
56
+ end
57
+
58
+ # @api private
59
+ # @since 2.2.0
60
+ def new(key)
61
+ self.class.new_adapter(key)
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dry/core"
4
+
5
+ module Hanami
6
+ module Providers
7
+ class DB < Hanami::Provider::Source
8
+ # @api public
9
+ # @since 2.2.0
10
+ class Config < Dry::Configurable::Config
11
+ include Dry::Core::Constants
12
+
13
+ # @api public
14
+ # @since 2.2.0
15
+ def gateway(key)
16
+ gateway = (gateways[key] ||= Gateway.new)
17
+ yield gateway if block_given?
18
+ gateway
19
+ end
20
+
21
+ # @api public
22
+ # @since 2.2.0
23
+ def adapter(name)
24
+ adapter = adapters.adapter(name)
25
+ yield adapter if block_given?
26
+ adapter
27
+ end
28
+
29
+ # @api private
30
+ def each_plugin
31
+ return to_enum(__method__) unless block_given?
32
+
33
+ gateways.values.group_by(&:adapter_name).each do |adapter_name, adapter_gateways|
34
+ per_adapter_plugins = adapter_gateways.map { _1.adapter.plugins }.flatten(1).uniq
35
+
36
+ per_adapter_plugins.each do |plugin_spec, config_block|
37
+ yield adapter_name, plugin_spec, config_block
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dry/configurable"
4
+ require "dry/core"
5
+
6
+ module Hanami
7
+ module Providers
8
+ class DB < Hanami::Provider::Source
9
+ # @api public
10
+ # @since 2.2.0
11
+ class Gateway
12
+ include Dry::Core::Constants
13
+ include Dry::Configurable
14
+
15
+ setting :database_url
16
+ setting :adapter_name, default: :sql
17
+ setting :adapter, mutable: true
18
+ setting :connection_options, default: {}
19
+
20
+ # @api public
21
+ # @since 2.2.0
22
+ def adapter(name = Undefined)
23
+ return config.adapter if name.eql?(Undefined)
24
+
25
+ if block_given?
26
+ # If a block is given, explicitly configure the gateway's adapter
27
+ config.adapter_name = name
28
+ adapter = (config.adapter ||= Adapters.new_adapter(name))
29
+ yield adapter
30
+ adapter
31
+ else
32
+ # If an adapter name is given without a block, use the default adapter configured with
33
+ # the same name
34
+ config.adapter_name = adapter_name
35
+ end
36
+ end
37
+
38
+ # @api public
39
+ # @since 2.2.0
40
+ def connection_options(**options)
41
+ if options.any?
42
+ config.connection_options.merge!(options)
43
+ end
44
+
45
+ config.connection_options
46
+ end
47
+
48
+ # @api public
49
+ # @since 2.2.0
50
+ def options
51
+ {**connection_options, **config.adapter.gateway_options}
52
+ end
53
+
54
+ # @api private
55
+ def configure_adapter(default_adapters)
56
+ default_adapter = default_adapters.find(config.adapter_name)
57
+ config.adapter ||= default_adapter.dup
58
+
59
+ config.adapter.configure_from_adapter(default_adapter)
60
+
61
+ config.adapter.configure_for_database(config.database_url)
62
+
63
+ self
64
+ end
65
+
66
+ # @api private
67
+ def cache_keys
68
+ [config.database_url, config.connection_options, config.adapter.gateway_cache_keys]
69
+ end
70
+
71
+ private
72
+
73
+ def method_missing(name, *args, &block)
74
+ if config.respond_to?(name)
75
+ config.public_send(name, *args, &block)
76
+ else
77
+ super
78
+ end
79
+ end
80
+
81
+ def respond_to_missing?(name, _include_all = false)
82
+ config.respond_to?(name) || super
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end