hanami 2.1.0.beta1 → 2.1.0.beta2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +27 -4
  3. data/README.md +1 -1
  4. data/lib/hanami/app.rb +5 -0
  5. data/lib/hanami/config/actions.rb +4 -7
  6. data/lib/hanami/config/assets.rb +84 -0
  7. data/lib/hanami/config/null_config.rb +3 -0
  8. data/lib/hanami/config.rb +17 -5
  9. data/lib/hanami/extensions/action.rb +4 -2
  10. data/lib/hanami/extensions/view/standard_helpers.rb +4 -0
  11. data/lib/hanami/helpers/assets_helper.rb +772 -0
  12. data/lib/hanami/middleware/assets.rb +21 -0
  13. data/lib/hanami/middleware/render_errors.rb +4 -7
  14. data/lib/hanami/providers/assets.rb +44 -0
  15. data/lib/hanami/rake_tasks.rb +19 -18
  16. data/lib/hanami/settings.rb +1 -1
  17. data/lib/hanami/slice.rb +25 -4
  18. data/lib/hanami/version.rb +1 -1
  19. data/lib/hanami.rb +2 -2
  20. data/spec/integration/assets/assets_spec.rb +101 -0
  21. data/spec/integration/assets/serve_static_assets_spec.rb +152 -0
  22. data/spec/integration/logging/exception_logging_spec.rb +115 -0
  23. data/spec/integration/logging/notifications_spec.rb +68 -0
  24. data/spec/integration/logging/request_logging_spec.rb +128 -0
  25. data/spec/integration/rack_app/middleware_spec.rb +4 -4
  26. data/spec/integration/rack_app/rack_app_spec.rb +0 -221
  27. data/spec/integration/rake_tasks_spec.rb +107 -0
  28. data/spec/integration/view/context/assets_spec.rb +3 -9
  29. data/spec/integration/web/render_detailed_errors_spec.rb +17 -0
  30. data/spec/integration/web/render_errors_spec.rb +6 -4
  31. data/spec/support/app_integration.rb +46 -2
  32. data/spec/unit/hanami/config/actions/content_security_policy_spec.rb +24 -36
  33. data/spec/unit/hanami/config/actions/csrf_protection_spec.rb +4 -3
  34. data/spec/unit/hanami/config/actions/default_values_spec.rb +3 -2
  35. data/spec/unit/hanami/env_spec.rb +11 -25
  36. data/spec/unit/hanami/helpers/assets_helper/asset_url_spec.rb +109 -0
  37. data/spec/unit/hanami/helpers/assets_helper/audio_spec.rb +136 -0
  38. data/spec/unit/hanami/helpers/assets_helper/favicon_spec.rb +91 -0
  39. data/spec/unit/hanami/helpers/assets_helper/image_spec.rb +96 -0
  40. data/spec/unit/hanami/helpers/assets_helper/javascript_spec.rb +147 -0
  41. data/spec/unit/hanami/helpers/assets_helper/stylesheet_spec.rb +130 -0
  42. data/spec/unit/hanami/helpers/assets_helper/video_spec.rb +136 -0
  43. data/spec/unit/hanami/version_spec.rb +1 -1
  44. metadata +32 -4
  45. data/lib/hanami/assets/app_config.rb +0 -61
  46. data/lib/hanami/assets/config.rb +0 -53
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rack/static"
4
+
5
+ module Hanami
6
+ module Middleware
7
+ class Assets < Rack::Static
8
+ def initialize(app, options = {}, config: Hanami.app.config)
9
+ root = config.actions.public_directory
10
+ urls = [config.assets.path_prefix]
11
+
12
+ defaults = {
13
+ root: root,
14
+ urls: urls
15
+ }
16
+
17
+ super(app, defaults.merge(options))
18
+ end
19
+ end
20
+ end
21
+ end
@@ -56,18 +56,15 @@ module Hanami
56
56
  def call(env)
57
57
  @app.call(env)
58
58
  rescue Exception => exception
59
- request = Rack::Request.new(env)
59
+ raise unless @config.render_errors
60
60
 
61
- if @config.render_errors
62
- render_exception(request, exception)
63
- else
64
- raise exception
65
- end
61
+ render_exception(env, exception)
66
62
  end
67
63
 
68
64
  private
69
65
 
70
- def render_exception(request, exception)
66
+ def render_exception(env, exception)
67
+ request = Rack::Request.new(env)
71
68
  renderable = RenderableException.new(exception, responses: @config.render_error_responses)
72
69
 
73
70
  status = renderable.status_code
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hanami
4
+ # @api private
5
+ module Providers
6
+ # Provider source to register routes helper component in Hanami slices.
7
+ #
8
+ # @see Hanami::Slice::RoutesHelper
9
+ #
10
+ # @api private
11
+ # @since 2.0.0
12
+ class Assets < Dry::System::Provider::Source
13
+ # @api private
14
+ def self.for_slice(slice)
15
+ Class.new(self) do |klass|
16
+ klass.instance_variable_set(:@slice, slice)
17
+ end
18
+ end
19
+
20
+ # @api private
21
+ def self.slice
22
+ @slice || Hanami.app
23
+ end
24
+
25
+ # @api private
26
+ def prepare
27
+ require "hanami/assets"
28
+ end
29
+
30
+ # @api private
31
+ def start
32
+ assets = Hanami::Assets.new(config: slice.config.assets)
33
+
34
+ register(:assets, assets)
35
+ end
36
+
37
+ private
38
+
39
+ def slice
40
+ self.class.slice
41
+ end
42
+ end
43
+ end
44
+ end
@@ -10,7 +10,7 @@ Hanami::CLI::RakeTasks.register_tasks do
10
10
 
11
11
  # Ruby ecosystem compatibility
12
12
  #
13
- # Most of the SaaS automatic tasks are designed after Ruby on Rails.
13
+ # Most of the hosting SaaS automatic tasks are designed after Ruby on Rails.
14
14
  # They expect the following Rake tasks to be present:
15
15
  #
16
16
  # * db:migrate
@@ -20,31 +20,32 @@ Hanami::CLI::RakeTasks.register_tasks do
20
20
  #
21
21
  # ===
22
22
  #
23
- # These Rake tasks aren't listed when someone runs `rake -T`, because we
24
- # want to encourage developers to use `hanami` commands.
23
+ # These Rake tasks are **NOT** listed when someone runs `rake -T`, because we
24
+ # want to encourage developers to use `hanami` CLI commands.
25
25
  #
26
- # In order to migrate the database or precompile assets a developer should
27
- # use:
26
+ # In order to migrate the database or compile assets a developer should use:
28
27
  #
29
28
  # * hanami db migrate
30
- # * hanami assets precompile
29
+ # * hanami assets compile
31
30
  #
32
31
  # This is the preferred way to run Hanami command line tasks.
33
32
  # Please use them when you're in control of your deployment environment.
34
33
  #
35
34
  # If you're not in control and your deployment requires these "standard"
36
35
  # Rake tasks, they are here to solve this only specific problem.
37
- namespace :db do
38
- task :migrate do
39
- # TODO(@jodosha): Enable when we'll integrate with ROM
40
- # run_hanami_command("db migrate")
41
- end
42
- end
43
-
44
- namespace :assets do
45
- task :precompile do
46
- # TODO(@jodosha): Enable when we'll integrate with hanami-assets
47
- # run_hanami_command("assets precompile")
36
+ #
37
+ # namespace :db do
38
+ # task :migrate do
39
+ # # TODO(@jodosha): Enable when we'll integrate with ROM
40
+ # # run_hanami_command("db migrate")
41
+ # end
42
+ # end
43
+
44
+ if Hanami.bundled?("hanami-assets")
45
+ namespace :assets do
46
+ task :precompile do
47
+ run_hanami_command("assets compile")
48
+ end
48
49
  end
49
50
  end
50
51
 
@@ -53,7 +54,7 @@ Hanami::CLI::RakeTasks.register_tasks do
53
54
  @_hanami_cli_bundler = Hanami::CLI::Bundler.new
54
55
 
55
56
  def run_hanami_command(command)
56
- @_hanami_cli_bundler.exec(command)
57
+ @_hanami_cli_bundler.hanami_exec(command)
57
58
  end
58
59
  end
59
60
 
@@ -45,7 +45,7 @@ module Hanami
45
45
  #
46
46
  # Setting values are loaded from a configurable store, which defaults to
47
47
  # {Hanami::Settings::EnvStore}, which fetches the values from equivalent upper-cased keys in
48
- # `ENV`. You can configue an alternative store via {Hanami::Config#settings_store}. Setting stores
48
+ # `ENV`. You can configure an alternative store via {Hanami::Config#settings_store}. Setting stores
49
49
  # must implement a `#fetch` method with the same signature as `Hash#fetch`.
50
50
  #
51
51
  # [dry-c]: https://dry-rb.org/gems/dry-configurable/
data/lib/hanami/slice.rb CHANGED
@@ -952,12 +952,21 @@ module Hanami
952
952
  config = self.config
953
953
  rack_monitor = self["rack.monitor"]
954
954
 
955
+ render_errors = render_errors?
956
+ render_detailed_errors = render_detailed_errors?
957
+
958
+ error_handlers = {}.tap do |hsh|
959
+ if render_errors || render_detailed_errors
960
+ hsh[:not_allowed] = ROUTER_NOT_ALLOWED_HANDLER
961
+ hsh[:not_found] = ROUTER_NOT_FOUND_HANDLER
962
+ end
963
+ end
964
+
955
965
  Slice::Router.new(
956
966
  inspector: inspector,
957
967
  routes: routes,
958
968
  resolver: config.router.resolver.new(slice: self),
959
- not_allowed: ROUTER_NOT_ALLOWED_HANDLER,
960
- not_found: ROUTER_NOT_FOUND_HANDLER,
969
+ **error_handlers,
961
970
  **config.router.options
962
971
  ) do
963
972
  use(rack_monitor)
@@ -968,19 +977,31 @@ module Hanami
968
977
  Hanami::Middleware::PublicErrorsApp.new(slice.root.join("public"))
969
978
  )
970
979
 
971
- if config.render_detailed_errors && Hanami.bundled?("hanami-webconsole")
980
+ if render_detailed_errors
972
981
  require "hanami/webconsole"
973
- use(Hanami::Webconsole::Middleware)
982
+ use(Hanami::Webconsole::Middleware, config)
974
983
  end
975
984
 
976
985
  if Hanami.bundled?("hanami-controller") && config.actions.sessions.enabled?
977
986
  use(*config.actions.sessions.middleware)
978
987
  end
979
988
 
989
+ if Hanami.bundled?("hanami-assets") && config.assets.serve
990
+ use(Hanami::Middleware::Assets)
991
+ end
992
+
980
993
  middleware_stack.update(config.middleware_stack)
981
994
  end
982
995
  end
983
996
 
997
+ def render_errors?
998
+ config.render_errors
999
+ end
1000
+
1001
+ def render_detailed_errors?
1002
+ config.render_detailed_errors && Hanami.bundled?("hanami-webconsole")
1003
+ end
1004
+
984
1005
  ROUTER_NOT_ALLOWED_HANDLER = -> env, allowed_http_methods {
985
1006
  raise Hanami::Router::NotAllowedError.new(env, allowed_http_methods)
986
1007
  }.freeze
@@ -7,7 +7,7 @@ module Hanami
7
7
  # @api private
8
8
  module Version
9
9
  # @api public
10
- VERSION = "2.1.0.beta1"
10
+ VERSION = "2.1.0.beta2.1"
11
11
 
12
12
  # @since 0.9.0
13
13
  # @api private
data/lib/hanami.rb CHANGED
@@ -144,8 +144,8 @@ module Hanami
144
144
  #
145
145
  # @api public
146
146
  # @since 2.0.0
147
- def self.env
148
- ENV.fetch("HANAMI_ENV") { ENV.fetch("RACK_ENV", "development") }.to_sym
147
+ def self.env(e: ENV)
148
+ e.fetch("HANAMI_ENV") { e.fetch("RACK_ENV", "development") }.to_sym
149
149
  end
150
150
 
151
151
  # Returns true if {.env} matches any of the given names
@@ -0,0 +1,101 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rack/test"
4
+ require "stringio"
5
+
6
+ RSpec.describe "Assets", :app_integration do
7
+ include Rack::Test::Methods
8
+ let(:app) { Hanami.app }
9
+ let(:root) { make_tmp_directory }
10
+
11
+ before do
12
+ with_directory(root) do
13
+ write "config/app.rb", <<~RUBY
14
+ module TestApp
15
+ class App < Hanami::App
16
+ config.logger.stream = StringIO.new
17
+ end
18
+ end
19
+ RUBY
20
+
21
+ write "config/routes.rb", <<~RUBY
22
+ module TestApp
23
+ class Routes < Hanami::Routes
24
+ get "posts/:id/edit", to: "posts.edit"
25
+ put "posts/:id", to: "posts.update"
26
+ end
27
+ end
28
+ RUBY
29
+
30
+ write "app/action.rb", <<~RUBY
31
+ # auto_register: false
32
+
33
+ require "hanami/action"
34
+
35
+ module TestApp
36
+ class Action < Hanami::Action
37
+ end
38
+ end
39
+ RUBY
40
+
41
+ write "app/view.rb", <<~RUBY
42
+ # auto_register: false
43
+
44
+ require "hanami/view"
45
+
46
+ module TestApp
47
+ class View < Hanami::View
48
+ config.layout = nil
49
+ end
50
+ end
51
+ RUBY
52
+
53
+ write "app/views/posts/show.rb", <<~RUBY
54
+ module TestApp
55
+ module Views
56
+ module Posts
57
+ class Show < TestApp::View
58
+ end
59
+ end
60
+ end
61
+ end
62
+ RUBY
63
+
64
+ write "app/templates/posts/show.html.erb", <<~ERB
65
+ <%= stylesheet_link_tag("app") %>
66
+ <%= css("app") %>
67
+ <%= javascript_tag("app") %>
68
+ <%= js("app") %>
69
+ ERB
70
+
71
+ write "app/assets/js/app.ts", <<~TS
72
+ import "../css/app.css";
73
+
74
+ console.log("Hello from index.ts");
75
+ TS
76
+
77
+ write "app/assets/css/app.css", <<~CSS
78
+ .btn {
79
+ background: #f00;
80
+ }
81
+ CSS
82
+
83
+ before_prepare if respond_to?(:before_prepare)
84
+ require "hanami/prepare"
85
+ end
86
+ end
87
+
88
+ specify "assets are available in helpers and in `assets` component" do
89
+ compile_assets!
90
+
91
+ output = Hanami.app["views.posts.show"].call.to_s
92
+
93
+ expect(output).to match(%r{<link href="/assets/app-[A-Z0-9]{8}.css" type="text/css" rel="stylesheet">})
94
+ expect(output).to match(%r{<script src="/assets/app-[A-Z0-9]{8}.js" type="text/javascript"></script>})
95
+
96
+ assets = Hanami.app["assets"]
97
+
98
+ expect(assets["app.css"].to_s).to match(%r{/assets/app-[A-Z0-9]{8}.css})
99
+ expect(assets["app.js"].to_s).to match(%r{/assets/app-[A-Z0-9]{8}.js})
100
+ end
101
+ end
@@ -0,0 +1,152 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rack/test"
4
+ require "stringio"
5
+
6
+ RSpec.describe "Serve Static Assets", :app_integration do
7
+ include Rack::Test::Methods
8
+ let(:app) { Hanami.app }
9
+ let(:root) { make_tmp_directory }
10
+ let!(:env) { ENV.to_h }
11
+
12
+ before do
13
+ with_directory(root) do
14
+ write "config/app.rb", <<~RUBY
15
+ module TestApp
16
+ class App < Hanami::App
17
+ config.logger.stream = StringIO.new
18
+ end
19
+ end
20
+ RUBY
21
+
22
+ write "config/routes.rb", <<~RUBY
23
+ module TestApp
24
+ class Routes < Hanami::Routes
25
+ root to: ->(env) { [200, {}, ["Hello from root"]] }
26
+ end
27
+ end
28
+ RUBY
29
+
30
+ write "public/assets/app.js", <<~JS
31
+ console.log("Hello from app.js");
32
+ JS
33
+ end
34
+ end
35
+
36
+ after do
37
+ ENV.replace(env)
38
+ end
39
+
40
+ context "with default configuration" do
41
+ before do
42
+ with_directory(root) do
43
+ require "hanami/prepare"
44
+ end
45
+ end
46
+
47
+ it "serves static assets" do
48
+ get "/assets/app.js"
49
+
50
+ expect(last_response.status).to eq(200)
51
+ expect(last_response.body).to match(/Hello/)
52
+ end
53
+
54
+ it "returns 404 for missing asset" do
55
+ get "/assets/missing.js"
56
+
57
+ expect(last_response.status).to eq(404)
58
+ expect(last_response.body).to match(/Not Found/i)
59
+ end
60
+
61
+ it "doesn't escape from root directory" do
62
+ get "/assets/../../config/app.rb"
63
+
64
+ expect(last_response.status).to eq(404)
65
+ expect(last_response.body).to match(/Not Found/i)
66
+ end
67
+ end
68
+
69
+ context "when configuration is set to false" do
70
+ before do
71
+ with_directory(root) do
72
+ write "config/app.rb", <<~RUBY
73
+ module TestApp
74
+ class App < Hanami::App
75
+ config.logger.stream = StringIO.new
76
+ config.assets.serve = false
77
+ end
78
+ end
79
+ RUBY
80
+
81
+ require "hanami/boot"
82
+ end
83
+ end
84
+
85
+ it "doesn't serve static assets" do
86
+ get "/assets/app.js"
87
+
88
+ expect(last_response.status).to eq(404)
89
+ end
90
+ end
91
+
92
+ context "when env var is set to true" do
93
+ before do
94
+ with_directory(root) do
95
+ ENV["HANAMI_SERVE_ASSETS"] = "true"
96
+ require "hanami/boot"
97
+ end
98
+ end
99
+
100
+ it "serves static assets" do
101
+ get "/assets/app.js"
102
+
103
+ expect(last_response.status).to eq(200)
104
+ end
105
+ end
106
+
107
+ context "when env var is set to false" do
108
+ before do
109
+ with_directory(root) do
110
+ ENV["HANAMI_SERVE_ASSETS"] = "false"
111
+ require "hanami/boot"
112
+ end
113
+ end
114
+
115
+ it "doesn't serve static assets" do
116
+ get "/assets/app.js"
117
+
118
+ expect(last_response.status).to eq(404)
119
+ end
120
+ end
121
+
122
+ context "when Hanami.env is not :development or :test" do
123
+ before do
124
+ with_directory(root) do
125
+ ENV["HANAMI_ENV"] = "production"
126
+ require "hanami/boot"
127
+ end
128
+ end
129
+
130
+ it "doesn't serve static assets" do
131
+ get "/assets/app.js"
132
+
133
+ expect(last_response.status).to eq(404)
134
+ end
135
+ end
136
+
137
+ context "when Hanami.env is not :development or :test, but env var is set to true" do
138
+ before do
139
+ with_directory(root) do
140
+ ENV["HANAMI_ENV"] = "production"
141
+ ENV["HANAMI_SERVE_ASSETS"] = "true"
142
+ require "hanami/boot"
143
+ end
144
+ end
145
+
146
+ it "serves static assets" do
147
+ get "/assets/app.js"
148
+
149
+ expect(last_response.status).to eq(200)
150
+ end
151
+ end
152
+ end
@@ -0,0 +1,115 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rack/test"
4
+ require "stringio"
5
+
6
+ RSpec.describe "Logging / Exception logging", :app_integration do
7
+ include Rack::Test::Methods
8
+
9
+ let(:app) { Hanami.app }
10
+
11
+ let(:logger_stream) { StringIO.new }
12
+
13
+ def configure_logger
14
+ Hanami.app.config.logger.stream = logger_stream
15
+ end
16
+
17
+ def logs
18
+ @logs ||= (logger_stream.rewind and logger_stream.read)
19
+ end
20
+
21
+ before do
22
+ with_directory(make_tmp_directory) do
23
+ write "config/app.rb", <<~RUBY
24
+ module TestApp
25
+ class App < Hanami::App
26
+ # Disable framework-level error rendering so we can test the raw action behavior
27
+ config.render_errors = false
28
+ config.render_detailed_errors = false
29
+ end
30
+ end
31
+ RUBY
32
+
33
+ write "config/routes.rb", <<~RUBY
34
+ module TestApp
35
+ class Routes < Hanami::Routes
36
+ root to: "test"
37
+ end
38
+ end
39
+ RUBY
40
+
41
+ require "hanami/setup"
42
+ configure_logger
43
+
44
+ before_prepare if respond_to?(:before_prepare)
45
+ require "hanami/prepare"
46
+ end
47
+ end
48
+
49
+ describe "unhandled exceptions" do
50
+ def before_prepare
51
+ write "app/actions/test.rb", <<~RUBY
52
+ module TestApp
53
+ module Actions
54
+ class Test < Hanami::Action
55
+ UnhandledError = Class.new(StandardError)
56
+
57
+ def handle(request, response)
58
+ raise UnhandledError, "unhandled"
59
+ end
60
+ end
61
+ end
62
+ end
63
+ RUBY
64
+ end
65
+
66
+ it "logs a 500 error and full exception details when an exception is raised" do
67
+ # Make the request with a rescue so the raised exception doesn't crash the tests
68
+ begin
69
+ get "/"
70
+ rescue TestApp::Actions::Test::UnhandledError # rubocop:disable Lint/SuppressedException
71
+ end
72
+
73
+ expect(logs.lines.length).to be > 10
74
+ expect(logs).to match %r{GET 500 \d+(µs|ms) 127.0.0.1 /}
75
+ expect(logs).to include("unhandled (TestApp::Actions::Test::UnhandledError)")
76
+ expect(logs).to include("app/actions/test.rb:7:in `handle'")
77
+ end
78
+
79
+ it "re-raises the exception" do
80
+ expect { get "/" }.to raise_error(TestApp::Actions::Test::UnhandledError)
81
+ end
82
+ end
83
+
84
+ describe "errors handled by handle_exception" do
85
+ def before_prepare
86
+ write "app/actions/test.rb", <<~RUBY
87
+ module TestApp
88
+ module Actions
89
+ class Test < Hanami::Action
90
+ NotFoundError = Class.new(StandardError)
91
+
92
+ handle_exception NotFoundError => :handle_not_found_error
93
+
94
+ def handle(request, response)
95
+ raise NotFoundError
96
+ end
97
+
98
+ private
99
+
100
+ def handle_not_found_error(request, response, exception)
101
+ halt 404
102
+ end
103
+ end
104
+ end
105
+ end
106
+ RUBY
107
+ end
108
+
109
+ it "does not log an error" do
110
+ get "/"
111
+
112
+ expect(logs).to match %r{GET 404 \d+(µs|ms) 127.0.0.1 /}
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rack/test"
4
+
5
+ RSpec.describe "Logging / Notifications", :app_integration do
6
+ include Rack::Test::Methods
7
+
8
+ let(:app) { Hanami.app }
9
+
10
+ specify "Request logging continues even when notifications bus has already been used" do
11
+ dir = Dir.mktmpdir
12
+
13
+ with_tmp_directory(dir) do
14
+ write "config/app.rb", <<~RUBY
15
+ require "hanami"
16
+
17
+ module TestApp
18
+ class App < Hanami::App
19
+ config.actions.format :json
20
+ config.logger.options = {colorize: true}
21
+ config.logger.stream = config.root.join("test.log")
22
+ end
23
+ end
24
+ RUBY
25
+
26
+ write "config/routes.rb", <<~RUBY
27
+ module TestApp
28
+ class Routes < Hanami::Routes
29
+ post "/users", to: "users.create"
30
+ end
31
+ end
32
+ RUBY
33
+
34
+ write "app/actions/users/create.rb", <<~RUBY
35
+ module TestApp
36
+ module Actions
37
+ module Users
38
+ class Create < Hanami::Action
39
+ def handle(req, resp)
40
+ resp.body = req.params.to_h.keys
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ RUBY
47
+
48
+ require "hanami/prepare"
49
+
50
+ # Simulate any component interacting with the notifications bus such that it creates its
51
+ # internal bus with a duplicate copy of all currently registered events. This means that the
52
+ # class-level Dry::Monitor::Notification events implicitly registered by the
53
+ # Dry::Monitor::Rack::Middleware activated via the rack provider are ignored, unless our
54
+ # provider explicitly re-registers them on _instance_ of the notifications bus.
55
+ #
56
+ # See Hanami::Providers::Rack for more detail.
57
+ Hanami.app["notifications"].instrument(:sql)
58
+
59
+ logs = -> { Pathname(dir).join("test.log").realpath.read }
60
+
61
+ post "/users", JSON.generate(name: "jane", password: "secret"), {"CONTENT_TYPE" => "application/json"}
62
+ expect(logs.()).to match %r{POST 200 \d+(µs|ms) 127.0.0.1 /}
63
+ end
64
+ end
65
+ end
66
+
67
+
68
+