hanami 2.0.0.beta4 → 2.0.0.rc1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +19 -0
  3. data/hanami.gemspec +8 -7
  4. data/lib/hanami/app.rb +47 -36
  5. data/lib/hanami/assets/app_config.rb +7 -15
  6. data/lib/hanami/assets/config.rb +5 -6
  7. data/lib/hanami/config/actions/content_security_policy.rb +1 -1
  8. data/lib/hanami/config/actions/cookies.rb +27 -0
  9. data/lib/hanami/config/actions/sessions.rb +42 -5
  10. data/lib/hanami/config/actions.rb +81 -17
  11. data/lib/hanami/config/logger.rb +112 -23
  12. data/lib/hanami/config/router.rb +0 -1
  13. data/lib/hanami/config/views.rb +6 -10
  14. data/lib/hanami/config.rb +235 -73
  15. data/lib/hanami/constants.rb +4 -0
  16. data/lib/hanami/errors.rb +17 -0
  17. data/lib/hanami/extensions/action/slice_configured_action.rb +9 -5
  18. data/lib/hanami/extensions/action.rb +59 -7
  19. data/lib/hanami/extensions/view/context.rb +3 -4
  20. data/lib/hanami/extensions/view/slice_configured_view.rb +4 -4
  21. data/lib/hanami/extensions/view.rb +7 -5
  22. data/lib/hanami/providers/inflector.rb +6 -0
  23. data/lib/hanami/providers/logger.rb +8 -0
  24. data/lib/hanami/providers/rack.rb +12 -0
  25. data/lib/hanami/providers/routes.rb +14 -4
  26. data/lib/hanami/routes.rb +36 -1
  27. data/lib/hanami/settings/env_store.rb +1 -1
  28. data/lib/hanami/settings.rb +102 -36
  29. data/lib/hanami/slice/router.rb +38 -16
  30. data/lib/hanami/slice/routing/middleware/stack.rb +66 -42
  31. data/lib/hanami/slice/routing/resolver.rb +10 -17
  32. data/lib/hanami/slice/view_name_inferrer.rb +1 -1
  33. data/lib/hanami/slice.rb +553 -14
  34. data/lib/hanami/slice_registrar.rb +20 -15
  35. data/lib/hanami/version.rb +2 -3
  36. data/lib/hanami/web/rack_logger.rb +14 -4
  37. data/lib/hanami.rb +122 -23
  38. data/spec/integration/action/csrf_protection_spec.rb +1 -1
  39. data/spec/integration/container/application_routes_helper_spec.rb +3 -1
  40. data/spec/integration/container/provider_lifecycle_spec.rb +61 -0
  41. data/spec/integration/container/standard_providers/rack_provider_spec.rb +44 -0
  42. data/spec/integration/container/{standard_bootable_components_spec.rb → standard_providers_spec.rb} +3 -3
  43. data/spec/integration/rack_app/body_parser_spec.rb +3 -0
  44. data/spec/integration/rack_app/middleware_spec.rb +427 -3
  45. data/spec/integration/rack_app/non_booted_rack_app_spec.rb +2 -1
  46. data/spec/integration/rack_app/rack_app_spec.rb +39 -11
  47. data/spec/integration/setup_spec.rb +4 -4
  48. data/spec/integration/slices/external_slice_spec.rb +2 -1
  49. data/spec/integration/slices/slice_configuration_spec.rb +3 -1
  50. data/spec/integration/slices/slice_loading_spec.rb +4 -4
  51. data/spec/integration/slices/slice_routing_spec.rb +4 -3
  52. data/spec/integration/slices_spec.rb +100 -0
  53. data/spec/isolation/hanami/boot/success_spec.rb +1 -1
  54. data/spec/support/app_integration.rb +2 -10
  55. data/spec/unit/hanami/config/actions/content_security_policy_spec.rb +7 -7
  56. data/spec/unit/hanami/config/actions/default_values_spec.rb +1 -1
  57. data/spec/unit/hanami/config/actions/sessions_spec.rb +1 -3
  58. data/spec/unit/hanami/config/actions_spec.rb +1 -12
  59. data/spec/unit/hanami/config/logger_spec.rb +38 -55
  60. data/spec/unit/hanami/config/router_spec.rb +1 -1
  61. data/spec/unit/hanami/config/views_spec.rb +3 -13
  62. data/spec/unit/hanami/settings_spec.rb +1 -1
  63. data/spec/unit/hanami/slice_configurable_spec.rb +5 -5
  64. data/spec/unit/hanami/slice_spec.rb +32 -0
  65. data/spec/unit/hanami/version_spec.rb +1 -1
  66. data/spec/unit/hanami/web/rack_logger_spec.rb +13 -2
  67. metadata +54 -45
  68. data/lib/hanami/config/sessions.rb +0 -50
  69. data/spec/unit/hanami/config_spec.rb +0 -43
@@ -1,11 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "constants"
4
- require_relative "slice"
5
4
 
6
5
  module Hanami
7
6
  # @api private
8
7
  class SliceRegistrar
8
+ VALID_SLICE_NAME_RE = /^[a-z][a-z0-9_]+$/
9
9
  SLICE_DELIMITER = CONTAINER_KEY_DELIMITER
10
10
 
11
11
  attr_reader :parent, :slices
@@ -17,14 +17,16 @@ module Hanami
17
17
  end
18
18
 
19
19
  def register(name, slice_class = nil, &block)
20
+ unless name.to_s =~ VALID_SLICE_NAME_RE
21
+ raise ArgumentError, "slice name #{name.inspect} must be lowercase alphanumeric text and underscores only"
22
+ end
23
+
20
24
  return unless filter_slice_names([name]).any?
21
25
 
22
26
  if slices.key?(name.to_sym)
23
27
  raise SliceLoadError, "Slice '#{name}' is already registered"
24
28
  end
25
29
 
26
- # TODO: raise error unless name meets format (i.e. single level depth only)
27
-
28
30
  slice = slice_class || build_slice(name, &block)
29
31
 
30
32
  configure_slice(name, slice)
@@ -44,8 +46,6 @@ module Hanami
44
46
  end
45
47
 
46
48
  def load_slices
47
- return self unless root
48
-
49
49
  slice_configs = Dir[root.join(CONFIG_DIR, SLICES_DIR, "*#{RB_EXT}")]
50
50
  .map { |file| File.basename(file, RB_EXT) }
51
51
 
@@ -77,7 +77,9 @@ module Hanami
77
77
 
78
78
  def with_nested
79
79
  to_a.flat_map { |slice|
80
- [slice] + slice.slices.with_nested
80
+ # Return nested slices first so that their more specific namespaces may be picked up first
81
+ # by SliceConfigurable#slice_for
82
+ slice.slices.with_nested + [slice]
81
83
  }
82
84
  end
83
85
 
@@ -91,36 +93,39 @@ module Hanami
91
93
  parent.inflector
92
94
  end
93
95
 
94
- # Runs when a slice file has been found at `config/slices/[slice_name].rb`, or a slice
95
- # directory at `slices/[slice_name]`. Attempts to require the slice class, if defined,
96
- # or generates a new slice class for the given slice name.
96
+ def parent_slice_namespace
97
+ parent.eql?(parent.app) ? Object : parent.namespace
98
+ end
99
+
100
+ # Runs when a slice file has been found at `config/slices/[slice_name].rb`, or a slice directory
101
+ # at `slices/[slice_name]`. Attempts to require the slice class, if defined, before registering
102
+ # the slice. If a slice class is not found, registering the slice will generate the slice class.
97
103
  def load_slice(slice_name)
98
- slice_const_name = inflector.camelize(slice_name)
99
104
  slice_require_path = root.join(CONFIG_DIR, SLICES_DIR, slice_name).to_s
100
-
101
105
  begin
102
106
  require(slice_require_path)
103
107
  rescue LoadError => e
104
108
  raise e unless e.path == slice_require_path
105
109
  end
106
110
 
111
+ slice_module_name = inflector.camelize("#{parent_slice_namespace.name}#{PATH_DELIMITER}#{slice_name}")
107
112
  slice_class =
108
113
  begin
109
- inflector.constantize("#{slice_const_name}::Slice")
114
+ inflector.constantize("#{slice_module_name}#{MODULE_DELIMITER}Slice")
110
115
  rescue NameError => e
111
- raise e unless e.name.to_s == slice_const_name || e.name.to_s == :Slice
116
+ raise e unless e.name.to_s == inflector.camelize(slice_name) || e.name.to_s == :Slice
112
117
  end
113
118
 
114
119
  register(slice_name, slice_class)
115
120
  end
116
121
 
117
122
  def build_slice(slice_name, &block)
123
+ slice_module_name = inflector.camelize("#{parent_slice_namespace.name}#{PATH_DELIMITER}#{slice_name}")
118
124
  slice_module =
119
125
  begin
120
- slice_module_name = inflector.camelize(slice_name.to_s)
121
126
  inflector.constantize(slice_module_name)
122
127
  rescue NameError
123
- Object.const_set(inflector.camelize(slice_module_name), Module.new)
128
+ parent_slice_namespace.const_set(inflector.camelize(slice_name), Module.new)
124
129
  end
125
130
 
126
131
  slice_module.const_set(:Slice, Class.new(Hanami::Slice, &block))
@@ -6,9 +6,8 @@ module Hanami
6
6
  # @since 0.9.0
7
7
  # @api private
8
8
  module Version
9
- # @since 0.9.0
10
- # @api private
11
- VERSION = "2.0.0.beta4"
9
+ # @api public
10
+ VERSION = "2.0.0.rc1"
12
11
 
13
12
  # @since 0.9.0
14
13
  # @api private
@@ -1,8 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Hanami
4
+ # @api private
4
5
  module Web
5
6
  # Rack logger for Hanami apps
7
+ #
8
+ # @api private
9
+ # @since 2.0.0
6
10
  class RackLogger
7
11
  REQUEST_METHOD = "REQUEST_METHOD"
8
12
  private_constant :REQUEST_METHOD
@@ -25,10 +29,14 @@ module Hanami
25
29
  CONTENT_LENGTH = "Content-Length"
26
30
  private_constant :CONTENT_LENGTH
27
31
 
32
+ # @api private
33
+ # @since 2.0.0
28
34
  def initialize(logger)
29
35
  @logger = logger
30
36
  end
31
37
 
38
+ # @api private
39
+ # @since 2.0.0
32
40
  def attach(rack_monitor)
33
41
  rack_monitor.on :stop do |event|
34
42
  log_request event[:env], event[:status], event[:time]
@@ -39,6 +47,8 @@ module Hanami
39
47
  end
40
48
  end
41
49
 
50
+ # @api private
51
+ # @since 2.0.0
42
52
  def log_request(env, status, elapsed)
43
53
  data = {
44
54
  verb: env[REQUEST_METHOD],
@@ -47,16 +57,16 @@ module Hanami
47
57
  ip: env[HTTP_X_FORWARDED_FOR] || env[REMOTE_ADDR],
48
58
  path: env[SCRIPT_NAME] + env[PATH_INFO].to_s,
49
59
  length: extract_content_length(env),
50
- params: env[ROUTER_PARAMS],
51
- time: Time.now,
60
+ params: env[ROUTER_PARAMS]
52
61
  }
53
62
 
54
63
  logger.info(data)
55
64
  end
56
65
 
66
+ # @api private
67
+ # @since 2.0.0
57
68
  def log_exception(exception)
58
- logger.error exception.message
59
- logger.error exception.backtrace.join("\n")
69
+ logger.error(exception)
60
70
  end
61
71
 
62
72
  private
data/lib/hanami.rb CHANGED
@@ -1,5 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "zeitwerk"
4
+ require_relative "hanami/constants"
5
+
3
6
  # A complete web framework for Ruby
4
7
  #
5
8
  # @since 0.1.0
@@ -9,11 +12,21 @@ module Hanami
9
12
  @_mutex = Mutex.new
10
13
  @_bundled = {}
11
14
 
15
+ # @api private
16
+ # @since 2.0.0
17
+ def self.loader
18
+ @loader ||= Zeitwerk::Loader.for_gem.tap do |loader|
19
+ loader.ignore(
20
+ "#{loader.dirs.first}/hanami/{constants,boot,errors,prepare,rake_tasks,setup}.rb"
21
+ )
22
+ end
23
+ end
24
+
12
25
  # Finds and loads the Hanami app file (`config/app.rb`).
13
26
  #
14
27
  # Raises an exception if the app file cannot be found.
15
28
  #
16
- # @return [Hanami::App] the loaded app class
29
+ # @return [app] the loaded app class
17
30
  #
18
31
  # @api public
19
32
  # @since 2.0.0
@@ -34,32 +47,18 @@ module Hanami
34
47
  end
35
48
  end
36
49
 
37
- # Finds and returns the absolute path for the Hanami app file (`config/app.rb`).
50
+ # Returns the Hamami app class.
38
51
  #
39
- # Searches within the given directory, then searches upwards through parent directories until the
40
- # app file can be found.
52
+ # To ensure your Hanami app is loaded, run {.setup} (or `require "hanami/setup"`) first.
41
53
  #
42
- # @param dir [String] The directory from which to start searching. Defaults to the current
43
- # directory.
54
+ # @return [Hanami::App] the app class
44
55
  #
45
- # @return [String, nil] the app file path, or nil if not found.
56
+ # @raise [AppLoadError] if the app has not been loaded
57
+ #
58
+ # @see .setup
46
59
  #
47
60
  # @api public
48
61
  # @since 2.0.0
49
- def self.app_path(dir = Dir.pwd)
50
- dir = Pathname(dir).expand_path
51
- path = dir.join(APP_PATH)
52
-
53
- if path.file?
54
- path.to_s
55
- elsif !dir.root?
56
- app_path(dir.parent)
57
- end
58
- end
59
-
60
- APP_PATH = "config/app.rb"
61
- private_constant :APP_PATH
62
-
63
62
  def self.app
64
63
  @_mutex.synchronize do
65
64
  unless defined?(@_app)
@@ -72,10 +71,18 @@ module Hanami
72
71
  end
73
72
  end
74
73
 
74
+ # Returns true if the Hanami app class has been loaded.
75
+ #
76
+ # @return [Boolean]
77
+ #
78
+ # @api public
79
+ # @since 2.0.0
75
80
  def self.app?
76
81
  instance_variable_defined?(:@_app)
77
82
  end
78
83
 
84
+ # @api private
85
+ # @since 2.0.0
79
86
  def self.app=(klass)
80
87
  @_mutex.synchronize do
81
88
  if instance_variable_defined?(:@_app)
@@ -86,30 +93,117 @@ module Hanami
86
93
  end
87
94
  end
88
95
 
96
+ # Finds and returns the absolute path for the Hanami app file (`config/app.rb`).
97
+ #
98
+ # Searches within the given directory, then searches upwards through parent directories until the
99
+ # app file can be found.
100
+ #
101
+ # @param dir [String] The directory from which to start searching. Defaults to the current
102
+ # directory.
103
+ #
104
+ # @return [String, nil] the app file path, or nil if not found.
105
+ #
106
+ # @api public
107
+ # @since 2.0.0
108
+ def self.app_path(dir = Dir.pwd)
109
+ dir = Pathname(dir).expand_path
110
+ path = dir.join(APP_PATH)
111
+
112
+ if path.file?
113
+ path.to_s
114
+ elsif !dir.root?
115
+ app_path(dir.parent)
116
+ end
117
+ end
118
+
119
+ # Returns the Hanami app environment as loaded from the `HANAMI_ENV` environment variable.
120
+ #
121
+ # @example
122
+ # Hanami.env # => :development
123
+ #
124
+ # @return [Symbol] the environment name
125
+ #
126
+ # @api public
127
+ # @since 2.0.0
89
128
  def self.env
90
129
  ENV.fetch("HANAMI_ENV") { ENV.fetch("RACK_ENV", "development") }.to_sym
91
130
  end
92
131
 
132
+ # Returns true if {.env} matches any of the given names
133
+ #
134
+ # @example
135
+ # Hanami.env # => :development
136
+ # Hanami.env?(:development, :test) # => true
137
+ #
138
+ # @param names [Array<Symbol>] the environment names to check
139
+ #
140
+ # @return [Boolean]
141
+ #
142
+ # @api public
143
+ # @since 2.0.0
93
144
  def self.env?(*names)
94
145
  names.map(&:to_sym).include?(env)
95
146
  end
96
147
 
148
+ # Returns the app's logger.
149
+ #
150
+ # Direct global access to the logger via this method is not recommended. Instead, consider
151
+ # accessing the logger via the app or slice container, in most cases as an dependency using the
152
+ # `Deps` mixin.
153
+ #
154
+ # @example
155
+ # # app/my_component.rb
156
+ #
157
+ # module MyApp
158
+ # class MyComponent
159
+ # include Deps["logger"]
160
+ #
161
+ # def some_method
162
+ # logger.info("hello")
163
+ # end
164
+ # end
165
+ # end
166
+ #
167
+ # @return [Dry::Logger::Dispatcher]
168
+ #
169
+ # @api public
170
+ # @since 1.0.0
97
171
  def self.logger
98
172
  app[:logger]
99
173
  end
100
174
 
175
+ # Prepares the Hanami app.
176
+ #
177
+ # @see App::ClassMethods#prepare
178
+ #
179
+ # @api public
180
+ # @since 2.0.0
101
181
  def self.prepare
102
182
  app.prepare
103
183
  end
104
184
 
185
+ # Boots the Hanami app.
186
+ #
187
+ # @see App::ClassMethods#boot
188
+ #
189
+ # @api public
190
+ # @since 2.0.0
105
191
  def self.boot
106
192
  app.boot
107
193
  end
108
194
 
195
+ # Shuts down the Hanami app.
196
+ #
197
+ # @see App::ClassMethods#shutdown
198
+ #
199
+ # @api public
200
+ # @since 2.0.0
109
201
  def self.shutdown
110
202
  app.shutdown
111
203
  end
112
204
 
205
+ # @api private
206
+ # @since 2.0.0
113
207
  def self.bundled?(gem_name)
114
208
  @_mutex.synchronize do
115
209
  @_bundled[gem_name] ||= begin
@@ -120,12 +214,17 @@ module Hanami
120
214
  end
121
215
  end
122
216
 
217
+ # Returns an array of bundler group names to be eagerly loaded by hanami-cli and other CLI
218
+ # extensions.
219
+ #
220
+ # @api private
221
+ # @since 2.0.0
123
222
  def self.bundler_groups
124
223
  [:plugins]
125
224
  end
126
225
 
127
- require_relative "hanami/version"
226
+ loader.setup
227
+
128
228
  require_relative "hanami/errors"
129
229
  require_relative "hanami/extensions"
130
- require_relative "hanami/app"
131
230
  end
@@ -35,7 +35,7 @@ RSpec.describe "App action / CSRF protection", :app_integration do
35
35
  context "CSRF protection explicitly disabled" do
36
36
  let(:app_hook) {
37
37
  proc do
38
- config.sessions = :cookie, {secret: "abc123"}
38
+ config.actions.sessions = :cookie, {secret: "abc123"}
39
39
  config.actions.csrf_protection = false
40
40
  end
41
41
  }
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "stringio"
4
+
3
5
  RSpec.describe "App routes helper", :app_integration do
4
6
  specify "Routing to actions based on their container identifiers" do
5
7
  with_tmp_directory(Dir.mktmpdir) do
@@ -8,7 +10,7 @@ RSpec.describe "App routes helper", :app_integration do
8
10
 
9
11
  module TestApp
10
12
  class App < Hanami::App
11
- config.logger.stream = File.new("/dev/null", "w")
13
+ config.logger.stream = StringIO.new
12
14
  end
13
15
  end
14
16
  RUBY
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe "Container / Provider lifecycle", :app_integration do
4
+ let!(:slice) {
5
+ module TestApp
6
+ class App < Hanami::App
7
+ register_provider :connection do
8
+ prepare do
9
+ module ::TestApp
10
+ class Connection
11
+ def initialize
12
+ @connected = true
13
+ end
14
+
15
+ def disconnect
16
+ @connected = false
17
+ end
18
+
19
+ def connected?
20
+ @connected
21
+ end
22
+ end
23
+ end
24
+ end
25
+
26
+ start do
27
+ register("connection", TestApp::Connection.new)
28
+ end
29
+
30
+ stop do
31
+ container["connection"].disconnect
32
+ end
33
+ end
34
+ end
35
+ end
36
+
37
+ TestApp::App
38
+ }
39
+
40
+ before do
41
+ require "hanami/setup"
42
+ end
43
+
44
+ specify "individual providers can be prepared, started and stopped" do
45
+ expect { TestApp::Connection }.to raise_error NameError
46
+
47
+ slice.prepare :connection
48
+
49
+ expect(TestApp::Connection).to be
50
+ expect(slice.container.registered?("connection")).to be false
51
+
52
+ slice.start :connection
53
+
54
+ expect(slice.container.registered?("connection")).to be true
55
+ expect(slice["connection"]).to be_connected
56
+
57
+ slice.stop :connection
58
+
59
+ expect(slice["connection"]).not_to be_connected
60
+ end
61
+ end
@@ -0,0 +1,44 @@
1
+ RSpec.describe "Container / Standard providers / Rack", :app_integration do
2
+ specify "Rack provider is loaded when rack is bundled" do
3
+ with_tmp_directory(Dir.mktmpdir) do
4
+ write "config/app.rb", <<~RUBY
5
+ require "hanami"
6
+
7
+ module TestApp
8
+ class App < Hanami::App
9
+ end
10
+ end
11
+ RUBY
12
+
13
+ write "slices/main/.keep", ""
14
+
15
+ require "hanami/prepare"
16
+
17
+ expect(Hanami.app["rack.monitor"]).to be_a_kind_of(Dry::Monitor::Rack::Middleware)
18
+ expect(Main::Slice["rack.monitor"]).to be_a_kind_of(Dry::Monitor::Rack::Middleware)
19
+ end
20
+ end
21
+
22
+ specify "Rack provider is not loaded when rack is not bundled" do
23
+ allow(Hanami).to receive(:bundled?).and_call_original
24
+ allow(Hanami).to receive(:bundled?).with("rack").and_return false
25
+
26
+ with_tmp_directory(Dir.mktmpdir) do
27
+ write "config/app.rb", <<~RUBY
28
+ require "hanami"
29
+
30
+ module TestApp
31
+ class App < Hanami::App
32
+ end
33
+ end
34
+ RUBY
35
+
36
+ write "slices/main/.keep", ""
37
+
38
+ require "hanami/prepare"
39
+
40
+ expect(Hanami.app.key?("rack.monitor")).to be false
41
+ expect(Main::Slice.key?("rack.monitor")).to be false
42
+ end
43
+ end
44
+ end
@@ -1,4 +1,4 @@
1
- RSpec.describe "Container / Standard bootable components", :app_integration do
1
+ RSpec.describe "Container / Standard providers", :app_integration do
2
2
  specify "Standard components are available on booted container" do
3
3
  with_tmp_directory(Dir.mktmpdir) do
4
4
  write "config/app.rb", <<~RUBY
@@ -15,7 +15,7 @@ RSpec.describe "Container / Standard bootable components", :app_integration do
15
15
 
16
16
  expect(Hanami.app.key?(:settings)).to be false
17
17
  expect(Hanami.app["inflector"]).to eql Hanami.app.inflector
18
- expect(Hanami.app["logger"]).to be_a_kind_of(Hanami::Logger)
18
+ expect(Hanami.app["logger"]).to be_a_kind_of(Dry::Logger::Dispatcher)
19
19
  expect(Hanami.app["rack.monitor"]).to be_a_kind_of(Dry::Monitor::Rack::Middleware)
20
20
  end
21
21
  end
@@ -36,7 +36,7 @@ RSpec.describe "Container / Standard bootable components", :app_integration do
36
36
 
37
37
  expect(Hanami.app.key?(:settings)).to be false
38
38
  expect(Hanami.app["inflector"]).to eql Hanami.app.inflector
39
- expect(Hanami.app["logger"]).to be_a_kind_of(Hanami::Logger)
39
+ expect(Hanami.app["logger"]).to be_a_kind_of(Dry::Logger::Dispatcher)
40
40
  expect(Hanami.app["rack.monitor"]).to be_a_kind_of(Dry::Monitor::Rack::Middleware)
41
41
  end
42
42
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "rack/test"
4
+ require "stringio"
4
5
 
5
6
  RSpec.describe "Hanami web app", :app_integration do
6
7
  include Rack::Test::Methods
@@ -18,6 +19,7 @@ RSpec.describe "Hanami web app", :app_integration do
18
19
  module TestApp
19
20
  class App < Hanami::App
20
21
  config.middleware.use :body_parser, :json
22
+ config.logger.stream = StringIO.new
21
23
  end
22
24
  end
23
25
  RUBY
@@ -66,6 +68,7 @@ RSpec.describe "Hanami web app", :app_integration do
66
68
  class App < Hanami::App
67
69
  config.actions.formats["application/json+scim"] = :json
68
70
  config.middleware.use :body_parser, [json: "application/json+scim"]
71
+ config.logger.stream = StringIO.new
69
72
  end
70
73
  end
71
74
  RUBY