piko-lite-box 0.0.1

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 (99) hide show
  1. checksums.yaml +7 -0
  2. data/hanami-3.0.1/CHANGELOG.md +1701 -0
  3. data/hanami-3.0.1/LICENSE +20 -0
  4. data/hanami-3.0.1/README.md +89 -0
  5. data/hanami-3.0.1/hanami.gemspec +49 -0
  6. data/hanami-3.0.1/lib/hanami/app.rb +197 -0
  7. data/hanami-3.0.1/lib/hanami/boot.rb +5 -0
  8. data/hanami-3.0.1/lib/hanami/config/actions/content_security_policy.rb +140 -0
  9. data/hanami-3.0.1/lib/hanami/config/actions/cookies.rb +57 -0
  10. data/hanami-3.0.1/lib/hanami/config/actions/sessions.rb +83 -0
  11. data/hanami-3.0.1/lib/hanami/config/actions.rb +189 -0
  12. data/hanami-3.0.1/lib/hanami/config/assets.rb +84 -0
  13. data/hanami-3.0.1/lib/hanami/config/console.rb +79 -0
  14. data/hanami-3.0.1/lib/hanami/config/db.rb +35 -0
  15. data/hanami-3.0.1/lib/hanami/config/i18n.rb +138 -0
  16. data/hanami-3.0.1/lib/hanami/config/logger.rb +207 -0
  17. data/hanami-3.0.1/lib/hanami/config/null_config.rb +17 -0
  18. data/hanami-3.0.1/lib/hanami/config/router.rb +48 -0
  19. data/hanami-3.0.1/lib/hanami/config/views.rb +103 -0
  20. data/hanami-3.0.1/lib/hanami/config.rb +564 -0
  21. data/hanami-3.0.1/lib/hanami/constants.rb +62 -0
  22. data/hanami-3.0.1/lib/hanami/env.rb +52 -0
  23. data/hanami-3.0.1/lib/hanami/errors.rb +41 -0
  24. data/hanami-3.0.1/lib/hanami/extensions/action/i18n_helper.rb +64 -0
  25. data/hanami-3.0.1/lib/hanami/extensions/action/name_inferrer.rb +34 -0
  26. data/hanami-3.0.1/lib/hanami/extensions/action/slice_configured_action.rb +190 -0
  27. data/hanami-3.0.1/lib/hanami/extensions/action.rb +147 -0
  28. data/hanami-3.0.1/lib/hanami/extensions/db/repo.rb +108 -0
  29. data/hanami-3.0.1/lib/hanami/extensions/mailer/slice_configured_mailer.rb +134 -0
  30. data/hanami-3.0.1/lib/hanami/extensions/mailer.rb +28 -0
  31. data/hanami-3.0.1/lib/hanami/extensions/operation/slice_configured_db_operation.rb +90 -0
  32. data/hanami-3.0.1/lib/hanami/extensions/operation.rb +33 -0
  33. data/hanami-3.0.1/lib/hanami/extensions/router/errors.rb +58 -0
  34. data/hanami-3.0.1/lib/hanami/extensions/view/context.rb +298 -0
  35. data/hanami-3.0.1/lib/hanami/extensions/view/part.rb +92 -0
  36. data/hanami-3.0.1/lib/hanami/extensions/view/scope.rb +36 -0
  37. data/hanami-3.0.1/lib/hanami/extensions/view/slice_configured_context.rb +73 -0
  38. data/hanami-3.0.1/lib/hanami/extensions/view/slice_configured_helpers.rb +55 -0
  39. data/hanami-3.0.1/lib/hanami/extensions/view/slice_configured_part.rb +73 -0
  40. data/hanami-3.0.1/lib/hanami/extensions/view/slice_configured_view.rb +243 -0
  41. data/hanami-3.0.1/lib/hanami/extensions/view/standard_helpers.rb +26 -0
  42. data/hanami-3.0.1/lib/hanami/extensions/view.rb +38 -0
  43. data/hanami-3.0.1/lib/hanami/extensions.rb +30 -0
  44. data/hanami-3.0.1/lib/hanami/helpers/assets_helper.rb +804 -0
  45. data/hanami-3.0.1/lib/hanami/helpers/form_helper/form_builder.rb +1389 -0
  46. data/hanami-3.0.1/lib/hanami/helpers/form_helper/values.rb +75 -0
  47. data/hanami-3.0.1/lib/hanami/helpers/form_helper.rb +213 -0
  48. data/hanami-3.0.1/lib/hanami/helpers/i18n_helper.rb +241 -0
  49. data/hanami-3.0.1/lib/hanami/logger/rack_formatter.rb +73 -0
  50. data/hanami-3.0.1/lib/hanami/logger/sql_formatter.rb +80 -0
  51. data/hanami-3.0.1/lib/hanami/logger/sql_logger.rb +48 -0
  52. data/hanami-3.0.1/lib/hanami/middleware/assets.rb +21 -0
  53. data/hanami-3.0.1/lib/hanami/middleware/content_security_policy_nonce.rb +53 -0
  54. data/hanami-3.0.1/lib/hanami/middleware/public_errors_app.rb +75 -0
  55. data/hanami-3.0.1/lib/hanami/middleware/render_errors.rb +90 -0
  56. data/hanami-3.0.1/lib/hanami/port.rb +45 -0
  57. data/hanami-3.0.1/lib/hanami/prepare.rb +5 -0
  58. data/hanami-3.0.1/lib/hanami/provider/source.rb +16 -0
  59. data/hanami-3.0.1/lib/hanami/provider_registrar.rb +28 -0
  60. data/hanami-3.0.1/lib/hanami/providers/assets.rb +28 -0
  61. data/hanami-3.0.1/lib/hanami/providers/db/adapter.rb +75 -0
  62. data/hanami-3.0.1/lib/hanami/providers/db/adapters.rb +66 -0
  63. data/hanami-3.0.1/lib/hanami/providers/db/config.rb +44 -0
  64. data/hanami-3.0.1/lib/hanami/providers/db/gateway.rb +87 -0
  65. data/hanami-3.0.1/lib/hanami/providers/db/sql_adapter.rb +100 -0
  66. data/hanami-3.0.1/lib/hanami/providers/db.rb +325 -0
  67. data/hanami-3.0.1/lib/hanami/providers/db_logging.rb +19 -0
  68. data/hanami-3.0.1/lib/hanami/providers/i18n/backend.rb +373 -0
  69. data/hanami-3.0.1/lib/hanami/providers/i18n/locale/en.yml +57 -0
  70. data/hanami-3.0.1/lib/hanami/providers/i18n.rb +114 -0
  71. data/hanami-3.0.1/lib/hanami/providers/inflector.rb +17 -0
  72. data/hanami-3.0.1/lib/hanami/providers/logger.rb +41 -0
  73. data/hanami-3.0.1/lib/hanami/providers/mailers.rb +101 -0
  74. data/hanami-3.0.1/lib/hanami/providers/rack.rb +47 -0
  75. data/hanami-3.0.1/lib/hanami/providers/relations.rb +31 -0
  76. data/hanami-3.0.1/lib/hanami/providers/routes.rb +29 -0
  77. data/hanami-3.0.1/lib/hanami/rake_tasks.rb +54 -0
  78. data/hanami-3.0.1/lib/hanami/routes.rb +146 -0
  79. data/hanami-3.0.1/lib/hanami/settings/composite_store.rb +53 -0
  80. data/hanami-3.0.1/lib/hanami/settings/env_store.rb +32 -0
  81. data/hanami-3.0.1/lib/hanami/settings.rb +234 -0
  82. data/hanami-3.0.1/lib/hanami/setup.rb +6 -0
  83. data/hanami-3.0.1/lib/hanami/slice/router.rb +280 -0
  84. data/hanami-3.0.1/lib/hanami/slice/routes_helper.rb +37 -0
  85. data/hanami-3.0.1/lib/hanami/slice/routing/middleware/stack.rb +216 -0
  86. data/hanami-3.0.1/lib/hanami/slice/routing/resolver.rb +88 -0
  87. data/hanami-3.0.1/lib/hanami/slice/view_name_inferrer.rb +63 -0
  88. data/hanami-3.0.1/lib/hanami/slice.rb +1239 -0
  89. data/hanami-3.0.1/lib/hanami/slice_configurable.rb +72 -0
  90. data/hanami-3.0.1/lib/hanami/slice_name.rb +111 -0
  91. data/hanami-3.0.1/lib/hanami/slice_registrar.rb +215 -0
  92. data/hanami-3.0.1/lib/hanami/universal_logger.rb +250 -0
  93. data/hanami-3.0.1/lib/hanami/version.rb +45 -0
  94. data/hanami-3.0.1/lib/hanami/web/rack_logger.rb +126 -0
  95. data/hanami-3.0.1/lib/hanami/web/welcome.html.erb +584 -0
  96. data/hanami-3.0.1/lib/hanami/web/welcome.rb +46 -0
  97. data/hanami-3.0.1/lib/hanami.rb +276 -0
  98. data/piko-lite-box.gemspec +11 -0
  99. metadata +137 -0
@@ -0,0 +1,280 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "hanami/router"
4
+ require_relative "routing/resolver"
5
+ require_relative "routing/middleware/stack"
6
+
7
+ module Hanami
8
+ class Slice
9
+ # `Hanami::Router` subclass with enhancements for use within Hanami apps.
10
+ #
11
+ # This is loaded from Hanami apps and slices and made available as their
12
+ # {Hanami::Slice::ClassMethods#router router}.
13
+ #
14
+ # @api private
15
+ class Router < ::Hanami::Router
16
+ # @api private
17
+ attr_reader :inflector
18
+
19
+ # @api private
20
+ attr_reader :middleware_stack
21
+
22
+ # @api private
23
+ attr_reader :path_prefix
24
+
25
+ # @api private
26
+ def initialize(
27
+ routes:,
28
+ inflector:,
29
+ middleware_stack: Routing::Middleware::Stack.new,
30
+ prefix: ::Hanami::Router::DEFAULT_PREFIX,
31
+ **kwargs,
32
+ &blk
33
+ )
34
+ @path_prefix = Hanami::Router::Prefix.new(prefix)
35
+ @inflector = inflector
36
+ @middleware_stack = middleware_stack
37
+ @resource_scope = []
38
+ instance_eval(&blk)
39
+ super(**kwargs, &routes)
40
+ end
41
+
42
+ # @api private
43
+ def freeze
44
+ return self if frozen?
45
+
46
+ remove_instance_variable(:@middleware_stack)
47
+ super
48
+ end
49
+
50
+ # @api private
51
+ def to_rack_app
52
+ middleware_stack.to_rack_app(self)
53
+ end
54
+
55
+ # @api private
56
+ def use(*args, **kwargs, &blk)
57
+ middleware_stack.use(*args, **kwargs.merge(path_prefix: path_prefix.to_s), &blk)
58
+ end
59
+
60
+ # Yields a block for routes to resolve their action components from the given slice.
61
+ #
62
+ # An optional URL prefix may be supplied with `at:`.
63
+ #
64
+ # @example
65
+ # # config/routes.rb
66
+ #
67
+ # module MyApp
68
+ # class Routes < Hanami::Routes
69
+ # slice :admin, at: "/admin" do
70
+ # # Will route to the "actions.posts.index" component in Admin::Slice
71
+ # get "posts", to: "posts.index"
72
+ # end
73
+ # end
74
+ # end
75
+ #
76
+ # @param slice_name [Symbol] the slice's name
77
+ # @param at [String, nil] optional URL prefix for the routes
78
+ #
79
+ # @api public
80
+ # @since 2.0.0
81
+ def slice(slice_name, at:, as: nil, &blk)
82
+ blk ||= @resolver.find_slice(slice_name).routes
83
+
84
+ prev_resolver = @resolver
85
+ @resolver = @resolver.to_slice(slice_name)
86
+
87
+ scope(at, as:, &blk)
88
+ ensure
89
+ @resolver = prev_resolver
90
+ end
91
+
92
+ # Generates RESTful routes for a plural resource.
93
+ #
94
+ # @param name [Symbol] the resource name (plural)
95
+ # @param options [Hash] options for customizing the routes
96
+ # @option options [Array<Symbol>] :only Limit to specific actions
97
+ # @option options [Array<Symbol>] :except Exclude specific actions
98
+ # @option options [String] :to the action key namespace, e.g. "namespace.action"
99
+ # @option options [String] :path the URL path
100
+ # @option options [String, Symbol] :as the route name prefix
101
+ #
102
+ # @example
103
+ # resources :users
104
+ # # Generates:
105
+ # # GET /users users.index
106
+ # # GET /users/new users.new
107
+ # # POST /users users.create
108
+ # # GET /users/:id users.show
109
+ # # GET /users/:id/edit users.edit
110
+ # # PATCH /users/:id users.update
111
+ # # DELETE /users/:id users.destroy
112
+ #
113
+ # @api public
114
+ # @since 2.3.0
115
+ def resources(name, **options, &block)
116
+ build_resource(name, :plural, options, &block)
117
+ end
118
+
119
+ # Generates RESTful routes for a singular resource.
120
+ #
121
+ # @param name [Symbol] the resource name (singular)
122
+ # @param options [Hash] options for customizing the routes
123
+ # @option options [Array<Symbol>] :only limit to specific actions
124
+ # @option options [Array<Symbol>] :except exclude specific actions
125
+ # @option options [String] :to the action key namespace, e.g. "namespace.action"
126
+ # @option options [String] :path the URL path
127
+ # @option options [String, Symbol] :as the route name prefix
128
+ #
129
+ # @example
130
+ # resource :profile
131
+ # # Generates (singular, no index):
132
+ # # GET /profile/new profile.new
133
+ # # POST /profile profile.create
134
+ # # GET /profile profile.show
135
+ # # GET /profile/edit profile.edit
136
+ # # PATCH /profile profile.update
137
+ # # DELETE /profile profile.destroy
138
+ #
139
+ # @api public
140
+ # @since 2.3.0
141
+ def resource(name, **options, &block)
142
+ build_resource(name, :singular, options, &block)
143
+ end
144
+
145
+ private
146
+
147
+ def build_resource(name, type, options, &block)
148
+ resource_builder = ResourceBuilder.new(
149
+ name: name,
150
+ type: type,
151
+ resource_scope: @resource_scope,
152
+ options: options,
153
+ inflector: inflector
154
+ )
155
+
156
+ resource_builder.add_routes(self)
157
+ resource_builder.scope(self, &block) if block
158
+ end
159
+
160
+ # Builds RESTful routes for a resource
161
+ #
162
+ # @api private
163
+ class ResourceBuilder
164
+ ROUTE_OPTIONS = {
165
+ index: {method: :get},
166
+ new: {method: :get, path_suffix: "/new", name_prefix: "new"},
167
+ create: {method: :post},
168
+ show: {method: :get, path_suffix: "/:id"},
169
+ edit: {method: :get, path_suffix: "/:id/edit", name_prefix: "edit"},
170
+ update: {method: :patch, path_suffix: "/:id"},
171
+ destroy: {method: :delete, path_suffix: "/:id"}
172
+ }.freeze
173
+
174
+ PLURAL_ACTIONS = %i[index new create show edit update destroy].freeze
175
+ SINGULAR_ACTIONS = (PLURAL_ACTIONS - %i[index]).freeze
176
+
177
+ def initialize(name:, type:, resource_scope:, options:, inflector:)
178
+ @name = name
179
+ @type = type
180
+ @resource_scope = resource_scope
181
+ @options = options
182
+ @inflector = inflector
183
+
184
+ @path = options[:path] || name.to_s
185
+ end
186
+
187
+ def scope(router, &block)
188
+ @resource_scope.push(@name)
189
+ router.scope(scope_path, as: scope_name, &block)
190
+ ensure
191
+ @resource_scope.pop
192
+ end
193
+
194
+ def add_routes(router)
195
+ actions.each do |action|
196
+ add_route(router, action, ROUTE_OPTIONS.fetch(action))
197
+ end
198
+ end
199
+
200
+ private
201
+
202
+ def plural?
203
+ @type == :plural
204
+ end
205
+
206
+ def singular?
207
+ !plural?
208
+ end
209
+
210
+ def scope_path
211
+ if plural?
212
+ "#{@path}/:#{@inflector.singularize(@path.to_s)}_id"
213
+ else
214
+ @path
215
+ end
216
+ end
217
+
218
+ def scope_name
219
+ @inflector.singularize(@name)
220
+ end
221
+
222
+ def actions
223
+ default_actions = plural? ? PLURAL_ACTIONS : SINGULAR_ACTIONS
224
+ if @options[:only]
225
+ Array(@options[:only]) & default_actions
226
+ elsif @options[:except]
227
+ default_actions - Array(@options[:except])
228
+ else
229
+ default_actions
230
+ end
231
+ end
232
+
233
+ def add_route(router, action, route_options)
234
+ path = "/#{@path}#{route_suffix(route_options[:path_suffix])}"
235
+ to = "#{key_path_base}#{CONTAINER_KEY_DELIMITER}#{action}"
236
+ as = route_name(action, route_options[:name_prefix])
237
+
238
+ router.public_send(route_options[:method], path, to:, as:)
239
+ end
240
+
241
+ def route_suffix(suffix)
242
+ return suffix.sub(LEADING_ID_REGEX, "") if suffix && singular?
243
+
244
+ suffix
245
+ end
246
+ LEADING_ID_REGEX = %r{\A/:id}
247
+
248
+ def key_path_base
249
+ @key_path_base ||= @options[:to] || begin
250
+ if @resource_scope.any?
251
+ prefix = @resource_scope.join(CONTAINER_KEY_DELIMITER)
252
+ "#{prefix}#{CONTAINER_KEY_DELIMITER}#{@name}"
253
+ else
254
+ @name.to_s
255
+ end
256
+ end
257
+ end
258
+
259
+ def route_name(action, prefix)
260
+ name = route_name_base
261
+ name = @inflector.pluralize(name) if plural? && PLURALIZED_NAME_ACTIONS.include?(action)
262
+
263
+ [prefix, name]
264
+ end
265
+ PLURALIZED_NAME_ACTIONS = %i[index create].freeze
266
+
267
+ def route_name_base
268
+ @route_name_base ||=
269
+ if @options[:as]
270
+ @options[:as].to_s
271
+ elsif plural?
272
+ @inflector.singularize(@name.to_s)
273
+ else
274
+ @name.to_s
275
+ end
276
+ end
277
+ end
278
+ end
279
+ end
280
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hanami
4
+ class Slice
5
+ # Hanami app routes helpers
6
+ #
7
+ # An instance of this class will be registered with slice (at the "routes" key). You
8
+ # can use it to access the route helpers for your app.
9
+ #
10
+ # @example
11
+ # MyApp::App["routes"].path(:root) # => "/"
12
+ #
13
+ # @see Hanami::Router::UrlHelpers
14
+ # @since 2.0.0
15
+ class RoutesHelper
16
+ # @since 2.0.0
17
+ # @api private
18
+ def initialize(router)
19
+ @router = router
20
+ end
21
+
22
+ # @see Hanami::Router::UrlHelpers#path
23
+ def path(...)
24
+ router.path(...)
25
+ end
26
+
27
+ # @see Hanami::Router::UrlHelpers#url
28
+ def url(...)
29
+ router.url(...)
30
+ end
31
+
32
+ private
33
+
34
+ attr_reader :router
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,216 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "hanami/router"
4
+ require "hanami/middleware"
5
+ require "hanami/middleware/app"
6
+ require "hanami/errors"
7
+
8
+ module Hanami
9
+ class Slice
10
+ module Routing
11
+ # @since 2.0.0
12
+ # @api private
13
+ module Middleware
14
+ # Wraps a rack app with a middleware stack
15
+ #
16
+ # We use this class to add middlewares to the rack application generated from
17
+ # {Hanami::Slice::Router}.
18
+ #
19
+ # ```
20
+ # stack = Hanami::Slice::Routing::Middleware::Stack.new
21
+ # stack.use(Rack::ContentType, "text/html")
22
+ # stack.to_rack_app(a_rack_app)
23
+ # ```
24
+ #
25
+ # Middlewares can be mounted on specific paths:
26
+ #
27
+ # ```
28
+ # stack.with("/api") do
29
+ # stack.use(Rack::ContentType, "application/json")
30
+ # end
31
+ # ```
32
+ #
33
+ # @see Hanami::Config#middleware
34
+ #
35
+ # @since 2.0.0
36
+ # @api private
37
+ class Stack
38
+ include Enumerable
39
+
40
+ # @since 2.0.0
41
+ # @api private
42
+ attr_reader :stack
43
+
44
+ # Returns an array of Ruby namespaces from which to load middleware classes specified by
45
+ # symbol names given to {#use}.
46
+ #
47
+ # Defaults to `[Hanami::Middleware]`.
48
+ #
49
+ # @return [Array<Object>]
50
+ #
51
+ # @api public
52
+ # @since 2.0.0
53
+ attr_reader :namespaces
54
+
55
+ # @since 2.0.0
56
+ # @api private
57
+ def initialize
58
+ @stack = Hash.new { |hash, key| hash[key] = [] }
59
+ @namespaces = [Hanami::Middleware]
60
+ end
61
+
62
+ # @since 2.0.0
63
+ # @api private
64
+ def initialize_copy(source)
65
+ super
66
+ @stack = stack.dup
67
+ @namespaces = namespaces.dup
68
+ end
69
+
70
+ # Adds a middleware to the stack.
71
+ #
72
+ # @example
73
+ # # Using a symbol name; adds Hanami::Middleware::BodyParser.new([:json])
74
+ # middleware.use :body_parser, :json
75
+ #
76
+ # # Using a class name
77
+ # middleware.use MyMiddleware
78
+ #
79
+ # # Adding a middleware before or after others
80
+ # middleware.use MyMiddleware, before: SomeMiddleware
81
+ # middleware.use MyMiddleware, after: OtherMiddleware
82
+ #
83
+ # @param spec [Symbol, Class] the middleware name or class name
84
+ # @param args [Array, nil] Arguments to pass to the middleware's `.new` method
85
+ # @param before [Class, nil] an optional (already added) middleware class to add the
86
+ # middleware before
87
+ # @param after [Class, nil] an optional (already added) middleware class to add the
88
+ # middleware after
89
+ #
90
+ # @return [self]
91
+ #
92
+ # @api public
93
+ # @since 2.0.0
94
+ def use(spec, *args, path_prefix: ::Hanami::Router::DEFAULT_PREFIX, before: nil, after: nil, **kwargs, &blk)
95
+ middleware = resolve_middleware_class(spec)
96
+ item = [middleware, args, kwargs, blk]
97
+
98
+ if before
99
+ @stack[path_prefix].insert((idx = index_of(before, path_prefix)).zero? ? 0 : idx - 1, item)
100
+ elsif after
101
+ @stack[path_prefix].insert(index_of(after, path_prefix) + 1, item)
102
+ else
103
+ @stack[path_prefix].push(item)
104
+ end
105
+
106
+ self
107
+ end
108
+
109
+ # @since 2.0.0
110
+ # @api private
111
+ def update(other)
112
+ other.stack.each do |path_prefix, items|
113
+ stack[path_prefix].concat(items)
114
+ end
115
+ self
116
+ end
117
+
118
+ # @since 2.0.0
119
+ # @api private
120
+ def to_rack_app(app)
121
+ unless Hanami.bundled?("rack")
122
+ raise "Add \"rack\" to your `Gemfile` to run Hanami as a rack app"
123
+ end
124
+
125
+ mapping = to_hash
126
+ return app if mapping.empty?
127
+
128
+ Hanami::Middleware::App.new(app, mapping)
129
+ end
130
+
131
+ # @since 2.0.0
132
+ # @api private
133
+ def to_hash
134
+ @stack.each_with_object({}) do |(path, _), result|
135
+ result[path] = stack_for(path)
136
+ end
137
+ end
138
+
139
+ # @since 2.0.0
140
+ # @api private
141
+ def empty?
142
+ @stack.empty?
143
+ end
144
+
145
+ # @since 2.0.0
146
+ # @api private
147
+ def each(&blk)
148
+ @stack.each(&blk)
149
+ end
150
+
151
+ # @since 2.0.0
152
+ # @api private
153
+ def mapped(builder, prefix, &blk)
154
+ if prefix == ::Hanami::Router::DEFAULT_PREFIX
155
+ builder.instance_eval(&blk)
156
+ else
157
+ builder.map(prefix, &blk)
158
+ end
159
+ end
160
+
161
+ private
162
+
163
+ # @since 2.0.0
164
+ def index_of(middleware, path_prefix)
165
+ @stack[path_prefix].index { |(m, *)| m.equal?(middleware) }
166
+ end
167
+
168
+ # @since 2.0.0
169
+ # @api private
170
+ def stack_for(current_path)
171
+ @stack.each_with_object([]) do |(path, stack), result|
172
+ next unless current_path.start_with?(path)
173
+
174
+ result.push(stack)
175
+ end.flatten(1)
176
+ end
177
+
178
+ # @since 2.0.0
179
+ def resolve_middleware_class(spec)
180
+ case spec
181
+ when Symbol then load_middleware_class(spec)
182
+ when Class, Module then spec
183
+ else
184
+ if spec.respond_to?(:call)
185
+ spec
186
+ else
187
+ raise UnsupportedMiddlewareSpecError, spec
188
+ end
189
+ end
190
+ end
191
+
192
+ # @since 2.0.0
193
+ def load_middleware_class(spec)
194
+ begin
195
+ require "hanami/middleware/#{spec}"
196
+ rescue LoadError # rubocop:disable Lint/SuppressedException
197
+ end
198
+
199
+ # FIXME: Classify must use App inflector
200
+ class_name = Hanami::Utils::String.classify(spec.to_s)
201
+ namespace = namespaces.detect { |ns| ns.const_defined?(class_name) }
202
+
203
+ if namespace
204
+ namespace.const_get(class_name)
205
+ else
206
+ raise(
207
+ UnsupportedMiddlewareSpecError,
208
+ "Failed to find corresponding middleware class for `#{spec}` in #{namespaces.join(', ')}"
209
+ )
210
+ end
211
+ end
212
+ end
213
+ end
214
+ end
215
+ end
216
+ end
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hanami
4
+ class Slice
5
+ # @api private
6
+ module Routing
7
+ # Hanami app router endpoint resolver
8
+ #
9
+ # This resolves endpoints objects from a slice container using the strings passed to `to:` as
10
+ # their container keys.
11
+ #
12
+ # @api private
13
+ # @since 2.0.0
14
+ class Resolver
15
+ SLICE_ACTIONS_KEY_NAMESPACE = "actions"
16
+
17
+ # @api private
18
+ # @since 2.0.0
19
+ def initialize(slice:)
20
+ @slice = slice
21
+ end
22
+
23
+ # @api private
24
+ # @since 2.0.0
25
+ def find_slice(slice_name)
26
+ slice.slices[slice_name]
27
+ end
28
+
29
+ # @api private
30
+ # @since 2.0.0
31
+ def to_slice(slice_name)
32
+ self.class.new(slice: find_slice(slice_name))
33
+ end
34
+
35
+ # @api private
36
+ # @since 2.0.0
37
+ def call(_path, endpoint)
38
+ endpoint =
39
+ case endpoint
40
+ when String
41
+ resolve_slice_action(endpoint)
42
+ when Class
43
+ endpoint.respond_to?(:call) ? endpoint : endpoint.new
44
+ else
45
+ endpoint
46
+ end
47
+
48
+ unless endpoint.respond_to?(:call)
49
+ raise Routes::NotCallableEndpointError.new(endpoint)
50
+ end
51
+
52
+ endpoint
53
+ end
54
+
55
+ private
56
+
57
+ # @api private
58
+ # @since 2.0.0
59
+ attr_reader :slice
60
+
61
+ # @api private
62
+ # @since 2.0.0
63
+ def resolve_slice_action(key)
64
+ action_key = "#{SLICE_ACTIONS_KEY_NAMESPACE}.#{key}"
65
+
66
+ ensure_action_in_slice(action_key)
67
+
68
+ # Lazily resolve action from the slice to reduce router initialization time, and
69
+ # circumvent endless loops from the action requiring access to router-related
70
+ # concerns (which may not be fully loaded at the time of reading the routes)
71
+ -> (*args) {
72
+ action = slice.resolve(action_key) do
73
+ raise Routes::MissingActionError.new(action_key, slice)
74
+ end
75
+
76
+ action.call(*args)
77
+ }
78
+ end
79
+
80
+ def ensure_action_in_slice(key)
81
+ return unless slice.booted?
82
+
83
+ raise Routes::MissingActionError.new(key, slice) unless slice.key?(key)
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../constants"
4
+
5
+ module Hanami
6
+ class Slice
7
+ # Infers a view name for automatically rendering within actions.
8
+ #
9
+ # @api private
10
+ # @since 2.0.0
11
+ class ViewNameInferrer
12
+ ALTERNATIVE_NAMES = {
13
+ "create" => "new",
14
+ "update" => "edit"
15
+ }.freeze
16
+
17
+ class << self
18
+ # Returns an array of container keys for views matching the given action.
19
+ #
20
+ # Also provides alternative view keys for common RESTful actions.
21
+ #
22
+ # @example
23
+ # ViewNameInferrer.call(action_name: "Main::Actions::Posts::Create", slice: Main::Slice)
24
+ # # => ["views.posts.create", "views.posts.new"]
25
+ #
26
+ # @param action_class_name [String] action class name
27
+ # @param slice [Hanami::Slice, Hanami::Application] Hanami slice containing the action
28
+ #
29
+ # @return [Array<string>] array of paired view container keys
30
+ def call(action_class_name:, slice:)
31
+ action_key_base = slice.config.actions.name_inference_base
32
+ view_key_base = slice.config.actions.view_name_inference_base
33
+
34
+ action_name_key = action_name_key(action_class_name, slice, action_key_base)
35
+
36
+ view_key = [view_key_base, action_name_key].compact.join(CONTAINER_KEY_DELIMITER)
37
+
38
+ [view_key, alternative_view_key(view_key)].compact
39
+ end
40
+
41
+ private
42
+
43
+ def action_name_key(action_name, slice, key_base)
44
+ slice
45
+ .inflector
46
+ .underscore(action_name)
47
+ .sub(%r{^#{slice.slice_name.path}#{PATH_DELIMITER}}, "")
48
+ .sub(%r{^#{key_base}#{PATH_DELIMITER}}, "")
49
+ .gsub("/", CONTAINER_KEY_DELIMITER)
50
+ end
51
+
52
+ def alternative_view_key(view_key)
53
+ parts = view_key.split(CONTAINER_KEY_DELIMITER)
54
+
55
+ alternative_name = ALTERNATIVE_NAMES[parts.last]
56
+ return unless alternative_name
57
+
58
+ [parts[0..-2], alternative_name].join(CONTAINER_KEY_DELIMITER)
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end