hanami 2.1.0.beta2 → 2.1.0.rc1

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 (38) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +41 -9
  3. data/hanami.gemspec +1 -1
  4. data/lib/hanami/config/actions.rb +14 -0
  5. data/lib/hanami/extensions/action/slice_configured_action.rb +5 -5
  6. data/lib/hanami/extensions/action.rb +4 -4
  7. data/lib/hanami/extensions/view/context.rb +0 -11
  8. data/lib/hanami/extensions/view/part.rb +51 -2
  9. data/lib/hanami/extensions/view/slice_configured_part.rb +72 -0
  10. data/lib/hanami/extensions/view/slice_configured_view.rb +2 -2
  11. data/lib/hanami/helpers/assets_helper.rb +31 -43
  12. data/lib/hanami/routes.rb +33 -2
  13. data/lib/hanami/slice.rb +12 -2
  14. data/lib/hanami/slice_registrar.rb +48 -23
  15. data/lib/hanami/version.rb +1 -1
  16. data/lib/hanami/web/rack_logger.rb +70 -2
  17. data/lib/hanami/web/welcome.html.erb +203 -0
  18. data/lib/hanami/web/welcome.rb +46 -0
  19. data/spec/integration/assets/assets_spec.rb +14 -3
  20. data/spec/integration/logging/request_logging_spec.rb +65 -7
  21. data/spec/integration/rack_app/method_override_spec.rb +97 -0
  22. data/spec/integration/slices_spec.rb +275 -5
  23. data/spec/integration/view/context/assets_spec.rb +0 -8
  24. data/spec/integration/view/context/inflector_spec.rb +0 -8
  25. data/spec/integration/view/context/settings_spec.rb +0 -8
  26. data/spec/integration/view/helpers/part_helpers_spec.rb +2 -2
  27. data/spec/integration/view/helpers/user_defined_helpers/part_helpers_spec.rb +10 -10
  28. data/spec/integration/view/parts/default_rendering_spec.rb +138 -0
  29. data/spec/integration/web/welcome_view_spec.rb +84 -0
  30. data/spec/support/app_integration.rb +22 -4
  31. data/spec/unit/hanami/helpers/assets_helper/audio_tag_spec.rb +2 -2
  32. data/spec/unit/hanami/helpers/assets_helper/{favicon_link_tag_spec.rb → favicon_tag_spec.rb} +8 -12
  33. data/spec/unit/hanami/helpers/assets_helper/image_tag_spec.rb +1 -1
  34. data/spec/unit/hanami/helpers/assets_helper/javascript_tag_spec.rb +3 -3
  35. data/spec/unit/hanami/helpers/assets_helper/{stylesheet_link_tag_spec.rb → stylesheet_tag_spec.rb} +13 -13
  36. data/spec/unit/hanami/helpers/assets_helper/video_tag_spec.rb +5 -1
  37. data/spec/unit/hanami/version_spec.rb +1 -1
  38. metadata +15 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 06d7766d5bdb4f510945a2065cc15457acbf00f43970d691afda74219f0aeb02
4
- data.tar.gz: 58935ca7f23c3d3612a91b277827ed941c30839d04c9bf577e35a095fa667f6f
3
+ metadata.gz: 3ba29263713547809e78df051e0138f202d947ce6e9cf2bc9d06c5bf79438cb7
4
+ data.tar.gz: 0677ae36e85bbb92ed1bdde4ae48e92ac062ccf89f6d77b86b85578fd8975a06
5
5
  SHA512:
6
- metadata.gz: 50a785250f4cf249c5721dd37a5e5a7a240282e5d3820fe848a53c133b1727ea57e5beb351429e636bb4a7d89812bfc53794b5357865b134598a5d54e60666db
7
- data.tar.gz: 677f40136e0477c57e493e41ed9a25666d9b53d6dd4ae4c23ee6e61d062f9537c39d86eb16d264f194eda4209e5eb0e38fa0554fd3efaf2d39ea23afa1ead27b
6
+ metadata.gz: f24d67421d88e019d3fdf1d282fd55cff3a303e59fa7195fc8b152a6b85d1990aafe1cf3c583eeb2b561cfcae4b76e4120ef9863f12cee424c8372526cfb178a
7
+ data.tar.gz: 8e9ab011e12426e7d2e03ed784858309f7ca33fa8aa84d23ae84cd7d9435563998f38cb9d53322e2631c017334465cec10e0b2f65a58d8dcb0290c360dff5b1c
data/CHANGELOG.md CHANGED
@@ -2,26 +2,58 @@
2
2
 
3
3
  The web, with simplicity.
4
4
 
5
+ ## v2.1.0.rc1 - 2023-11-01
6
+
7
+ ### Added
8
+
9
+ - [Aaron Moodie & Tim Riley] Render a welcome page when no routes are defined (#1353)
10
+ - [Luca Guidi] Allow _Method Override_ by default, by mounting `Rack::MethodOverride` middleware. (#1344)
11
+
12
+ ### Fixed
13
+
14
+ - [Luca Guidi] Ensure compatibility with Ruby Logger from stdlib (#1352)
15
+
16
+ ### Changed
17
+
18
+ - [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.
19
+ - [Tim Riley] Use `helpers` prefix to access helper methods from view parts (e.g. `helpers.format_number`)
20
+ - [Tim Riley] Make it possible to directly initialize view parts to ease their unit tests (#1357)
21
+ - [Tim Riley] Remove short names for assets helpers (#1356):
22
+ - Keep `javascript_tag` (remove `javascript` and `js` aliases)
23
+ - Keep `stylesheet_tag` (renamed from `stylesheet_link_tag`; remove `stylesheet` and `css` aliases)
24
+ - Keep `favicon_tag` (renamed from `favicon_link_tag`; remove `favicon` alias)
25
+ - Keep `image_tag` (remove `image` alias)
26
+ - Keep `video_tag` (remove `video` alias)
27
+ - Keep `audio_tag` (remove `audio` alias)
28
+
29
+
30
+ ## v2.1.0.beta2.1 - 2023-10-04
31
+
32
+ ### Added
33
+
34
+ - [Tim Riley, Luca Guidi] Added assets helpers aliases (#1319, #1339):
35
+ - Added `#js` and `#javascript_tag` as alias for `#javascript`
36
+ - Added `#css` and `#stylesheet_link_tag` as alias for `#stylesheet`
37
+ - Added `#image_tag` as alias for `#image`
38
+ - Added `#favicon_link_tag` as alias for `#favicon`
39
+ - Added `#video_tag` as alias for `#video`
40
+ - Added `#audio_tag` as alias for `#audio`
41
+
5
42
  ## v2.1.0.beta2 - 2023-10-04
6
43
 
7
44
  ### Added
8
45
 
9
46
  - [Luca Guidi, Tim Riley] Reimplement assets integration (#1319, #1332, #1333, #1336)
10
47
  - [Tim Riley] Introduce `Hanami::Helpers::AssetsHelper`, automatically included in view templates, scopes and parts when hanami-assets is bundled (#1319)
11
- - [Tim Riley] Renamed assets helpers (#1319):
12
- - Removed `#asset_path` in favour of `#asset_url` only
13
- - Renamed `#javascript` to `#javascript_tag`, retaining `#js` alias
14
- - Renamed `#stylesheet` to `#stylesheet_link_tag`, retaining `#css` alias
15
- - Renamed `#image` to `#image_tag`
16
- - Renamed `#favicon` to `#favicon_link_tag`, with `#favicon` retained as an alias
17
- - Renamed `#video` to `#video_tag`
18
- - Renamed `#audio` to `#audio_tag`
19
-
20
48
 
21
49
  ### Fixed
22
50
 
23
51
  - [Tim Riley] Return appropriate response statuses based on error type (#1330)
24
52
 
53
+ ### Changed
54
+
55
+ - [Tim Riley] Removed `#asset_path` in favour of `#asset_url` only (#1319)
56
+
25
57
  ## v2.1.0.beta1 - 2023-06-29
26
58
 
27
59
  ### 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|
@@ -74,6 +74,20 @@ module Hanami
74
74
  # @since 2.0.0
75
75
  attr_accessor :content_security_policy
76
76
 
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
90
+
77
91
  # The following settings are for view and assets integration with actions, and are NOT
78
92
  # publicly released as of 2.0.0. We'll make full documentation available when these become
79
93
  # public in a subsequent release.
@@ -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
@@ -104,17 +104,6 @@ module Hanami
104
104
  @content_for = source.instance_variable_get(:@content_for).dup
105
105
  end
106
106
 
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
-
118
107
  def assets
119
108
  unless @assets
120
109
  raise Hanami::ComponentLoadError, "the hanami-assets gem is required to access assets"
@@ -4,20 +4,69 @@ module Hanami
4
4
  module Extensions
5
5
  module View
6
6
  # @api private
7
+ # @since 2.1.0
7
8
  module Part
8
9
  def self.included(part_class)
9
10
  super
10
11
 
11
12
  part_class.extend(Hanami::SliceConfigurable)
12
- part_class.include(StandardHelpers)
13
13
  part_class.extend(ClassMethods)
14
14
  end
15
15
 
16
16
  module ClassMethods
17
17
  def configure_for_slice(slice)
18
- extend SliceConfiguredHelpers.new(slice)
18
+ extend SliceConfiguredPart.new(slice)
19
+
20
+ const_set :PartHelpers, Class.new(PartHelpers) { |klass|
21
+ klass.configure_for_slice(slice)
22
+ }
19
23
  end
20
24
  end
25
+
26
+ # Returns an object including the default Hanami helpers as well as the user-defined helpers
27
+ # for the part's slice.
28
+ #
29
+ # Use this when you need to access helpers inside your part classes.
30
+ #
31
+ # @return PartHelpers
32
+ #
33
+ # @api public
34
+ # @since 2.1.0
35
+ def helpers
36
+ @helpers ||= self.class.const_get(:PartHelpers).new(context: _context)
37
+ end
38
+ end
39
+
40
+ # Standalone helpers class including both {StandardHelpers} as well as the user-defined
41
+ # helpers for the slice.
42
+ #
43
+ # Used used where helpers should be addressed via an intermediary object (i.e. in parts),
44
+ # rather than mixed into a class directly.
45
+ #
46
+ # @api private
47
+ # @since 2.1.0
48
+ class PartHelpers
49
+ extend Hanami::SliceConfigurable
50
+
51
+ include StandardHelpers
52
+
53
+ def self.configure_for_slice(slice)
54
+ extend SliceConfiguredHelpers.new(slice)
55
+ end
56
+
57
+ # @api public
58
+ # @since 2.1.0
59
+ attr_reader :_context
60
+
61
+ # @api public
62
+ # @since 2.1.0
63
+ alias_method :context, :_context
64
+
65
+ # @api private
66
+ # @since 2.1.0
67
+ def initialize(context:)
68
+ @_context = context
69
+ end
21
70
  end
22
71
  end
23
72
  end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hanami
4
+ module Extensions
5
+ module View
6
+ # Provides slice-specific configuration and behavior for any view part class defined within a
7
+ # slice's module namespace.
8
+ #
9
+ # @api private
10
+ # @since 2.1.0
11
+ class SliceConfiguredPart < Module
12
+ attr_reader :slice
13
+
14
+ # @api private
15
+ # @since 2.1.0
16
+ def initialize(slice)
17
+ super()
18
+ @slice = slice
19
+ end
20
+
21
+ # @api private
22
+ # @since 2.1.0
23
+ def extended(klass)
24
+ define_new
25
+ end
26
+
27
+ # @api private
28
+ # @since 2.1.0
29
+ def inspect
30
+ "#<#{self.class.name}[#{slice.name}]>"
31
+ end
32
+
33
+ private
34
+
35
+ # Defines a `.new` method on the part class that provides a default `rendering:` argument of
36
+ # a rendering coming from a view configured for the slice. This means that any part can be
37
+ # initialized standalone (with a `value:` only) and still have access to all the integrated
38
+ # view facilities from the slice, such as helpers. This is helpful when unit testing parts.
39
+ #
40
+ # @example
41
+ # module MyApp::Views::Parts
42
+ # class Post < MyApp::View::Part
43
+ # def title_tag
44
+ # helpers.h1(value.title)
45
+ # end
46
+ # end
47
+ # end
48
+ #
49
+ # # Useful when unit testing parts
50
+ # part = MyApp::Views::Parts::Post.new(value: hello_world_post)
51
+ # part.title_tag # => "<h1>Hello world</h1>"
52
+ #
53
+ # @api private
54
+ # @since 2.1.0
55
+ def define_new
56
+ slice = self.slice
57
+
58
+ define_method(:new) do |**args|
59
+ return super(**args) if args.key?(:rendering)
60
+
61
+ slice_rendering = Class.new(Hanami::View)
62
+ .configure_for_slice(slice)
63
+ .new
64
+ .rendering
65
+
66
+ super(rendering: slice_rendering, **args)
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -108,7 +108,7 @@ module Hanami
108
108
  # - We are a slice, and the view's inherited `paths` is identical to the parent's config
109
109
  # (which would result in the view in a slice erroneously trying to find templates in
110
110
  # the app)
111
- if (!slice.parent && view_class.config.paths.empty?) ||
111
+ if view_class.config.paths.empty? ||
112
112
  (slice.parent && view_class.config.paths.map(&:dir) == [templates_path(slice.parent)])
113
113
  view_class.config.paths = templates_path(slice)
114
114
  end
@@ -178,7 +178,7 @@ module Hanami
178
178
  else
179
179
  views_namespace.const_set(:Part, Class.new(part_superclass).tap { |klass|
180
180
  # Give the slice to `configure_for_slice`, since it cannot be inferred when it is
181
- # called via `.inherited`, since the class is anonymous at this point
181
+ # called via `.inherited`, because the class is anonymous at this point
182
182
  klass.configure_for_slice(slice)
183
183
  })
184
184
  end
@@ -173,10 +173,6 @@ module Hanami
173
173
  end
174
174
  end
175
175
 
176
- # @api public
177
- # @since 2.1.0
178
- alias_method :js, :javascript_tag
179
-
180
176
  # Generate `link` tag for given source(s)
181
177
  #
182
178
  # It accepts one or more strings representing the name of the asset, if it
@@ -251,7 +247,7 @@ module Hanami
251
247
  #
252
248
  # # <link href="https://assets.bookshelf.org/assets/application-28a6b886de2372ee3922fcaf3f78f2d8.css"
253
249
  # # type="text/css" rel="stylesheet">
254
- def stylesheet_link_tag(*source_paths, **options)
250
+ def stylesheet_tag(*source_paths, **options)
255
251
  options = options.reject { |k, _| k.to_sym == :href }
256
252
 
257
253
  _safe_tags(*source_paths) do |source_path|
@@ -271,10 +267,6 @@ module Hanami
271
267
  end
272
268
  end
273
269
 
274
- # @api public
275
- # @since 2.1.0
276
- alias_method :css, :stylesheet_link_tag
277
-
278
270
  # Generate `img` tag for given source
279
271
  #
280
272
  # It accepts one string representing the name of the asset, if it comes
@@ -305,37 +297,37 @@ module Hanami
305
297
  #
306
298
  # @example Basic Usage
307
299
  #
308
- # <%= image_tag "logo.png" %>
300
+ # <%= image "logo.png" %>
309
301
  #
310
302
  # # <img src="/assets/logo.png" alt="Logo">
311
303
  #
312
304
  # @example Custom alt Attribute
313
305
  #
314
- # <%= image_tag "logo.png", alt: "Application Logo" %>
306
+ # <%= image "logo.png", alt: "Application Logo" %>
315
307
  #
316
308
  # # <img src="/assets/logo.png" alt="Application Logo">
317
309
  #
318
310
  # @example Custom HTML Attributes
319
311
  #
320
- # <%= image_tag "logo.png", id: "logo", class: "image" %>
312
+ # <%= image "logo.png", id: "logo", class: "image" %>
321
313
  #
322
314
  # # <img src="/assets/logo.png" alt="Logo" id="logo" class="image">
323
315
  #
324
316
  # @example Absolute URL
325
317
  #
326
- # <%= image_tag "https://example-cdn.com/images/logo.png" %>
318
+ # <%= image "https://example-cdn.com/images/logo.png" %>
327
319
  #
328
320
  # # <img src="https://example-cdn.com/images/logo.png" alt="Logo">
329
321
  #
330
322
  # @example Fingerprint Mode
331
323
  #
332
- # <%= image_tag "logo.png" %>
324
+ # <%= image "logo.png" %>
333
325
  #
334
326
  # # <img src="/assets/logo-28a6b886de2372ee3922fcaf3f78f2d8.png" alt="Logo">
335
327
  #
336
328
  # @example CDN Mode
337
329
  #
338
- # <%= image_tag "logo.png" %>
330
+ # <%= image "logo.png" %>
339
331
  #
340
332
  # # <img src="https://assets.bookshelf.org/assets/logo-28a6b886de2372ee3922fcaf3f78f2d8.png" alt="Logo">
341
333
  def image_tag(source, options = {})
@@ -376,35 +368,35 @@ module Hanami
376
368
  #
377
369
  # @example Basic Usage
378
370
  #
379
- # <%= favicon_link_tag %>
371
+ # <%= favicon %>
380
372
  #
381
373
  # # <link href="/assets/favicon.ico" rel="shortcut icon" type="image/x-icon">
382
374
  #
383
375
  # @example Custom Path
384
376
  #
385
- # <%= favicon_link_tag "fav.ico" %>
377
+ # <%= favicon "fav.ico" %>
386
378
  #
387
379
  # # <link href="/assets/fav.ico" rel="shortcut icon" type="image/x-icon">
388
380
  #
389
381
  # @example Custom HTML Attributes
390
382
  #
391
- # <%= favicon_link_tag "favicon.ico", id: "fav" %>
383
+ # <%= favicon "favicon.ico", id: "fav" %>
392
384
  #
393
385
  # # <link id: "fav" href="/assets/favicon.ico" rel="shortcut icon" type="image/x-icon">
394
386
  #
395
387
  # @example Fingerprint Mode
396
388
  #
397
- # <%= favicon_link_tag %>
389
+ # <%= favicon %>
398
390
  #
399
391
  # # <link href="/assets/favicon-28a6b886de2372ee3922fcaf3f78f2d8.ico" rel="shortcut icon" type="image/x-icon">
400
392
  #
401
393
  # @example CDN Mode
402
394
  #
403
- # <%= favicon_link_tag %>
395
+ # <%= favicon %>
404
396
  #
405
397
  # # <link href="https://assets.bookshelf.org/assets/favicon-28a6b886de2372ee3922fcaf3f78f2d8.ico"
406
398
  # rel="shortcut icon" type="image/x-icon">
407
- def favicon_link_tag(source = DEFAULT_FAVICON, options = {})
399
+ def favicon_tag(source = DEFAULT_FAVICON, options = {})
408
400
  options = options.reject { |k, _| k.to_sym == :href }
409
401
 
410
402
  attributes = {
@@ -417,10 +409,6 @@ module Hanami
417
409
  tag.link(**attributes)
418
410
  end
419
411
 
420
- # @api public
421
- # @since 2.1.0
422
- alias_method :favicon, :favicon_link_tag
423
-
424
412
  # Generate `video` tag for given source
425
413
  #
426
414
  # It accepts one string representing the name of the asset, if it comes
@@ -454,26 +442,26 @@ module Hanami
454
442
  #
455
443
  # @example Basic Usage
456
444
  #
457
- # <%= video_tag "movie.mp4" %>
445
+ # <%= video "movie.mp4" %>
458
446
  #
459
447
  # # <video src="/assets/movie.mp4"></video>
460
448
  #
461
449
  # @example Absolute URL
462
450
  #
463
- # <%= video_tag "https://example-cdn.com/assets/movie.mp4" %>
451
+ # <%= video "https://example-cdn.com/assets/movie.mp4" %>
464
452
  #
465
453
  # # <video src="https://example-cdn.com/assets/movie.mp4"></video>
466
454
  #
467
455
  # @example Custom HTML Attributes
468
456
  #
469
- # <%= video_tag("movie.mp4", autoplay: true, controls: true) %>
457
+ # <%= video("movie.mp4", autoplay: true, controls: true) %>
470
458
  #
471
459
  # # <video src="/assets/movie.mp4" autoplay="autoplay" controls="controls"></video>
472
460
  #
473
461
  # @example Fallback Content
474
462
  #
475
463
  # <%=
476
- # video_tag("movie.mp4") do
464
+ # video("movie.mp4") do
477
465
  # "Your browser does not support the video tag"
478
466
  # end
479
467
  # %>
@@ -485,7 +473,7 @@ module Hanami
485
473
  # @example Tracks
486
474
  #
487
475
  # <%=
488
- # video_tag("movie.mp4") do
476
+ # video("movie.mp4") do
489
477
  # tag.track(kind: "captions", src: asset_url("movie.en.vtt"),
490
478
  # srclang: "en", label: "English")
491
479
  # end
@@ -497,25 +485,25 @@ module Hanami
497
485
  #
498
486
  # @example Without Any Argument
499
487
  #
500
- # <%= video_tag %>
488
+ # <%= video %>
501
489
  #
502
490
  # # ArgumentError
503
491
  #
504
492
  # @example Without src And Without Block
505
493
  #
506
- # <%= video_tag(content: true) %>
494
+ # <%= video(content: true) %>
507
495
  #
508
496
  # # ArgumentError
509
497
  #
510
498
  # @example Fingerprint Mode
511
499
  #
512
- # <%= video_tag "movie.mp4" %>
500
+ # <%= video "movie.mp4" %>
513
501
  #
514
502
  # # <video src="/assets/movie-28a6b886de2372ee3922fcaf3f78f2d8.mp4"></video>
515
503
  #
516
504
  # @example CDN Mode
517
505
  #
518
- # <%= video_tag "movie.mp4" %>
506
+ # <%= video "movie.mp4" %>
519
507
  #
520
508
  # # <video src="https://assets.bookshelf.org/assets/movie-28a6b886de2372ee3922fcaf3f78f2d8.mp4"></video>
521
509
  def video_tag(source = nil, options = {}, &blk)
@@ -556,26 +544,26 @@ module Hanami
556
544
  #
557
545
  # @example Basic Usage
558
546
  #
559
- # <%= audio_tag "song.ogg" %>
547
+ # <%= audio "song.ogg" %>
560
548
  #
561
549
  # # <audio src="/assets/song.ogg"></audio>
562
550
  #
563
551
  # @example Absolute URL
564
552
  #
565
- # <%= audio_tag "https://example-cdn.com/assets/song.ogg" %>
553
+ # <%= audio "https://example-cdn.com/assets/song.ogg" %>
566
554
  #
567
555
  # # <audio src="https://example-cdn.com/assets/song.ogg"></audio>
568
556
  #
569
557
  # @example Custom HTML Attributes
570
558
  #
571
- # <%= audio_tag("song.ogg", autoplay: true, controls: true) %>
559
+ # <%= audio("song.ogg", autoplay: true, controls: true) %>
572
560
  #
573
561
  # # <audio src="/assets/song.ogg" autoplay="autoplay" controls="controls"></audio>
574
562
  #
575
563
  # @example Fallback Content
576
564
  #
577
565
  # <%=
578
- # audio_tag("song.ogg") do
566
+ # audio("song.ogg") do
579
567
  # "Your browser does not support the audio tag"
580
568
  # end
581
569
  # %>
@@ -587,7 +575,7 @@ module Hanami
587
575
  # @example Tracks
588
576
  #
589
577
  # <%=
590
- # audio_tag("song.ogg") do
578
+ # audio("song.ogg") do
591
579
  # tag.track(kind: "captions", src: asset_url("song.pt-BR.vtt"),
592
580
  # srclang: "pt-BR", label: "Portuguese")
593
581
  # end
@@ -599,25 +587,25 @@ module Hanami
599
587
  #
600
588
  # @example Without Any Argument
601
589
  #
602
- # <%= audio_tag %>
590
+ # <%= audio %>
603
591
  #
604
592
  # # ArgumentError
605
593
  #
606
594
  # @example Without src And Without Block
607
595
  #
608
- # <%= audio_tag(controls: true) %>
596
+ # <%= audio(controls: true) %>
609
597
  #
610
598
  # # ArgumentError
611
599
  #
612
600
  # @example Fingerprint Mode
613
601
  #
614
- # <%= audio_tag "song.ogg" %>
602
+ # <%= audio "song.ogg" %>
615
603
  #
616
604
  # # <audio src="/assets/song-28a6b886de2372ee3922fcaf3f78f2d8.ogg"></audio>
617
605
  #
618
606
  # @example CDN Mode
619
607
  #
620
- # <%= audio_tag "song.ogg" %>
608
+ # <%= audio "song.ogg" %>
621
609
  #
622
610
  # # <audio src="https://assets.bookshelf.org/assets/song-28a6b886de2372ee3922fcaf3f78f2d8.ogg"></audio>
623
611
  def audio_tag(source = nil, options = {}, &blk)
data/lib/hanami/routes.rb CHANGED
@@ -60,6 +60,35 @@ module Hanami
60
60
  end
61
61
  end
62
62
 
63
+ # Wrapper class for the (otherwise opaque) proc returned from {.routes}, adding an `#empty?`
64
+ # method that returns true if no routes were defined.
65
+ #
66
+ # This is useful when needing to determine behaviour based on the presence of user-defined
67
+ # routes, such as determining whether to show the Hanami welcome page in {Slice#load_router}.
68
+ #
69
+ # @api private
70
+ # @since 2.1.0
71
+ class RoutesProc < DelegateClass(Proc)
72
+ # @api private
73
+ # @since 2.1.0
74
+ def self.empty
75
+ new(proc {}, empty: true)
76
+ end
77
+
78
+ # @api private
79
+ # @since 2.1.0
80
+ def initialize(proc, empty: false)
81
+ @empty = empty
82
+ super(proc)
83
+ end
84
+
85
+ # @api private
86
+ # @since 2.1.0
87
+ def empty?
88
+ !!@empty
89
+ end
90
+ end
91
+
63
92
  # @api private
64
93
  def self.routes
65
94
  @routes ||= build_routes
@@ -68,9 +97,9 @@ module Hanami
68
97
  class << self
69
98
  # @api private
70
99
  def build_routes(definitions = self.definitions)
71
- return if definitions.empty?
100
+ return RoutesProc.empty if definitions.empty?
72
101
 
73
- proc do
102
+ routes_proc = proc do
74
103
  definitions.each do |(name, args, kwargs, block)|
75
104
  if block
76
105
  public_send(name, *args, **kwargs, &block)
@@ -79,6 +108,8 @@ module Hanami
79
108
  end
80
109
  end
81
110
  end
111
+
112
+ RoutesProc.new(routes_proc)
82
113
  end
83
114
 
84
115
  # @api private