hanami 2.3.2 → 3.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +55 -19
- data/LICENSE +20 -0
- data/README.md +18 -35
- data/hanami.gemspec +36 -37
- data/lib/hanami/config/db.rb +2 -0
- data/lib/hanami/config/i18n.rb +138 -0
- data/lib/hanami/config/logger.rb +15 -7
- data/lib/hanami/config/null_config.rb +1 -1
- data/lib/hanami/config/views.rb +17 -0
- data/lib/hanami/config.rb +66 -22
- data/lib/hanami/errors.rb +6 -0
- data/lib/hanami/extensions/action/slice_configured_action.rb +1 -1
- data/lib/hanami/extensions/action.rb +2 -2
- data/lib/hanami/extensions/mailer/slice_configured_mailer.rb +120 -0
- data/lib/hanami/extensions/mailer.rb +28 -0
- data/lib/hanami/extensions/operation/slice_configured_db_operation.rb +2 -0
- data/lib/hanami/extensions/view/context.rb +26 -4
- data/lib/hanami/extensions/view/part.rb +2 -0
- data/lib/hanami/extensions/view/slice_configured_context.rb +7 -0
- data/lib/hanami/extensions/view/slice_configured_part.rb +2 -0
- data/lib/hanami/extensions/view/slice_configured_view.rb +8 -8
- data/lib/hanami/extensions/view/standard_helpers.rb +4 -0
- data/lib/hanami/extensions.rb +6 -1
- data/lib/hanami/helpers/assets_helper.rb +0 -4
- data/lib/hanami/helpers/form_helper.rb +1 -1
- data/lib/hanami/helpers/i18n_helper.rb +176 -0
- data/lib/hanami/logger/rack_formatter.rb +73 -0
- data/lib/hanami/logger/sql_formatter.rb +80 -0
- data/lib/hanami/logger/sql_logger.rb +48 -0
- data/lib/hanami/middleware/render_errors.rb +2 -2
- data/lib/hanami/providers/db.rb +7 -2
- data/lib/hanami/providers/db_logging.rb +4 -7
- data/lib/hanami/providers/i18n/backend.rb +369 -0
- data/lib/hanami/providers/i18n/locale/en.yml +57 -0
- data/lib/hanami/providers/i18n.rb +114 -0
- data/lib/hanami/providers/mailers.rb +101 -0
- data/lib/hanami/routes.rb +1 -0
- data/lib/hanami/settings/composite_store.rb +53 -0
- data/lib/hanami/settings.rb +4 -4
- data/lib/hanami/slice/router.rb +15 -10
- data/lib/hanami/slice.rb +71 -11
- data/lib/hanami/slice_registrar.rb +2 -2
- data/lib/hanami/universal_logger.rb +250 -0
- data/lib/hanami/version.rb +1 -1
- data/lib/hanami/web/rack_logger.rb +2 -80
- data/lib/hanami/web/welcome.html.erb +443 -58
- data/lib/hanami.rb +4 -2
- metadata +28 -276
- data/CODE_OF_CONDUCT.md +0 -74
- data/FEATURES.md +0 -269
- data/LICENSE.md +0 -22
- data/spec/integration/action/cookies_spec.rb +0 -58
- data/spec/integration/action/csrf_protection_spec.rb +0 -54
- data/spec/integration/action/format_config_spec.rb +0 -129
- data/spec/integration/action/routes_spec.rb +0 -71
- data/spec/integration/action/sessions_spec.rb +0 -50
- data/spec/integration/action/slice_configuration_spec.rb +0 -284
- data/spec/integration/action/view_rendering/automatic_rendering_spec.rb +0 -247
- data/spec/integration/action/view_rendering/paired_view_inference_spec.rb +0 -115
- data/spec/integration/action/view_rendering/view_context_spec.rb +0 -221
- data/spec/integration/action/view_rendering_spec.rb +0 -89
- data/spec/integration/assets/assets_spec.rb +0 -155
- data/spec/integration/assets/cross_slice_assets_helpers_spec.rb +0 -129
- data/spec/integration/assets/serve_static_assets_spec.rb +0 -152
- data/spec/integration/code_loading/loading_from_app_spec.rb +0 -152
- data/spec/integration/code_loading/loading_from_lib_spec.rb +0 -242
- data/spec/integration/code_loading/loading_from_slice_spec.rb +0 -165
- data/spec/integration/container/application_routes_helper_spec.rb +0 -48
- data/spec/integration/container/auto_injection_spec.rb +0 -53
- data/spec/integration/container/auto_registration_spec.rb +0 -86
- data/spec/integration/container/autoloader_spec.rb +0 -82
- data/spec/integration/container/imports_spec.rb +0 -253
- data/spec/integration/container/prepare_container_spec.rb +0 -125
- data/spec/integration/container/provider_environment_spec.rb +0 -52
- data/spec/integration/container/provider_lifecycle_spec.rb +0 -61
- data/spec/integration/container/shutdown_spec.rb +0 -91
- data/spec/integration/container/standard_providers/rack_provider_spec.rb +0 -44
- data/spec/integration/container/standard_providers_spec.rb +0 -124
- data/spec/integration/db/auto_registration_spec.rb +0 -39
- data/spec/integration/db/commands_spec.rb +0 -80
- data/spec/integration/db/db_inflector_spec.rb +0 -57
- data/spec/integration/db/db_slices_spec.rb +0 -398
- data/spec/integration/db/db_spec.rb +0 -245
- data/spec/integration/db/gateways_spec.rb +0 -361
- data/spec/integration/db/logging_spec.rb +0 -301
- data/spec/integration/db/mappers_spec.rb +0 -84
- data/spec/integration/db/provider_config_spec.rb +0 -88
- data/spec/integration/db/provider_spec.rb +0 -35
- data/spec/integration/db/relations_spec.rb +0 -60
- data/spec/integration/db/repo_spec.rb +0 -300
- data/spec/integration/db/slices_importing_from_parent.rb +0 -130
- data/spec/integration/dotenv_loading_spec.rb +0 -138
- data/spec/integration/logging/exception_logging_spec.rb +0 -120
- data/spec/integration/logging/notifications_spec.rb +0 -68
- data/spec/integration/logging/request_logging_spec.rb +0 -202
- data/spec/integration/operations/extension_spec.rb +0 -122
- data/spec/integration/rack_app/body_parser_spec.rb +0 -108
- data/spec/integration/rack_app/method_override_spec.rb +0 -97
- data/spec/integration/rack_app/middleware_spec.rb +0 -720
- data/spec/integration/rack_app/non_booted_rack_app_spec.rb +0 -104
- data/spec/integration/rack_app/rack_app_spec.rb +0 -442
- data/spec/integration/rake_tasks_spec.rb +0 -107
- data/spec/integration/router/resource_routes_spec.rb +0 -281
- data/spec/integration/settings/access_in_slice_class_body_spec.rb +0 -83
- data/spec/integration/settings/access_to_constants_spec.rb +0 -46
- data/spec/integration/settings/loading_from_env_spec.rb +0 -188
- data/spec/integration/settings/settings_component_loading_spec.rb +0 -113
- data/spec/integration/settings/slice_registration_spec.rb +0 -145
- data/spec/integration/settings/using_types_spec.rb +0 -80
- data/spec/integration/setup_spec.rb +0 -165
- data/spec/integration/slices/external_slice_spec.rb +0 -91
- data/spec/integration/slices/slice_configuration_spec.rb +0 -42
- data/spec/integration/slices/slice_loading_spec.rb +0 -171
- data/spec/integration/slices/slice_registrations_spec.rb +0 -80
- data/spec/integration/slices/slice_routing_spec.rb +0 -219
- data/spec/integration/slices_spec.rb +0 -471
- data/spec/integration/view/config/default_context_spec.rb +0 -149
- data/spec/integration/view/config/inflector_spec.rb +0 -57
- data/spec/integration/view/config/part_class_spec.rb +0 -147
- data/spec/integration/view/config/part_namespace_spec.rb +0 -103
- data/spec/integration/view/config/paths_spec.rb +0 -119
- data/spec/integration/view/config/scope_class_spec.rb +0 -147
- data/spec/integration/view/config/scope_namespace_spec.rb +0 -103
- data/spec/integration/view/config/template_spec.rb +0 -38
- data/spec/integration/view/context/assets_spec.rb +0 -79
- data/spec/integration/view/context/inflector_spec.rb +0 -40
- data/spec/integration/view/context/request_spec.rb +0 -57
- data/spec/integration/view/context/routes_spec.rb +0 -84
- data/spec/integration/view/helpers/form_helper_spec.rb +0 -174
- data/spec/integration/view/helpers/part_helpers_spec.rb +0 -124
- data/spec/integration/view/helpers/scope_helpers_spec.rb +0 -84
- data/spec/integration/view/helpers/user_defined_helpers/part_helpers_spec.rb +0 -162
- data/spec/integration/view/helpers/user_defined_helpers/scope_helpers_spec.rb +0 -119
- data/spec/integration/view/parts/default_rendering_spec.rb +0 -138
- data/spec/integration/view/slice_configuration_spec.rb +0 -289
- data/spec/integration/view/views_spec.rb +0 -103
- data/spec/integration/web/content_security_policy_nonce_spec.rb +0 -251
- data/spec/integration/web/render_detailed_errors_spec.rb +0 -107
- data/spec/integration/web/render_errors_spec.rb +0 -242
- data/spec/integration/web/welcome_view_spec.rb +0 -84
- data/spec/spec_helper.rb +0 -28
- data/spec/support/app_integration.rb +0 -157
- data/spec/support/coverage.rb +0 -1
- data/spec/support/matchers.rb +0 -32
- data/spec/support/rspec.rb +0 -27
- data/spec/unit/hanami/config/actions/content_security_policy_spec.rb +0 -96
- data/spec/unit/hanami/config/actions/cookies_spec.rb +0 -46
- data/spec/unit/hanami/config/actions/csrf_protection_spec.rb +0 -58
- data/spec/unit/hanami/config/actions/default_values_spec.rb +0 -43
- data/spec/unit/hanami/config/actions/sessions_spec.rb +0 -48
- data/spec/unit/hanami/config/actions_spec.rb +0 -52
- data/spec/unit/hanami/config/base_url_spec.rb +0 -25
- data/spec/unit/hanami/config/console_spec.rb +0 -22
- data/spec/unit/hanami/config/db_spec.rb +0 -38
- data/spec/unit/hanami/config/inflector_spec.rb +0 -35
- data/spec/unit/hanami/config/logger_spec.rb +0 -195
- data/spec/unit/hanami/config/render_detailed_errors_spec.rb +0 -25
- data/spec/unit/hanami/config/render_errors_spec.rb +0 -25
- data/spec/unit/hanami/config/router_spec.rb +0 -44
- data/spec/unit/hanami/config/slices_spec.rb +0 -34
- data/spec/unit/hanami/config/views_spec.rb +0 -80
- data/spec/unit/hanami/env_spec.rb +0 -37
- data/spec/unit/hanami/extensions/view/context_spec.rb +0 -59
- data/spec/unit/hanami/helpers/assets_helper/asset_url_spec.rb +0 -120
- data/spec/unit/hanami/helpers/assets_helper/audio_tag_spec.rb +0 -132
- data/spec/unit/hanami/helpers/assets_helper/favicon_tag_spec.rb +0 -87
- data/spec/unit/hanami/helpers/assets_helper/image_tag_spec.rb +0 -92
- data/spec/unit/hanami/helpers/assets_helper/javascript_tag_spec.rb +0 -143
- data/spec/unit/hanami/helpers/assets_helper/stylesheet_tag_spec.rb +0 -126
- data/spec/unit/hanami/helpers/assets_helper/video_tag_spec.rb +0 -136
- data/spec/unit/hanami/helpers/form_helper_spec.rb +0 -2857
- data/spec/unit/hanami/port_spec.rb +0 -117
- data/spec/unit/hanami/providers/db/config/default_config_spec.rb +0 -100
- data/spec/unit/hanami/providers/db/config/gateway_spec.rb +0 -73
- data/spec/unit/hanami/providers/db/config_spec.rb +0 -143
- data/spec/unit/hanami/router/errors/not_allowed_error_spec.rb +0 -27
- data/spec/unit/hanami/router/errors/not_found_error_spec.rb +0 -22
- data/spec/unit/hanami/settings/env_store_spec.rb +0 -52
- data/spec/unit/hanami/settings_spec.rb +0 -111
- data/spec/unit/hanami/slice_configurable_spec.rb +0 -141
- data/spec/unit/hanami/slice_name_spec.rb +0 -47
- data/spec/unit/hanami/slice_spec.rb +0 -99
- data/spec/unit/hanami/web/rack_logger_spec.rb +0 -99
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
# rubocop:disable Style/FetchEnvVar
|
|
4
|
-
|
|
5
|
-
RSpec.describe "Dotenv loading", :app_integration do
|
|
6
|
-
before do
|
|
7
|
-
@orig_env = ENV.to_h
|
|
8
|
-
allow(Hanami::Env).to receive(:loaded?).and_return(false)
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
after do
|
|
12
|
-
ENV.replace(@orig_env)
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
context "dotenv gem is available" do
|
|
16
|
-
before do
|
|
17
|
-
require "dotenv"
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
context "hanami env is development" do
|
|
21
|
-
it "loads .env.development.local, .env.local, .env.development and .env (in this order) into ENV", :aggregate_failures do
|
|
22
|
-
with_tmp_directory(Dir.mktmpdir) do
|
|
23
|
-
write "config/app.rb", <<~'RUBY'
|
|
24
|
-
require "hanami"
|
|
25
|
-
|
|
26
|
-
module TestApp
|
|
27
|
-
class App < Hanami::App
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
RUBY
|
|
31
|
-
|
|
32
|
-
write ".env.development.local", <<~'TEXT'
|
|
33
|
-
FROM_SPECIFIC_ENV_LOCAL="from .env.development.local"
|
|
34
|
-
TEXT
|
|
35
|
-
|
|
36
|
-
write ".env.local", <<~'TEXT'
|
|
37
|
-
FROM_BASE_LOCAL="from .env.local"
|
|
38
|
-
FROM_SPECIFIC_ENV_LOCAL=nope
|
|
39
|
-
TEXT
|
|
40
|
-
|
|
41
|
-
write ".env.development", <<~'TEXT'
|
|
42
|
-
FROM_SPECIFIC_ENV="from .env.development"
|
|
43
|
-
FROM_SPECIFIC_ENV_LOCAL=nope
|
|
44
|
-
FROM_BASE_LOCAL=nope
|
|
45
|
-
TEXT
|
|
46
|
-
|
|
47
|
-
write ".env", <<~'TEXT'
|
|
48
|
-
FROM_BASE="from .env"
|
|
49
|
-
FROM_SPECIFIC_ENV_LOCAL=nope
|
|
50
|
-
FROM_BASE_LOCAL=nope
|
|
51
|
-
FROM_SPECIFIC_ENV=nope
|
|
52
|
-
TEXT
|
|
53
|
-
|
|
54
|
-
ENV["HANAMI_ENV"] = "development"
|
|
55
|
-
|
|
56
|
-
require "hanami/setup"
|
|
57
|
-
|
|
58
|
-
expect(ENV["FROM_SPECIFIC_ENV_LOCAL"]).to eq "from .env.development.local"
|
|
59
|
-
expect(ENV["FROM_BASE_LOCAL"]).to eq "from .env.local"
|
|
60
|
-
expect(ENV["FROM_SPECIFIC_ENV"]).to eq "from .env.development"
|
|
61
|
-
expect(ENV["FROM_BASE"]).to eq "from .env"
|
|
62
|
-
end
|
|
63
|
-
end
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
context "hanami env is test" do
|
|
67
|
-
it "loads .env.development.local, .env.development and .env (in this order) into ENV", :aggregate_failures do
|
|
68
|
-
with_tmp_directory(Dir.mktmpdir) do
|
|
69
|
-
write "config/app.rb", <<~'RUBY'
|
|
70
|
-
require "hanami"
|
|
71
|
-
|
|
72
|
-
module TestApp
|
|
73
|
-
class App < Hanami::App
|
|
74
|
-
end
|
|
75
|
-
end
|
|
76
|
-
RUBY
|
|
77
|
-
|
|
78
|
-
write ".env.test.local", <<~'TEXT'
|
|
79
|
-
FROM_SPECIFIC_ENV_LOCAL="from .env.test.local"
|
|
80
|
-
TEXT
|
|
81
|
-
|
|
82
|
-
write ".env.local", <<~'TEXT'
|
|
83
|
-
FROM_BASE_LOCAL="from .env.local"
|
|
84
|
-
TEXT
|
|
85
|
-
|
|
86
|
-
write ".env.test", <<~'TEXT'
|
|
87
|
-
FROM_SPECIFIC_ENV="from .env.test"
|
|
88
|
-
FROM_SPECIFIC_ENV_LOCAL=nope
|
|
89
|
-
TEXT
|
|
90
|
-
|
|
91
|
-
write ".env", <<~'TEXT'
|
|
92
|
-
FROM_BASE="from .env"
|
|
93
|
-
FROM_SPECIFIC_ENV_LOCAL=nope
|
|
94
|
-
FROM_SPECIFIC_ENV=nope
|
|
95
|
-
TEXT
|
|
96
|
-
|
|
97
|
-
ENV["HANAMI_ENV"] = "test"
|
|
98
|
-
|
|
99
|
-
require "hanami/prepare"
|
|
100
|
-
|
|
101
|
-
expect(ENV["FROM_SPECIFIC_ENV_LOCAL"]).to eq "from .env.test.local"
|
|
102
|
-
expect(ENV["FROM_BASE_LOCAL"]).to be nil
|
|
103
|
-
expect(ENV["FROM_SPECIFIC_ENV"]).to eq "from .env.test"
|
|
104
|
-
expect(ENV["FROM_BASE"]).to eq "from .env"
|
|
105
|
-
end
|
|
106
|
-
end
|
|
107
|
-
end
|
|
108
|
-
end
|
|
109
|
-
|
|
110
|
-
context "dotenv gem is unavailable" do
|
|
111
|
-
before do
|
|
112
|
-
allow_any_instance_of(Object).to receive(:gem).and_call_original
|
|
113
|
-
allow_any_instance_of(Object).to receive(:gem).with("dotenv").and_raise(Gem::LoadError)
|
|
114
|
-
end
|
|
115
|
-
|
|
116
|
-
it "does not load from .env files" do
|
|
117
|
-
with_tmp_directory(Dir.mktmpdir) do
|
|
118
|
-
write "config/app.rb", <<~'RUBY'
|
|
119
|
-
require "hanami"
|
|
120
|
-
|
|
121
|
-
module TestApp
|
|
122
|
-
class App < Hanami::App
|
|
123
|
-
end
|
|
124
|
-
end
|
|
125
|
-
RUBY
|
|
126
|
-
|
|
127
|
-
write ".env", <<~'TEXT'
|
|
128
|
-
FOO=bar
|
|
129
|
-
TEXT
|
|
130
|
-
|
|
131
|
-
expect { require "hanami/prepare" }.not_to(change { ENV.to_h })
|
|
132
|
-
expect(ENV.key?("FOO")).to be false
|
|
133
|
-
end
|
|
134
|
-
end
|
|
135
|
-
end
|
|
136
|
-
end
|
|
137
|
-
|
|
138
|
-
# rubocop:enable Style/FetchEnvVar
|
|
@@ -1,120 +0,0 @@
|
|
|
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
|
-
|
|
77
|
-
if RUBY_VERSION < "3.4"
|
|
78
|
-
expect(logs).to include("app/actions/test.rb:7:in `handle'")
|
|
79
|
-
else
|
|
80
|
-
expect(logs).to include("app/actions/test.rb:7:in 'TestApp::Actions::Test#handle'")
|
|
81
|
-
end
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
it "re-raises the exception" do
|
|
85
|
-
expect { get "/" }.to raise_error(TestApp::Actions::Test::UnhandledError)
|
|
86
|
-
end
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
describe "errors handled by handle_exception" do
|
|
90
|
-
def before_prepare
|
|
91
|
-
write "app/actions/test.rb", <<~RUBY
|
|
92
|
-
module TestApp
|
|
93
|
-
module Actions
|
|
94
|
-
class Test < Hanami::Action
|
|
95
|
-
NotFoundError = Class.new(StandardError)
|
|
96
|
-
|
|
97
|
-
handle_exception NotFoundError => :handle_not_found_error
|
|
98
|
-
|
|
99
|
-
def handle(request, response)
|
|
100
|
-
raise NotFoundError
|
|
101
|
-
end
|
|
102
|
-
|
|
103
|
-
private
|
|
104
|
-
|
|
105
|
-
def handle_not_found_error(request, response, exception)
|
|
106
|
-
halt 404
|
|
107
|
-
end
|
|
108
|
-
end
|
|
109
|
-
end
|
|
110
|
-
end
|
|
111
|
-
RUBY
|
|
112
|
-
end
|
|
113
|
-
|
|
114
|
-
it "does not log an error" do
|
|
115
|
-
get "/"
|
|
116
|
-
|
|
117
|
-
expect(logs).to match %r{GET 404 \d+(µs|ms) 127.0.0.1 /}
|
|
118
|
-
end
|
|
119
|
-
end
|
|
120
|
-
end
|
|
@@ -1,68 +0,0 @@
|
|
|
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
|
-
|
|
@@ -1,202 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "json"
|
|
4
|
-
require "rack/test"
|
|
5
|
-
require "stringio"
|
|
6
|
-
|
|
7
|
-
RSpec.describe "Logging / Request logging", :app_integration do
|
|
8
|
-
include Rack::Test::Methods
|
|
9
|
-
|
|
10
|
-
let(:app) { Hanami.app }
|
|
11
|
-
|
|
12
|
-
let(:logger_stream) { StringIO.new }
|
|
13
|
-
|
|
14
|
-
let(:logger_level) { nil }
|
|
15
|
-
|
|
16
|
-
let(:root) { make_tmp_directory }
|
|
17
|
-
|
|
18
|
-
def configure_logger
|
|
19
|
-
Hanami.app.config.logger.stream = logger_stream
|
|
20
|
-
Hanami.app.config.logger.level = logger_level if logger_level
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
def logs
|
|
24
|
-
@logs ||= (logger_stream.rewind and logger_stream.read)
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
def generate_app
|
|
28
|
-
write "config/app.rb", <<~RUBY
|
|
29
|
-
module TestApp
|
|
30
|
-
class App < Hanami::App
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
RUBY
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
before do
|
|
37
|
-
with_directory(root) do
|
|
38
|
-
generate_app
|
|
39
|
-
|
|
40
|
-
require "hanami/setup"
|
|
41
|
-
configure_logger
|
|
42
|
-
|
|
43
|
-
before_prepare if respond_to?(:before_prepare)
|
|
44
|
-
require "hanami/prepare"
|
|
45
|
-
end
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
describe "app router" do
|
|
49
|
-
def before_prepare
|
|
50
|
-
write "config/routes.rb", <<~RUBY
|
|
51
|
-
module TestApp
|
|
52
|
-
class Routes < Hanami::Routes
|
|
53
|
-
root to: ->(env) { [200, {}, ["OK"]] }
|
|
54
|
-
end
|
|
55
|
-
end
|
|
56
|
-
RUBY
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
it "logs the requests" do
|
|
60
|
-
get "/"
|
|
61
|
-
|
|
62
|
-
expect(logs.split("\n").length).to eq 1
|
|
63
|
-
expect(logs).to match %r{GET 200 \d+(µs|ms) 127.0.0.1 /}
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
context "production env" do
|
|
67
|
-
around do |example|
|
|
68
|
-
@prev_hanami_env = ENV["HANAMI_ENV"]
|
|
69
|
-
ENV["HANAMI_ENV"] = "production"
|
|
70
|
-
example.run
|
|
71
|
-
ensure
|
|
72
|
-
ENV["HANAMI_ENV"] = @prev_hanami_env
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
it "logs the requests as JSON" do
|
|
76
|
-
get "/"
|
|
77
|
-
|
|
78
|
-
expect(logs.split("\n").length).to eq 1
|
|
79
|
-
|
|
80
|
-
json = JSON.parse(logs, symbolize_names: true)
|
|
81
|
-
expect(json).to include(
|
|
82
|
-
verb: "GET",
|
|
83
|
-
path: "/",
|
|
84
|
-
ip: "127.0.0.1",
|
|
85
|
-
elapsed: Integer,
|
|
86
|
-
elapsed_unit: a_string_matching(/(µs|ms)/),
|
|
87
|
-
)
|
|
88
|
-
end
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
context "log level error" do
|
|
92
|
-
let(:logger_level) { :error }
|
|
93
|
-
before do
|
|
94
|
-
expect_any_instance_of(Hanami::Web::RackLogger).to_not receive(:data)
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
it "does not log info" do
|
|
98
|
-
get "/"
|
|
99
|
-
|
|
100
|
-
expect(logs.split("\n").length).to eq 0
|
|
101
|
-
end
|
|
102
|
-
end
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
describe "slice router" do
|
|
106
|
-
let(:app) { Main::Slice.rack_app }
|
|
107
|
-
|
|
108
|
-
def before_prepare
|
|
109
|
-
write "slices/main/config/routes.rb", <<~RUBY
|
|
110
|
-
module Main
|
|
111
|
-
class Routes < Hanami::Routes
|
|
112
|
-
root to: ->(env) { [200, {}, ["OK"]] }
|
|
113
|
-
end
|
|
114
|
-
end
|
|
115
|
-
RUBY
|
|
116
|
-
end
|
|
117
|
-
|
|
118
|
-
it "logs the requests" do
|
|
119
|
-
get "/"
|
|
120
|
-
|
|
121
|
-
expect(logs.split("\n").length).to eq 1
|
|
122
|
-
expect(logs).to match %r{GET 200 \d+(µs|ms) 127.0.0.1 /}
|
|
123
|
-
end
|
|
124
|
-
|
|
125
|
-
context "production env" do
|
|
126
|
-
around do |example|
|
|
127
|
-
@prev_hanami_env = ENV["HANAMI_ENV"]
|
|
128
|
-
ENV["HANAMI_ENV"] = "production"
|
|
129
|
-
example.run
|
|
130
|
-
ensure
|
|
131
|
-
ENV["HANAMI_ENV"] = @prev_hanami_env
|
|
132
|
-
end
|
|
133
|
-
|
|
134
|
-
it "logs the requests as JSON" do
|
|
135
|
-
get "/"
|
|
136
|
-
|
|
137
|
-
expect(logs.split("\n").length).to eq 1
|
|
138
|
-
|
|
139
|
-
json = JSON.parse(logs, symbolize_names: true)
|
|
140
|
-
expect(json).to include(
|
|
141
|
-
verb: "GET",
|
|
142
|
-
path: "/",
|
|
143
|
-
ip: "127.0.0.1",
|
|
144
|
-
elapsed: Integer,
|
|
145
|
-
elapsed_unit: a_string_matching(/(µs|ms)/),
|
|
146
|
-
)
|
|
147
|
-
end
|
|
148
|
-
end
|
|
149
|
-
end
|
|
150
|
-
|
|
151
|
-
context "when using ::Logger from Ruby stdlib" do
|
|
152
|
-
def generate_app
|
|
153
|
-
write "config/app.rb", <<~RUBY
|
|
154
|
-
require "logger"
|
|
155
|
-
require "pathname"
|
|
156
|
-
|
|
157
|
-
module TestApp
|
|
158
|
-
class App < Hanami::App
|
|
159
|
-
stream = Pathname.new(#{root.to_s.inspect}).join("log").tap(&:mkpath).join("test.log").to_s
|
|
160
|
-
config.logger = ::Logger.new(stream, progname: "custom-logger-app")
|
|
161
|
-
end
|
|
162
|
-
end
|
|
163
|
-
RUBY
|
|
164
|
-
end
|
|
165
|
-
|
|
166
|
-
def before_prepare
|
|
167
|
-
with_directory(root) do
|
|
168
|
-
write "config/routes.rb", <<~RUBY
|
|
169
|
-
module TestApp
|
|
170
|
-
class Routes < Hanami::Routes
|
|
171
|
-
root to: ->(env) { [200, {}, ["OK"]] }
|
|
172
|
-
end
|
|
173
|
-
end
|
|
174
|
-
RUBY
|
|
175
|
-
end
|
|
176
|
-
end
|
|
177
|
-
|
|
178
|
-
let(:logs) do
|
|
179
|
-
Pathname.new(root).join("log", "test.log").readlines
|
|
180
|
-
end
|
|
181
|
-
|
|
182
|
-
it "logs the requests with the payload serialized as JSON" do
|
|
183
|
-
get "/"
|
|
184
|
-
|
|
185
|
-
request_log = logs.last
|
|
186
|
-
|
|
187
|
-
# Expected log line follows the standard Logger structure:
|
|
188
|
-
#
|
|
189
|
-
# I, [2023-10-14T14:55:16.638753 #94836] INFO -- custom-logger-app: {"verb":"GET", ...}
|
|
190
|
-
expect(request_log).to match(%r{INFO -- custom-logger-app:})
|
|
191
|
-
|
|
192
|
-
# The log message should be JSON, after the progname
|
|
193
|
-
log_message = request_log.split("custom-logger-app: ").last
|
|
194
|
-
log_payload = JSON.parse(log_message, symbolize_names: true)
|
|
195
|
-
|
|
196
|
-
expect(log_payload).to include(
|
|
197
|
-
verb: "GET",
|
|
198
|
-
status: 200
|
|
199
|
-
)
|
|
200
|
-
end
|
|
201
|
-
end
|
|
202
|
-
end
|
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "dry/operation"
|
|
4
|
-
|
|
5
|
-
RSpec.describe "Operation / Extensions", :app_integration do
|
|
6
|
-
before do
|
|
7
|
-
@env = ENV.to_h
|
|
8
|
-
allow(Hanami::Env).to receive(:loaded?).and_return(false)
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
after { ENV.replace(@env) }
|
|
12
|
-
|
|
13
|
-
specify "Transaction interface is made available automatically" do
|
|
14
|
-
with_tmp_directory(Dir.mktmpdir) do
|
|
15
|
-
write "config/app.rb", <<~RUBY
|
|
16
|
-
require "hanami"
|
|
17
|
-
|
|
18
|
-
module TestApp
|
|
19
|
-
class App < Hanami::App
|
|
20
|
-
end
|
|
21
|
-
end
|
|
22
|
-
RUBY
|
|
23
|
-
|
|
24
|
-
write "app/operation.rb", <<~RUBY
|
|
25
|
-
module TestApp
|
|
26
|
-
class Operation < Dry::Operation
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
RUBY
|
|
30
|
-
|
|
31
|
-
write "slices/main/operation.rb", <<~RUBY
|
|
32
|
-
module Main
|
|
33
|
-
class Operation < Dry::Operation
|
|
34
|
-
end
|
|
35
|
-
end
|
|
36
|
-
RUBY
|
|
37
|
-
|
|
38
|
-
write "db/.keep", ""
|
|
39
|
-
write "app/relations/.keep", ""
|
|
40
|
-
|
|
41
|
-
write "slices/main/db/.keep", ""
|
|
42
|
-
write "slices/main/relations/.keep", ""
|
|
43
|
-
|
|
44
|
-
ENV["DATABASE_URL"] = "sqlite::memory"
|
|
45
|
-
ENV["MAIN__DATABASE_URL"] = "sqlite::memory"
|
|
46
|
-
|
|
47
|
-
require "hanami/prepare"
|
|
48
|
-
|
|
49
|
-
app = TestApp::Operation.new
|
|
50
|
-
main = Main::Operation.new
|
|
51
|
-
|
|
52
|
-
expect(app).to respond_to(:transaction)
|
|
53
|
-
|
|
54
|
-
expect(app.rom).to be TestApp::App["db.rom"]
|
|
55
|
-
expect(app.rom).not_to be Main::Slice["db.rom"]
|
|
56
|
-
expect(main.rom).to be Main::Slice["db.rom"]
|
|
57
|
-
end
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
context "hanami-db bundled, but no db configured" do
|
|
61
|
-
it "does not extend the operation class" do
|
|
62
|
-
with_tmp_directory(Dir.mktmpdir) do
|
|
63
|
-
write "config/app.rb", <<~RUBY
|
|
64
|
-
require "hanami"
|
|
65
|
-
|
|
66
|
-
module TestApp
|
|
67
|
-
class App < Hanami::App
|
|
68
|
-
end
|
|
69
|
-
end
|
|
70
|
-
RUBY
|
|
71
|
-
|
|
72
|
-
write "app/operation.rb", <<~RUBY
|
|
73
|
-
module TestApp
|
|
74
|
-
class Operation < Dry::Operation
|
|
75
|
-
end
|
|
76
|
-
end
|
|
77
|
-
RUBY
|
|
78
|
-
|
|
79
|
-
require "hanami/prepare"
|
|
80
|
-
|
|
81
|
-
operation = TestApp::Operation.new
|
|
82
|
-
|
|
83
|
-
expect(operation.rom).to be nil
|
|
84
|
-
expect { operation.transaction }.to raise_error Hanami::ComponentLoadError, "A configured db for TestApp::App is required to run transactions."
|
|
85
|
-
end
|
|
86
|
-
end
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
context "hanami-db not bundled" do
|
|
90
|
-
before do
|
|
91
|
-
allow(Hanami).to receive(:bundled?).and_call_original
|
|
92
|
-
allow(Hanami).to receive(:bundled?).with("hanami-db").and_return false
|
|
93
|
-
end
|
|
94
|
-
|
|
95
|
-
it "does not extend the operation class" do
|
|
96
|
-
with_tmp_directory(Dir.mktmpdir) do
|
|
97
|
-
write "config/app.rb", <<~RUBY
|
|
98
|
-
require "hanami"
|
|
99
|
-
|
|
100
|
-
module TestApp
|
|
101
|
-
class App < Hanami::App
|
|
102
|
-
end
|
|
103
|
-
end
|
|
104
|
-
RUBY
|
|
105
|
-
|
|
106
|
-
write "app/operation.rb", <<~RUBY
|
|
107
|
-
module TestApp
|
|
108
|
-
class Operation < Dry::Operation
|
|
109
|
-
end
|
|
110
|
-
end
|
|
111
|
-
RUBY
|
|
112
|
-
|
|
113
|
-
require "hanami/prepare"
|
|
114
|
-
|
|
115
|
-
operation = TestApp::Operation.new
|
|
116
|
-
|
|
117
|
-
expect { operation.rom }.to raise_error NoMethodError
|
|
118
|
-
expect { operation.transaction }.to raise_error NoMethodError
|
|
119
|
-
end
|
|
120
|
-
end
|
|
121
|
-
end
|
|
122
|
-
end
|