hanami 2.0.0.alpha7.1 → 2.0.0.beta1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +457 -237
- 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 +118 -0
- data/lib/hanami/configuration/actions/cookies.rb +29 -0
- data/lib/hanami/configuration/actions/sessions.rb +46 -0
- data/lib/hanami/configuration/actions.rb +23 -12
- data/lib/hanami/configuration/logger.rb +13 -10
- data/lib/hanami/configuration/router.rb +2 -6
- data/lib/hanami/configuration/sessions.rb +1 -1
- data/lib/hanami/configuration/views.rb +86 -0
- data/lib/hanami/configuration.rb +139 -82
- data/lib/hanami/constants.rb +30 -2
- data/lib/hanami/errors.rb +4 -1
- data/lib/hanami/extensions/action/slice_configured_action.rb +103 -0
- data/lib/hanami/extensions/action.rb +79 -0
- data/lib/hanami/extensions/view/context.rb +106 -0
- data/lib/hanami/extensions/view/slice_configured_context.rb +71 -0
- data/lib/hanami/extensions/view/slice_configured_view.rb +107 -0
- 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/slice/view_name_inferrer.rb +63 -0
- data/lib/hanami/slice.rb +252 -82
- data/lib/hanami/slice_configurable.rb +62 -0
- data/lib/hanami/slice_name.rb +111 -0
- 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 +363 -55
- 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.rb +0 -299
- data/lib/hanami/boot/source_dirs.rb +0 -44
- 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,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
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../constants"
|
4
|
+
|
5
|
+
module Hanami
|
6
|
+
class Slice
|
7
|
+
# Infers a view name for automatically rendering within actions.
|
8
|
+
#
|
9
|
+
# @api private
|
10
|
+
# @since 2.0.0
|
11
|
+
class ViewNameInferrer
|
12
|
+
ALTERNATIVE_NAMES = {
|
13
|
+
"create" => "new",
|
14
|
+
"update" => "edit"
|
15
|
+
}.freeze
|
16
|
+
|
17
|
+
class << self
|
18
|
+
# Returns an array of container keys for views matching the given action.
|
19
|
+
#
|
20
|
+
# Also provides alternative view keys for common RESTful actions.
|
21
|
+
#
|
22
|
+
# @example
|
23
|
+
# ViewNameInferrer.call(action_name: "Main::Actions::Posts::Create", slice: Main::Slice)
|
24
|
+
# # => ["views.posts.create", "views.posts.new"]
|
25
|
+
#
|
26
|
+
# @param action_name [String] action class name
|
27
|
+
# @param slice [Hanami::Slice, Hanami::Application] Hanami slice containing the action
|
28
|
+
#
|
29
|
+
# @return [Array<string>] array of paired view container keys
|
30
|
+
def call(action_class_name:, slice:)
|
31
|
+
action_key_base = slice.config.actions.name_inference_base
|
32
|
+
view_key_base = slice.config.actions.view_name_inference_base
|
33
|
+
|
34
|
+
action_name_key = action_name_key(action_class_name, slice, action_key_base)
|
35
|
+
|
36
|
+
view_key = [view_key_base, action_name_key].compact.join(CONTAINER_KEY_DELIMITER)
|
37
|
+
|
38
|
+
[view_key, alternative_view_key(view_key)].compact
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def action_name_key(action_name, slice, key_base)
|
44
|
+
slice
|
45
|
+
.inflector
|
46
|
+
.underscore(action_name)
|
47
|
+
.sub(%r{^#{slice.slice_name.path}#{PATH_DELIMITER}}, "")
|
48
|
+
.sub(%r{^#{key_base}#{PATH_DELIMITER}}, "")
|
49
|
+
.gsub("/", CONTAINER_KEY_DELIMITER)
|
50
|
+
end
|
51
|
+
|
52
|
+
def alternative_view_key(view_key)
|
53
|
+
parts = view_key.split(CONTAINER_KEY_DELIMITER)
|
54
|
+
|
55
|
+
alternative_name = ALTERNATIVE_NAMES[parts.last]
|
56
|
+
return unless alternative_name
|
57
|
+
|
58
|
+
[parts[0..-2], alternative_name].join(CONTAINER_KEY_DELIMITER)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|