hanami-view 2.3.1 → 3.0.0
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 +428 -184
- data/LICENSE +20 -0
- data/README.md +17 -22
- data/hanami-view.gemspec +19 -19
- data/lib/hanami/view/context.rb +11 -0
- data/lib/hanami/view/decorated_attributes.rb +0 -2
- data/lib/hanami/view/erb/filters/block.rb +2 -1
- data/lib/hanami/view/erb/filters/trimming.rb +14 -8
- data/lib/hanami/view/erb/parser.rb +5 -1
- data/lib/hanami/view/exposure.rb +5 -7
- data/lib/hanami/view/exposures.rb +6 -2
- data/lib/hanami/view/helpers/escape_helper.rb +3 -2
- data/lib/hanami/view/helpers/number_formatting_helper.rb +2 -4
- data/lib/hanami/view/helpers/tag_helper/tag_builder.rb +10 -7
- data/lib/hanami/view/helpers/tag_helper.rb +1 -3
- data/lib/hanami/view/html.rb +0 -2
- data/lib/hanami/view/html_safe_string_buffer.rb +1 -1
- data/lib/hanami/view/part.rb +2 -5
- data/lib/hanami/view/part_builder.rb +7 -9
- data/lib/hanami/view/renderer.rb +78 -28
- data/lib/hanami/view/rendering.rb +33 -23
- data/lib/hanami/view/rendering_missing.rb +11 -14
- data/lib/hanami/view/scope.rb +29 -15
- data/lib/hanami/view/scope_builder.rb +6 -6
- data/lib/hanami/view/tilt/haml_adapter.rb +1 -1
- data/lib/hanami/view/tilt/slim_adapter.rb +1 -1
- data/lib/hanami/view/tilt.rb +14 -4
- data/lib/hanami/view/version.rb +1 -2
- data/lib/hanami/view.rb +66 -29
- metadata +14 -53
|
@@ -3,54 +3,64 @@
|
|
|
3
3
|
module Hanami
|
|
4
4
|
class View
|
|
5
5
|
# @api private
|
|
6
|
-
# @since 2.1.0
|
|
7
6
|
class Rendering
|
|
8
7
|
# @api private
|
|
9
|
-
|
|
10
|
-
attr_reader :config, :format
|
|
8
|
+
attr_reader :format
|
|
11
9
|
|
|
12
|
-
# @api private
|
|
13
|
-
# @since 2.1.0
|
|
14
10
|
attr_reader :inflector, :part_builder, :scope_builder
|
|
15
11
|
|
|
16
|
-
|
|
17
|
-
|
|
12
|
+
attr_reader :part_class, :part_namespace, :scope_class, :scope_namespace
|
|
13
|
+
|
|
14
|
+
# Stable identity for the underlying config snapshot.
|
|
15
|
+
attr_reader :cache_key
|
|
16
|
+
|
|
18
17
|
attr_reader :context, :renderer
|
|
19
18
|
|
|
20
|
-
|
|
21
|
-
# @since 2.1.0
|
|
22
|
-
def initialize(config:, format:, context:)
|
|
23
|
-
@config = config
|
|
19
|
+
def initialize(config_data:, format:, context:)
|
|
24
20
|
@format = format
|
|
25
21
|
|
|
26
|
-
@inflector =
|
|
27
|
-
@part_builder =
|
|
28
|
-
@scope_builder =
|
|
22
|
+
@inflector = config_data.inflector
|
|
23
|
+
@part_builder = config_data.part_builder
|
|
24
|
+
@scope_builder = config_data.scope_builder
|
|
25
|
+
|
|
26
|
+
@part_class = config_data.part_class
|
|
27
|
+
@part_namespace = config_data.part_namespace
|
|
28
|
+
@scope_class = config_data.scope_class
|
|
29
|
+
@scope_namespace = config_data.scope_namespace
|
|
30
|
+
@cache_key = config_data.object_id
|
|
29
31
|
|
|
30
32
|
@context = context.dup_for_rendering(self)
|
|
31
|
-
@renderer = Renderer.new(
|
|
33
|
+
@renderer = Renderer.new(config_data)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Returns the resolved name of the template or partial currently being rendered, or nil if
|
|
37
|
+
# no render is in progress.
|
|
38
|
+
#
|
|
39
|
+
# @return [String, nil]
|
|
40
|
+
def current_template_name
|
|
41
|
+
renderer.current_template_name
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Returns the stack of resolved names for the templates and partials currently being
|
|
45
|
+
# rendered.
|
|
46
|
+
#
|
|
47
|
+
# @return [Array<String>]
|
|
48
|
+
def current_template_names
|
|
49
|
+
renderer.current_template_names
|
|
32
50
|
end
|
|
33
51
|
|
|
34
|
-
# @api private
|
|
35
|
-
# @since 2.1.0
|
|
36
52
|
def template(name, scope, &block)
|
|
37
53
|
renderer.template(name, format, scope, &block)
|
|
38
54
|
end
|
|
39
55
|
|
|
40
|
-
# @api private
|
|
41
|
-
# @since 2.1.0
|
|
42
56
|
def partial(name, scope, &block)
|
|
43
57
|
renderer.partial(name, format, scope, &block)
|
|
44
58
|
end
|
|
45
59
|
|
|
46
|
-
# @api private
|
|
47
|
-
# @since 2.1.0
|
|
48
60
|
def part(name, value, as: nil)
|
|
49
61
|
part_builder.(name, value, as: as, rendering: self)
|
|
50
62
|
end
|
|
51
63
|
|
|
52
|
-
# @api private
|
|
53
|
-
# @since 2.1.0
|
|
54
64
|
def scope(name = nil, locals) # rubocop:disable Style/OptionalArguments
|
|
55
65
|
scope_builder.(name, locals: locals, rendering: self)
|
|
56
66
|
end
|
|
@@ -8,47 +8,44 @@ module Hanami
|
|
|
8
8
|
# @api private
|
|
9
9
|
# @since 2.1.0
|
|
10
10
|
class RenderingMissing
|
|
11
|
-
# @api private
|
|
12
|
-
# @since 2.1.0
|
|
13
11
|
def format
|
|
14
12
|
raise RenderingMissingError
|
|
15
13
|
end
|
|
16
14
|
|
|
17
|
-
# @api private
|
|
18
|
-
# @since 2.1.0
|
|
19
15
|
def context
|
|
20
16
|
raise RenderingMissingError
|
|
21
17
|
end
|
|
22
18
|
|
|
23
|
-
# @api private
|
|
24
|
-
# @since 2.1.0
|
|
25
19
|
def part(_name, _value, **_options)
|
|
26
20
|
raise RenderingMissingError
|
|
27
21
|
end
|
|
28
22
|
|
|
29
|
-
# @api private
|
|
30
|
-
# @since 2.1.0
|
|
31
23
|
def scope(_name = nil, _locals) # rubocop:disable Style/OptionalArguments
|
|
32
24
|
raise RenderingMissingError
|
|
33
25
|
end
|
|
34
26
|
|
|
35
|
-
# @api private
|
|
36
|
-
# @since 2.1.0
|
|
37
27
|
def template(_name, _scope)
|
|
38
28
|
raise RenderingMissingError
|
|
39
29
|
end
|
|
40
30
|
|
|
41
|
-
# @api private
|
|
42
|
-
# @since 2.1.0
|
|
43
31
|
def partial(_name, _scope)
|
|
44
32
|
raise RenderingMissingError
|
|
45
33
|
end
|
|
46
34
|
|
|
47
|
-
# @api private
|
|
48
|
-
# @since 2.1.0
|
|
49
35
|
def inflector
|
|
50
36
|
@inflector ||= Dry::Inflector.new
|
|
51
37
|
end
|
|
38
|
+
|
|
39
|
+
def current_template_name
|
|
40
|
+
nil
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def current_template_names
|
|
44
|
+
EMPTY_TEMPLATE_NAMES
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
EMPTY_TEMPLATE_NAMES = [].freeze
|
|
48
|
+
private_constant :EMPTY_TEMPLATE_NAMES
|
|
52
49
|
end
|
|
53
50
|
end
|
|
54
51
|
end
|
data/lib/hanami/view/scope.rb
CHANGED
|
@@ -19,7 +19,7 @@ module Hanami
|
|
|
19
19
|
# @since 2.1.0
|
|
20
20
|
class Scope
|
|
21
21
|
# @api private
|
|
22
|
-
CONVENIENCE_METHODS = %i[format context locals].freeze
|
|
22
|
+
CONVENIENCE_METHODS = %i[format context locals template_name].freeze
|
|
23
23
|
|
|
24
24
|
include Dry::Equalizer(:_name, :_locals, :_rendering)
|
|
25
25
|
|
|
@@ -35,10 +35,10 @@ module Hanami
|
|
|
35
35
|
#
|
|
36
36
|
# @overload _locals
|
|
37
37
|
# Returns the locals.
|
|
38
|
+
# @return [Hash{Symbol => Object}]
|
|
38
39
|
# @overload locals
|
|
39
40
|
# A convenience alias for `#_locals.` Is available unless there is a local named `locals`
|
|
40
|
-
#
|
|
41
|
-
# @return [Hash[<Symbol, Object>]
|
|
41
|
+
# @return [Hash{Symbol => Object}]
|
|
42
42
|
#
|
|
43
43
|
# @api public
|
|
44
44
|
# @since 2.1.0
|
|
@@ -76,16 +76,16 @@ module Hanami
|
|
|
76
76
|
# Renders a partial using the scope.
|
|
77
77
|
#
|
|
78
78
|
# @param partial_name [Symbol, String] partial name
|
|
79
|
-
# @param locals [Hash
|
|
79
|
+
# @param locals [Hash{Symbol => Object}] partial locals
|
|
80
80
|
# @yieldreturn [String] string content to include where the partial calls `yield`
|
|
81
|
+
# @return [String] the rendered partial output
|
|
81
82
|
#
|
|
82
83
|
# @overload render(**locals, &block)
|
|
83
84
|
# Renders a partial (named after the scope's own name) using the scope.
|
|
84
85
|
#
|
|
85
|
-
# @param locals[Hash
|
|
86
|
+
# @param locals [Hash{Symbol => Object}] partial locals
|
|
86
87
|
# @yieldreturn [String] string content to include where the partial calls `yield`
|
|
87
|
-
#
|
|
88
|
-
# @return [String] the rendered partial output
|
|
88
|
+
# @return [String] the rendered partial output
|
|
89
89
|
#
|
|
90
90
|
# @api public
|
|
91
91
|
# @since 2.1.0
|
|
@@ -120,11 +120,10 @@ module Hanami
|
|
|
120
120
|
#
|
|
121
121
|
# @overload _format
|
|
122
122
|
# Returns the format.
|
|
123
|
+
# @return [Symbol] format
|
|
123
124
|
# @overload format
|
|
124
|
-
# A convenience alias for `#_format.` Is available unless there is a
|
|
125
|
-
#
|
|
126
|
-
#
|
|
127
|
-
# @return [Symbol] format
|
|
125
|
+
# A convenience alias for `#_format.` Is available unless there is a local named `format`.
|
|
126
|
+
# @return [Symbol] format
|
|
128
127
|
#
|
|
129
128
|
# @api public
|
|
130
129
|
# @since 2.1.0
|
|
@@ -136,11 +135,10 @@ module Hanami
|
|
|
136
135
|
#
|
|
137
136
|
# @overload _context
|
|
138
137
|
# Returns the context.
|
|
138
|
+
# @return [Context] context
|
|
139
139
|
# @overload context
|
|
140
|
-
# A convenience alias for `#_context`. Is available unless there is a
|
|
141
|
-
#
|
|
142
|
-
#
|
|
143
|
-
# @return [Context] context
|
|
140
|
+
# A convenience alias for `#_context`. Is available unless there is a local named `context`.
|
|
141
|
+
# @return [Context] context
|
|
144
142
|
#
|
|
145
143
|
# @api public
|
|
146
144
|
# @since 2.1.0
|
|
@@ -148,6 +146,22 @@ module Hanami
|
|
|
148
146
|
_rendering.context
|
|
149
147
|
end
|
|
150
148
|
|
|
149
|
+
# Returns the name of the template or partial currently being rendered.
|
|
150
|
+
#
|
|
151
|
+
# @overload _template_name
|
|
152
|
+
# Returns the current template name.
|
|
153
|
+
# @return [String, nil]
|
|
154
|
+
# @overload template_name
|
|
155
|
+
# A convenience alias for `#_template_name`. Is available unless there is a local named
|
|
156
|
+
# `template_name`.
|
|
157
|
+
# @return [String, nil]
|
|
158
|
+
#
|
|
159
|
+
# @api public
|
|
160
|
+
# @since 3.0.0
|
|
161
|
+
def _template_name
|
|
162
|
+
_rendering.current_template_name
|
|
163
|
+
end
|
|
164
|
+
|
|
151
165
|
private
|
|
152
166
|
|
|
153
167
|
# Handles missing methods, according to the following rules:
|
|
@@ -17,7 +17,7 @@ module Hanami
|
|
|
17
17
|
#
|
|
18
18
|
# @api public
|
|
19
19
|
# @since 2.1.0
|
|
20
|
-
def call(name = nil, locals:, rendering:)
|
|
20
|
+
def call(name = nil, locals:, rendering:)
|
|
21
21
|
klass = scope_class(name, rendering: rendering)
|
|
22
22
|
|
|
23
23
|
klass.new(name: name, locals: locals, rendering: rendering)
|
|
@@ -27,11 +27,11 @@ module Hanami
|
|
|
27
27
|
|
|
28
28
|
def scope_class(name = nil, rendering:)
|
|
29
29
|
if name.nil?
|
|
30
|
-
rendering.
|
|
30
|
+
rendering.scope_class
|
|
31
31
|
elsif name.is_a?(Class)
|
|
32
32
|
name
|
|
33
33
|
else
|
|
34
|
-
View.cache.fetch_or_store(name, rendering.
|
|
34
|
+
View.cache.fetch_or_store(name, rendering.cache_key) do
|
|
35
35
|
resolve_scope_class(name: name, rendering: rendering)
|
|
36
36
|
end
|
|
37
37
|
end
|
|
@@ -40,12 +40,12 @@ module Hanami
|
|
|
40
40
|
def resolve_scope_class(name:, rendering:)
|
|
41
41
|
name = rendering.inflector.camelize(name.to_s)
|
|
42
42
|
|
|
43
|
-
namespace = rendering.
|
|
43
|
+
namespace = rendering.scope_namespace
|
|
44
44
|
|
|
45
45
|
# Give autoloaders a chance to act
|
|
46
46
|
begin
|
|
47
47
|
klass = namespace.const_get(name)
|
|
48
|
-
rescue NameError # rubocop:disable Lint/
|
|
48
|
+
rescue NameError # rubocop:disable Lint/SuppressedException
|
|
49
49
|
end
|
|
50
50
|
|
|
51
51
|
if !klass && namespace.const_defined?(name, false)
|
|
@@ -55,7 +55,7 @@ module Hanami
|
|
|
55
55
|
if klass && klass < Scope
|
|
56
56
|
klass
|
|
57
57
|
else
|
|
58
|
-
rendering.
|
|
58
|
+
rendering.scope_class
|
|
59
59
|
end
|
|
60
60
|
end
|
|
61
61
|
end
|
data/lib/hanami/view/tilt.rb
CHANGED
|
@@ -10,14 +10,24 @@ module Hanami
|
|
|
10
10
|
# @api private
|
|
11
11
|
# @since 2.1.0
|
|
12
12
|
Mapping = ::Tilt.default_mapping.dup.tap { |mapping|
|
|
13
|
-
#
|
|
14
|
-
#
|
|
15
|
-
|
|
13
|
+
# Unregister any existing mappings for the extensions we provide our own engines for.
|
|
14
|
+
#
|
|
15
|
+
# Tilt preferences non-lazy registrations over lazy ones (see `Tilt::Mapping#lookup`). So if
|
|
16
|
+
# "haml" or "slim" has been required before this mapping is built (each of which registers
|
|
17
|
+
# its own non-lazy mapping with Tilt), our own lazy-registered adapters below would be
|
|
18
|
+
# shadowed and never picked up, and templates would render without our specific required
|
|
19
|
+
# behavior.
|
|
20
|
+
#
|
|
21
|
+
# Unregistering first ensures our engines are always used, regardless of load order.
|
|
22
|
+
mapping.unregister "erb", "rhtml", "haml", "slim"
|
|
16
23
|
|
|
17
24
|
# Register our own ERB template.
|
|
18
25
|
mapping.register_lazy "Hanami::View::ERB::Template", "hanami/view/erb/template", "erb", "rhtml"
|
|
19
26
|
|
|
20
|
-
# Register
|
|
27
|
+
# Register templates for Haml and Slim that set the `use_html_safe: true` option.
|
|
28
|
+
#
|
|
29
|
+
# We register these lazily so that the optional "haml" and "slim" gems only need to be
|
|
30
|
+
# installed when their templates are actually used.
|
|
21
31
|
#
|
|
22
32
|
# Our template namespaces below have the "Adapter" suffix to work around a bug in Tilt's
|
|
23
33
|
# `Mapping#const_defined?`, which (if slim was already required) would receive
|
data/lib/hanami/view/version.rb
CHANGED
data/lib/hanami/view.rb
CHANGED
|
@@ -22,7 +22,6 @@ module Hanami
|
|
|
22
22
|
# @since 2.1.0
|
|
23
23
|
class View
|
|
24
24
|
# @api private
|
|
25
|
-
# @since 2.1.0
|
|
26
25
|
def self.gem_loader
|
|
27
26
|
@gem_loader ||= Zeitwerk::Loader.new.tap do |loader|
|
|
28
27
|
root = File.expand_path("..", __dir__)
|
|
@@ -32,12 +31,17 @@ module Hanami
|
|
|
32
31
|
"#{root}/hanami-view.rb",
|
|
33
32
|
"#{root}/hanami/view/version.rb",
|
|
34
33
|
"#{root}/hanami/view/errors.rb",
|
|
34
|
+
# These adapters require the optional "haml" and "slim" gems. They are loaded lazily by
|
|
35
|
+
# Tilt (see Hanami::View::Tilt) only when their respective template engines are used;
|
|
36
|
+
# ignore them here to allow eager loading without those gems installed.
|
|
37
|
+
"#{root}/hanami/view/tilt/haml_adapter.rb",
|
|
38
|
+
"#{root}/hanami/view/tilt/slim_adapter.rb"
|
|
35
39
|
)
|
|
36
40
|
loader.inflector = Zeitwerk::GemInflector.new("#{root}/hanami-view.rb")
|
|
37
41
|
loader.inflector.inflect(
|
|
38
42
|
"erb" => "ERB",
|
|
39
43
|
"html" => "HTML",
|
|
40
|
-
"html_safe_string_buffer" => "HTMLSafeStringBuffer"
|
|
44
|
+
"html_safe_string_buffer" => "HTMLSafeStringBuffer"
|
|
41
45
|
)
|
|
42
46
|
end
|
|
43
47
|
end
|
|
@@ -45,7 +49,6 @@ module Hanami
|
|
|
45
49
|
gem_loader.setup
|
|
46
50
|
|
|
47
51
|
# @api private
|
|
48
|
-
# @since 2.1.0
|
|
49
52
|
DEFAULT_RENDERER_OPTIONS = {default_encoding: "utf-8"}.freeze
|
|
50
53
|
|
|
51
54
|
include Dry::Equalizer(:config, :exposures)
|
|
@@ -235,6 +238,23 @@ module Hanami
|
|
|
235
238
|
# @!scope class
|
|
236
239
|
setting :inflector, default: Dry::Inflector.new
|
|
237
240
|
|
|
241
|
+
# @overload config.decorate_exposures=(value)
|
|
242
|
+
# Controls whether exposures are decorated by default.
|
|
243
|
+
#
|
|
244
|
+
# When set to `true`, all exposures will be decorated with matching Parts unless explicitly
|
|
245
|
+
# marked with `decorate: false`.
|
|
246
|
+
#
|
|
247
|
+
# When set to `false` (the default), exposures will not be decorated unless explicitly marked
|
|
248
|
+
# with `decorate: true`, or declared with `decorate`.
|
|
249
|
+
#
|
|
250
|
+
# Defaults to `false`.
|
|
251
|
+
#
|
|
252
|
+
# @param value [Boolean] whether to decorate exposures by default
|
|
253
|
+
# @api public
|
|
254
|
+
# @since 3.0.0
|
|
255
|
+
# @!scope class
|
|
256
|
+
setting :decorate_exposures, default: false
|
|
257
|
+
|
|
238
258
|
# @overload config.renderer_options=(options)
|
|
239
259
|
# A hash of options to pass to the template engine. Template engines are
|
|
240
260
|
# provided by Tilt; see Tilt's documentation for what options your
|
|
@@ -273,7 +293,6 @@ module Hanami
|
|
|
273
293
|
# @!endgroup
|
|
274
294
|
|
|
275
295
|
# @api private
|
|
276
|
-
# @since 2.1.0
|
|
277
296
|
def self.inherited(klass)
|
|
278
297
|
super
|
|
279
298
|
|
|
@@ -288,13 +307,13 @@ module Hanami
|
|
|
288
307
|
# @param options [Hash] the exposure's options
|
|
289
308
|
# @option options [Boolean] :layout expose this value to the layout (defaults to false)
|
|
290
309
|
# @option options [Boolean] :decorate decorate this value in a matching Part (defaults to
|
|
291
|
-
#
|
|
310
|
+
# false, or the value of `config.decorate_exposures`)
|
|
292
311
|
# @option options [Symbol, Class] :as an alternative name or class to use when finding a
|
|
293
312
|
# matching Part
|
|
294
313
|
|
|
295
314
|
# @overload expose(name, **options, &block)
|
|
296
315
|
# Define a value to be passed to the template. The return value of the
|
|
297
|
-
# block will be
|
|
316
|
+
# block will be passed to the template.
|
|
298
317
|
#
|
|
299
318
|
# The block will be evaluated with the view instance as its `self`. The
|
|
300
319
|
# block's parameters will determine what it is given:
|
|
@@ -329,8 +348,8 @@ module Hanami
|
|
|
329
348
|
#
|
|
330
349
|
# @overload expose(name, **options)
|
|
331
350
|
# Define a value to be passed to the template, provided by an instance
|
|
332
|
-
# method matching the name. The method's return value will be
|
|
333
|
-
#
|
|
351
|
+
# method matching the name. The method's return value will be passed to
|
|
352
|
+
# the template.
|
|
334
353
|
#
|
|
335
354
|
# The method's parameters will determine what it is given:
|
|
336
355
|
#
|
|
@@ -369,8 +388,8 @@ module Hanami
|
|
|
369
388
|
#
|
|
370
389
|
# @overload expose(name, **options)
|
|
371
390
|
# Define a single value to pass through from the input data (when there is
|
|
372
|
-
# no instance method matching the `name`). This value will be
|
|
373
|
-
#
|
|
391
|
+
# no instance method matching the `name`). This value will be passed to
|
|
392
|
+
# the template.
|
|
374
393
|
#
|
|
375
394
|
# @param name [Symbol] name for the exposure
|
|
376
395
|
# @macro exposure_options
|
|
@@ -380,7 +399,7 @@ module Hanami
|
|
|
380
399
|
# @overload expose(*names, **options)
|
|
381
400
|
# Define multiple values to pass through from the input data (when there
|
|
382
401
|
# is no instance methods matching their names). These values will be
|
|
383
|
-
#
|
|
402
|
+
# passed through to the template.
|
|
384
403
|
#
|
|
385
404
|
# The provided options will be applied to all the exposures.
|
|
386
405
|
#
|
|
@@ -411,12 +430,23 @@ module Hanami
|
|
|
411
430
|
expose(*names, **options, private: true, &block)
|
|
412
431
|
end
|
|
413
432
|
|
|
433
|
+
# Defines an exposure that will be decorated with a matching Part.
|
|
434
|
+
#
|
|
435
|
+
# This is a shorthand for `expose(..., decorate: true)`.
|
|
436
|
+
#
|
|
437
|
+
# @see expose
|
|
438
|
+
#
|
|
439
|
+
# @api public
|
|
440
|
+
# @since 2.1.0
|
|
441
|
+
def self.decorate(*names, **options, &block)
|
|
442
|
+
expose(*names, **options, decorate: true, &block)
|
|
443
|
+
end
|
|
444
|
+
|
|
414
445
|
# Returns the defined exposures. These are unbound, since bound exposures
|
|
415
446
|
# are only created when initializing a View instance.
|
|
416
447
|
#
|
|
417
448
|
# @return [Exposures]
|
|
418
449
|
# @api private
|
|
419
|
-
# @since 2.1.0
|
|
420
450
|
def self.exposures
|
|
421
451
|
@exposures ||= Exposures.new
|
|
422
452
|
end
|
|
@@ -512,13 +542,6 @@ module Hanami
|
|
|
512
542
|
# @!endgroup
|
|
513
543
|
|
|
514
544
|
# @api private
|
|
515
|
-
# @since 2.1.0
|
|
516
|
-
def self.layout_path(layout)
|
|
517
|
-
File.join(*[config.layouts_dir, layout].compact)
|
|
518
|
-
end
|
|
519
|
-
|
|
520
|
-
# @api private
|
|
521
|
-
# @since 2.1.0
|
|
522
545
|
def self.cache
|
|
523
546
|
Cache
|
|
524
547
|
end
|
|
@@ -534,6 +557,7 @@ module Hanami
|
|
|
534
557
|
self.class.config.finalize!
|
|
535
558
|
ensure_config
|
|
536
559
|
|
|
560
|
+
@config_data = config.to_data
|
|
537
561
|
@exposures = self.class.exposures.bind(self)
|
|
538
562
|
end
|
|
539
563
|
|
|
@@ -550,8 +574,7 @@ module Hanami
|
|
|
550
574
|
# @return [Exposures]
|
|
551
575
|
#
|
|
552
576
|
# @api private
|
|
553
|
-
#
|
|
554
|
-
def exposures
|
|
577
|
+
def exposures # rubocop:disable Style/TrivialAccessors
|
|
555
578
|
@exposures
|
|
556
579
|
end
|
|
557
580
|
|
|
@@ -566,16 +589,20 @@ module Hanami
|
|
|
566
589
|
#
|
|
567
590
|
# @api public
|
|
568
591
|
# @since 2.1.0
|
|
569
|
-
def call(format:
|
|
592
|
+
def call(format: config_data.default_format,
|
|
593
|
+
context: config_data.default_context,
|
|
594
|
+
layout: config_data.layout,
|
|
595
|
+
**input)
|
|
570
596
|
rendering = self.rendering(format: format, context: context)
|
|
597
|
+
scope_class = config_data.scope
|
|
571
598
|
|
|
572
599
|
locals = locals(rendering, input)
|
|
573
|
-
output = rendering.template(
|
|
600
|
+
output = rendering.template(config_data.template, rendering.scope(scope_class, locals))
|
|
574
601
|
|
|
575
602
|
if layout
|
|
576
603
|
output = rendering.template(
|
|
577
|
-
|
|
578
|
-
rendering.scope(
|
|
604
|
+
layout_path(layout),
|
|
605
|
+
rendering.scope(scope_class, layout_locals(locals))
|
|
579
606
|
) { output }
|
|
580
607
|
end
|
|
581
608
|
|
|
@@ -583,13 +610,18 @@ module Hanami
|
|
|
583
610
|
end
|
|
584
611
|
|
|
585
612
|
# @api private
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
Rendering.new(config: config, format: format, context: context)
|
|
613
|
+
def rendering(format: config_data.default_format, context: config_data.default_context)
|
|
614
|
+
Rendering.new(config_data:, format:, context:)
|
|
589
615
|
end
|
|
590
616
|
|
|
591
617
|
private
|
|
592
618
|
|
|
619
|
+
# Frozen Data snapshot of the view's resolved configuration values.
|
|
620
|
+
# Used for fast hot-path reads during rendering.
|
|
621
|
+
#
|
|
622
|
+
# @api private
|
|
623
|
+
attr_reader :config_data
|
|
624
|
+
|
|
593
625
|
def ensure_config
|
|
594
626
|
raise UndefinedConfigError, :paths unless Array(config.paths).any?
|
|
595
627
|
raise UndefinedConfigError, :template unless config.template
|
|
@@ -597,7 +629,7 @@ module Hanami
|
|
|
597
629
|
|
|
598
630
|
def locals(rendering, input)
|
|
599
631
|
exposures.(context: rendering.context, **input) do |value, exposure|
|
|
600
|
-
if exposure.decorate? && value
|
|
632
|
+
if exposure.decorate?(default: config_data.decorate_exposures) && value
|
|
601
633
|
rendering.part(exposure.name, value, as: exposure.options[:as])
|
|
602
634
|
else
|
|
603
635
|
value
|
|
@@ -605,6 +637,11 @@ module Hanami
|
|
|
605
637
|
end
|
|
606
638
|
end
|
|
607
639
|
|
|
640
|
+
# @api private
|
|
641
|
+
def layout_path(layout)
|
|
642
|
+
File.join(*[config_data.layouts_dir, layout].compact)
|
|
643
|
+
end
|
|
644
|
+
|
|
608
645
|
def layout_locals(locals)
|
|
609
646
|
locals.each_with_object({}) do |(key, value), layout_locals|
|
|
610
647
|
layout_locals[key] = value if exposures[key].for_layout?
|