hanami 2.0.0.beta3 → 2.0.0.beta4
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 +20 -0
- data/hanami.gemspec +5 -5
- data/lib/hanami/app.rb +4 -4
- data/lib/hanami/assets/{app_configuration.rb → app_config.rb} +10 -10
- data/lib/hanami/assets/{configuration.rb → config.rb} +4 -4
- data/lib/hanami/{configuration → config}/actions/content_security_policy.rb +2 -2
- data/lib/hanami/{configuration → config}/actions/cookies.rb +6 -5
- data/lib/hanami/{configuration → config}/actions/sessions.rb +2 -2
- data/lib/hanami/{configuration → config}/actions.rb +11 -12
- data/lib/hanami/{configuration → config}/logger.rb +2 -2
- data/lib/hanami/config/null_config.rb +14 -0
- data/lib/hanami/{configuration → config}/router.rb +8 -8
- data/lib/hanami/{configuration → config}/sessions.rb +2 -2
- data/lib/hanami/{configuration → config}/views.rb +10 -10
- data/lib/hanami/{configuration.rb → config.rb} +19 -19
- data/lib/hanami/errors.rb +3 -0
- data/lib/hanami/extensions/action/slice_configured_action.rb +1 -1
- data/lib/hanami/providers/inflector.rb +0 -2
- data/lib/hanami/providers/logger.rb +1 -3
- data/lib/hanami/providers/rack.rb +0 -2
- data/lib/hanami/providers/routes.rb +0 -2
- data/lib/hanami/settings/env_store.rb +3 -3
- data/lib/hanami/settings.rb +87 -5
- data/lib/hanami/slice/routing/middleware/stack.rb +45 -1
- data/lib/hanami/slice.rb +52 -37
- data/lib/hanami/slice_configurable.rb +1 -1
- data/lib/hanami/slice_registrar.rb +6 -0
- data/lib/hanami/version.rb +1 -1
- data/lib/hanami.rb +0 -1
- data/spec/integration/container/prepare_container_spec.rb +2 -0
- data/spec/integration/rack_app/body_parser_spec.rb +108 -0
- data/spec/integration/rack_app/middleware_spec.rb +28 -0
- data/spec/integration/settings/access_in_slice_class_body_spec.rb +82 -0
- data/spec/integration/settings/access_to_constants_spec.rb +23 -146
- data/spec/integration/{slices/slice_settings_spec.rb → settings/slice_registration_spec.rb} +5 -1
- data/spec/integration/settings/using_types_spec.rb +4 -11
- data/spec/support/app_integration.rb +18 -15
- data/spec/unit/hanami/{configuration → config}/actions/content_security_policy_spec.rb +10 -10
- data/spec/unit/hanami/{configuration → config}/actions/cookies_spec.rb +6 -6
- data/spec/unit/hanami/{configuration → config}/actions/csrf_protection_spec.rb +12 -12
- data/spec/unit/hanami/config/actions/default_values_spec.rb +54 -0
- data/spec/unit/hanami/{configuration → config}/actions/sessions_spec.rb +5 -5
- data/spec/unit/hanami/{configuration → config}/actions_spec.rb +9 -10
- data/spec/unit/hanami/{configuration → config}/base_url_spec.rb +2 -2
- data/spec/unit/hanami/{configuration → config}/inflector_spec.rb +2 -2
- data/spec/unit/hanami/{configuration → config}/logger_spec.rb +4 -4
- data/spec/unit/hanami/{configuration → config}/router_spec.rb +7 -8
- data/spec/unit/hanami/{configuration → config}/slices_spec.rb +2 -2
- data/spec/unit/hanami/{configuration → config}/views_spec.rb +11 -12
- data/spec/unit/hanami/{configuration_spec.rb → config_spec.rb} +3 -3
- data/spec/unit/hanami/settings_spec.rb +65 -10
- data/spec/unit/hanami/slice_configurable_spec.rb +21 -2
- data/spec/unit/hanami/version_spec.rb +1 -1
- metadata +73 -58
- data/lib/hanami/configuration/null_configuration.rb +0 -14
- data/lib/hanami/providers/settings.rb +0 -98
- data/spec/unit/hanami/configuration/actions/default_values_spec.rb +0 -52
data/lib/hanami/settings.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "dry/core"
|
3
4
|
require "dry/configurable"
|
4
|
-
require "dry/core/constants"
|
5
5
|
|
6
6
|
module Hanami
|
7
7
|
# App settings
|
@@ -33,11 +33,72 @@ module Hanami
|
|
33
33
|
# needs to do is implementing a `#fetch` method with the same signature as `Hash#fetch`.
|
34
34
|
#
|
35
35
|
# @see Hanami::Settings::DotenvStore
|
36
|
+
#
|
37
|
+
# @api public
|
36
38
|
# @since 2.0.0
|
37
39
|
class Settings
|
40
|
+
class << self
|
41
|
+
def inherited(subclass)
|
42
|
+
super
|
43
|
+
|
44
|
+
if Hanami.bundled?("dry-types")
|
45
|
+
require "dry/types"
|
46
|
+
subclass.const_set(:Types, Dry.Types())
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# Loads the settings for a slice.
|
51
|
+
#
|
52
|
+
# Returns nil if no settings class is defined.
|
53
|
+
#
|
54
|
+
# @return [Settings, nil]
|
55
|
+
#
|
56
|
+
# @api private
|
57
|
+
def load_for_slice(slice)
|
58
|
+
return unless settings_defined?(slice)
|
59
|
+
|
60
|
+
require_slice_settings(slice) unless slice_settings_class?(slice)
|
61
|
+
|
62
|
+
slice_settings_class(slice).new(slice.config.settings_store)
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
# Returns true if settings are defined for the slice.
|
68
|
+
#
|
69
|
+
# Settings are considered defined if a `Settings` class is already defined in the slice
|
70
|
+
# namespace, or a `config/settings.rb` exists under the slice root.
|
71
|
+
def settings_defined?(slice)
|
72
|
+
slice.namespace.const_defined?(SETTINGS_CLASS_NAME) ||
|
73
|
+
slice.root.join("#{SETTINGS_PATH}#{RB_EXT}").file?
|
74
|
+
end
|
75
|
+
|
76
|
+
def slice_settings_class?(slice)
|
77
|
+
slice.namespace.const_defined?(SETTINGS_CLASS_NAME)
|
78
|
+
end
|
79
|
+
|
80
|
+
def slice_settings_class(slice)
|
81
|
+
slice.namespace.const_get(SETTINGS_CLASS_NAME)
|
82
|
+
end
|
83
|
+
|
84
|
+
def require_slice_settings(slice)
|
85
|
+
require "hanami/settings"
|
86
|
+
|
87
|
+
slice_settings_require_path = File.join(slice.root, SETTINGS_PATH)
|
88
|
+
|
89
|
+
begin
|
90
|
+
require slice_settings_require_path
|
91
|
+
rescue LoadError => e
|
92
|
+
raise e unless e.path == slice_settings_require_path
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
38
97
|
# Exception for errors in the definition of settings.
|
39
98
|
#
|
40
99
|
# Its message collects all the individual errors that can be raised for each setting.
|
100
|
+
#
|
101
|
+
# @api public
|
41
102
|
InvalidSettingsError = Class.new(StandardError) do
|
42
103
|
def initialize(errors)
|
43
104
|
@errors = errors
|
@@ -52,6 +113,9 @@ module Hanami
|
|
52
113
|
end
|
53
114
|
end
|
54
115
|
|
116
|
+
# @api private
|
117
|
+
Undefined = Dry::Core::Constants::Undefined
|
118
|
+
|
55
119
|
# @api private
|
56
120
|
EMPTY_STORE = Dry::Core::Constants::EMPTY_HASH
|
57
121
|
|
@@ -59,14 +123,32 @@ module Hanami
|
|
59
123
|
|
60
124
|
# @api private
|
61
125
|
def initialize(store = EMPTY_STORE)
|
62
|
-
errors = config._settings.map(&:name).
|
63
|
-
|
64
|
-
|
126
|
+
errors = config._settings.map(&:name).each_with_object({}) do |name, errs|
|
127
|
+
value = store.fetch(name, Undefined)
|
128
|
+
|
129
|
+
if value.eql?(Undefined)
|
130
|
+
# When a key is missing entirely from the store, _read_ its value from the config instead,
|
131
|
+
# which ensures its setting constructor runs (with a `nil` argument given) and raises any
|
132
|
+
# necessary errors.
|
133
|
+
public_send(name)
|
134
|
+
else
|
135
|
+
public_send("#{name}=", value)
|
136
|
+
end
|
65
137
|
rescue => e # rubocop:disable Style/RescueStandardError
|
66
|
-
errs
|
138
|
+
errs[name] = e
|
67
139
|
end
|
68
140
|
|
69
141
|
raise InvalidSettingsError, errors if errors.any?
|
142
|
+
|
143
|
+
config.finalize!
|
144
|
+
end
|
145
|
+
|
146
|
+
def inspect
|
147
|
+
"#<#{self.class.to_s} [#{config._settings.map(&:name).join(", ")}]>"
|
148
|
+
end
|
149
|
+
|
150
|
+
def inspect_values
|
151
|
+
"#<#{self.class.to_s} #{config._settings.map { |setting| "#{setting.name}=#{config[setting.name].inspect}" }.join(" ")}>"
|
70
152
|
end
|
71
153
|
|
72
154
|
private
|
@@ -1,5 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "hanami/middleware"
|
4
|
+
require "hanami/errors"
|
5
|
+
|
3
6
|
module Hanami
|
4
7
|
class Slice
|
5
8
|
module Routing
|
@@ -39,11 +42,16 @@ module Hanami
|
|
39
42
|
# @api private
|
40
43
|
attr_reader :stack
|
41
44
|
|
45
|
+
# @since 2.0.0
|
46
|
+
# @api public
|
47
|
+
attr_reader :namespaces
|
48
|
+
|
42
49
|
# @since 2.0.0
|
43
50
|
# @api private
|
44
51
|
def initialize
|
45
52
|
@prefix = ROOT_PREFIX
|
46
53
|
@stack = Hash.new { |hash, key| hash[key] = [] }
|
54
|
+
@namespaces = [Hanami::Middleware]
|
47
55
|
end
|
48
56
|
|
49
57
|
# @since 2.0.0
|
@@ -52,11 +60,13 @@ module Hanami
|
|
52
60
|
super
|
53
61
|
@prefix = source.instance_variable_get(:@prefix).dup
|
54
62
|
@stack = stack.dup
|
63
|
+
@namespaces = namespaces.dup
|
55
64
|
end
|
56
65
|
|
57
66
|
# @since 2.0.0
|
58
67
|
# @api private
|
59
|
-
def use(
|
68
|
+
def use(spec, *args, before: nil, after: nil, &blk)
|
69
|
+
middleware = resolve_middleware_class(spec)
|
60
70
|
item = [middleware, args, blk]
|
61
71
|
|
62
72
|
if before
|
@@ -141,6 +151,40 @@ module Hanami
|
|
141
151
|
def index_of(middleware)
|
142
152
|
@stack[@prefix].index { |(m, *)| m.equal?(middleware) }
|
143
153
|
end
|
154
|
+
|
155
|
+
# @since 2.0.0
|
156
|
+
def resolve_middleware_class(spec)
|
157
|
+
case spec
|
158
|
+
when Symbol then load_middleware_class(spec)
|
159
|
+
when Class, Module then spec
|
160
|
+
else
|
161
|
+
if spec.respond_to?(:call)
|
162
|
+
spec
|
163
|
+
else
|
164
|
+
raise UnsupportedMiddlewareSpecError, spec
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
# @since 2.0.0
|
170
|
+
def load_middleware_class(spec)
|
171
|
+
begin
|
172
|
+
require "hanami/middleware/#{spec}"
|
173
|
+
rescue LoadError # rubocop:disable Lint/SuppressedException
|
174
|
+
end
|
175
|
+
|
176
|
+
class_name = Hanami::Utils::String.classify(spec.to_s)
|
177
|
+
namespace = namespaces.detect { |ns| ns.const_defined?(class_name) }
|
178
|
+
|
179
|
+
if namespace
|
180
|
+
namespace.const_get(class_name)
|
181
|
+
else
|
182
|
+
raise(
|
183
|
+
UnsupportedMiddlewareSpecError,
|
184
|
+
"Failed to find corresponding middleware class for `#{spec}` in #{namespaces.join(', ')}"
|
185
|
+
)
|
186
|
+
end
|
187
|
+
end
|
144
188
|
end
|
145
189
|
end
|
146
190
|
end
|
data/lib/hanami/slice.rb
CHANGED
@@ -1,12 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "dry/system/container"
|
4
3
|
require "zeitwerk"
|
4
|
+
require "dry/system"
|
5
|
+
|
6
|
+
require_relative "../hanami"
|
5
7
|
require_relative "constants"
|
6
8
|
require_relative "errors"
|
9
|
+
require_relative "settings"
|
7
10
|
require_relative "slice_name"
|
8
11
|
require_relative "slice_registrar"
|
9
|
-
require_relative "providers/settings"
|
10
12
|
|
11
13
|
module Hanami
|
12
14
|
# A slice represents any distinct area of concern within an Hanami app.
|
@@ -17,12 +19,11 @@ module Hanami
|
|
17
19
|
# Each slice corresponds a single module namespace and a single root directory of source
|
18
20
|
# files for loading as components into its container.
|
19
21
|
#
|
20
|
-
# Each slice has its own
|
21
|
-
#
|
22
|
+
# Each slice has its own config, and may optionally have its own settings, routes, as well as
|
23
|
+
# other nested slices.
|
22
24
|
#
|
23
|
-
# Slices expect an Hanami app to be defined (which itself is a slice). They will
|
24
|
-
#
|
25
|
-
# certain components
|
25
|
+
# Slices expect an Hanami app to be defined (which itself is a slice). They will initialize their
|
26
|
+
# config as a copy of the app's, and will also configure certain components
|
26
27
|
#
|
27
28
|
# Slices must be _prepared_ and optionally _booted_ before they can be used (see
|
28
29
|
# {ClassMethods.prepare} and {ClassMethods.boot}). A prepared slice will lazily load its
|
@@ -56,15 +57,14 @@ module Hanami
|
|
56
57
|
Hanami.app
|
57
58
|
end
|
58
59
|
|
59
|
-
# A slice's
|
60
|
-
#
|
61
|
-
def
|
62
|
-
@
|
60
|
+
# A slice's config is copied from the app config at time of first access. The app should have
|
61
|
+
# its config completed before slices are loaded.
|
62
|
+
def config
|
63
|
+
@config ||= app.config.dup.tap do |slice_config|
|
63
64
|
# Remove specific values from app that will not apply to this slice
|
64
|
-
|
65
|
+
slice_config.root = nil
|
65
66
|
end
|
66
67
|
end
|
67
|
-
alias_method :config, :configuration
|
68
68
|
|
69
69
|
def slice_name
|
70
70
|
@slice_name ||= SliceName.new(self, inflector: method(:inflector))
|
@@ -75,11 +75,22 @@ module Hanami
|
|
75
75
|
end
|
76
76
|
|
77
77
|
def root
|
78
|
-
|
78
|
+
# Provide a best guess for a root when it is not yet configured.
|
79
|
+
#
|
80
|
+
# This is particularly useful for user-defined slice classes that access `settings` inside
|
81
|
+
# the class body (since the root needed to find the settings file). In this case,
|
82
|
+
# `configuration.root` may be nil when `settings` is called, since the root is configured by
|
83
|
+
# `SliceRegistrar#configure_slice` _after_ the class is loaded.
|
84
|
+
#
|
85
|
+
# In common cases, this best guess will be correct since most Hanami slices will be expected
|
86
|
+
# to live in the app SLICES_DIR. For advanced cases, the correct slice root should be
|
87
|
+
# explicitly configured at the beginning of the slice class body, before any calls to
|
88
|
+
# `settings`.
|
89
|
+
config.root || app.root.join(SLICES_DIR, slice_name.to_s)
|
79
90
|
end
|
80
91
|
|
81
92
|
def inflector
|
82
|
-
|
93
|
+
config.inflector
|
83
94
|
end
|
84
95
|
|
85
96
|
def prepare(provider_name = nil)
|
@@ -180,6 +191,12 @@ module Hanami
|
|
180
191
|
end
|
181
192
|
end
|
182
193
|
|
194
|
+
def settings
|
195
|
+
return @settings if instance_variable_defined?(:@settings)
|
196
|
+
|
197
|
+
@settings = Settings.load_for_slice(self)
|
198
|
+
end
|
199
|
+
|
183
200
|
def routes
|
184
201
|
@routes ||= load_routes
|
185
202
|
end
|
@@ -209,7 +226,7 @@ module Hanami
|
|
209
226
|
def prepare_slice
|
210
227
|
return self if prepared?
|
211
228
|
|
212
|
-
|
229
|
+
config.finalize!
|
213
230
|
|
214
231
|
ensure_slice_name
|
215
232
|
ensure_slice_consts
|
@@ -222,8 +239,6 @@ module Hanami
|
|
222
239
|
|
223
240
|
prepare_autoloader
|
224
241
|
|
225
|
-
ensure_prepared
|
226
|
-
|
227
242
|
# Load child slices last, ensuring their parent is fully prepared beforehand
|
228
243
|
# (useful e.g. for slices that may wish to access constants defined in the
|
229
244
|
# parent's autoloaded directories)
|
@@ -250,17 +265,13 @@ module Hanami
|
|
250
265
|
end
|
251
266
|
|
252
267
|
def ensure_root
|
253
|
-
unless
|
268
|
+
unless config.root
|
254
269
|
raise SliceLoadError, "Slice must have a `config.root` before it can be prepared"
|
255
270
|
end
|
256
271
|
end
|
257
272
|
|
258
|
-
def ensure_prepared
|
259
|
-
# Load settings so we can fail early in case of non-conformant values
|
260
|
-
self[:settings] if key?(:settings)
|
261
|
-
end
|
262
|
-
|
263
273
|
def prepare_all
|
274
|
+
prepare_settings
|
264
275
|
prepare_container_consts
|
265
276
|
prepare_container_plugins
|
266
277
|
prepare_container_base_config
|
@@ -269,6 +280,15 @@ module Hanami
|
|
269
280
|
prepare_container_providers
|
270
281
|
end
|
271
282
|
|
283
|
+
def prepare_settings
|
284
|
+
container.register(:settings, settings) if settings
|
285
|
+
end
|
286
|
+
|
287
|
+
def prepare_container_consts
|
288
|
+
namespace.const_set :Container, container
|
289
|
+
namespace.const_set :Deps, container.injector
|
290
|
+
end
|
291
|
+
|
272
292
|
def prepare_container_plugins
|
273
293
|
container.use(:env, inferrer: -> { Hanami.env })
|
274
294
|
|
@@ -285,8 +305,8 @@ module Hanami
|
|
285
305
|
container.config.root = root
|
286
306
|
container.config.provider_dirs = [File.join("config", "providers")]
|
287
307
|
|
288
|
-
container.config.env =
|
289
|
-
container.config.inflector =
|
308
|
+
container.config.env = config.env
|
309
|
+
container.config.inflector = config.inflector
|
290
310
|
end
|
291
311
|
|
292
312
|
def prepare_container_component_dirs
|
@@ -304,7 +324,7 @@ module Hanami
|
|
304
324
|
# When auto-registering components in the root, ignore files in `config/` (this is
|
305
325
|
# for framework config only), `lib/` (these will be auto-registered as above), as
|
306
326
|
# well as the configured no_auto_register_paths
|
307
|
-
no_auto_register_paths = ([LIB_DIR, CONFIG_DIR] +
|
327
|
+
no_auto_register_paths = ([LIB_DIR, CONFIG_DIR] + config.no_auto_register_paths)
|
308
328
|
.map { |path|
|
309
329
|
path.end_with?(File::SEPARATOR) ? path : "#{path}#{File::SEPARATOR}"
|
310
330
|
}
|
@@ -335,8 +355,6 @@ module Hanami
|
|
335
355
|
require_relative "providers/routes"
|
336
356
|
register_provider(:routes, source: Providers::Routes.for_slice(self))
|
337
357
|
end
|
338
|
-
|
339
|
-
Providers::Settings.register_with_slice(self)
|
340
358
|
end
|
341
359
|
|
342
360
|
def prepare_autoloader
|
@@ -358,17 +376,14 @@ module Hanami
|
|
358
376
|
autoloader.setup
|
359
377
|
end
|
360
378
|
|
361
|
-
def prepare_container_consts
|
362
|
-
namespace.const_set :Container, container
|
363
|
-
namespace.const_set :Deps, container.injector
|
364
|
-
end
|
365
|
-
|
366
379
|
def prepare_slices
|
367
380
|
slices.load_slices.each(&:prepare)
|
368
381
|
slices.freeze
|
369
382
|
end
|
370
383
|
|
371
384
|
def load_routes
|
385
|
+
return false unless Hanami.bundled?("hanami-router")
|
386
|
+
|
372
387
|
if root.directory?
|
373
388
|
routes_require_path = File.join(root, ROUTES_PATH)
|
374
389
|
|
@@ -393,14 +408,14 @@ module Hanami
|
|
393
408
|
|
394
409
|
require_relative "slice/router"
|
395
410
|
|
396
|
-
config =
|
411
|
+
config = self.config
|
397
412
|
rack_monitor = self["rack.monitor"]
|
398
413
|
|
399
414
|
Slice::Router.new(
|
400
415
|
inspector: inspector,
|
401
416
|
routes: routes,
|
402
|
-
resolver:
|
403
|
-
**
|
417
|
+
resolver: config.router.resolver.new(slice: self),
|
418
|
+
**config.router.options
|
404
419
|
) do
|
405
420
|
use(rack_monitor)
|
406
421
|
use(*config.sessions.middleware) if config.sessions.enabled?
|
data/lib/hanami/version.rb
CHANGED
data/lib/hanami.rb
CHANGED
@@ -0,0 +1,108 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rack/test"
|
4
|
+
|
5
|
+
RSpec.describe "Hanami web app", :app_integration do
|
6
|
+
include Rack::Test::Methods
|
7
|
+
|
8
|
+
let(:app) { Hanami.app }
|
9
|
+
|
10
|
+
around do |example|
|
11
|
+
with_tmp_directory(Dir.mktmpdir, &example)
|
12
|
+
end
|
13
|
+
|
14
|
+
specify "Setting middlewares in the config" do
|
15
|
+
write "config/app.rb", <<~RUBY
|
16
|
+
require "hanami"
|
17
|
+
|
18
|
+
module TestApp
|
19
|
+
class App < Hanami::App
|
20
|
+
config.middleware.use :body_parser, :json
|
21
|
+
end
|
22
|
+
end
|
23
|
+
RUBY
|
24
|
+
|
25
|
+
write "config/routes.rb", <<~RUBY
|
26
|
+
module TestApp
|
27
|
+
class Routes < Hanami::Routes
|
28
|
+
post "/users", to: "users.create"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
RUBY
|
32
|
+
|
33
|
+
write "app/actions/users/create.rb", <<~RUBY
|
34
|
+
module TestApp
|
35
|
+
module Actions
|
36
|
+
module Users
|
37
|
+
class Create < Hanami::Action
|
38
|
+
accept :json
|
39
|
+
|
40
|
+
def handle(req, res)
|
41
|
+
res.body = req.params[:users].join("-")
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
RUBY
|
48
|
+
|
49
|
+
require "hanami/boot"
|
50
|
+
|
51
|
+
post(
|
52
|
+
"/users",
|
53
|
+
JSON.dump("users" => %w[jane john jade joe]),
|
54
|
+
"CONTENT_TYPE" => "application/json"
|
55
|
+
)
|
56
|
+
|
57
|
+
expect(last_response).to be_successful
|
58
|
+
expect(last_response.body).to eql("jane-john-jade-joe")
|
59
|
+
end
|
60
|
+
|
61
|
+
specify "Configuring custom mime-types and body parser" do
|
62
|
+
write "config/app.rb", <<~RUBY
|
63
|
+
require "hanami"
|
64
|
+
|
65
|
+
module TestApp
|
66
|
+
class App < Hanami::App
|
67
|
+
config.actions.formats["application/json+scim"] = :json
|
68
|
+
config.middleware.use :body_parser, [json: "application/json+scim"]
|
69
|
+
end
|
70
|
+
end
|
71
|
+
RUBY
|
72
|
+
|
73
|
+
write "config/routes.rb", <<~RUBY
|
74
|
+
module TestApp
|
75
|
+
class Routes < Hanami::Routes
|
76
|
+
post "/users", to: "users.create"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
RUBY
|
80
|
+
|
81
|
+
write "app/actions/users/create.rb", <<~RUBY
|
82
|
+
module TestApp
|
83
|
+
module Actions
|
84
|
+
module Users
|
85
|
+
class Create < Hanami::Action
|
86
|
+
accept :json
|
87
|
+
|
88
|
+
def handle(req, res)
|
89
|
+
res.body = req.params[:users].join("-")
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
RUBY
|
96
|
+
|
97
|
+
require "hanami/boot"
|
98
|
+
|
99
|
+
post(
|
100
|
+
"/users",
|
101
|
+
JSON.dump("users" => %w[jane john jade joe]),
|
102
|
+
"CONTENT_TYPE" => "application/json+scim"
|
103
|
+
)
|
104
|
+
|
105
|
+
expect(last_response).to be_successful
|
106
|
+
expect(last_response.body).to eql("jane-john-jade-joe")
|
107
|
+
end
|
108
|
+
end
|
@@ -206,4 +206,32 @@ RSpec.describe "Hanami web app", :app_integration do
|
|
206
206
|
expect(last_response).to be_successful
|
207
207
|
expect(last_response.body).to eql("yes")
|
208
208
|
end
|
209
|
+
|
210
|
+
context "Using module as a middleware" do
|
211
|
+
it "sets the module as the middleware" do
|
212
|
+
mod = Module.new
|
213
|
+
app = Class.new(Hanami::App) { config.middleware.use(mod) }
|
214
|
+
|
215
|
+
expect(app.config.middleware.stack["/"][0]).to include(mod)
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
context "Setting an unsupported middleware" do
|
220
|
+
it "raises meaningful error when an unsupported middleware spec was passed" do
|
221
|
+
expect {
|
222
|
+
Class.new(Hanami::App) do
|
223
|
+
config.middleware.use("oops")
|
224
|
+
end
|
225
|
+
}.to raise_error(Hanami::UnsupportedMiddlewareSpecError)
|
226
|
+
end
|
227
|
+
|
228
|
+
it "raises meaningful error when corresponding file failed to load" do
|
229
|
+
expect {
|
230
|
+
Class.new(Hanami::App) do
|
231
|
+
config.middleware.namespaces.delete(Hanami::Middleware)
|
232
|
+
config.middleware.use(:body_parser)
|
233
|
+
end
|
234
|
+
}.to raise_error(Hanami::UnsupportedMiddlewareSpecError)
|
235
|
+
end
|
236
|
+
end
|
209
237
|
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
RSpec.describe "Settings / Access within slice class bodies", :app_integration do
|
4
|
+
before do
|
5
|
+
@env = ENV.to_h
|
6
|
+
end
|
7
|
+
|
8
|
+
after do
|
9
|
+
ENV.replace(@env)
|
10
|
+
end
|
11
|
+
|
12
|
+
context "app class" do
|
13
|
+
it "provides access to the settings inside the class body" do
|
14
|
+
with_directory(make_tmp_directory) do
|
15
|
+
write "config/app.rb", <<~'RUBY'
|
16
|
+
require "hanami"
|
17
|
+
|
18
|
+
module TestApp
|
19
|
+
class App < Hanami::App
|
20
|
+
@some_flag = settings.some_flag
|
21
|
+
end
|
22
|
+
end
|
23
|
+
RUBY
|
24
|
+
|
25
|
+
write ".env", <<~'TEXT'
|
26
|
+
SOME_FLAG=true
|
27
|
+
TEXT
|
28
|
+
|
29
|
+
write "config/settings.rb", <<~'RUBY'
|
30
|
+
module TestApp
|
31
|
+
class Settings < Hanami::Settings
|
32
|
+
setting :some_flag
|
33
|
+
end
|
34
|
+
end
|
35
|
+
RUBY
|
36
|
+
|
37
|
+
require "hanami/setup"
|
38
|
+
|
39
|
+
expect(Hanami.app.instance_variable_get(:@some_flag)).to eq "true"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
context "slice class" do
|
45
|
+
it "provides access to the settings inside the class body" do
|
46
|
+
with_directory(make_tmp_directory) do
|
47
|
+
write "config/app.rb", <<~'RUBY'
|
48
|
+
require "hanami"
|
49
|
+
|
50
|
+
module TestApp
|
51
|
+
class App < Hanami::App
|
52
|
+
end
|
53
|
+
end
|
54
|
+
RUBY
|
55
|
+
|
56
|
+
write "config/slices/main.rb", <<~'RUBY'
|
57
|
+
module Main
|
58
|
+
class Slice < Hanami::Slice
|
59
|
+
@some_flag = settings.some_flag
|
60
|
+
end
|
61
|
+
end
|
62
|
+
RUBY
|
63
|
+
|
64
|
+
write ".env", <<~'TEXT'
|
65
|
+
SOME_FLAG=true
|
66
|
+
TEXT
|
67
|
+
|
68
|
+
write "slices/main/config/settings.rb", <<~'RUBY'
|
69
|
+
module Main
|
70
|
+
class Settings < Hanami::Settings
|
71
|
+
setting :some_flag
|
72
|
+
end
|
73
|
+
end
|
74
|
+
RUBY
|
75
|
+
|
76
|
+
require "hanami/prepare"
|
77
|
+
|
78
|
+
expect(Main::Slice.instance_variable_get(:@some_flag)).to eq "true"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|