hanami 2.1.0.beta2.1 → 2.1.0.rc2

Sign up to get free protection for your applications and to get access to all the features.
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`