hanami 2.0.0.alpha7.1 → 2.0.0.alpha8
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 +15 -0
- data/lib/hanami/application/action/slice_configured_action.rb +103 -0
- data/lib/hanami/application/action.rb +72 -0
- data/lib/hanami/application/view/context.rb +95 -0
- data/lib/hanami/application/view/slice_configured_context.rb +71 -0
- data/lib/hanami/application/view/slice_configured_view.rb +101 -0
- data/lib/hanami/application/view.rb +24 -0
- data/lib/hanami/application/view_name_inferrer.rb +63 -0
- data/lib/hanami/application.rb +22 -48
- 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 +16 -11
- data/lib/hanami/configuration/logger.rb +11 -8
- data/lib/hanami/configuration/views.rb +81 -0
- data/lib/hanami/configuration.rb +25 -40
- data/lib/hanami/constants.rb +6 -0
- data/lib/hanami/errors.rb +3 -0
- data/lib/hanami/slice.rb +11 -14
- data/lib/hanami/slice_configurable.rb +75 -0
- data/lib/hanami/slice_name.rb +111 -0
- data/lib/hanami/version.rb +1 -1
- metadata +16 -4
- data/lib/hanami/boot/source_dirs.rb +0 -44
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 36c07c10545a327f0eab55e1a23e3a6f1b258dd7c4db890d16155d98a132bb90
|
|
4
|
+
data.tar.gz: 02a8dbd6f028e198dead1d972aef4a78b04246b3201e2c90f94e2827f4582906
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 938be86da6f4c1d5e797db8f0cc185feb2ac070c4d8ba10f48b6bbe922f8b1fd8b618c9871306a7f3d2d75c34a8fab857949d7f18dd04bb833be092fbb8ced27
|
|
7
|
+
data.tar.gz: 459f220765e02c024fe4b1a5e17ec7a13bba10e2cb3f1b64dfa059cd351bea4cffcd47be1ca5a1cd8b2c89757f20dbcaaa7e77fbfdacb05c9fc87345974e7ec0
|
data/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,21 @@
|
|
|
1
1
|
# Hanami
|
|
2
2
|
The web, with simplicity.
|
|
3
3
|
|
|
4
|
+
## v2.0.0.alpha8 - 2020-05-19
|
|
5
|
+
|
|
6
|
+
## Added
|
|
7
|
+
- [Tim Riley] Introduced `Hanami::Application::Action` as base class for actions that integrate with Hanami applications. Base action classes in Hanami applications should now inherit from this.
|
|
8
|
+
- [Tim Riley] Introduced `Hanami::Application::View` and `Hanami::Application::View::Context` as base classes for views and view contexts that integrate with Hanami applications. Base view classes in Hanami applications should now inherit from these.
|
|
9
|
+
- [Tim Riley] Introduced `Hanami::Application.application_name`, which returns an `Hanami::SliceName` instance, with methods for representing the application name in various formats.
|
|
10
|
+
|
|
11
|
+
## Fixed
|
|
12
|
+
- [Andrew Croome] When a request is halted, do not attempt to automatically render any view paired with an `Hanami::Application::Action`
|
|
13
|
+
|
|
14
|
+
## Changed
|
|
15
|
+
- [Tim Riley] `Hanami::Application.namespace_name`, `.namespace_path` have been removed. These can now be accessed from the `.application_name`.
|
|
16
|
+
- [Tim Riley] `Hanami::Slice.slice_name` now returns an `Hanami::SliceName` instance instead of a Symbol
|
|
17
|
+
- [Tim Riley] `Hanami::Slice.namespace_path` has been removed. This can now be accessed from the `.slice_name`.
|
|
18
|
+
|
|
4
19
|
## v2.0.0.alpha7.1 - 2020-03-09
|
|
5
20
|
|
|
6
21
|
## Fixed
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "hanami/action"
|
|
4
|
+
|
|
5
|
+
module Hanami
|
|
6
|
+
class Application
|
|
7
|
+
class Action < Hanami::Action
|
|
8
|
+
# Provides slice-specific configuration and behavior for any action class defined
|
|
9
|
+
# within a slice's module namespace.
|
|
10
|
+
#
|
|
11
|
+
# @api private
|
|
12
|
+
# @since 2.0.0
|
|
13
|
+
class SliceConfiguredAction < Module
|
|
14
|
+
attr_reader :slice
|
|
15
|
+
|
|
16
|
+
def initialize(slice)
|
|
17
|
+
super()
|
|
18
|
+
@slice = slice
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def extended(action_class)
|
|
22
|
+
configure_action(action_class)
|
|
23
|
+
extend_behavior(action_class)
|
|
24
|
+
define_new
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def inspect
|
|
28
|
+
"#<#{self.class.name}[#{slice.name}]>"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
# @see Hanami::Application::Action#initialize
|
|
34
|
+
def define_new
|
|
35
|
+
resolve_view = method(:resolve_paired_view)
|
|
36
|
+
resolve_view_context = method(:resolve_view_context)
|
|
37
|
+
resolve_routes = method(:resolve_routes)
|
|
38
|
+
|
|
39
|
+
define_method(:new) do |**kwargs|
|
|
40
|
+
super(
|
|
41
|
+
view: kwargs.fetch(:view) { resolve_view.(self) },
|
|
42
|
+
view_context: kwargs.fetch(:view_context) { resolve_view_context.(self) },
|
|
43
|
+
routes: kwargs.fetch(:routes) { resolve_routes.() },
|
|
44
|
+
**kwargs,
|
|
45
|
+
)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def configure_action(action_class)
|
|
50
|
+
action_class.config.settings.each do |setting|
|
|
51
|
+
action_class.config.public_send :"#{setting}=", actions_config.public_send(:"#{setting}")
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def extend_behavior(action_class)
|
|
56
|
+
if actions_config.sessions.enabled?
|
|
57
|
+
require "hanami/action/session"
|
|
58
|
+
action_class.include Hanami::Action::Session
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
if actions_config.csrf_protection
|
|
62
|
+
require "hanami/action/csrf_protection"
|
|
63
|
+
action_class.include Hanami::Action::CSRFProtection
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
if actions_config.cookies.enabled?
|
|
67
|
+
require "hanami/action/cookies"
|
|
68
|
+
action_class.include Hanami::Action::Cookies
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def resolve_paired_view(action_class)
|
|
73
|
+
view_identifiers = actions_config.view_name_inferrer.call(
|
|
74
|
+
action_class_name: action_class.name,
|
|
75
|
+
slice: slice,
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
view_identifiers.detect do |identifier|
|
|
79
|
+
break slice[identifier] if slice.key?(identifier)
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def resolve_view_context(_action_class)
|
|
84
|
+
identifier = actions_config.view_context_identifier
|
|
85
|
+
|
|
86
|
+
if slice.key?(identifier)
|
|
87
|
+
slice[identifier]
|
|
88
|
+
elsif slice.application.key?(identifier)
|
|
89
|
+
slice.application[identifier]
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def resolve_routes
|
|
94
|
+
slice.application[:routes_helper] if slice.application.key?(:routes_helper)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def actions_config
|
|
98
|
+
slice.application.config.actions
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "hanami/action"
|
|
4
|
+
require "hanami/slice_configurable"
|
|
5
|
+
require_relative "action/slice_configured_action"
|
|
6
|
+
|
|
7
|
+
module Hanami
|
|
8
|
+
class Application
|
|
9
|
+
# Superclass for actions intended for use within an Hanami application.
|
|
10
|
+
#
|
|
11
|
+
# @see Hanami::Action
|
|
12
|
+
#
|
|
13
|
+
# @api public
|
|
14
|
+
# @since 2.0.0
|
|
15
|
+
class Action < Hanami::Action
|
|
16
|
+
extend Hanami::SliceConfigurable
|
|
17
|
+
|
|
18
|
+
class << self
|
|
19
|
+
# @api private
|
|
20
|
+
def configure_for_slice(slice)
|
|
21
|
+
extend SliceConfiguredAction.new(slice)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
attr_reader :view, :view_context, :routes
|
|
26
|
+
|
|
27
|
+
# @see SliceConfiguredAction#define_new
|
|
28
|
+
# @api public
|
|
29
|
+
def initialize(view: nil, view_context: nil, routes: nil, **kwargs)
|
|
30
|
+
@view = view
|
|
31
|
+
@view_context = view_context
|
|
32
|
+
@routes = routes
|
|
33
|
+
|
|
34
|
+
super(**kwargs)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
def build_response(**options)
|
|
40
|
+
options = options.merge(view_options: method(:view_options))
|
|
41
|
+
super(**options)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def view_options(req, res)
|
|
45
|
+
{context: view_context&.with(**view_context_options(req, res))}.compact
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def view_context_options(req, res)
|
|
49
|
+
{request: req, response: res}
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def finish(req, res, halted)
|
|
53
|
+
res.render(view, **req.params) if !halted && auto_render?(res)
|
|
54
|
+
super
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Returns true if a view should automatically be rendered onto the response body.
|
|
58
|
+
#
|
|
59
|
+
# This may be overridden to enable/disable automatic rendering.
|
|
60
|
+
#
|
|
61
|
+
# @param res [Hanami::Action::Response]
|
|
62
|
+
#
|
|
63
|
+
# @return [Boolean]
|
|
64
|
+
#
|
|
65
|
+
# @since 2.0.0
|
|
66
|
+
# @api public
|
|
67
|
+
def auto_render?(res)
|
|
68
|
+
view && res.body.empty?
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "hanami/view"
|
|
4
|
+
require "hanami/view/context"
|
|
5
|
+
require_relative "../../errors"
|
|
6
|
+
require_relative "../../slice_configurable"
|
|
7
|
+
require_relative "slice_configured_context"
|
|
8
|
+
|
|
9
|
+
module Hanami
|
|
10
|
+
class Application
|
|
11
|
+
class View < Hanami::View
|
|
12
|
+
# View context for views in Hanami applications.
|
|
13
|
+
#
|
|
14
|
+
# @api public
|
|
15
|
+
# @since 2.0.0
|
|
16
|
+
class Context < Hanami::View::Context
|
|
17
|
+
extend Hanami::SliceConfigurable
|
|
18
|
+
|
|
19
|
+
# @api private
|
|
20
|
+
def self.configure_for_slice(slice)
|
|
21
|
+
extend SliceConfiguredContext.new(slice)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# @see SliceConfiguredContext#define_new
|
|
25
|
+
def initialize(**kwargs)
|
|
26
|
+
defaults = {content: {}}
|
|
27
|
+
|
|
28
|
+
super(**kwargs, **defaults)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def inflector
|
|
32
|
+
_options.fetch(:inflector)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def routes
|
|
36
|
+
_options.fetch(:routes)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def settings
|
|
40
|
+
_options.fetch(:settings)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def assets
|
|
44
|
+
unless _options[:assets]
|
|
45
|
+
raise Hanami::ComponentLoadError, "hanami-assets gem is required to access assets"
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
_options[:assets]
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def content_for(key, value = nil, &block)
|
|
52
|
+
content = _options[:content]
|
|
53
|
+
output = nil
|
|
54
|
+
|
|
55
|
+
if block
|
|
56
|
+
content[key] = yield
|
|
57
|
+
elsif value
|
|
58
|
+
content[key] = value
|
|
59
|
+
else
|
|
60
|
+
output = content[key]
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
output
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def current_path
|
|
67
|
+
request.fullpath
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def csrf_token
|
|
71
|
+
request.session[Hanami::Action::CSRFProtection::CSRF_TOKEN]
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def request
|
|
75
|
+
_options.fetch(:request)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def session
|
|
79
|
+
request.session
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def flash
|
|
83
|
+
response.flash
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
private
|
|
87
|
+
|
|
88
|
+
# TODO: create `Request#flash` so we no longer need the `response`
|
|
89
|
+
def response
|
|
90
|
+
_options.fetch(:response)
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "hanami/view"
|
|
4
|
+
|
|
5
|
+
module Hanami
|
|
6
|
+
class Application
|
|
7
|
+
class View < Hanami::View
|
|
8
|
+
# Provides slice-specific configuration and behavior for any view context class
|
|
9
|
+
# defined within a slice's module namespace.
|
|
10
|
+
#
|
|
11
|
+
# @api private
|
|
12
|
+
# @since 2.0.0
|
|
13
|
+
class SliceConfiguredContext < Module
|
|
14
|
+
attr_reader :slice
|
|
15
|
+
|
|
16
|
+
def initialize(slice)
|
|
17
|
+
super()
|
|
18
|
+
@slice = slice
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def extended(_context_class)
|
|
22
|
+
define_new
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def inspect
|
|
26
|
+
"#<#{self.class.name}[#{slice.name}]>"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
# Defines a {.new} method on the context class that resolves key components from
|
|
32
|
+
# the application container and provides them to {#initialize} as injected
|
|
33
|
+
# dependencies.
|
|
34
|
+
#
|
|
35
|
+
# This includes the following application components:
|
|
36
|
+
#
|
|
37
|
+
# - the configured inflector as `inflector`
|
|
38
|
+
# - "settings" from the application container as `settings`
|
|
39
|
+
# - "routes" from the application container as `routes`
|
|
40
|
+
# - "assets" from the application container as `assets`
|
|
41
|
+
def define_new
|
|
42
|
+
inflector = slice.inflector
|
|
43
|
+
resolve_settings = method(:resolve_settings)
|
|
44
|
+
resolve_routes = method(:resolve_routes)
|
|
45
|
+
resolve_assets = method(:resolve_assets)
|
|
46
|
+
|
|
47
|
+
define_method :new do |**kwargs|
|
|
48
|
+
kwargs[:inflector] ||= inflector
|
|
49
|
+
kwargs[:settings] ||= resolve_settings.()
|
|
50
|
+
kwargs[:routes] ||= resolve_routes.()
|
|
51
|
+
kwargs[:assets] ||= resolve_assets.()
|
|
52
|
+
|
|
53
|
+
super(**kwargs)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def resolve_settings
|
|
58
|
+
slice.application[:settings] if slice.application.key?(:settings)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def resolve_routes
|
|
62
|
+
slice.application[:routes_helper] if slice.application.key?(:routes_helper)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def resolve_assets
|
|
66
|
+
slice.application[:assets] if slice.application.key?(:assets)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "hanami/view"
|
|
4
|
+
|
|
5
|
+
module Hanami
|
|
6
|
+
class Application
|
|
7
|
+
class View < Hanami::View
|
|
8
|
+
# Provides slice-specific configuration and behavior for any view class defined
|
|
9
|
+
# within a slice's module namespace.
|
|
10
|
+
#
|
|
11
|
+
# @api private
|
|
12
|
+
# @since 2.0.0
|
|
13
|
+
class SliceConfiguredView < Module
|
|
14
|
+
attr_reader :slice
|
|
15
|
+
|
|
16
|
+
def initialize(slice)
|
|
17
|
+
super()
|
|
18
|
+
@slice = slice
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def extended(view_class)
|
|
22
|
+
configure_view(view_class)
|
|
23
|
+
define_inherited
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def inspect
|
|
27
|
+
"#<#{self.class.name}[#{slice.name}]>"
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
# rubocop:disable Metrics/AbcSize
|
|
33
|
+
def configure_view(view_class)
|
|
34
|
+
view_class.settings.each do |setting|
|
|
35
|
+
if slice.application.config.views.respond_to?(:"#{setting}")
|
|
36
|
+
view_class.config.public_send(
|
|
37
|
+
:"#{setting}=",
|
|
38
|
+
slice.application.config.views.public_send(:"#{setting}")
|
|
39
|
+
)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
view_class.config.inflector = inflector
|
|
44
|
+
view_class.config.paths = prepare_paths(slice, view_class.config.paths)
|
|
45
|
+
view_class.config.template = template_name(view_class)
|
|
46
|
+
|
|
47
|
+
if (part_namespace = namespace_from_path(slice.application.config.views.parts_path))
|
|
48
|
+
view_class.config.part_namespace = part_namespace
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
# rubocop:enable Metrics/AbcSize
|
|
52
|
+
|
|
53
|
+
def define_inherited
|
|
54
|
+
template_name = method(:template_name)
|
|
55
|
+
|
|
56
|
+
define_method(:inherited) do |subclass|
|
|
57
|
+
super(subclass)
|
|
58
|
+
subclass.config.template = template_name.(subclass)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def prepare_paths(slice, configured_paths)
|
|
63
|
+
configured_paths.map { |path|
|
|
64
|
+
if path.dir.relative?
|
|
65
|
+
slice.root.join(path.dir)
|
|
66
|
+
else
|
|
67
|
+
path
|
|
68
|
+
end
|
|
69
|
+
}
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def namespace_from_path(path)
|
|
73
|
+
path = "#{slice.slice_name.path}/#{path}"
|
|
74
|
+
|
|
75
|
+
begin
|
|
76
|
+
require path
|
|
77
|
+
rescue LoadError => exception
|
|
78
|
+
raise exception unless exception.path == path
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
begin
|
|
82
|
+
inflector.constantize(inflector.camelize(path))
|
|
83
|
+
rescue NameError => exception
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def template_name(view_class)
|
|
88
|
+
slice
|
|
89
|
+
.inflector
|
|
90
|
+
.underscore(view_class.name)
|
|
91
|
+
.sub(/^#{slice.slice_name.path}\//, "")
|
|
92
|
+
.sub(/^#{view_class.config.template_inference_base}\//, "")
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def inflector
|
|
96
|
+
slice.inflector
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "hanami/view"
|
|
4
|
+
require_relative "../slice_configurable"
|
|
5
|
+
require_relative "view/slice_configured_view"
|
|
6
|
+
|
|
7
|
+
module Hanami
|
|
8
|
+
class Application
|
|
9
|
+
# Superclass for views intended for use within an Hanami application.
|
|
10
|
+
#
|
|
11
|
+
# @see Hanami::View
|
|
12
|
+
#
|
|
13
|
+
# @api public
|
|
14
|
+
# @since 2.0.0
|
|
15
|
+
class View < Hanami::View
|
|
16
|
+
extend Hanami::SliceConfigurable
|
|
17
|
+
|
|
18
|
+
# @api private
|
|
19
|
+
def self.configure_for_slice(slice)
|
|
20
|
+
extend SliceConfiguredView.new(slice)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../constants"
|
|
4
|
+
|
|
5
|
+
module Hanami
|
|
6
|
+
class Application
|
|
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.application.config.actions.name_inference_base
|
|
32
|
+
view_key_base = slice.application.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
|
data/lib/hanami/application.rb
CHANGED
|
@@ -7,6 +7,7 @@ require "rack"
|
|
|
7
7
|
require "zeitwerk"
|
|
8
8
|
require_relative "constants"
|
|
9
9
|
require_relative "slice"
|
|
10
|
+
require_relative "slice_name"
|
|
10
11
|
require_relative "application/slice_registrar"
|
|
11
12
|
|
|
12
13
|
module Hanami
|
|
@@ -17,21 +18,25 @@ module Hanami
|
|
|
17
18
|
@_mutex = Mutex.new
|
|
18
19
|
|
|
19
20
|
class << self
|
|
20
|
-
def inherited(
|
|
21
|
+
def inherited(subclass)
|
|
21
22
|
super
|
|
23
|
+
|
|
22
24
|
@_mutex.synchronize do
|
|
23
|
-
|
|
25
|
+
subclass.class_eval do
|
|
24
26
|
@_mutex = Mutex.new
|
|
25
|
-
@
|
|
27
|
+
@application_name = SliceName.new(subclass, inflector: -> { subclass.inflector })
|
|
28
|
+
@configuration = Hanami::Configuration.new(application_name: @application_name, env: Hanami.env)
|
|
26
29
|
@autoloader = Zeitwerk::Loader.new
|
|
27
30
|
@container = Class.new(Dry::System::Container)
|
|
28
31
|
|
|
32
|
+
@prepared = @booted = false
|
|
33
|
+
|
|
29
34
|
extend ClassMethods
|
|
30
35
|
end
|
|
31
36
|
|
|
32
|
-
|
|
37
|
+
subclass.send :prepare_base_load_path
|
|
33
38
|
|
|
34
|
-
Hanami.application =
|
|
39
|
+
Hanami.application = subclass
|
|
35
40
|
end
|
|
36
41
|
end
|
|
37
42
|
end
|
|
@@ -40,20 +45,16 @@ module Hanami
|
|
|
40
45
|
#
|
|
41
46
|
# rubocop:disable Metrics/ModuleLength
|
|
42
47
|
module ClassMethods
|
|
43
|
-
attr_reader :autoloader, :container
|
|
44
|
-
|
|
45
|
-
def self.extended(klass)
|
|
46
|
-
klass.class_eval do
|
|
47
|
-
@prepared = @booted = false
|
|
48
|
-
end
|
|
49
|
-
end
|
|
48
|
+
attr_reader :application_name, :configuration, :autoloader, :container
|
|
50
49
|
|
|
51
|
-
|
|
52
|
-
@_configuration
|
|
53
|
-
end
|
|
50
|
+
alias_method :slice_name, :application_name
|
|
54
51
|
|
|
55
52
|
alias_method :config, :configuration
|
|
56
53
|
|
|
54
|
+
def application
|
|
55
|
+
self
|
|
56
|
+
end
|
|
57
|
+
|
|
57
58
|
def prepare(provider_name = nil)
|
|
58
59
|
container.prepare(provider_name) and return self if provider_name
|
|
59
60
|
|
|
@@ -147,19 +148,7 @@ module Hanami
|
|
|
147
148
|
end
|
|
148
149
|
|
|
149
150
|
def namespace
|
|
150
|
-
|
|
151
|
-
end
|
|
152
|
-
|
|
153
|
-
def namespace_name
|
|
154
|
-
namespace.name
|
|
155
|
-
end
|
|
156
|
-
|
|
157
|
-
def namespace_path
|
|
158
|
-
inflector.underscore(namespace)
|
|
159
|
-
end
|
|
160
|
-
|
|
161
|
-
def application_name
|
|
162
|
-
configuration.application_name
|
|
151
|
+
application_name.namespace
|
|
163
152
|
end
|
|
164
153
|
|
|
165
154
|
def root
|
|
@@ -170,21 +159,6 @@ module Hanami
|
|
|
170
159
|
configuration.inflector
|
|
171
160
|
end
|
|
172
161
|
|
|
173
|
-
# @api private
|
|
174
|
-
def component_provider(component)
|
|
175
|
-
raise "Hanami.application must be prepared before detecting providers" unless prepared?
|
|
176
|
-
|
|
177
|
-
# e.g. [Admin, Main, MyApp]
|
|
178
|
-
providers = slices.to_a + [self]
|
|
179
|
-
|
|
180
|
-
component_class = component.is_a?(Class) ? component : component.class
|
|
181
|
-
component_name = component_class.name
|
|
182
|
-
|
|
183
|
-
return unless component_name
|
|
184
|
-
|
|
185
|
-
providers.detect { |provider| component_name.include?(provider.namespace.to_s) }
|
|
186
|
-
end
|
|
187
|
-
|
|
188
162
|
private
|
|
189
163
|
|
|
190
164
|
def prepare_base_load_path
|
|
@@ -222,8 +196,8 @@ module Hanami
|
|
|
222
196
|
|
|
223
197
|
def prepare_autoload_paths
|
|
224
198
|
# Autoload classes defined in lib/[app_namespace]/
|
|
225
|
-
if root.join("lib",
|
|
226
|
-
autoloader.push_dir(root.join("lib",
|
|
199
|
+
if root.join("lib", application_name.name).directory?
|
|
200
|
+
autoloader.push_dir(root.join("lib", application_name.name), namespace: namespace)
|
|
227
201
|
end
|
|
228
202
|
end
|
|
229
203
|
|
|
@@ -239,8 +213,8 @@ module Hanami
|
|
|
239
213
|
|
|
240
214
|
def prepare_autoloader
|
|
241
215
|
# Autoload classes defined in lib/[app_namespace]/
|
|
242
|
-
if root.join("lib",
|
|
243
|
-
autoloader.push_dir(root.join("lib",
|
|
216
|
+
if root.join("lib", application_name.name).directory?
|
|
217
|
+
autoloader.push_dir(root.join("lib", application_name.name), namespace: namespace)
|
|
244
218
|
end
|
|
245
219
|
|
|
246
220
|
autoloader.setup
|
|
@@ -258,7 +232,7 @@ module Hanami
|
|
|
258
232
|
end
|
|
259
233
|
|
|
260
234
|
def autodiscover_application_constant(constants)
|
|
261
|
-
inflector.constantize([namespace_name, *constants].join(MODULE_DELIMITER))
|
|
235
|
+
inflector.constantize([application_name.namespace_name, *constants].join(MODULE_DELIMITER))
|
|
262
236
|
end
|
|
263
237
|
|
|
264
238
|
def load_router
|