hanami 2.1.0.beta2.1 → 2.1.0.rc2

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.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +30 -0
  3. data/hanami.gemspec +3 -3
  4. data/lib/hanami/config/actions.rb +16 -3
  5. data/lib/hanami/config/assets.rb +1 -1
  6. data/lib/hanami/config/views.rb +10 -2
  7. data/lib/hanami/config.rb +21 -12
  8. data/lib/hanami/extensions/action/slice_configured_action.rb +5 -5
  9. data/lib/hanami/extensions/action.rb +4 -4
  10. data/lib/hanami/extensions/view/context.rb +111 -19
  11. data/lib/hanami/extensions/view/part.rb +64 -3
  12. data/lib/hanami/extensions/view/scope.rb +7 -0
  13. data/lib/hanami/extensions/view/slice_configured_context.rb +12 -8
  14. data/lib/hanami/extensions/view/slice_configured_helpers.rb +12 -1
  15. data/lib/hanami/extensions/view/slice_configured_part.rb +71 -0
  16. data/lib/hanami/extensions/view/slice_configured_view.rb +14 -6
  17. data/lib/hanami/extensions/view/standard_helpers.rb +4 -0
  18. data/lib/hanami/extensions/view.rb +5 -3
  19. data/lib/hanami/helpers/assets_helper.rb +47 -79
  20. data/lib/hanami/routes.rb +33 -2
  21. data/lib/hanami/slice.rb +12 -2
  22. data/lib/hanami/slice_registrar.rb +48 -23
  23. data/lib/hanami/version.rb +1 -1
  24. data/lib/hanami/web/rack_logger.rb +70 -2
  25. data/lib/hanami/web/welcome.html.erb +203 -0
  26. data/lib/hanami/web/welcome.rb +46 -0
  27. data/spec/integration/assets/assets_spec.rb +14 -3
  28. data/spec/integration/logging/request_logging_spec.rb +65 -7
  29. data/spec/integration/rack_app/method_override_spec.rb +97 -0
  30. data/spec/integration/slices_spec.rb +275 -5
  31. data/spec/integration/view/context/assets_spec.rb +0 -8
  32. data/spec/integration/view/context/inflector_spec.rb +0 -8
  33. data/spec/integration/view/context/settings_spec.rb +0 -8
  34. data/spec/integration/view/helpers/part_helpers_spec.rb +2 -2
  35. data/spec/integration/view/helpers/user_defined_helpers/part_helpers_spec.rb +10 -10
  36. data/spec/integration/view/parts/default_rendering_spec.rb +138 -0
  37. data/spec/integration/web/welcome_view_spec.rb +84 -0
  38. data/spec/support/app_integration.rb +22 -4
  39. data/spec/unit/hanami/config/render_detailed_errors_spec.rb +1 -1
  40. data/spec/unit/hanami/helpers/assets_helper/{audio_spec.rb → audio_tag_spec.rb} +10 -14
  41. data/spec/unit/hanami/helpers/assets_helper/{favicon_spec.rb → favicon_tag_spec.rb} +7 -11
  42. data/spec/unit/hanami/helpers/assets_helper/{image_spec.rb → image_tag_spec.rb} +8 -12
  43. data/spec/unit/hanami/helpers/assets_helper/{javascript_spec.rb → javascript_tag_spec.rb} +14 -18
  44. data/spec/unit/hanami/helpers/assets_helper/{stylesheet_spec.rb → stylesheet_tag_spec.rb} +12 -16
  45. data/spec/unit/hanami/helpers/assets_helper/{video_spec.rb → video_tag_spec.rb} +11 -11
  46. data/spec/unit/hanami/version_spec.rb +1 -1
  47. metadata +28 -19
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 219f0622f399b1e3f94c77c5d7556b6bdd26122181929b444eb8fca436e9d0d2
4
- data.tar.gz: 3dc2468f5561eb8d70fa62d3f5b7f755f096ab764cfbfd43fca93d6001061908
3
+ metadata.gz: 8a6835d72654251d1461ea2ad61c9fe56ad9b37b0ff333d23dc444e205b32ab1
4
+ data.tar.gz: 3daf6e28931aa6abf0c3579d95c8e403c1ba12bbcb5879549825e03ae8e81556
5
5
  SHA512:
6
- metadata.gz: f38c4b54000182e7ab4fb63be367131c6580df35519f97d5fd171de016b4ec96590c5f7c0822044421e0efec64813a492d4e106a803ea6028ba6d6d105db2ee6
7
- data.tar.gz: 8648d6b907fbc4a600a4561184631b2868a577a2280c6bade34af1726d3bd6458045268c8517956e52bc4e6b2a6dfc78b0bee938e254563cac3fbbae91afc6d2
6
+ metadata.gz: 4b79171920231066598260223766e9abaee75806c751bcf83ca9ddaa93ef2eabef1fabc3077674cb81f25e435fa8659a04f9c272200a4ed27fa02c271ac03531
7
+ data.tar.gz: 04072a74f320abda02874ace16c616db0f3db9bb63de4d3b00746f3c1898c1dc58a143abd47915c9ef0a32b0a5eda860bd9264bccd375779d87bb101e4686dc1
data/CHANGELOG.md CHANGED
@@ -2,6 +2,36 @@
2
2
 
3
3
  The web, with simplicity.
4
4
 
5
+ ## Changed
6
+
7
+ - [Tim Riley] Enable `config.render_detailed_errors` in development mode only by default. (#1365)
8
+ - [Tim Riley] Remove current_path from context (#1362)
9
+
10
+ ## v2.1.0.rc1 - 2023-11-01
11
+
12
+ ### Added
13
+
14
+ - [Aaron Moodie & Tim Riley] Render a welcome page when no routes are defined (#1353)
15
+ - [Luca Guidi] Allow _Method Override_ by default, by mounting `Rack::MethodOverride` middleware. (#1344)
16
+
17
+ ### Fixed
18
+
19
+ - [Luca Guidi] Ensure compatibility with Ruby Logger from stdlib (#1352)
20
+
21
+ ### Changed
22
+
23
+ - [Philip Arndt] Add support for a slice's class definition file to exist inside either `config/slices/[slice_name].rb` or `slices/[slice_name]/config/slice.rb`, in that order of precedence, so that a slice can provide its own definition which the app, or the slice's parent slice if that exists, can override if needed.
24
+ - [Tim Riley] Use `helpers` prefix to access helper methods from view parts (e.g. `helpers.format_number`)
25
+ - [Tim Riley] Make it possible to directly initialize view parts to ease their unit tests (#1357)
26
+ - [Tim Riley] Remove short names for assets helpers (#1356):
27
+ - Keep `javascript_tag` (remove `javascript` and `js` aliases)
28
+ - Keep `stylesheet_tag` (renamed from `stylesheet_link_tag`; remove `stylesheet` and `css` aliases)
29
+ - Keep `favicon_tag` (renamed from `favicon_link_tag`; remove `favicon` alias)
30
+ - Keep `image_tag` (remove `image` alias)
31
+ - Keep `video_tag` (remove `video` alias)
32
+ - Keep `audio_tag` (remove `audio` alias)
33
+
34
+
5
35
  ## v2.1.0.beta2.1 - 2023-10-04
6
36
 
7
37
  ### Added
data/hanami.gemspec CHANGED
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  lib = File.expand_path("../lib", __FILE__)
4
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ $LOAD_PATH.prepend(lib) unless $LOAD_PATH.include?(lib)
5
5
  require "hanami/version"
6
6
 
7
7
  Gem::Specification.new do |spec|
@@ -38,8 +38,8 @@ Gem::Specification.new do |spec|
38
38
  spec.add_dependency "dry-monitor", "~> 1.0", ">= 1.0.1", "< 2"
39
39
  spec.add_dependency "dry-system", "~> 1.0", "< 2"
40
40
  spec.add_dependency "dry-logger", "~> 1.0", "< 2"
41
- spec.add_dependency "hanami-cli", "~> 2.1.beta"
42
- spec.add_dependency "hanami-utils", "~> 2.1.beta"
41
+ spec.add_dependency "hanami-cli", "~> 2.1.rc"
42
+ spec.add_dependency "hanami-utils", "~> 2.1.rc"
43
43
  spec.add_dependency "zeitwerk", "~> 2.6"
44
44
 
45
45
  spec.add_development_dependency "rspec", "~> 3.8"
@@ -74,20 +74,33 @@ module Hanami
74
74
  # @since 2.0.0
75
75
  attr_accessor :content_security_policy
76
76
 
77
- # The following settings are for view and assets integration with actions, and are NOT
78
- # publicly released as of 2.0.0. We'll make full documentation available when these become
79
- # public in a subsequent release.
77
+ # @!attribute [rw] method_override
78
+ # Sets or returns whether HTTP method override should be enabled for action classes.
79
+ #
80
+ # Defaults to true. You can override this by explicitly setting a
81
+ # true or false value.
82
+ #
83
+ # When true, this will mount `Rack::MethodOverride` in the Rack middleware stack of the App.
84
+ #
85
+ # @return [Boolean]
86
+ #
87
+ # @api public
88
+ # @since 2.1.0
89
+ setting :method_override, default: true
80
90
 
81
91
  # @!attribute [rw] name_inference_base
82
92
  # @api private
93
+ # @since 2.1.0
83
94
  setting :name_inference_base, default: "actions"
84
95
 
85
96
  # @!attribute [rw] view_name_inferrer
86
97
  # @api private
98
+ # @since 2.1.0
87
99
  setting :view_name_inferrer, default: Slice::ViewNameInferrer
88
100
 
89
101
  # @!attribute [rw] view_name_inference_base
90
102
  # @api private
103
+ # @since 2.1.0
91
104
  setting :view_name_inference_base, default: "views"
92
105
 
93
106
  # @api private
@@ -9,7 +9,7 @@ module Hanami
9
9
  #
10
10
  # This exposes all the settings from the standalone `Hanami::Assets` class, pre-configured with
11
11
  # sensible defaults for actions within a full Hanami app. It also provides additional settings
12
- # for further integration of actions with other full stack app components.
12
+ # for further integration of assets with other full stack app components.
13
13
  #
14
14
  # @since 2.1.0
15
15
  # @api public
@@ -7,16 +7,22 @@ module Hanami
7
7
  class Config
8
8
  # Hanami views config
9
9
  #
10
- # This is NOT RELEASED as of 2.0.0.
10
+ # This exposes all the settings from the standalone `Hanami::View` class, pre-configured with
11
+ # sensible defaults for actions within a full Hanami app. It also provides additional settings
12
+ # for further integration of views with other full stack app components.
11
13
  #
12
- # @api private
14
+ # @since 2.1.0
15
+ # @api public
13
16
  class Views
14
17
  include Dry::Configurable
15
18
 
19
+ # @api private
20
+ # @since 2.1.0
16
21
  attr_reader :base_config
17
22
  protected :base_config
18
23
 
19
24
  # @api private
25
+ # @since 2.1.0
20
26
  def initialize(*)
21
27
  super
22
28
 
@@ -26,6 +32,7 @@ module Hanami
26
32
  end
27
33
 
28
34
  # @api private
35
+ # @since 2.1.0
29
36
  def initialize_copy(source)
30
37
  super
31
38
  @base_config = source.base_config.dup
@@ -33,6 +40,7 @@ module Hanami
33
40
  private :initialize_copy
34
41
 
35
42
  # @api private
43
+ # @since 2.1.0
36
44
  def finalize!
37
45
  return self if frozen?
38
46
 
data/lib/hanami/config.rb CHANGED
@@ -251,16 +251,30 @@ module Hanami
251
251
 
252
252
  # Returns the app's views config, or a null config if hanami-view is not bundled.
253
253
  #
254
- # This is NOT RELEASED as of 2.0.0.
254
+ # @example When hanami-view is bundled
255
+ # config.views.paths # => [...]
255
256
  #
256
- # @api private
257
+ # @example When hanami-view is not bundled
258
+ # config.views.paths # => NoMethodError
259
+ #
260
+ # @return [Hanami::Config::Views, Hanami::Config::NullConfig]
261
+ #
262
+ # @api public
263
+ # @since 2.1.0
257
264
  attr_reader :views
258
265
 
259
- # Returns the app's assets config.
266
+ # Returns the app's views config, or a null config if hanami-view is not bundled.
260
267
  #
261
- # This is NOT RELEASED as of 2.0.0.
268
+ # @example When hanami-view is bundled
269
+ # config.views.paths # => [...]
262
270
  #
263
- # @api private
271
+ # @example When hanami-view is not bundled
272
+ # config.views.paths # => NoMethodError
273
+ #
274
+ # @return [Hanami::Config::Assets, Hanami::Config::NullConfig]
275
+ #
276
+ # @api public
277
+ # @since 2.1.0
264
278
  attr_reader :assets
265
279
 
266
280
  # @api private
@@ -272,7 +286,7 @@ module Hanami
272
286
  # Apply default values that are only knowable at initialize-time (vs require-time)
273
287
  self.root = Dir.pwd
274
288
  self.render_errors = (env == :production)
275
- self.render_detailed_errors = (env != :production)
289
+ self.render_detailed_errors = (env == :development)
276
290
  load_from_env
277
291
 
278
292
  @logger = Config::Logger.new(env: env, app_name: app_name)
@@ -296,13 +310,8 @@ module Hanami
296
310
  @assets = load_dependent_config("hanami-assets") {
297
311
  require_relative "config/assets"
298
312
 
299
- public_dir = root.join("public")
300
-
301
313
  Hanami::Config::Assets.new(
302
- # TODO: check if `sources` are still needed
303
- sources: root.join("app", "assets"),
304
- destination: public_dir.join("assets"),
305
- manifest_path: public_dir.join("assets.json")
314
+ manifest_path: root.join("public", "assets.json")
306
315
  )
307
316
  }
308
317
 
@@ -31,14 +31,14 @@ module Hanami
31
31
  # @see Hanami::Extensions::Action::InstanceMethods#initialize
32
32
  def define_new
33
33
  resolve_view = method(:resolve_paired_view)
34
- resolve_view_context = method(:resolve_view_context)
34
+ view_context_class = method(:view_context_class)
35
35
  resolve_routes = method(:resolve_routes)
36
36
  resolve_rack_monitor = method(:resolve_rack_monitor)
37
37
 
38
38
  define_method(:new) do |**kwargs|
39
39
  super(
40
40
  view: kwargs.fetch(:view) { resolve_view.(self) },
41
- view_context: kwargs.fetch(:view_context) { resolve_view_context.() },
41
+ view_context_class: kwargs.fetch(:view_context_class) { view_context_class.() },
42
42
  routes: kwargs.fetch(:routes) { resolve_routes.() },
43
43
  rack_monitor: kwargs.fetch(:rack_monitor) { resolve_rack_monitor.() },
44
44
  **kwargs,
@@ -128,9 +128,9 @@ module Hanami
128
128
  nil
129
129
  end
130
130
 
131
- def resolve_view_context
131
+ def view_context_class
132
132
  if Hanami.bundled?("hanami-view")
133
- return Extensions::View::Context.context_class(slice).new
133
+ return Extensions::View::Context.context_class(slice)
134
134
  end
135
135
 
136
136
  # If hanami-view isn't bundled, try and find a possible third party context class with the
@@ -139,7 +139,7 @@ module Hanami
139
139
  views_namespace = slice.namespace.const_get(:Views)
140
140
 
141
141
  if views_namespace.const_defined?(:Context)
142
- views_namespace.const_get(:Context).new
142
+ views_namespace.const_get(:Context)
143
143
  end
144
144
  end
145
145
  end
@@ -40,7 +40,7 @@ module Hanami
40
40
  attr_reader :view
41
41
 
42
42
  # @api private
43
- attr_reader :view_context
43
+ attr_reader :view_context_class
44
44
 
45
45
  # Returns the app or slice's {Hanami::Slice::RoutesHelper RoutesHelper} for use within
46
46
  # action instance methods.
@@ -70,9 +70,9 @@ module Hanami
70
70
  #
71
71
  # @api public
72
72
  # @since 2.0.0
73
- def initialize(view: nil, view_context: nil, rack_monitor: nil, routes: nil, **kwargs)
73
+ def initialize(view: nil, view_context_class: nil, rack_monitor: nil, routes: nil, **kwargs)
74
74
  @view = view
75
- @view_context = view_context
75
+ @view_context_class = view_context_class
76
76
  @routes = routes
77
77
  @rack_monitor = rack_monitor
78
78
 
@@ -104,7 +104,7 @@ module Hanami
104
104
 
105
105
  # @api private
106
106
  def view_options(request, response)
107
- {context: view_context&.with(**view_context_options(request, response))}.compact
107
+ {context: view_context_class&.new(**view_context_options(request, response))}.compact
108
108
  end
109
109
 
110
110
  # @api private
@@ -7,15 +7,15 @@ module Hanami
7
7
  module View
8
8
  # View context for views in Hanami apps.
9
9
  #
10
- # This is NOT RELEASED as of 2.0.0.
11
- #
12
- # @api private
10
+ # @api public
11
+ # @since 2.1.0
13
12
  module Context
14
13
  class << self
15
14
  # Returns a context class for the given slice. If a context class is not defined, defines
16
15
  # a class named `Views::Context` within the slice's namespace.
17
16
  #
18
17
  # @api private
18
+ # @since 2.1.0
19
19
  def context_class(slice)
20
20
  views_namespace = views_namespace(slice)
21
21
 
@@ -30,6 +30,8 @@ module Hanami
30
30
 
31
31
  private
32
32
 
33
+ # @api private
34
+ # @since 2.1.0
33
35
  def context_superclass(slice)
34
36
  return Hanami::View::Context if Hanami.app.equal?(slice)
35
37
 
@@ -44,8 +46,10 @@ module Hanami
44
46
  end
45
47
  end
46
48
 
47
- # TODO: this could be moved into the top-level Extensions::View
49
+ # @api private
50
+ # @since 2.1.0
48
51
  def views_namespace(slice)
52
+ # TODO: this could be moved into the top-level Extensions::View
49
53
  if slice.namespace.const_defined?(:Views)
50
54
  slice.namespace.const_get(:Views)
51
55
  else
@@ -54,6 +58,8 @@ module Hanami
54
58
  end
55
59
  end
56
60
 
61
+ # @api private
62
+ # @since 2.1.0
57
63
  module ClassExtension
58
64
  def self.included(context_class)
59
65
  super
@@ -63,18 +69,39 @@ module Hanami
63
69
  context_class.prepend(InstanceMethods)
64
70
  end
65
71
 
72
+ # @api private
73
+ # @since 2.1.0
66
74
  module ClassMethods
75
+ # @api private
76
+ # @since 2.1.0
67
77
  def configure_for_slice(slice)
68
78
  extend SliceConfiguredContext.new(slice)
69
79
  end
70
80
  end
71
81
 
82
+ # @api public
83
+ # @since 2.1.0
72
84
  module InstanceMethods
85
+ # Returns the app's inflector.
86
+ #
87
+ # @return [Dry::Inflector] the inflector
88
+ #
89
+ # @api public
90
+ # @since 2.1.0
73
91
  attr_reader :inflector
74
92
 
93
+ # Returns the app's settings.
94
+ #
95
+ # @return [Hanami::Settings] the settings
96
+ #
97
+ # @api public
98
+ # @since 2.1.0
75
99
  attr_reader :settings
76
100
 
77
101
  # @see SliceConfiguredContext#define_new
102
+ #
103
+ # @api private
104
+ # @since 2.1.0
78
105
  def initialize( # rubocop:disable Metrics/ParameterLists
79
106
  inflector: nil,
80
107
  settings: nil,
@@ -94,6 +121,8 @@ module Hanami
94
121
  super(**args)
95
122
  end
96
123
 
124
+ # @api private
125
+ # @since 2.1.0
97
126
  def initialize_copy(source)
98
127
  # The standard implementation of initialize_copy will make shallow copies of all
99
128
  # instance variables from the source. This is fine for most of our ivars.
@@ -104,17 +133,14 @@ module Hanami
104
133
  @content_for = source.instance_variable_get(:@content_for).dup
105
134
  end
106
135
 
107
- def with(**args)
108
- self.class.new(
109
- inflector: @inflector,
110
- settings: @settings,
111
- assets: @assets,
112
- routes: @routes,
113
- request: @request,
114
- **args
115
- )
116
- end
117
-
136
+ # Returns the app's assets.
137
+ #
138
+ # @return [Hanami::Assets] the assets
139
+ #
140
+ # @raise [Hanami::ComponentLoadError] if the hanami-assets gem is not bundled
141
+ #
142
+ # @api public
143
+ # @since 2.1.0
118
144
  def assets
119
145
  unless @assets
120
146
  raise Hanami::ComponentLoadError, "the hanami-assets gem is required to access assets"
@@ -123,6 +149,14 @@ module Hanami
123
149
  @assets
124
150
  end
125
151
 
152
+ # Returns the current request, if the view is rendered from within an action.
153
+ #
154
+ # @return [Hanami::Action::Request] the request
155
+ #
156
+ # @raise [Hanami::ComponentLoadError] if the view is not rendered from within a request
157
+ #
158
+ # @api public
159
+ # @since 2.1.0
126
160
  def request
127
161
  unless @request
128
162
  raise Hanami::ComponentLoadError, "only views rendered from Hanami::Action instances have a request"
@@ -131,6 +165,15 @@ module Hanami
131
165
  @request
132
166
  end
133
167
 
168
+ # Returns the app's routes helper.
169
+ #
170
+ # @return [Hanami::Slice::RoutesHelper] the routes helper
171
+ #
172
+ # @raise [Hanami::ComponentLoadError] if the hanami-router gem is not bundled or routes
173
+ # are not defined
174
+ #
175
+ # @api public
176
+ # @since 2.1.0
134
177
  def routes
135
178
  unless @routes
136
179
  raise Hanami::ComponentLoadError, "the hanami-router gem is required to access routes"
@@ -139,6 +182,32 @@ module Hanami
139
182
  @routes
140
183
  end
141
184
 
185
+ # @overload content_for(key, value = nil, &block)
186
+ # Stores a string or block of template markup for later use.
187
+ #
188
+ # @param key [Symbol] the content key, for later retrieval
189
+ # @param value [String, nil] the content, if no block is given
190
+ #
191
+ # @return [String] the content
192
+ #
193
+ # @example
194
+ # content_for(:page_title, "Hello world")
195
+ #
196
+ # @example In a template
197
+ # <% content_for :page_title do %>
198
+ # <h1>Hello world</h1>
199
+ # <% end %>
200
+ #
201
+ # @overload content_for(key)
202
+ # Returns the previously stored content for the given key.
203
+ #
204
+ # @param key [Symbol] the content key
205
+ #
206
+ # @return [String, nil] the content, or nil if no content previously stored with the
207
+ # key
208
+ #
209
+ # @api public
210
+ # @since 2.1.0
142
211
  def content_for(key, value = nil)
143
212
  if block_given?
144
213
  @content_for[key] = yield
@@ -149,18 +218,41 @@ module Hanami
149
218
  end
150
219
  end
151
220
 
152
- def current_path
153
- request.fullpath
154
- end
155
-
221
+ # Returns the current request's CSRF token.
222
+ #
223
+ # @return [String] the token
224
+ #
225
+ # @raise [Hanami::ComponentLoadError] if the view is not rendered from within a request
226
+ # @raise [Hanami::Action::MissingSessionError] if sessions are not enabled
227
+ #
228
+ # @api public
229
+ # @since 2.1.0
156
230
  def csrf_token
157
231
  request.session[Hanami::Action::CSRFProtection::CSRF_TOKEN]
158
232
  end
159
233
 
234
+ # Returns the session for the current request.
235
+ #
236
+ # @return [Rack::Session::Abstract::SessionHash] the session hash
237
+ #
238
+ # @raise [Hanami::ComponentLoadError] if the view is not rendered from within a request
239
+ # @raise [Hanami::Action::MissingSessionError] if sessions are not enabled
240
+ #
241
+ # @api public
242
+ # @since 2.1.0
160
243
  def session
161
244
  request.session
162
245
  end
163
246
 
247
+ # Returns the flash hash for the current request.
248
+ #
249
+ # @return []
250
+ #
251
+ # @raise [Hanami::ComponentLoadError] if the view is not rendered from within a request
252
+ # @raise [Hanami::Action::MissingSessionError] if sessions are not enabled
253
+ #
254
+ # @api public
255
+ # @since 2.1.0
164
256
  def flash
165
257
  request.flash
166
258
  end
@@ -3,21 +3,82 @@
3
3
  module Hanami
4
4
  module Extensions
5
5
  module View
6
- # @api private
6
+ # @api public
7
+ # @since 2.1.0
7
8
  module Part
9
+ # @api private
10
+ # @since 2.1.0
8
11
  def self.included(part_class)
9
12
  super
10
13
 
11
14
  part_class.extend(Hanami::SliceConfigurable)
12
- part_class.include(StandardHelpers)
13
15
  part_class.extend(ClassMethods)
14
16
  end
15
17
 
18
+ # @api private
19
+ # @since 2.1.0
16
20
  module ClassMethods
21
+ # @api private
22
+ # @since 2.1.0
17
23
  def configure_for_slice(slice)
18
- extend SliceConfiguredHelpers.new(slice)
24
+ extend SliceConfiguredPart.new(slice)
25
+
26
+ const_set :PartHelpers, Class.new(PartHelpers) { |klass|
27
+ klass.configure_for_slice(slice)
28
+ }
19
29
  end
20
30
  end
31
+
32
+ # Returns an object including the default Hanami helpers as well as the user-defined helpers
33
+ # for the part's slice.
34
+ #
35
+ # Use this when you need to access helpers inside your part classes.
36
+ #
37
+ # @return [Object] the helpers object
38
+ #
39
+ # @api public
40
+ # @since 2.1.0
41
+ def helpers
42
+ @helpers ||= self.class.const_get(:PartHelpers).new(context: _context)
43
+ end
44
+ end
45
+
46
+ # Standalone helpers class including both {StandardHelpers} as well as the user-defined
47
+ # helpers for the slice.
48
+ #
49
+ # Used used where helpers should be addressed via an intermediary object (i.e. in parts),
50
+ # rather than mixed into a class directly.
51
+ #
52
+ # @api private
53
+ # @since 2.1.0
54
+ class PartHelpers
55
+ extend Hanami::SliceConfigurable
56
+
57
+ include StandardHelpers
58
+
59
+ # @api private
60
+ # @since 2.1.0
61
+ def self.configure_for_slice(slice)
62
+ extend SliceConfiguredHelpers.new(slice)
63
+ end
64
+
65
+ # Returns the context for the current view rendering.
66
+ #
67
+ # @return [Hanami::View::Context] the context
68
+ #
69
+ # @api public
70
+ # @since 2.1.0
71
+ attr_reader :_context
72
+
73
+ # @api public
74
+ # @since 2.1.0
75
+ alias_method :context, :_context
76
+
77
+ # @api private
78
+ # @since 2.1.0
79
+ def initialize(context:)
80
+ @_context = context
81
+ end
21
82
  end
22
83
  end
23
84
  end
@@ -4,7 +4,10 @@ module Hanami
4
4
  module Extensions
5
5
  module View
6
6
  # @api private
7
+ # @since 2.1.0
7
8
  module Scope
9
+ # @api private
10
+ # @since 2.1.0
8
11
  def self.included(scope_class)
9
12
  super
10
13
 
@@ -13,7 +16,11 @@ module Hanami
13
16
  scope_class.extend(ClassMethods)
14
17
  end
15
18
 
19
+ # @api private
20
+ # @since 2.1.0
16
21
  module ClassMethods
22
+ # @api private
23
+ # @since 2.1.0
17
24
  def configure_for_slice(slice)
18
25
  extend SliceConfiguredHelpers.new(slice)
19
26
  end
@@ -3,35 +3,39 @@
3
3
  module Hanami
4
4
  module Extensions
5
5
  module View
6
- # Provides slice-specific configuration and behavior for any view context class
7
- # defined within a slice's module namespace.
6
+ # Provides slice-specific configuration and behavior for any view context class defined within
7
+ # a slice's module namespace.
8
8
  #
9
- # @api private
10
- # @since 2.0.0
9
+ # @api public
10
+ # @since 2.1.0
11
11
  class SliceConfiguredContext < Module
12
12
  attr_reader :slice
13
13
 
14
+ # @api private
15
+ # @since 2.1.0
14
16
  def initialize(slice)
15
17
  super()
16
18
  @slice = slice
17
19
  end
18
20
 
21
+ # @api private
22
+ # @since 2.1.0
19
23
  def extended(_context_class)
20
24
  define_new
21
25
  end
22
26
 
27
+ # @api public
28
+ # @since 2.1.0
23
29
  def inspect
24
30
  "#<#{self.class.name}[#{slice.name}]>"
25
31
  end
26
32
 
27
33
  private
28
34
 
29
- # Defines a {.new} method on the context class that resolves key components from
30
- # the app container and provides them to {#initialize} as injected
31
- # dependencies.
35
+ # Defines a {.new} method on the context class that resolves key components from the app
36
+ # container and provides them to {#initialize} as injected dependencies.
32
37
  #
33
38
  # This includes the following app components:
34
- #
35
39
  # - the configured inflector as `inflector`
36
40
  # - "settings" from the app container as `settings`
37
41
  # - "routes" from the app container as `routes`