hanami 2.1.0.beta2.1 → 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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +25 -0
- data/hanami.gemspec +1 -1
- data/lib/hanami/config/actions.rb +14 -0
- data/lib/hanami/extensions/action/slice_configured_action.rb +5 -5
- data/lib/hanami/extensions/action.rb +4 -4
- data/lib/hanami/extensions/view/context.rb +0 -11
- data/lib/hanami/extensions/view/part.rb +51 -2
- data/lib/hanami/extensions/view/slice_configured_part.rb +72 -0
- data/lib/hanami/extensions/view/slice_configured_view.rb +2 -2
- data/lib/hanami/helpers/assets_helper.rb +6 -38
- data/lib/hanami/routes.rb +33 -2
- data/lib/hanami/slice.rb +12 -2
- data/lib/hanami/slice_registrar.rb +48 -23
- data/lib/hanami/version.rb +1 -1
- data/lib/hanami/web/rack_logger.rb +70 -2
- data/lib/hanami/web/welcome.html.erb +203 -0
- data/lib/hanami/web/welcome.rb +46 -0
- data/spec/integration/assets/assets_spec.rb +14 -3
- data/spec/integration/logging/request_logging_spec.rb +65 -7
- data/spec/integration/rack_app/method_override_spec.rb +97 -0
- data/spec/integration/slices_spec.rb +275 -5
- data/spec/integration/view/context/assets_spec.rb +0 -8
- data/spec/integration/view/context/inflector_spec.rb +0 -8
- data/spec/integration/view/context/settings_spec.rb +0 -8
- data/spec/integration/view/helpers/part_helpers_spec.rb +2 -2
- data/spec/integration/view/helpers/user_defined_helpers/part_helpers_spec.rb +10 -10
- data/spec/integration/view/parts/default_rendering_spec.rb +138 -0
- data/spec/integration/web/welcome_view_spec.rb +84 -0
- data/spec/support/app_integration.rb +22 -4
- data/spec/unit/hanami/helpers/assets_helper/{audio_spec.rb → audio_tag_spec.rb} +10 -14
- data/spec/unit/hanami/helpers/assets_helper/{favicon_spec.rb → favicon_tag_spec.rb} +7 -11
- data/spec/unit/hanami/helpers/assets_helper/{image_spec.rb → image_tag_spec.rb} +8 -12
- data/spec/unit/hanami/helpers/assets_helper/{javascript_spec.rb → javascript_tag_spec.rb} +14 -18
- data/spec/unit/hanami/helpers/assets_helper/{stylesheet_spec.rb → stylesheet_tag_spec.rb} +12 -16
- data/spec/unit/hanami/helpers/assets_helper/{video_spec.rb → video_tag_spec.rb} +11 -11
- data/spec/unit/hanami/version_spec.rb +1 -1
- metadata +23 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3ba29263713547809e78df051e0138f202d947ce6e9cf2bc9d06c5bf79438cb7
|
4
|
+
data.tar.gz: 0677ae36e85bbb92ed1bdde4ae48e92ac062ccf89f6d77b86b85578fd8975a06
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f24d67421d88e019d3fdf1d282fd55cff3a303e59fa7195fc8b152a6b85d1990aafe1cf3c583eeb2b561cfcae4b76e4120ef9863f12cee424c8372526cfb178a
|
7
|
+
data.tar.gz: 8e9ab011e12426e7d2e03ed784858309f7ca33fa8aa84d23ae84cd7d9435563998f38cb9d53322e2631c017334465cec10e0b2f65a58d8dcb0290c360dff5b1c
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,31 @@
|
|
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
|
+
|
5
30
|
## v2.1.0.beta2.1 - 2023-10-04
|
6
31
|
|
7
32
|
### Added
|
data/hanami.gemspec
CHANGED
@@ -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
|
-
|
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
|
-
|
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
|
131
|
+
def view_context_class
|
132
132
|
if Hanami.bundled?("hanami-view")
|
133
|
-
return Extensions::View::Context.context_class(slice)
|
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)
|
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 :
|
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,
|
73
|
+
def initialize(view: nil, view_context_class: nil, rack_monitor: nil, routes: nil, **kwargs)
|
74
74
|
@view = view
|
75
|
-
@
|
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:
|
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
|
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
|
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`,
|
181
|
+
# called via `.inherited`, because the class is anonymous at this point
|
182
182
|
klass.configure_for_slice(slice)
|
183
183
|
})
|
184
184
|
end
|
@@ -154,7 +154,7 @@ module Hanami
|
|
154
154
|
#
|
155
155
|
# # <script src="https://assets.bookshelf.org/assets/application-28a6b886de2372ee3922fcaf3f78f2d8.js"
|
156
156
|
# # type="text/javascript"></script>
|
157
|
-
def
|
157
|
+
def javascript_tag(*source_paths, **options)
|
158
158
|
options = options.reject { |k, _| k.to_sym == :src }
|
159
159
|
|
160
160
|
_safe_tags(*source_paths) do |source|
|
@@ -173,14 +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
|
179
|
-
|
180
|
-
# @api public
|
181
|
-
# @since 2.1.0
|
182
|
-
alias_method :javascript_tag, :javascript
|
183
|
-
|
184
176
|
# Generate `link` tag for given source(s)
|
185
177
|
#
|
186
178
|
# It accepts one or more strings representing the name of the asset, if it
|
@@ -255,7 +247,7 @@ module Hanami
|
|
255
247
|
#
|
256
248
|
# # <link href="https://assets.bookshelf.org/assets/application-28a6b886de2372ee3922fcaf3f78f2d8.css"
|
257
249
|
# # type="text/css" rel="stylesheet">
|
258
|
-
def
|
250
|
+
def stylesheet_tag(*source_paths, **options)
|
259
251
|
options = options.reject { |k, _| k.to_sym == :href }
|
260
252
|
|
261
253
|
_safe_tags(*source_paths) do |source_path|
|
@@ -275,14 +267,6 @@ module Hanami
|
|
275
267
|
end
|
276
268
|
end
|
277
269
|
|
278
|
-
# @api public
|
279
|
-
# @since 2.1.0
|
280
|
-
alias_method :css, :stylesheet
|
281
|
-
|
282
|
-
# @api public
|
283
|
-
# @since 2.1.0
|
284
|
-
alias_method :stylesheet_link_tag, :stylesheet
|
285
|
-
|
286
270
|
# Generate `img` tag for given source
|
287
271
|
#
|
288
272
|
# It accepts one string representing the name of the asset, if it comes
|
@@ -346,7 +330,7 @@ module Hanami
|
|
346
330
|
# <%= image "logo.png" %>
|
347
331
|
#
|
348
332
|
# # <img src="https://assets.bookshelf.org/assets/logo-28a6b886de2372ee3922fcaf3f78f2d8.png" alt="Logo">
|
349
|
-
def
|
333
|
+
def image_tag(source, options = {})
|
350
334
|
options = options.reject { |k, _| k.to_sym == :src }
|
351
335
|
attributes = {
|
352
336
|
src: asset_url(source),
|
@@ -357,10 +341,6 @@ module Hanami
|
|
357
341
|
tag.img(**attributes)
|
358
342
|
end
|
359
343
|
|
360
|
-
# @api public
|
361
|
-
# @since 2.1.0
|
362
|
-
alias_method :image_tag, :image
|
363
|
-
|
364
344
|
# Generate `link` tag application favicon.
|
365
345
|
#
|
366
346
|
# If no argument is given, it assumes `favico.ico` from the application.
|
@@ -416,7 +396,7 @@ module Hanami
|
|
416
396
|
#
|
417
397
|
# # <link href="https://assets.bookshelf.org/assets/favicon-28a6b886de2372ee3922fcaf3f78f2d8.ico"
|
418
398
|
# rel="shortcut icon" type="image/x-icon">
|
419
|
-
def
|
399
|
+
def favicon_tag(source = DEFAULT_FAVICON, options = {})
|
420
400
|
options = options.reject { |k, _| k.to_sym == :href }
|
421
401
|
|
422
402
|
attributes = {
|
@@ -429,10 +409,6 @@ module Hanami
|
|
429
409
|
tag.link(**attributes)
|
430
410
|
end
|
431
411
|
|
432
|
-
# @api public
|
433
|
-
# @since 2.1.0
|
434
|
-
alias_method :favicon_link_tag, :favicon
|
435
|
-
|
436
412
|
# Generate `video` tag for given source
|
437
413
|
#
|
438
414
|
# It accepts one string representing the name of the asset, if it comes
|
@@ -530,15 +506,11 @@ module Hanami
|
|
530
506
|
# <%= video "movie.mp4" %>
|
531
507
|
#
|
532
508
|
# # <video src="https://assets.bookshelf.org/assets/movie-28a6b886de2372ee3922fcaf3f78f2d8.mp4"></video>
|
533
|
-
def
|
509
|
+
def video_tag(source = nil, options = {}, &blk)
|
534
510
|
options = _source_options(source, options, &blk)
|
535
511
|
tag.video(**options, &blk)
|
536
512
|
end
|
537
513
|
|
538
|
-
# @api public
|
539
|
-
# @since 2.1.0
|
540
|
-
alias_method :video_tag, :video
|
541
|
-
|
542
514
|
# Generate `audio` tag for given source
|
543
515
|
#
|
544
516
|
# It accepts one string representing the name of the asset, if it comes
|
@@ -636,15 +608,11 @@ module Hanami
|
|
636
608
|
# <%= audio "song.ogg" %>
|
637
609
|
#
|
638
610
|
# # <audio src="https://assets.bookshelf.org/assets/song-28a6b886de2372ee3922fcaf3f78f2d8.ogg"></audio>
|
639
|
-
def
|
611
|
+
def audio_tag(source = nil, options = {}, &blk)
|
640
612
|
options = _source_options(source, options, &blk)
|
641
613
|
tag.audio(**options, &blk)
|
642
614
|
end
|
643
615
|
|
644
|
-
# @api public
|
645
|
-
# @since 2.1.0
|
646
|
-
alias_method :audio_tag, :audio
|
647
|
-
|
648
616
|
# It generates the relative or absolute URL for the given asset.
|
649
617
|
# It automatically decides if it has to use the relative or absolute
|
650
618
|
# depending on the configuration and current environment.
|
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
|
data/lib/hanami/slice.rb
CHANGED
@@ -952,6 +952,7 @@ module Hanami
|
|
952
952
|
config = self.config
|
953
953
|
rack_monitor = self["rack.monitor"]
|
954
954
|
|
955
|
+
show_welcome = Hanami.env?(:development) && routes.empty?
|
955
956
|
render_errors = render_errors?
|
956
957
|
render_detailed_errors = render_detailed_errors?
|
957
958
|
|
@@ -971,6 +972,8 @@ module Hanami
|
|
971
972
|
) do
|
972
973
|
use(rack_monitor)
|
973
974
|
|
975
|
+
use(Hanami::Web::Welcome) if show_welcome
|
976
|
+
|
974
977
|
use(
|
975
978
|
Hanami::Middleware::RenderErrors,
|
976
979
|
config,
|
@@ -982,8 +985,15 @@ module Hanami
|
|
982
985
|
use(Hanami::Webconsole::Middleware, config)
|
983
986
|
end
|
984
987
|
|
985
|
-
if Hanami.bundled?("hanami-controller")
|
986
|
-
|
988
|
+
if Hanami.bundled?("hanami-controller")
|
989
|
+
if config.actions.method_override
|
990
|
+
require "rack/method_override"
|
991
|
+
use(Rack::MethodOverride)
|
992
|
+
end
|
993
|
+
|
994
|
+
if config.actions.sessions.enabled?
|
995
|
+
use(*config.actions.sessions.middleware)
|
996
|
+
end
|
987
997
|
end
|
988
998
|
|
989
999
|
if Hanami.bundled?("hanami-assets") && config.assets.serve
|
@@ -46,19 +46,16 @@ module Hanami
|
|
46
46
|
end
|
47
47
|
|
48
48
|
def load_slices
|
49
|
-
slice_configs =
|
50
|
-
.map {
|
49
|
+
slice_configs = root.join(CONFIG_DIR, SLICES_DIR).glob("*#{RB_EXT}")
|
50
|
+
.map { _1.basename(RB_EXT) }
|
51
51
|
|
52
|
-
slice_dirs =
|
53
|
-
.select
|
54
|
-
.map {
|
52
|
+
slice_dirs = root.join(SLICES_DIR).glob("*")
|
53
|
+
.select(&:directory?)
|
54
|
+
.map { _1.basename }
|
55
55
|
|
56
|
-
|
56
|
+
(slice_dirs + slice_configs).uniq.sort
|
57
57
|
.then { filter_slice_names(_1) }
|
58
|
-
|
59
|
-
slice_names.each do |slice_name|
|
60
|
-
load_slice(slice_name)
|
61
|
-
end
|
58
|
+
.each(&method(:load_slice))
|
62
59
|
|
63
60
|
self
|
64
61
|
end
|
@@ -97,21 +94,20 @@ module Hanami
|
|
97
94
|
parent.eql?(parent.app) ? Object : parent.namespace
|
98
95
|
end
|
99
96
|
|
100
|
-
# Runs when a slice file has been found at `config/slices/[slice_name].rb`,
|
101
|
-
# at `slices/[slice_name]`.
|
102
|
-
#
|
97
|
+
# Runs when a slice file has been found inside the app at `config/slices/[slice_name].rb`,
|
98
|
+
# or when a slice directory exists at `slices/[slice_name]`.
|
99
|
+
#
|
100
|
+
# If a slice definition file is found by `find_slice_require_path`, then `load_slice` will
|
101
|
+
# require the file before registering the slice class.
|
102
|
+
#
|
103
|
+
# If a slice class is not found, registering the slice will generate the slice class.
|
103
104
|
def load_slice(slice_name)
|
104
|
-
slice_require_path =
|
105
|
-
|
106
|
-
require(slice_require_path)
|
107
|
-
rescue LoadError => e
|
108
|
-
raise e unless e.path == slice_require_path
|
109
|
-
end
|
105
|
+
slice_require_path = find_slice_require_path(slice_name)
|
106
|
+
require slice_require_path if slice_require_path
|
110
107
|
|
111
|
-
slice_module_name = inflector.camelize("#{parent_slice_namespace.name}#{PATH_DELIMITER}#{slice_name}")
|
112
108
|
slice_class =
|
113
109
|
begin
|
114
|
-
inflector.constantize("#{slice_module_name}#{MODULE_DELIMITER}Slice")
|
110
|
+
inflector.constantize("#{slice_module_name(slice_name)}#{MODULE_DELIMITER}Slice")
|
115
111
|
rescue NameError => e
|
116
112
|
raise e unless e.name.to_s == inflector.camelize(slice_name) || e.name.to_s == :Slice
|
117
113
|
end
|
@@ -119,11 +115,36 @@ module Hanami
|
|
119
115
|
register(slice_name, slice_class)
|
120
116
|
end
|
121
117
|
|
118
|
+
# Finds the path to the slice's definition file, if it exists, in the following order:
|
119
|
+
#
|
120
|
+
# 1. `config/slices/[slice_name].rb`
|
121
|
+
# 2. `slices/[parent_slice_name]/config/[slice_name].rb` (unless parent is the app)
|
122
|
+
# 3. `slices/[slice_name]/config/slice.rb`
|
123
|
+
#
|
124
|
+
# If the slice is nested under another slice then it will look in the following order:
|
125
|
+
#
|
126
|
+
# 1. `config/slices/[parent_slice_name]/[slice_name].rb`
|
127
|
+
# 2. `slices/[parent_slice_name]/config/[slice_name].rb`
|
128
|
+
# 3. `slices/[parent_slice_name]/[slice_name]/config/slice.rb`
|
129
|
+
def find_slice_require_path(slice_name)
|
130
|
+
app_slice_file_path = [slice_name]
|
131
|
+
app_slice_file_path.prepend(parent.slice_name) unless parent.eql?(parent.app)
|
132
|
+
ancestors = [
|
133
|
+
parent.app.root.join(CONFIG_DIR, SLICES_DIR, app_slice_file_path.join(File::SEPARATOR)),
|
134
|
+
parent.root.join(CONFIG_DIR, SLICES_DIR, slice_name),
|
135
|
+
root.join(SLICES_DIR, slice_name, CONFIG_DIR, "slice")
|
136
|
+
]
|
137
|
+
|
138
|
+
ancestors
|
139
|
+
.uniq
|
140
|
+
.find { _1.sub_ext(RB_EXT).file? }
|
141
|
+
&.to_s
|
142
|
+
end
|
143
|
+
|
122
144
|
def build_slice(slice_name, &block)
|
123
|
-
slice_module_name = inflector.camelize("#{parent_slice_namespace.name}#{PATH_DELIMITER}#{slice_name}")
|
124
145
|
slice_module =
|
125
146
|
begin
|
126
|
-
inflector.constantize(slice_module_name)
|
147
|
+
inflector.constantize(slice_module_name(slice_name))
|
127
148
|
rescue NameError
|
128
149
|
parent_slice_namespace.const_set(inflector.camelize(slice_name), Module.new)
|
129
150
|
end
|
@@ -131,6 +152,10 @@ module Hanami
|
|
131
152
|
slice_module.const_set(:Slice, Class.new(Hanami::Slice, &block))
|
132
153
|
end
|
133
154
|
|
155
|
+
def slice_module_name(slice_name)
|
156
|
+
inflector.camelize("#{parent_slice_namespace.name}#{PATH_DELIMITER}#{slice_name}")
|
157
|
+
end
|
158
|
+
|
134
159
|
def configure_slice(slice_name, slice)
|
135
160
|
slice.instance_variable_set(:@parent, parent)
|
136
161
|
|