hanami 2.0.0.alpha8 → 2.0.0.beta1
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +442 -241
- data/FEATURES.md +30 -9
- data/README.md +1 -3
- data/hanami.gemspec +21 -11
- data/lib/hanami/app.rb +141 -0
- data/lib/hanami/assets/application_configuration.rb +10 -4
- data/lib/hanami/configuration/actions/content_security_policy.rb +5 -5
- data/lib/hanami/configuration/actions/cookies.rb +2 -2
- data/lib/hanami/configuration/actions.rb +10 -4
- data/lib/hanami/configuration/logger.rb +4 -4
- data/lib/hanami/configuration/router.rb +2 -6
- data/lib/hanami/configuration/sessions.rb +1 -1
- data/lib/hanami/configuration/views.rb +9 -4
- data/lib/hanami/configuration.rb +118 -46
- data/lib/hanami/constants.rb +24 -2
- data/lib/hanami/errors.rb +1 -1
- data/lib/hanami/{application → extensions}/action/slice_configured_action.rb +9 -9
- data/lib/hanami/extensions/action.rb +79 -0
- data/lib/hanami/extensions/view/context.rb +106 -0
- data/lib/hanami/{application → extensions}/view/slice_configured_context.rb +10 -10
- data/lib/hanami/{application → extensions}/view/slice_configured_view.rb +12 -6
- data/lib/hanami/extensions/view.rb +33 -0
- data/lib/hanami/extensions.rb +10 -0
- data/lib/hanami/providers/inflector.rb +13 -0
- data/lib/hanami/providers/logger.rb +13 -0
- data/lib/hanami/providers/rack.rb +27 -0
- data/lib/hanami/providers/routes.rb +33 -0
- data/lib/hanami/providers/settings.rb +23 -0
- data/lib/hanami/rake_tasks.rb +61 -0
- data/lib/hanami/routes.rb +51 -0
- data/lib/hanami/server.rb +1 -1
- data/lib/hanami/settings/dotenv_store.rb +58 -0
- data/lib/hanami/settings.rb +90 -0
- data/lib/hanami/setup.rb +4 -2
- data/lib/hanami/{application → slice}/router.rb +18 -13
- data/lib/hanami/slice/routes_helper.rb +37 -0
- data/lib/hanami/{application → slice}/routing/middleware/stack.rb +43 -5
- data/lib/hanami/slice/routing/resolver.rb +97 -0
- data/lib/hanami/{application → slice}/view_name_inferrer.rb +3 -3
- data/lib/hanami/slice.rb +246 -73
- data/lib/hanami/slice_configurable.rb +4 -17
- data/lib/hanami/slice_name.rb +6 -6
- data/lib/hanami/slice_registrar.rb +119 -0
- data/lib/hanami/version.rb +1 -1
- data/lib/hanami/web/rack_logger.rb +1 -1
- data/lib/hanami.rb +34 -26
- data/spec/integration/application_middleware_stack_spec.rb +84 -0
- data/spec/integration/assets/cdn_spec.rb +48 -0
- data/spec/integration/assets/fingerprint_spec.rb +42 -0
- data/spec/integration/assets/helpers_spec.rb +50 -0
- data/spec/integration/assets/serve_spec.rb +70 -0
- data/spec/integration/assets/subresource_integrity_spec.rb +54 -0
- data/spec/integration/body_parsers_spec.rb +50 -0
- data/spec/integration/cli/assets/precompile_spec.rb +147 -0
- data/spec/integration/cli/assets_spec.rb +14 -0
- data/spec/integration/cli/console_spec.rb +105 -0
- data/spec/integration/cli/db/apply_spec.rb +74 -0
- data/spec/integration/cli/db/console_spec.rb +40 -0
- data/spec/integration/cli/db/create_spec.rb +50 -0
- data/spec/integration/cli/db/drop_spec.rb +54 -0
- data/spec/integration/cli/db/migrate_spec.rb +108 -0
- data/spec/integration/cli/db/prepare_spec.rb +36 -0
- data/spec/integration/cli/db/rollback_spec.rb +96 -0
- data/spec/integration/cli/db/version_spec.rb +38 -0
- data/spec/integration/cli/db_spec.rb +21 -0
- data/spec/integration/cli/destroy/action_spec.rb +143 -0
- data/spec/integration/cli/destroy/app_spec.rb +118 -0
- data/spec/integration/cli/destroy/mailer_spec.rb +74 -0
- data/spec/integration/cli/destroy/migration_spec.rb +70 -0
- data/spec/integration/cli/destroy/model_spec.rb +113 -0
- data/spec/integration/cli/destroy_spec.rb +18 -0
- data/spec/integration/cli/generate/action_spec.rb +469 -0
- data/spec/integration/cli/generate/app_spec.rb +215 -0
- data/spec/integration/cli/generate/mailer_spec.rb +189 -0
- data/spec/integration/cli/generate/migration_spec.rb +72 -0
- data/spec/integration/cli/generate/model_spec.rb +290 -0
- data/spec/integration/cli/generate/secret_spec.rb +56 -0
- data/spec/integration/cli/generate_spec.rb +19 -0
- data/spec/integration/cli/new/database_spec.rb +235 -0
- data/spec/integration/cli/new/hanami_head_spec.rb +27 -0
- data/spec/integration/cli/new/template_spec.rb +118 -0
- data/spec/integration/cli/new/test_spec.rb +274 -0
- data/spec/integration/cli/new_spec.rb +970 -0
- data/spec/integration/cli/plugins_spec.rb +39 -0
- data/spec/integration/cli/routes_spec.rb +49 -0
- data/spec/integration/cli/server_spec.rb +626 -0
- data/spec/integration/cli/version_spec.rb +85 -0
- data/spec/integration/early_hints_spec.rb +35 -0
- data/spec/integration/handle_exceptions_spec.rb +244 -0
- data/spec/integration/head_spec.rb +89 -0
- data/spec/integration/http_headers_spec.rb +29 -0
- data/spec/integration/mailer_spec.rb +32 -0
- data/spec/integration/middleware_spec.rb +81 -0
- data/spec/integration/mount_applications_spec.rb +88 -0
- data/spec/integration/project_initializers_spec.rb +40 -0
- data/spec/integration/rackup_spec.rb +35 -0
- data/spec/integration/rake/with_minitest_spec.rb +67 -0
- data/spec/integration/rake/with_rspec_spec.rb +69 -0
- data/spec/integration/routing_helpers_spec.rb +61 -0
- data/spec/integration/security/content_security_policy_spec.rb +46 -0
- data/spec/integration/security/csrf_protection_spec.rb +42 -0
- data/spec/integration/security/force_ssl_spec.rb +29 -0
- data/spec/integration/security/x_content_type_options_spec.rb +46 -0
- data/spec/integration/security/x_frame_options_spec.rb +46 -0
- data/spec/integration/security/x_xss_protection_spec.rb +46 -0
- data/spec/integration/send_file_spec.rb +51 -0
- data/spec/integration/sessions_spec.rb +247 -0
- data/spec/integration/static_middleware_spec.rb +21 -0
- data/spec/integration/streaming_spec.rb +41 -0
- data/spec/integration/unsafe_send_file_spec.rb +52 -0
- data/spec/isolation/hanami/application/already_configured_spec.rb +19 -0
- data/spec/isolation/hanami/application/inherit_anonymous_class_spec.rb +10 -0
- data/spec/isolation/hanami/application/inherit_concrete_class_spec.rb +14 -0
- data/spec/isolation/hanami/application/not_configured_spec.rb +9 -0
- data/spec/isolation/hanami/application/routes/configured_spec.rb +44 -0
- data/spec/isolation/hanami/application/routes/not_configured_spec.rb +16 -0
- data/spec/isolation/hanami/boot/success_spec.rb +50 -0
- data/spec/new_integration/action/configuration_spec.rb +26 -0
- data/spec/new_integration/action/cookies_spec.rb +58 -0
- data/spec/new_integration/action/csrf_protection_spec.rb +54 -0
- data/spec/new_integration/action/routes_spec.rb +73 -0
- data/spec/new_integration/action/sessions_spec.rb +50 -0
- data/spec/new_integration/action/view_integration_spec.rb +165 -0
- data/spec/new_integration/action/view_rendering/automatic_rendering_spec.rb +247 -0
- data/spec/new_integration/action/view_rendering/paired_view_inference_spec.rb +115 -0
- data/spec/new_integration/action/view_rendering_spec.rb +107 -0
- data/spec/new_integration/code_loading/loading_from_app_spec.rb +152 -0
- data/spec/new_integration/code_loading/loading_from_slice_spec.rb +165 -0
- data/spec/new_integration/container/application_routes_helper_spec.rb +48 -0
- data/spec/new_integration/container/auto_injection_spec.rb +53 -0
- data/spec/new_integration/container/auto_registration_spec.rb +86 -0
- data/spec/new_integration/container/autoloader_spec.rb +80 -0
- data/spec/new_integration/container/imports_spec.rb +253 -0
- data/spec/new_integration/container/prepare_container_spec.rb +123 -0
- data/spec/new_integration/container/shutdown_spec.rb +91 -0
- data/spec/new_integration/container/standard_bootable_components_spec.rb +124 -0
- data/spec/new_integration/rack_app/middleware_spec.rb +215 -0
- data/spec/new_integration/rack_app/non_booted_rack_app_spec.rb +105 -0
- data/spec/new_integration/rack_app/rack_app_spec.rb +524 -0
- data/spec/new_integration/settings_spec.rb +115 -0
- data/spec/new_integration/slices/external_slice_spec.rb +92 -0
- data/spec/new_integration/slices/slice_configuration_spec.rb +40 -0
- data/spec/new_integration/slices/slice_routing_spec.rb +226 -0
- data/spec/new_integration/slices/slice_settings_spec.rb +141 -0
- data/spec/new_integration/slices_spec.rb +101 -0
- data/spec/new_integration/view/configuration_spec.rb +49 -0
- data/spec/new_integration/view/context/assets_spec.rb +67 -0
- data/spec/new_integration/view/context/inflector_spec.rb +48 -0
- data/spec/new_integration/view/context/request_spec.rb +61 -0
- data/spec/new_integration/view/context/routes_spec.rb +86 -0
- data/spec/new_integration/view/context/settings_spec.rb +50 -0
- data/spec/new_integration/view/inflector_spec.rb +57 -0
- data/spec/new_integration/view/part_namespace_spec.rb +96 -0
- data/spec/new_integration/view/path_spec.rb +56 -0
- data/spec/new_integration/view/template_spec.rb +68 -0
- data/spec/new_integration/view/views_spec.rb +103 -0
- data/spec/spec_helper.rb +16 -0
- data/spec/support/app_integration.rb +91 -0
- data/spec/support/coverage.rb +1 -0
- data/spec/support/fixtures/hanami-plugin/Gemfile +8 -0
- data/spec/support/fixtures/hanami-plugin/README.md +35 -0
- data/spec/support/fixtures/hanami-plugin/Rakefile +4 -0
- data/spec/support/fixtures/hanami-plugin/bin/console +15 -0
- data/spec/support/fixtures/hanami-plugin/bin/setup +8 -0
- data/spec/support/fixtures/hanami-plugin/hanami-plugin.gemspec +28 -0
- data/spec/support/fixtures/hanami-plugin/lib/hanami/plugin/cli.rb +19 -0
- data/spec/support/fixtures/hanami-plugin/lib/hanami/plugin/version.rb +7 -0
- data/spec/support/fixtures/hanami-plugin/lib/hanami/plugin.rb +8 -0
- data/spec/support/rspec.rb +27 -0
- data/spec/support/shared_examples/cli/generate/app.rb +494 -0
- data/spec/support/shared_examples/cli/generate/migration.rb +32 -0
- data/spec/support/shared_examples/cli/generate/model.rb +81 -0
- data/spec/support/shared_examples/cli/new.rb +97 -0
- data/spec/unit/hanami/configuration/actions/content_security_policy_spec.rb +102 -0
- data/spec/unit/hanami/configuration/actions/cookies_spec.rb +46 -0
- data/spec/unit/hanami/configuration/actions/csrf_protection_spec.rb +57 -0
- data/spec/unit/hanami/configuration/actions/default_values_spec.rb +52 -0
- data/spec/unit/hanami/configuration/actions/sessions_spec.rb +50 -0
- data/spec/unit/hanami/configuration/actions_spec.rb +78 -0
- data/spec/unit/hanami/configuration/base_url_spec.rb +25 -0
- data/spec/unit/hanami/configuration/inflector_spec.rb +35 -0
- data/spec/unit/hanami/configuration/logger_spec.rb +203 -0
- data/spec/unit/hanami/configuration/views_spec.rb +120 -0
- data/spec/unit/hanami/configuration_spec.rb +43 -0
- data/spec/unit/hanami/env_spec.rb +54 -0
- data/spec/unit/hanami/routes_spec.rb +25 -0
- data/spec/unit/hanami/settings/dotenv_store_spec.rb +119 -0
- data/spec/unit/hanami/settings_spec.rb +56 -0
- data/spec/unit/hanami/slice_configurable_spec.rb +104 -0
- data/spec/unit/hanami/slice_name_spec.rb +47 -0
- data/spec/unit/hanami/slice_spec.rb +17 -0
- data/spec/unit/hanami/version_spec.rb +7 -0
- data/spec/unit/hanami/web/rack_logger_spec.rb +78 -0
- metadata +353 -57
- data/lib/hanami/application/action.rb +0 -72
- data/lib/hanami/application/container/providers/inflector.rb +0 -7
- data/lib/hanami/application/container/providers/logger.rb +0 -7
- data/lib/hanami/application/container/providers/rack_logger.rb +0 -15
- data/lib/hanami/application/container/providers/rack_monitor.rb +0 -12
- data/lib/hanami/application/container/providers/routes_helper.rb +0 -9
- data/lib/hanami/application/container/providers/settings.rb +0 -7
- data/lib/hanami/application/routes.rb +0 -55
- data/lib/hanami/application/routes_helper.rb +0 -34
- data/lib/hanami/application/routing/resolver/node.rb +0 -50
- data/lib/hanami/application/routing/resolver/trie.rb +0 -59
- data/lib/hanami/application/routing/resolver.rb +0 -87
- data/lib/hanami/application/routing/router.rb +0 -36
- data/lib/hanami/application/settings/dotenv_store.rb +0 -60
- data/lib/hanami/application/settings.rb +0 -93
- data/lib/hanami/application/slice_registrar.rb +0 -106
- data/lib/hanami/application/view/context.rb +0 -95
- data/lib/hanami/application/view.rb +0 -24
- data/lib/hanami/application.rb +0 -273
- data/lib/hanami/cli/application/cli.rb +0 -40
- data/lib/hanami/cli/application/command.rb +0 -47
- data/lib/hanami/cli/application/commands/console.rb +0 -81
- data/lib/hanami/cli/application/commands.rb +0 -16
- data/lib/hanami/cli/base_command.rb +0 -48
- data/lib/hanami/cli/commands/command.rb +0 -171
- data/lib/hanami/cli/commands/server.rb +0 -88
- data/lib/hanami/cli/commands.rb +0 -65
- data/lib/hanami/configuration/middleware.rb +0 -20
- data/lib/hanami/configuration/source_dirs.rb +0 -42
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "dry/system/provider/source"
|
|
4
|
+
|
|
5
|
+
module Hanami
|
|
6
|
+
module Providers
|
|
7
|
+
class Logger < Dry::System::Provider::Source
|
|
8
|
+
def start
|
|
9
|
+
register :logger, Hanami.app.configuration.logger_instance
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "dry/system/provider/source"
|
|
4
|
+
|
|
5
|
+
module Hanami
|
|
6
|
+
module Providers
|
|
7
|
+
class Rack < Dry::System::Provider::Source
|
|
8
|
+
def prepare
|
|
9
|
+
require "dry/monitor"
|
|
10
|
+
require "hanami/web/rack_logger"
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def start
|
|
14
|
+
target.start :logger
|
|
15
|
+
|
|
16
|
+
notifications = target[:notifications]
|
|
17
|
+
|
|
18
|
+
monitor_middleware = Dry::Monitor::Rack::Middleware.new(notifications)
|
|
19
|
+
|
|
20
|
+
rack_logger = Hanami::Web::RackLogger.new(target[:logger])
|
|
21
|
+
rack_logger.attach(monitor_middleware)
|
|
22
|
+
|
|
23
|
+
register "monitor", monitor_middleware
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "dry/system/provider/source"
|
|
4
|
+
|
|
5
|
+
module Hanami
|
|
6
|
+
module Providers
|
|
7
|
+
class Routes < Dry::System::Provider::Source
|
|
8
|
+
def self.for_slice(slice)
|
|
9
|
+
Class.new(self) do |klass|
|
|
10
|
+
klass.instance_variable_set(:@slice, slice)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def self.slice
|
|
15
|
+
@slice || Hanami.app
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def prepare
|
|
19
|
+
require "hanami/slice/routes_helper"
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def start
|
|
23
|
+
# Register a lazy instance of RoutesHelper to ensure we don't load prematurely
|
|
24
|
+
# load the router during the process of booting. This ensures the router's
|
|
25
|
+
# resolver can run strict action key checks once when it runs on a fully booted
|
|
26
|
+
# slice.
|
|
27
|
+
register :routes do
|
|
28
|
+
Hanami::Slice::RoutesHelper.new(self.class.slice.router)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "dry/system/provider/source"
|
|
4
|
+
|
|
5
|
+
module Hanami
|
|
6
|
+
module Providers
|
|
7
|
+
class Settings < Dry::System::Provider::Source
|
|
8
|
+
def self.for_slice(slice)
|
|
9
|
+
Class.new(self) do |klass|
|
|
10
|
+
klass.instance_variable_set(:@slice, slice)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def self.slice
|
|
15
|
+
@slice || Hanami.app
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def start
|
|
19
|
+
register :settings, self.class.slice.settings
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "hanami/cli/rake_tasks"
|
|
4
|
+
require "hanami/cli/command_line"
|
|
5
|
+
|
|
6
|
+
Hanami::CLI::RakeTasks.register_tasks do
|
|
7
|
+
@_hanami_command_line = Hanami::CLI::CommandLine.new
|
|
8
|
+
|
|
9
|
+
desc "Load the app environment"
|
|
10
|
+
task :environment do
|
|
11
|
+
require "hanami/prepare"
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Ruby ecosystem compatibility
|
|
15
|
+
#
|
|
16
|
+
# Most of the SaaS automatic tasks are designed after Ruby on Rails.
|
|
17
|
+
# They expect the following Rake tasks to be present:
|
|
18
|
+
#
|
|
19
|
+
# * db:migrate
|
|
20
|
+
# * assets:precompile
|
|
21
|
+
#
|
|
22
|
+
# See https://github.com/heroku/heroku-buildpack-ruby/issues/442
|
|
23
|
+
#
|
|
24
|
+
# ===
|
|
25
|
+
#
|
|
26
|
+
# These Rake tasks aren't listed when someone runs `rake -T`, because we
|
|
27
|
+
# want to encourage developers to use `hanami` commands.
|
|
28
|
+
#
|
|
29
|
+
# In order to migrate the database or precompile assets a developer should
|
|
30
|
+
# use:
|
|
31
|
+
#
|
|
32
|
+
# * hanami db migrate
|
|
33
|
+
# * hanami assets precompile
|
|
34
|
+
#
|
|
35
|
+
# This is the preferred way to run Hanami command line tasks.
|
|
36
|
+
# Please use them when you're in control of your deployment environment.
|
|
37
|
+
#
|
|
38
|
+
# If you're not in control and your deployment requires these "standard"
|
|
39
|
+
# Rake tasks, they are here to solve this only specific problem.
|
|
40
|
+
namespace :db do
|
|
41
|
+
task :migrate do
|
|
42
|
+
# TODO(@jodosha): Enable when we'll integrate with ROM
|
|
43
|
+
# run_hanami_command("db migrate")
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
namespace :assets do
|
|
48
|
+
task :precompile do
|
|
49
|
+
# TODO(@jodosha): Enable when we'll integrate with hanami-assets
|
|
50
|
+
# run_hanami_command("assets precompile")
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
private
|
|
55
|
+
|
|
56
|
+
def run_hanami_command(command)
|
|
57
|
+
@_hanami_command_line.call(command)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
Hanami::CLI::RakeTasks.install_tasks
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Hanami
|
|
4
|
+
# App routes
|
|
5
|
+
#
|
|
6
|
+
# Users are expected to inherit from this class to define their app
|
|
7
|
+
# routes.
|
|
8
|
+
#
|
|
9
|
+
# @example
|
|
10
|
+
# # config/routes.rb
|
|
11
|
+
# # frozen_string_literal: true
|
|
12
|
+
#
|
|
13
|
+
# require "hanami/routes"
|
|
14
|
+
#
|
|
15
|
+
# module MyApp
|
|
16
|
+
# class Routes < Hanami::Routes
|
|
17
|
+
# define do
|
|
18
|
+
# root to: "home.show"
|
|
19
|
+
# end
|
|
20
|
+
# end
|
|
21
|
+
# end
|
|
22
|
+
#
|
|
23
|
+
# See {Hanami::Slice::Router} for the syntax allowed within the
|
|
24
|
+
# `define` block.
|
|
25
|
+
#
|
|
26
|
+
# @see Hanami::Slice::Router
|
|
27
|
+
# @since 2.0.0
|
|
28
|
+
class Routes
|
|
29
|
+
# Defines app routes
|
|
30
|
+
#
|
|
31
|
+
# @yield DSL syntax to define app routes executed in the context
|
|
32
|
+
# of {Hanami::Slice::Router}
|
|
33
|
+
#
|
|
34
|
+
# @return [Proc]
|
|
35
|
+
def self.define(&block)
|
|
36
|
+
@_routes = block
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# @api private
|
|
40
|
+
def self.routes
|
|
41
|
+
@_routes || raise(<<~MSG)
|
|
42
|
+
Routes need to be defined before being able to fetch them. E.g.,
|
|
43
|
+
define do
|
|
44
|
+
slice :main, at: "/" do
|
|
45
|
+
root to: "home.show"
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
MSG
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
data/lib/hanami/server.rb
CHANGED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "dry/core/constants"
|
|
4
|
+
|
|
5
|
+
module Hanami
|
|
6
|
+
class Settings
|
|
7
|
+
# Default app settings store.
|
|
8
|
+
#
|
|
9
|
+
# Uses [dotenv](https://github.com/bkeepers/dotenv) (if available) to load
|
|
10
|
+
# .env files and then loads settings from ENV. For a given `HANAMI_ENV`
|
|
11
|
+
# environment, the following `.env` files are looked up in the following order:
|
|
12
|
+
#
|
|
13
|
+
# - .env.{environment}.local
|
|
14
|
+
# - .env.local (except if the environment is `test`)
|
|
15
|
+
# - .env.{environment}
|
|
16
|
+
# - .env
|
|
17
|
+
#
|
|
18
|
+
# @since 2.0.0
|
|
19
|
+
# @api private
|
|
20
|
+
class DotenvStore
|
|
21
|
+
Undefined = Dry::Core::Constants::Undefined
|
|
22
|
+
|
|
23
|
+
attr_reader :store,
|
|
24
|
+
:hanami_env
|
|
25
|
+
|
|
26
|
+
def initialize(store: ENV, hanami_env: Hanami.env)
|
|
27
|
+
@store = store
|
|
28
|
+
@hanami_env = hanami_env
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def fetch(name, default_value = Undefined, &block)
|
|
32
|
+
name = name.to_s.upcase
|
|
33
|
+
args = (default_value == Undefined) ? [name] : [name, default_value]
|
|
34
|
+
|
|
35
|
+
store.fetch(*args, &block)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def with_dotenv_loaded
|
|
39
|
+
require "dotenv"
|
|
40
|
+
Dotenv.load(*dotenv_files) if defined?(Dotenv)
|
|
41
|
+
self
|
|
42
|
+
rescue LoadError
|
|
43
|
+
self
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
private
|
|
47
|
+
|
|
48
|
+
def dotenv_files
|
|
49
|
+
[
|
|
50
|
+
".env.#{hanami_env}.local",
|
|
51
|
+
(".env.local" unless hanami_env == :test),
|
|
52
|
+
".env.#{hanami_env}",
|
|
53
|
+
".env"
|
|
54
|
+
].compact
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "dry/configurable"
|
|
4
|
+
require "dry/core/constants"
|
|
5
|
+
|
|
6
|
+
module Hanami
|
|
7
|
+
# App settings
|
|
8
|
+
#
|
|
9
|
+
# Users are expected to inherit from this class to define their app settings.
|
|
10
|
+
#
|
|
11
|
+
# @example
|
|
12
|
+
# # config/settings.rb
|
|
13
|
+
# # frozen_string_literal: true
|
|
14
|
+
#
|
|
15
|
+
# require "hanami/settings"
|
|
16
|
+
# require "my_app/types"
|
|
17
|
+
#
|
|
18
|
+
# module MyApp
|
|
19
|
+
# class Settings < Hanami::Settings
|
|
20
|
+
# setting :database_url
|
|
21
|
+
# setting :feature_flag, default: false, constructor: Types::Params::Bool
|
|
22
|
+
# end
|
|
23
|
+
# end
|
|
24
|
+
#
|
|
25
|
+
# Settings are defined with
|
|
26
|
+
# [dry-configurable](https://dry-rb.org/gems/dry-configurable/), so you can
|
|
27
|
+
# take a look there to see the supported syntax.
|
|
28
|
+
#
|
|
29
|
+
# Users work with an instance of this class made available within the
|
|
30
|
+
# `settings` key in the container. The instance gets its settings populated
|
|
31
|
+
# from a configurable store, which defaults to
|
|
32
|
+
# {Hanami::Settings::DotenvStore}.
|
|
33
|
+
#
|
|
34
|
+
# A different store can be set through the `settings_store` Hanami
|
|
35
|
+
# configuration option. All it needs to do is implementing a `#fetch` method
|
|
36
|
+
# with the same signature as `Hash#fetch`.
|
|
37
|
+
#
|
|
38
|
+
# @see Hanami::Settings::DotenvStore
|
|
39
|
+
# @since 2.0.0
|
|
40
|
+
class Settings
|
|
41
|
+
# Exception for errors in the definition of settings.
|
|
42
|
+
#
|
|
43
|
+
# Its message collects all the individual errors that can be raised for
|
|
44
|
+
# each setting.
|
|
45
|
+
InvalidSettingsError = Class.new(StandardError) do
|
|
46
|
+
def initialize(errors)
|
|
47
|
+
@errors = errors
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def to_s
|
|
51
|
+
<<~STR.strip
|
|
52
|
+
Could not initialize settings. The following settings were invalid:
|
|
53
|
+
|
|
54
|
+
#{@errors.map { |setting, message| "#{setting}: #{message}" }.join("\n")}
|
|
55
|
+
STR
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# @api private
|
|
60
|
+
EMPTY_STORE = Dry::Core::Constants::EMPTY_HASH
|
|
61
|
+
|
|
62
|
+
include Dry::Configurable
|
|
63
|
+
|
|
64
|
+
# @api private
|
|
65
|
+
def initialize(store = EMPTY_STORE)
|
|
66
|
+
errors = config._settings.map(&:name).reduce({}) do |errs, name|
|
|
67
|
+
public_send("#{name}=", store.fetch(name) { Dry::Core::Constants::Undefined })
|
|
68
|
+
errs
|
|
69
|
+
rescue => e # rubocop:disable Style/RescueStandardError
|
|
70
|
+
errs.merge(name => e)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
raise InvalidSettingsError, errors if errors.any?
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
private
|
|
77
|
+
|
|
78
|
+
def method_missing(name, *args, &block)
|
|
79
|
+
if config.respond_to?(name)
|
|
80
|
+
config.send(name, *args, &block)
|
|
81
|
+
else
|
|
82
|
+
super
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def respond_to_missing?(name, _include_all = false)
|
|
87
|
+
config.respond_to?(name) || super
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
data/lib/hanami/setup.rb
CHANGED
|
@@ -4,6 +4,8 @@ require "bundler/setup"
|
|
|
4
4
|
require "hanami"
|
|
5
5
|
|
|
6
6
|
begin
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
app_require_path = File.join(Dir.pwd, "config/app")
|
|
8
|
+
require app_require_path
|
|
9
|
+
rescue LoadError => e
|
|
10
|
+
raise e unless e.path == app_require_path
|
|
9
11
|
end
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "hanami/router"
|
|
4
|
-
|
|
4
|
+
require_relative "routing/middleware/stack"
|
|
5
5
|
|
|
6
6
|
module Hanami
|
|
7
|
-
class
|
|
8
|
-
# Hanami
|
|
7
|
+
class Slice
|
|
8
|
+
# Hanami app router
|
|
9
9
|
# @since 2.0.0
|
|
10
10
|
class Router < ::Hanami::Router
|
|
11
|
+
# @api private
|
|
12
|
+
attr_reader :middleware_stack
|
|
13
|
+
|
|
11
14
|
# @since 2.0.0
|
|
12
15
|
# @api private
|
|
13
16
|
def initialize(routes:, middleware_stack: Routing::Middleware::Stack.new, **kwargs, &blk)
|
|
@@ -27,32 +30,34 @@ module Hanami
|
|
|
27
30
|
|
|
28
31
|
# @since 2.0.0
|
|
29
32
|
# @api private
|
|
30
|
-
def use(
|
|
31
|
-
|
|
33
|
+
def use(...)
|
|
34
|
+
middleware_stack.use(...)
|
|
32
35
|
end
|
|
33
36
|
|
|
34
37
|
# @since 2.0.0
|
|
35
38
|
# @api private
|
|
36
39
|
def scope(*args, &blk)
|
|
37
|
-
|
|
40
|
+
middleware_stack.with(args.first) do
|
|
38
41
|
super
|
|
39
42
|
end
|
|
40
43
|
end
|
|
41
44
|
|
|
42
45
|
# @since 2.0.0
|
|
43
|
-
def slice(
|
|
44
|
-
|
|
45
|
-
@resolver.register_slice_at_path(name, path)
|
|
46
|
+
def slice(slice_name, at:, &blk)
|
|
47
|
+
blk ||= @resolver.find_slice(slice_name).routes
|
|
46
48
|
|
|
47
|
-
|
|
49
|
+
prev_resolver = @resolver
|
|
50
|
+
@resolver = @resolver.to_slice(slice_name)
|
|
51
|
+
|
|
52
|
+
scope(prefixed_path(at), &blk)
|
|
53
|
+
ensure
|
|
54
|
+
@resolver = prev_resolver
|
|
48
55
|
end
|
|
49
56
|
|
|
50
57
|
# @since 2.0.0
|
|
51
58
|
# @api private
|
|
52
59
|
def to_rack_app
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
@middleware_stack.to_rack_app(self)
|
|
60
|
+
middleware_stack.to_rack_app(self)
|
|
56
61
|
end
|
|
57
62
|
end
|
|
58
63
|
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Hanami
|
|
4
|
+
class Slice
|
|
5
|
+
# Hanami app routes helpers
|
|
6
|
+
#
|
|
7
|
+
# An instance of this class will be registered with slice (at the "routes" key). You
|
|
8
|
+
# can use it to access the route helpers for your app.
|
|
9
|
+
#
|
|
10
|
+
# @example
|
|
11
|
+
# MyApp::App["routes"].path(:root) # => "/"
|
|
12
|
+
#
|
|
13
|
+
# @see Hanami::Router::UrlHelpers
|
|
14
|
+
# @since 2.0.0
|
|
15
|
+
class RoutesHelper
|
|
16
|
+
# @since 2.0.0
|
|
17
|
+
# @api private
|
|
18
|
+
def initialize(router)
|
|
19
|
+
@router = router
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# @see Hanami::Router::UrlHelpers#path
|
|
23
|
+
def path(...)
|
|
24
|
+
router.path(...)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# @see Hanami::Router::UrlHelpers#url
|
|
28
|
+
def url(...)
|
|
29
|
+
router.url(...)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
attr_reader :router
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
require "rack/builder"
|
|
4
4
|
|
|
5
5
|
module Hanami
|
|
6
|
-
class
|
|
6
|
+
class Slice
|
|
7
7
|
module Routing
|
|
8
8
|
# Hanami::Applicatione::Router middleware stack
|
|
9
9
|
#
|
|
@@ -20,6 +20,10 @@ module Hanami
|
|
|
20
20
|
ROOT_PREFIX = "/"
|
|
21
21
|
private_constant :ROOT_PREFIX
|
|
22
22
|
|
|
23
|
+
# @since 2.0.0
|
|
24
|
+
# @api private
|
|
25
|
+
attr_reader :stack
|
|
26
|
+
|
|
23
27
|
# @since 2.0.0
|
|
24
28
|
# @api private
|
|
25
29
|
def initialize
|
|
@@ -29,8 +33,35 @@ module Hanami
|
|
|
29
33
|
|
|
30
34
|
# @since 2.0.0
|
|
31
35
|
# @api private
|
|
32
|
-
def
|
|
33
|
-
|
|
36
|
+
def initialize_copy(source)
|
|
37
|
+
super
|
|
38
|
+
@prefix = source.instance_variable_get(:@prefix).dup
|
|
39
|
+
@stack = stack.dup
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# @since 2.0.0
|
|
43
|
+
# @api private
|
|
44
|
+
def use(middleware, *args, before: nil, after: nil, &blk)
|
|
45
|
+
item = [middleware, args, blk]
|
|
46
|
+
|
|
47
|
+
if before
|
|
48
|
+
@stack[@prefix].insert((idx = index_of(before)).zero? ? 0 : idx - 1, item)
|
|
49
|
+
elsif after
|
|
50
|
+
@stack[@prefix].insert(index_of(after) + 1, item)
|
|
51
|
+
else
|
|
52
|
+
@stack[@prefix].push([middleware, args, blk])
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
self
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# @since 2.0.0
|
|
59
|
+
# @api private
|
|
60
|
+
def update(other)
|
|
61
|
+
other.stack.each do |prefix, items|
|
|
62
|
+
stack[prefix].concat(items)
|
|
63
|
+
end
|
|
64
|
+
self
|
|
34
65
|
end
|
|
35
66
|
|
|
36
67
|
# @since 2.0.0
|
|
@@ -45,7 +76,7 @@ module Hanami
|
|
|
45
76
|
|
|
46
77
|
# @since 2.0.0
|
|
47
78
|
# @api private
|
|
48
|
-
def to_rack_app(app)
|
|
79
|
+
def to_rack_app(app)
|
|
49
80
|
s = self
|
|
50
81
|
|
|
51
82
|
Rack::Builder.new do
|
|
@@ -58,7 +89,7 @@ module Hanami
|
|
|
58
89
|
|
|
59
90
|
run app
|
|
60
91
|
end
|
|
61
|
-
end
|
|
92
|
+
end
|
|
62
93
|
end
|
|
63
94
|
|
|
64
95
|
# @since 2.0.0
|
|
@@ -82,6 +113,13 @@ module Hanami
|
|
|
82
113
|
builder.map(prefix, &blk)
|
|
83
114
|
end
|
|
84
115
|
end
|
|
116
|
+
|
|
117
|
+
private
|
|
118
|
+
|
|
119
|
+
# @since 2.0.0
|
|
120
|
+
def index_of(middleware)
|
|
121
|
+
@stack[@prefix].index { |(m, *)| m.equal?(middleware) }
|
|
122
|
+
end
|
|
85
123
|
end
|
|
86
124
|
end
|
|
87
125
|
end
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Hanami
|
|
4
|
+
class Slice
|
|
5
|
+
module Routing
|
|
6
|
+
# @since 2.0.0
|
|
7
|
+
class UnknownActionError < Hanami::Error
|
|
8
|
+
def initialize(identifier)
|
|
9
|
+
super("unknown action referenced in router: `#{identifier.inspect}'")
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# @since 2.0.0
|
|
14
|
+
class NotCallableEndpointError < StandardError
|
|
15
|
+
def initialize(endpoint)
|
|
16
|
+
super("#{endpoint.inspect} is not compatible with Rack. Please make sure it implements #call.")
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Hanami app router endpoint resolver
|
|
21
|
+
#
|
|
22
|
+
# @since 2.0.0
|
|
23
|
+
class Resolver
|
|
24
|
+
SLICE_ACTIONS_KEY_NAMESPACE = "actions"
|
|
25
|
+
|
|
26
|
+
# @api private
|
|
27
|
+
# @since 2.0.0
|
|
28
|
+
def initialize(slice:)
|
|
29
|
+
@slice = slice
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# @api private
|
|
33
|
+
# @since 2.0.0
|
|
34
|
+
def find_slice(slice_name)
|
|
35
|
+
slice.slices[slice_name]
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# @api private
|
|
39
|
+
# @since 2.0.0
|
|
40
|
+
def to_slice(slice_name)
|
|
41
|
+
self.class.new(slice: find_slice(slice_name))
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# @api private
|
|
45
|
+
# @since 2.0.0
|
|
46
|
+
def call(_path, endpoint)
|
|
47
|
+
endpoint =
|
|
48
|
+
case endpoint
|
|
49
|
+
when String
|
|
50
|
+
resolve_slice_action(endpoint)
|
|
51
|
+
when Class
|
|
52
|
+
endpoint.respond_to?(:call) ? endpoint : endpoint.new
|
|
53
|
+
else
|
|
54
|
+
endpoint
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
unless endpoint.respond_to?(:call)
|
|
58
|
+
raise NotCallableEndpointError.new(endpoint)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
endpoint
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
private
|
|
65
|
+
|
|
66
|
+
# @api private
|
|
67
|
+
# @since 2.0.0
|
|
68
|
+
attr_reader :slice
|
|
69
|
+
|
|
70
|
+
# @api private
|
|
71
|
+
# @since 2.0.0
|
|
72
|
+
def resolve_slice_action(key)
|
|
73
|
+
action_key = "#{SLICE_ACTIONS_KEY_NAMESPACE}.#{key}"
|
|
74
|
+
|
|
75
|
+
ensure_action_in_slice(action_key)
|
|
76
|
+
|
|
77
|
+
# Lazily resolve action from the slice to reduce router initialization time, and
|
|
78
|
+
# circumvent endless loops from the action requiring access to router-related
|
|
79
|
+
# concerns (which may not be fully loaded at the time of reading the routes)
|
|
80
|
+
-> (*args) {
|
|
81
|
+
action = slice.resolve(action_key) do
|
|
82
|
+
raise UnknownActionError.new(key)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
action.call(*args)
|
|
86
|
+
}
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def ensure_action_in_slice(key)
|
|
90
|
+
return unless slice.booted?
|
|
91
|
+
|
|
92
|
+
raise UnknownActionError.new(key) unless slice.key?(key)
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|