hanami 2.0.0.alpha6 → 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 +79 -0
- data/lib/hanami/application/action/slice_configured_action.rb +103 -0
- data/lib/hanami/application/action.rb +72 -0
- data/lib/hanami/application/routing/router.rb +36 -0
- data/lib/hanami/application/slice_registrar.rb +106 -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 +64 -147
- 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 +95 -0
- data/lib/hanami/configuration/logger.rb +11 -8
- data/lib/hanami/configuration/views.rb +81 -0
- data/lib/hanami/configuration.rb +28 -62
- data/lib/hanami/constants.rb +29 -0
- data/lib/hanami/errors.rb +15 -0
- data/lib/hanami/slice.rb +157 -99
- data/lib/hanami/slice_configurable.rb +75 -0
- data/lib/hanami/slice_name.rb +111 -0
- data/lib/hanami/version.rb +1 -1
- data/lib/hanami.rb +13 -5
- metadata +21 -3
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,85 @@
|
|
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
|
+
|
19
|
+
## v2.0.0.alpha7.1 - 2020-03-09
|
20
|
+
|
21
|
+
## Fixed
|
22
|
+
- [Tim Riley] Fixed error creating slice classes when the enclosing module did not already exist
|
23
|
+
|
24
|
+
## v2.0.0.alpha7 - 2020-03-08
|
25
|
+
|
26
|
+
## Added
|
27
|
+
- [Tim Riley] Introduced `Hanami::ApplicationLoadError` and `Hanami::SliceLoadError` exceptions to represent errors encountered during application and slice loading.
|
28
|
+
- [Tim Riley] `Hanami::Slice.shutdown` can be used to stop all the providers in a slice
|
29
|
+
|
30
|
+
## Changed
|
31
|
+
- [Tim Riley] Slices are now represented as concrete classes (such as `Main::Slice`) inheriting from `Hanami::Slice`, as opposed to _instances_ of `Hanami::Slice`. You may create your own definitions for these slices in `config/slices/[slice_name].rb`, which you can then use for customising per-slice config and behavior, e.g.
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
# config/slices/main.rb:
|
35
|
+
|
36
|
+
module Main
|
37
|
+
class Slice < Hanami::Slice
|
38
|
+
# slice config here
|
39
|
+
end
|
40
|
+
end
|
41
|
+
```
|
42
|
+
- [Tim Riley] Application-level `config.slice(slice_name, &block)` setting has been removed in favour of slice configuration within concrete slice class definitions
|
43
|
+
- [Tim Riley] You can configure your slice imports inside your slice classes, e.g.
|
44
|
+
|
45
|
+
```ruby
|
46
|
+
# config/slices/main.rb:
|
47
|
+
|
48
|
+
module Main
|
49
|
+
class Slice < Hanami::Slice
|
50
|
+
# Import all exported components from "search" slice
|
51
|
+
import from: :search
|
52
|
+
end
|
53
|
+
end
|
54
|
+
```
|
55
|
+
- [Tim Riley] You can configure your slice exports inside your slice classes, e.g.
|
56
|
+
|
57
|
+
```ruby
|
58
|
+
# config/slices/search.rb:
|
59
|
+
|
60
|
+
module Search
|
61
|
+
class Slice < Hanami::Slice
|
62
|
+
# Export the "index_entity" component only
|
63
|
+
export ["index_entity"]
|
64
|
+
end
|
65
|
+
end
|
66
|
+
```
|
67
|
+
- [Tim Riley] For advanced cases, you can configure your slice's container via a `prepare_container` block:
|
68
|
+
|
69
|
+
```ruby
|
70
|
+
# config/slices/search.rb:
|
71
|
+
|
72
|
+
module Search
|
73
|
+
class Slice < Hanami::Slice
|
74
|
+
prepare_container do |container|
|
75
|
+
# `container` object is available here, with
|
76
|
+
# slice-specific configuration already applied
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
```
|
81
|
+
- [Tim Riley] `Hanami::Application.shutdown` will now also shutdown all registered slices
|
82
|
+
|
4
83
|
## v2.0.0.alpha6 - 2022-02-10
|
5
84
|
### Added
|
6
85
|
- [Luca Guidi] Official support for Ruby: MRI 3.1
|
@@ -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,36 @@
|
|
1
|
+
# # frozen_string_literal: true
|
2
|
+
|
3
|
+
# require "hanami/application/router"
|
4
|
+
|
5
|
+
# Hanami.application.register_provider :router do
|
6
|
+
# start do
|
7
|
+
# configuration = Hanami.application.configuration
|
8
|
+
|
9
|
+
# routes = begin
|
10
|
+
# require File.join(configuration.root, configuration.router.routes_path)
|
11
|
+
# routes_class = Hanami.application.send(:autodiscover_application_constant, configuration.router.routes_class_name) # WIP private
|
12
|
+
# routes_class.routes
|
13
|
+
# rescue LoadError
|
14
|
+
# proc {}
|
15
|
+
# end
|
16
|
+
|
17
|
+
# resolver = configuration.router.resolver.new(
|
18
|
+
# slices: Hanami.application.slices,
|
19
|
+
# inflector: Hanami.application.inflector # TODO: use container[:inflector]?
|
20
|
+
# )
|
21
|
+
|
22
|
+
# router = Hanami::Application::Router.new(
|
23
|
+
# routes: routes,
|
24
|
+
# resolver: resolver,
|
25
|
+
# **configuration.router.options,
|
26
|
+
# ) do
|
27
|
+
# use Hanami.application[:rack_monitor]
|
28
|
+
|
29
|
+
# Hanami.application.config.for_each_middleware do |m, *args, &block|
|
30
|
+
# use(m, *args, &block)
|
31
|
+
# end
|
32
|
+
# end
|
33
|
+
|
34
|
+
# register :router, router
|
35
|
+
# end
|
36
|
+
# end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../constants"
|
4
|
+
require_relative "../slice"
|
5
|
+
|
6
|
+
module Hanami
|
7
|
+
class Application
|
8
|
+
# @api private
|
9
|
+
class SliceRegistrar
|
10
|
+
attr_reader :application, :slices
|
11
|
+
private :application, :slices
|
12
|
+
|
13
|
+
def initialize(application)
|
14
|
+
@application = application
|
15
|
+
@slices = {}
|
16
|
+
end
|
17
|
+
|
18
|
+
def register(name, slice_class = nil, &block)
|
19
|
+
if slices.key?(name.to_sym)
|
20
|
+
raise SliceLoadError, "Slice '#{name}' is already registered"
|
21
|
+
end
|
22
|
+
|
23
|
+
# TODO: raise error unless name meets format (i.e. single level depth only)
|
24
|
+
|
25
|
+
slices[name.to_sym] = slice_class || build_slice(name, &block)
|
26
|
+
end
|
27
|
+
|
28
|
+
def [](name)
|
29
|
+
slices.fetch(name) do
|
30
|
+
raise SliceLoadError, "Slice '#{name}' not found"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def freeze
|
35
|
+
slices.freeze
|
36
|
+
super
|
37
|
+
end
|
38
|
+
|
39
|
+
def load_slices
|
40
|
+
slice_configs = Dir[root.join(CONFIG_DIR, "slices", "*#{RB_EXT}")]
|
41
|
+
.map { |file| File.basename(file, RB_EXT) }
|
42
|
+
|
43
|
+
slice_dirs = Dir[File.join(root, SLICES_DIR, "*")]
|
44
|
+
.select { |path| File.directory?(path) }
|
45
|
+
.map { |path| File.basename(path) }
|
46
|
+
|
47
|
+
(slice_dirs + slice_configs).uniq.sort.each do |slice_name|
|
48
|
+
load_slice(slice_name)
|
49
|
+
end
|
50
|
+
|
51
|
+
self
|
52
|
+
end
|
53
|
+
|
54
|
+
def each(&block)
|
55
|
+
slices.each_value(&block)
|
56
|
+
end
|
57
|
+
|
58
|
+
def to_a
|
59
|
+
slices.values
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
# Attempts to load a slice class defined in `config/slices/[slice_name].rb`, then
|
65
|
+
# registers the slice with the matching class, if found.
|
66
|
+
def load_slice(slice_name)
|
67
|
+
slice_const_name = inflector.camelize(slice_name)
|
68
|
+
slice_require_path = root.join("config", "slices", slice_name).to_s
|
69
|
+
|
70
|
+
begin
|
71
|
+
require(slice_require_path)
|
72
|
+
rescue LoadError => e
|
73
|
+
raise e unless e.path == slice_require_path
|
74
|
+
end
|
75
|
+
|
76
|
+
slice_class =
|
77
|
+
begin
|
78
|
+
inflector.constantize("#{slice_const_name}::Slice")
|
79
|
+
rescue NameError # rubocop:disable Lint/SuppressedException
|
80
|
+
end
|
81
|
+
|
82
|
+
register(slice_name, slice_class)
|
83
|
+
end
|
84
|
+
|
85
|
+
def build_slice(slice_name, &block)
|
86
|
+
slice_module =
|
87
|
+
begin
|
88
|
+
slice_module_name = inflector.camelize(slice_name.to_s)
|
89
|
+
inflector.constantize(slice_module_name)
|
90
|
+
rescue NameError
|
91
|
+
Object.const_set(inflector.camelize(slice_module_name), Module.new)
|
92
|
+
end
|
93
|
+
|
94
|
+
slice_module.const_set(:Slice, Class.new(Hanami::Slice, &block))
|
95
|
+
end
|
96
|
+
|
97
|
+
def root
|
98
|
+
application.root
|
99
|
+
end
|
100
|
+
|
101
|
+
def inflector
|
102
|
+
application.inflector
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
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
|