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.
Files changed (184) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +55 -19
  3. data/LICENSE +20 -0
  4. data/README.md +18 -35
  5. data/hanami.gemspec +36 -37
  6. data/lib/hanami/config/db.rb +2 -0
  7. data/lib/hanami/config/i18n.rb +138 -0
  8. data/lib/hanami/config/logger.rb +15 -7
  9. data/lib/hanami/config/null_config.rb +1 -1
  10. data/lib/hanami/config/views.rb +17 -0
  11. data/lib/hanami/config.rb +66 -22
  12. data/lib/hanami/errors.rb +6 -0
  13. data/lib/hanami/extensions/action/slice_configured_action.rb +1 -1
  14. data/lib/hanami/extensions/action.rb +2 -2
  15. data/lib/hanami/extensions/mailer/slice_configured_mailer.rb +120 -0
  16. data/lib/hanami/extensions/mailer.rb +28 -0
  17. data/lib/hanami/extensions/operation/slice_configured_db_operation.rb +2 -0
  18. data/lib/hanami/extensions/view/context.rb +26 -4
  19. data/lib/hanami/extensions/view/part.rb +2 -0
  20. data/lib/hanami/extensions/view/slice_configured_context.rb +7 -0
  21. data/lib/hanami/extensions/view/slice_configured_part.rb +2 -0
  22. data/lib/hanami/extensions/view/slice_configured_view.rb +8 -8
  23. data/lib/hanami/extensions/view/standard_helpers.rb +4 -0
  24. data/lib/hanami/extensions.rb +6 -1
  25. data/lib/hanami/helpers/assets_helper.rb +0 -4
  26. data/lib/hanami/helpers/form_helper.rb +1 -1
  27. data/lib/hanami/helpers/i18n_helper.rb +176 -0
  28. data/lib/hanami/logger/rack_formatter.rb +73 -0
  29. data/lib/hanami/logger/sql_formatter.rb +80 -0
  30. data/lib/hanami/logger/sql_logger.rb +48 -0
  31. data/lib/hanami/middleware/render_errors.rb +2 -2
  32. data/lib/hanami/providers/db.rb +7 -2
  33. data/lib/hanami/providers/db_logging.rb +4 -7
  34. data/lib/hanami/providers/i18n/backend.rb +369 -0
  35. data/lib/hanami/providers/i18n/locale/en.yml +57 -0
  36. data/lib/hanami/providers/i18n.rb +114 -0
  37. data/lib/hanami/providers/mailers.rb +101 -0
  38. data/lib/hanami/routes.rb +1 -0
  39. data/lib/hanami/settings/composite_store.rb +53 -0
  40. data/lib/hanami/settings.rb +4 -4
  41. data/lib/hanami/slice/router.rb +15 -10
  42. data/lib/hanami/slice.rb +71 -11
  43. data/lib/hanami/slice_registrar.rb +2 -2
  44. data/lib/hanami/universal_logger.rb +250 -0
  45. data/lib/hanami/version.rb +1 -1
  46. data/lib/hanami/web/rack_logger.rb +2 -80
  47. data/lib/hanami/web/welcome.html.erb +443 -58
  48. data/lib/hanami.rb +4 -2
  49. metadata +28 -276
  50. data/CODE_OF_CONDUCT.md +0 -74
  51. data/FEATURES.md +0 -269
  52. data/LICENSE.md +0 -22
  53. data/spec/integration/action/cookies_spec.rb +0 -58
  54. data/spec/integration/action/csrf_protection_spec.rb +0 -54
  55. data/spec/integration/action/format_config_spec.rb +0 -129
  56. data/spec/integration/action/routes_spec.rb +0 -71
  57. data/spec/integration/action/sessions_spec.rb +0 -50
  58. data/spec/integration/action/slice_configuration_spec.rb +0 -284
  59. data/spec/integration/action/view_rendering/automatic_rendering_spec.rb +0 -247
  60. data/spec/integration/action/view_rendering/paired_view_inference_spec.rb +0 -115
  61. data/spec/integration/action/view_rendering/view_context_spec.rb +0 -221
  62. data/spec/integration/action/view_rendering_spec.rb +0 -89
  63. data/spec/integration/assets/assets_spec.rb +0 -155
  64. data/spec/integration/assets/cross_slice_assets_helpers_spec.rb +0 -129
  65. data/spec/integration/assets/serve_static_assets_spec.rb +0 -152
  66. data/spec/integration/code_loading/loading_from_app_spec.rb +0 -152
  67. data/spec/integration/code_loading/loading_from_lib_spec.rb +0 -242
  68. data/spec/integration/code_loading/loading_from_slice_spec.rb +0 -165
  69. data/spec/integration/container/application_routes_helper_spec.rb +0 -48
  70. data/spec/integration/container/auto_injection_spec.rb +0 -53
  71. data/spec/integration/container/auto_registration_spec.rb +0 -86
  72. data/spec/integration/container/autoloader_spec.rb +0 -82
  73. data/spec/integration/container/imports_spec.rb +0 -253
  74. data/spec/integration/container/prepare_container_spec.rb +0 -125
  75. data/spec/integration/container/provider_environment_spec.rb +0 -52
  76. data/spec/integration/container/provider_lifecycle_spec.rb +0 -61
  77. data/spec/integration/container/shutdown_spec.rb +0 -91
  78. data/spec/integration/container/standard_providers/rack_provider_spec.rb +0 -44
  79. data/spec/integration/container/standard_providers_spec.rb +0 -124
  80. data/spec/integration/db/auto_registration_spec.rb +0 -39
  81. data/spec/integration/db/commands_spec.rb +0 -80
  82. data/spec/integration/db/db_inflector_spec.rb +0 -57
  83. data/spec/integration/db/db_slices_spec.rb +0 -398
  84. data/spec/integration/db/db_spec.rb +0 -245
  85. data/spec/integration/db/gateways_spec.rb +0 -361
  86. data/spec/integration/db/logging_spec.rb +0 -301
  87. data/spec/integration/db/mappers_spec.rb +0 -84
  88. data/spec/integration/db/provider_config_spec.rb +0 -88
  89. data/spec/integration/db/provider_spec.rb +0 -35
  90. data/spec/integration/db/relations_spec.rb +0 -60
  91. data/spec/integration/db/repo_spec.rb +0 -300
  92. data/spec/integration/db/slices_importing_from_parent.rb +0 -130
  93. data/spec/integration/dotenv_loading_spec.rb +0 -138
  94. data/spec/integration/logging/exception_logging_spec.rb +0 -120
  95. data/spec/integration/logging/notifications_spec.rb +0 -68
  96. data/spec/integration/logging/request_logging_spec.rb +0 -202
  97. data/spec/integration/operations/extension_spec.rb +0 -122
  98. data/spec/integration/rack_app/body_parser_spec.rb +0 -108
  99. data/spec/integration/rack_app/method_override_spec.rb +0 -97
  100. data/spec/integration/rack_app/middleware_spec.rb +0 -720
  101. data/spec/integration/rack_app/non_booted_rack_app_spec.rb +0 -104
  102. data/spec/integration/rack_app/rack_app_spec.rb +0 -442
  103. data/spec/integration/rake_tasks_spec.rb +0 -107
  104. data/spec/integration/router/resource_routes_spec.rb +0 -281
  105. data/spec/integration/settings/access_in_slice_class_body_spec.rb +0 -83
  106. data/spec/integration/settings/access_to_constants_spec.rb +0 -46
  107. data/spec/integration/settings/loading_from_env_spec.rb +0 -188
  108. data/spec/integration/settings/settings_component_loading_spec.rb +0 -113
  109. data/spec/integration/settings/slice_registration_spec.rb +0 -145
  110. data/spec/integration/settings/using_types_spec.rb +0 -80
  111. data/spec/integration/setup_spec.rb +0 -165
  112. data/spec/integration/slices/external_slice_spec.rb +0 -91
  113. data/spec/integration/slices/slice_configuration_spec.rb +0 -42
  114. data/spec/integration/slices/slice_loading_spec.rb +0 -171
  115. data/spec/integration/slices/slice_registrations_spec.rb +0 -80
  116. data/spec/integration/slices/slice_routing_spec.rb +0 -219
  117. data/spec/integration/slices_spec.rb +0 -471
  118. data/spec/integration/view/config/default_context_spec.rb +0 -149
  119. data/spec/integration/view/config/inflector_spec.rb +0 -57
  120. data/spec/integration/view/config/part_class_spec.rb +0 -147
  121. data/spec/integration/view/config/part_namespace_spec.rb +0 -103
  122. data/spec/integration/view/config/paths_spec.rb +0 -119
  123. data/spec/integration/view/config/scope_class_spec.rb +0 -147
  124. data/spec/integration/view/config/scope_namespace_spec.rb +0 -103
  125. data/spec/integration/view/config/template_spec.rb +0 -38
  126. data/spec/integration/view/context/assets_spec.rb +0 -79
  127. data/spec/integration/view/context/inflector_spec.rb +0 -40
  128. data/spec/integration/view/context/request_spec.rb +0 -57
  129. data/spec/integration/view/context/routes_spec.rb +0 -84
  130. data/spec/integration/view/helpers/form_helper_spec.rb +0 -174
  131. data/spec/integration/view/helpers/part_helpers_spec.rb +0 -124
  132. data/spec/integration/view/helpers/scope_helpers_spec.rb +0 -84
  133. data/spec/integration/view/helpers/user_defined_helpers/part_helpers_spec.rb +0 -162
  134. data/spec/integration/view/helpers/user_defined_helpers/scope_helpers_spec.rb +0 -119
  135. data/spec/integration/view/parts/default_rendering_spec.rb +0 -138
  136. data/spec/integration/view/slice_configuration_spec.rb +0 -289
  137. data/spec/integration/view/views_spec.rb +0 -103
  138. data/spec/integration/web/content_security_policy_nonce_spec.rb +0 -251
  139. data/spec/integration/web/render_detailed_errors_spec.rb +0 -107
  140. data/spec/integration/web/render_errors_spec.rb +0 -242
  141. data/spec/integration/web/welcome_view_spec.rb +0 -84
  142. data/spec/spec_helper.rb +0 -28
  143. data/spec/support/app_integration.rb +0 -157
  144. data/spec/support/coverage.rb +0 -1
  145. data/spec/support/matchers.rb +0 -32
  146. data/spec/support/rspec.rb +0 -27
  147. data/spec/unit/hanami/config/actions/content_security_policy_spec.rb +0 -96
  148. data/spec/unit/hanami/config/actions/cookies_spec.rb +0 -46
  149. data/spec/unit/hanami/config/actions/csrf_protection_spec.rb +0 -58
  150. data/spec/unit/hanami/config/actions/default_values_spec.rb +0 -43
  151. data/spec/unit/hanami/config/actions/sessions_spec.rb +0 -48
  152. data/spec/unit/hanami/config/actions_spec.rb +0 -52
  153. data/spec/unit/hanami/config/base_url_spec.rb +0 -25
  154. data/spec/unit/hanami/config/console_spec.rb +0 -22
  155. data/spec/unit/hanami/config/db_spec.rb +0 -38
  156. data/spec/unit/hanami/config/inflector_spec.rb +0 -35
  157. data/spec/unit/hanami/config/logger_spec.rb +0 -195
  158. data/spec/unit/hanami/config/render_detailed_errors_spec.rb +0 -25
  159. data/spec/unit/hanami/config/render_errors_spec.rb +0 -25
  160. data/spec/unit/hanami/config/router_spec.rb +0 -44
  161. data/spec/unit/hanami/config/slices_spec.rb +0 -34
  162. data/spec/unit/hanami/config/views_spec.rb +0 -80
  163. data/spec/unit/hanami/env_spec.rb +0 -37
  164. data/spec/unit/hanami/extensions/view/context_spec.rb +0 -59
  165. data/spec/unit/hanami/helpers/assets_helper/asset_url_spec.rb +0 -120
  166. data/spec/unit/hanami/helpers/assets_helper/audio_tag_spec.rb +0 -132
  167. data/spec/unit/hanami/helpers/assets_helper/favicon_tag_spec.rb +0 -87
  168. data/spec/unit/hanami/helpers/assets_helper/image_tag_spec.rb +0 -92
  169. data/spec/unit/hanami/helpers/assets_helper/javascript_tag_spec.rb +0 -143
  170. data/spec/unit/hanami/helpers/assets_helper/stylesheet_tag_spec.rb +0 -126
  171. data/spec/unit/hanami/helpers/assets_helper/video_tag_spec.rb +0 -136
  172. data/spec/unit/hanami/helpers/form_helper_spec.rb +0 -2857
  173. data/spec/unit/hanami/port_spec.rb +0 -117
  174. data/spec/unit/hanami/providers/db/config/default_config_spec.rb +0 -100
  175. data/spec/unit/hanami/providers/db/config/gateway_spec.rb +0 -73
  176. data/spec/unit/hanami/providers/db/config_spec.rb +0 -143
  177. data/spec/unit/hanami/router/errors/not_allowed_error_spec.rb +0 -27
  178. data/spec/unit/hanami/router/errors/not_found_error_spec.rb +0 -22
  179. data/spec/unit/hanami/settings/env_store_spec.rb +0 -52
  180. data/spec/unit/hanami/settings_spec.rb +0 -111
  181. data/spec/unit/hanami/slice_configurable_spec.rb +0 -141
  182. data/spec/unit/hanami/slice_name_spec.rb +0 -47
  183. data/spec/unit/hanami/slice_spec.rb +0 -99
  184. 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