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.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +20 -0
  3. data/hanami.gemspec +5 -5
  4. data/lib/hanami/app.rb +4 -4
  5. data/lib/hanami/assets/{app_configuration.rb → app_config.rb} +10 -10
  6. data/lib/hanami/assets/{configuration.rb → config.rb} +4 -4
  7. data/lib/hanami/{configuration → config}/actions/content_security_policy.rb +2 -2
  8. data/lib/hanami/{configuration → config}/actions/cookies.rb +6 -5
  9. data/lib/hanami/{configuration → config}/actions/sessions.rb +2 -2
  10. data/lib/hanami/{configuration → config}/actions.rb +11 -12
  11. data/lib/hanami/{configuration → config}/logger.rb +2 -2
  12. data/lib/hanami/config/null_config.rb +14 -0
  13. data/lib/hanami/{configuration → config}/router.rb +8 -8
  14. data/lib/hanami/{configuration → config}/sessions.rb +2 -2
  15. data/lib/hanami/{configuration → config}/views.rb +10 -10
  16. data/lib/hanami/{configuration.rb → config.rb} +19 -19
  17. data/lib/hanami/errors.rb +3 -0
  18. data/lib/hanami/extensions/action/slice_configured_action.rb +1 -1
  19. data/lib/hanami/providers/inflector.rb +0 -2
  20. data/lib/hanami/providers/logger.rb +1 -3
  21. data/lib/hanami/providers/rack.rb +0 -2
  22. data/lib/hanami/providers/routes.rb +0 -2
  23. data/lib/hanami/settings/env_store.rb +3 -3
  24. data/lib/hanami/settings.rb +87 -5
  25. data/lib/hanami/slice/routing/middleware/stack.rb +45 -1
  26. data/lib/hanami/slice.rb +52 -37
  27. data/lib/hanami/slice_configurable.rb +1 -1
  28. data/lib/hanami/slice_registrar.rb +6 -0
  29. data/lib/hanami/version.rb +1 -1
  30. data/lib/hanami.rb +0 -1
  31. data/spec/integration/container/prepare_container_spec.rb +2 -0
  32. data/spec/integration/rack_app/body_parser_spec.rb +108 -0
  33. data/spec/integration/rack_app/middleware_spec.rb +28 -0
  34. data/spec/integration/settings/access_in_slice_class_body_spec.rb +82 -0
  35. data/spec/integration/settings/access_to_constants_spec.rb +23 -146
  36. data/spec/integration/{slices/slice_settings_spec.rb → settings/slice_registration_spec.rb} +5 -1
  37. data/spec/integration/settings/using_types_spec.rb +4 -11
  38. data/spec/support/app_integration.rb +18 -15
  39. data/spec/unit/hanami/{configuration → config}/actions/content_security_policy_spec.rb +10 -10
  40. data/spec/unit/hanami/{configuration → config}/actions/cookies_spec.rb +6 -6
  41. data/spec/unit/hanami/{configuration → config}/actions/csrf_protection_spec.rb +12 -12
  42. data/spec/unit/hanami/config/actions/default_values_spec.rb +54 -0
  43. data/spec/unit/hanami/{configuration → config}/actions/sessions_spec.rb +5 -5
  44. data/spec/unit/hanami/{configuration → config}/actions_spec.rb +9 -10
  45. data/spec/unit/hanami/{configuration → config}/base_url_spec.rb +2 -2
  46. data/spec/unit/hanami/{configuration → config}/inflector_spec.rb +2 -2
  47. data/spec/unit/hanami/{configuration → config}/logger_spec.rb +4 -4
  48. data/spec/unit/hanami/{configuration → config}/router_spec.rb +7 -8
  49. data/spec/unit/hanami/{configuration → config}/slices_spec.rb +2 -2
  50. data/spec/unit/hanami/{configuration → config}/views_spec.rb +11 -12
  51. data/spec/unit/hanami/{configuration_spec.rb → config_spec.rb} +3 -3
  52. data/spec/unit/hanami/settings_spec.rb +65 -10
  53. data/spec/unit/hanami/slice_configurable_spec.rb +21 -2
  54. data/spec/unit/hanami/version_spec.rb +1 -1
  55. metadata +73 -58
  56. data/lib/hanami/configuration/null_configuration.rb +0 -14
  57. data/lib/hanami/providers/settings.rb +0 -98
  58. data/spec/unit/hanami/configuration/actions/default_values_spec.rb +0 -52
@@ -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).reduce({}) do |errs, name|
63
- public_send("#{name}=", store.fetch(name) { Dry::Core::Constants::Undefined })
64
- errs
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.merge(name => e)
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(middleware, *args, before: nil, after: nil, &blk)
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 configuration, and may optionally have its own settings,
21
- # routes, as well as other nested slices.
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
- # initialize their configuration as a copy of the app's, and will also configure
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 configuration is copied from the app configuration at time of first access. The
60
- # app should have its configuration completed before slices are loaded.
61
- def configuration
62
- @configuration ||= app.configuration.dup.tap do |config|
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
- config.root = nil
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
- configuration.root
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
- configuration.inflector
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
- configuration.finalize!
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 configuration.root
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 = configuration.env
289
- container.config.inflector = configuration.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] + configuration.no_auto_register_paths)
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 = configuration
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: configuration.router.resolver.new(slice: self),
403
- **configuration.router.options
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?
@@ -56,7 +56,7 @@ module Hanami
56
56
  def slice_for(klass)
57
57
  return unless klass.name
58
58
 
59
- slices = Hanami.app.slices.to_a + [Hanami.app]
59
+ slices = Hanami.app.slices.with_nested + [Hanami.app]
60
60
 
61
61
  slices.detect { |slice| klass.name.include?(slice.namespace.to_s) }
62
62
  end
@@ -75,6 +75,12 @@ module Hanami
75
75
  slices.values
76
76
  end
77
77
 
78
+ def with_nested
79
+ to_a.flat_map { |slice|
80
+ [slice] + slice.slices.with_nested
81
+ }
82
+ end
83
+
78
84
  private
79
85
 
80
86
  def root
@@ -8,7 +8,7 @@ module Hanami
8
8
  module Version
9
9
  # @since 0.9.0
10
10
  # @api private
11
- VERSION = "2.0.0.beta3"
11
+ VERSION = "2.0.0.beta4"
12
12
 
13
13
  # @since 0.9.0
14
14
  # @api private
data/lib/hanami.rb CHANGED
@@ -114,7 +114,6 @@ module Hanami
114
114
  @_mutex.synchronize do
115
115
  @_bundled[gem_name] ||= begin
116
116
  gem(gem_name)
117
- true
118
117
  rescue Gem::LoadError
119
118
  false
120
119
  end
@@ -23,6 +23,8 @@ RSpec.describe "Container / prepare_container", :app_integration do
23
23
 
24
24
  # The prepare_container block is called when the slice is prepared
25
25
  slice.prepare
26
+
27
+ autoloaders_teardown!
26
28
  end
27
29
 
28
30
  describe "in app", :in_prepare_container do
@@ -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