hanami 2.1.0.beta2.1 → 2.1.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 +25 -0
- data/hanami.gemspec +1 -1
- data/lib/hanami/config/actions.rb +14 -0
- data/lib/hanami/extensions/action/slice_configured_action.rb +5 -5
- data/lib/hanami/extensions/action.rb +4 -4
- data/lib/hanami/extensions/view/context.rb +0 -11
- data/lib/hanami/extensions/view/part.rb +51 -2
- data/lib/hanami/extensions/view/slice_configured_part.rb +72 -0
- data/lib/hanami/extensions/view/slice_configured_view.rb +2 -2
- data/lib/hanami/helpers/assets_helper.rb +6 -38
- data/lib/hanami/routes.rb +33 -2
- data/lib/hanami/slice.rb +12 -2
- data/lib/hanami/slice_registrar.rb +48 -23
- data/lib/hanami/version.rb +1 -1
- data/lib/hanami/web/rack_logger.rb +70 -2
- data/lib/hanami/web/welcome.html.erb +203 -0
- data/lib/hanami/web/welcome.rb +46 -0
- data/spec/integration/assets/assets_spec.rb +14 -3
- data/spec/integration/logging/request_logging_spec.rb +65 -7
- data/spec/integration/rack_app/method_override_spec.rb +97 -0
- data/spec/integration/slices_spec.rb +275 -5
- data/spec/integration/view/context/assets_spec.rb +0 -8
- data/spec/integration/view/context/inflector_spec.rb +0 -8
- data/spec/integration/view/context/settings_spec.rb +0 -8
- data/spec/integration/view/helpers/part_helpers_spec.rb +2 -2
- data/spec/integration/view/helpers/user_defined_helpers/part_helpers_spec.rb +10 -10
- data/spec/integration/view/parts/default_rendering_spec.rb +138 -0
- data/spec/integration/web/welcome_view_spec.rb +84 -0
- data/spec/support/app_integration.rb +22 -4
- data/spec/unit/hanami/helpers/assets_helper/{audio_spec.rb → audio_tag_spec.rb} +10 -14
- data/spec/unit/hanami/helpers/assets_helper/{favicon_spec.rb → favicon_tag_spec.rb} +7 -11
- data/spec/unit/hanami/helpers/assets_helper/{image_spec.rb → image_tag_spec.rb} +8 -12
- data/spec/unit/hanami/helpers/assets_helper/{javascript_spec.rb → javascript_tag_spec.rb} +14 -18
- data/spec/unit/hanami/helpers/assets_helper/{stylesheet_spec.rb → stylesheet_tag_spec.rb} +12 -16
- data/spec/unit/hanami/helpers/assets_helper/{video_spec.rb → video_tag_spec.rb} +11 -11
- data/spec/unit/hanami/version_spec.rb +1 -1
- metadata +23 -14
@@ -1,5 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "delegate"
|
4
|
+
require "json"
|
5
|
+
|
3
6
|
module Hanami
|
4
7
|
# @api private
|
5
8
|
module Web
|
@@ -53,10 +56,75 @@ module Hanami
|
|
53
56
|
end
|
54
57
|
end
|
55
58
|
|
59
|
+
# @since 2.1.0
|
60
|
+
# @api private
|
61
|
+
class UniversalLogger
|
62
|
+
class << self
|
63
|
+
# @since 2.1.0
|
64
|
+
# @api private
|
65
|
+
def call(logger)
|
66
|
+
return logger if compatible_logger?(logger)
|
67
|
+
|
68
|
+
new(logger)
|
69
|
+
end
|
70
|
+
|
71
|
+
# @since 2.1.0
|
72
|
+
# @api private
|
73
|
+
alias_method :[], :call
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
def compatible_logger?(logger)
|
78
|
+
logger.respond_to?(:tagged) && accepts_entry_payload?(logger)
|
79
|
+
end
|
80
|
+
|
81
|
+
def accepts_entry_payload?(logger)
|
82
|
+
logger.method(:info).parameters.last.then { |type, _| type == :keyrest }
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# @since 2.1.0
|
87
|
+
# @api private
|
88
|
+
attr_reader :logger
|
89
|
+
|
90
|
+
# @since 2.1.0
|
91
|
+
# @api private
|
92
|
+
def initialize(logger)
|
93
|
+
@logger = logger
|
94
|
+
end
|
95
|
+
|
96
|
+
# @since 2.1.0
|
97
|
+
# @api private
|
98
|
+
def tagged(*, &blk)
|
99
|
+
blk.call
|
100
|
+
end
|
101
|
+
|
102
|
+
# Logs the entry as JSON.
|
103
|
+
#
|
104
|
+
# This ensures a reasonable (and parseable) representation of our log payload structures for
|
105
|
+
# loggers that are configured to wholly replace Hanami's default logger.
|
106
|
+
#
|
107
|
+
# @since 2.1.0
|
108
|
+
# @api private
|
109
|
+
def info(message = nil, **payload)
|
110
|
+
payload[:message] = message if message
|
111
|
+
logger.info(JSON.fast_generate(payload))
|
112
|
+
end
|
113
|
+
|
114
|
+
# @see info
|
115
|
+
#
|
116
|
+
# @since 2.1.0
|
117
|
+
# @api private
|
118
|
+
def error(message = nil, **payload)
|
119
|
+
payload[:message] = message if message
|
120
|
+
logger.info(JSON.fast_generate(payload))
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
56
124
|
# @api private
|
57
125
|
# @since 2.0.0
|
58
126
|
def initialize(logger, env: :development)
|
59
|
-
@logger = logger
|
127
|
+
@logger = UniversalLogger[logger]
|
60
128
|
extend(Development) if %i[development test].include?(env)
|
61
129
|
end
|
62
130
|
|
@@ -77,7 +145,7 @@ module Hanami
|
|
77
145
|
# @since 2.0.0
|
78
146
|
def log_request(env, status, elapsed)
|
79
147
|
logger.tagged(:rack) do
|
80
|
-
logger.info(data(env, status: status, elapsed: elapsed))
|
148
|
+
logger.info(**data(env, status: status, elapsed: elapsed))
|
81
149
|
end
|
82
150
|
end
|
83
151
|
|
@@ -0,0 +1,203 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html lang="en">
|
3
|
+
<head>
|
4
|
+
<meta charset="UTF-8">
|
5
|
+
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
7
|
+
<title>Hanami</title>
|
8
|
+
<style>
|
9
|
+
:root {
|
10
|
+
--max-width: 1024px;
|
11
|
+
--foreground-rgb: 0, 0, 0;
|
12
|
+
--background-rgb: 255, 255, 255;
|
13
|
+
--card-border-rgb: 200, 200, 200;
|
14
|
+
--card-background-rgb: 100, 100, 100;
|
15
|
+
--font-sans: ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;
|
16
|
+
--gradient: radial-gradient(circle at 50% 125%, rgba(255,202,212,1) 0%, rgba(255,202,212,0) 40%);
|
17
|
+
}
|
18
|
+
|
19
|
+
@media (prefers-color-scheme: dark) {
|
20
|
+
:root {
|
21
|
+
--foreground-rgb: 255, 255, 255;
|
22
|
+
--background-rgb: 0, 0, 0;
|
23
|
+
--card-border-rgb: 200, 200, 200;
|
24
|
+
--card-background-rgb: 100, 100, 100;
|
25
|
+
--gradient: radial-gradient(circle at 50% 125%, rgba(255,73,108,0.75) 0%, rgba(255,73,108,0) 40%);
|
26
|
+
}
|
27
|
+
}
|
28
|
+
|
29
|
+
* {
|
30
|
+
box-sizing: border-box;
|
31
|
+
margin: 0;
|
32
|
+
padding: 0;
|
33
|
+
}
|
34
|
+
|
35
|
+
body,
|
36
|
+
html {
|
37
|
+
max-width: 100vw;
|
38
|
+
overflow-x: hidden;
|
39
|
+
font-size: 100%;
|
40
|
+
}
|
41
|
+
|
42
|
+
body {
|
43
|
+
color: rgb(var(--foreground-rgb));
|
44
|
+
background: var(--gradient) rgb(var(--background-rgb));
|
45
|
+
font-family: var(--font-sans);
|
46
|
+
font-style: normal;
|
47
|
+
}
|
48
|
+
|
49
|
+
main {
|
50
|
+
display: flex;
|
51
|
+
flex-direction: column;
|
52
|
+
align-items: center;
|
53
|
+
padding: 2rem 8vw;
|
54
|
+
min-height: 100vh;
|
55
|
+
}
|
56
|
+
|
57
|
+
a {
|
58
|
+
text-decoration: none;
|
59
|
+
color: rgb(var(--foreground-rgb));
|
60
|
+
}
|
61
|
+
|
62
|
+
.welcome {
|
63
|
+
display: flex;
|
64
|
+
flex-direction: column;
|
65
|
+
gap: 2vh;
|
66
|
+
justify-content: center;
|
67
|
+
flex-grow: 3;
|
68
|
+
align-items: center;
|
69
|
+
text-align: center;
|
70
|
+
position: relative;
|
71
|
+
margin-bottom: 4vh;
|
72
|
+
}
|
73
|
+
|
74
|
+
.welcome .logo {
|
75
|
+
display: block;
|
76
|
+
width: 120px;
|
77
|
+
height: 120px;
|
78
|
+
}
|
79
|
+
|
80
|
+
.welcome h1 {
|
81
|
+
font-size: 2.625rem;
|
82
|
+
font-weight: 500;
|
83
|
+
}
|
84
|
+
|
85
|
+
.grid {
|
86
|
+
display: grid;
|
87
|
+
grid-template-columns: repeat(4, 1fr);
|
88
|
+
column-gap: 20px;
|
89
|
+
max-width: 100%;
|
90
|
+
margin-bottom: 8vh;
|
91
|
+
width: var(--max-width);
|
92
|
+
}
|
93
|
+
|
94
|
+
.card {
|
95
|
+
padding: 1rem;
|
96
|
+
border-radius: 12px;
|
97
|
+
border: 1px solid rgba(var(--card-border-rgb), 0.3);
|
98
|
+
background: rgba(var(--card-background-rgb), 0);
|
99
|
+
transition: background 200ms, border 200ms;
|
100
|
+
}
|
101
|
+
|
102
|
+
.card:hover {
|
103
|
+
background: rgba(var(--card-background-rgb), 0.05);
|
104
|
+
}
|
105
|
+
|
106
|
+
.card h2 {
|
107
|
+
font-size: 1.5rem;
|
108
|
+
font-weight: 500;
|
109
|
+
margin-bottom: 0.8rem;
|
110
|
+
}
|
111
|
+
|
112
|
+
.card p {
|
113
|
+
line-height: 1.5;
|
114
|
+
opacity: 0.6;
|
115
|
+
}
|
116
|
+
|
117
|
+
.meta {
|
118
|
+
text-align: center;
|
119
|
+
opacity: 50%;
|
120
|
+
}
|
121
|
+
|
122
|
+
/* Mobile */
|
123
|
+
@media (max-width: 700px) {
|
124
|
+
|
125
|
+
.welcome {
|
126
|
+
justify-content: top;
|
127
|
+
flex-grow: 0;
|
128
|
+
margin-bottom: 4rem;
|
129
|
+
}
|
130
|
+
|
131
|
+
.welcome .logo {
|
132
|
+
width: 90px;
|
133
|
+
height: 90px;
|
134
|
+
}
|
135
|
+
|
136
|
+
.welcome h1 {
|
137
|
+
font-size: 2rem;
|
138
|
+
}
|
139
|
+
|
140
|
+
.grid {
|
141
|
+
grid-template-columns: 1fr;
|
142
|
+
row-gap: 20px;
|
143
|
+
}
|
144
|
+
|
145
|
+
.card {
|
146
|
+
text-align: center;
|
147
|
+
}
|
148
|
+
}
|
149
|
+
|
150
|
+
/* Tablet and Smaller Desktop */
|
151
|
+
@media (min-width: 701px) and (max-width: 1120px) {
|
152
|
+
.grid {
|
153
|
+
grid-template-columns: repeat(2, 1fr);
|
154
|
+
gap: 20px;
|
155
|
+
}
|
156
|
+
}
|
157
|
+
|
158
|
+
@media (prefers-color-scheme: dark) {
|
159
|
+
html {
|
160
|
+
color-scheme: dark;
|
161
|
+
}
|
162
|
+
.card {
|
163
|
+
border: 1px solid rgba(var(--card-border-rgb), 0.15);
|
164
|
+
background: rgba(var(--card-background-rgb), 0);
|
165
|
+
}
|
166
|
+
|
167
|
+
.card:hover {
|
168
|
+
background: rgba(var(--card-background-rgb), 0.1);
|
169
|
+
}
|
170
|
+
}
|
171
|
+
|
172
|
+
</style>
|
173
|
+
</head>
|
174
|
+
<body>
|
175
|
+
<main>
|
176
|
+
<div class="welcome">
|
177
|
+
<img src="data:image/svg+xml,%3Csvg height='840' width='840' viewBox='0 0 840 840' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'%3E%3Cmask id='a' fill='%23fff'%3E%3Cpath d='m0 0h840v840h-840z' fill='%23fff' fill-rule='evenodd'/%3E%3C/mask%3E%3Cg fill='none' fill-rule='evenodd' mask='url(%23a)'%3E%3Cg transform='translate(-115.9919 -103.9919)'%3E%3Cg fill='%23dc3610'%3E%3Cpath d='m523.491853 126.991853 377.093909 273.974762-144.037057 443.300476h-466.113705l-144.037056-443.300476z' opacity='.6' transform='matrix(.91354546 .40673664 -.40673664 .91354546 258.181591 -167.665085)'/%3E%3Cpath d='m525.491853 129.991853 377.093909 273.974762-144.037057 443.300476h-466.113705l-144.037056-443.300476z' opacity='.8' transform='matrix(.97437006 .22495105 -.22495105 .97437006 131.903231 -104.716004)'/%3E%3Cpath d='m535.491853 130.991853 377.093909 273.974762-144.037057 443.300476h-466.113705l-144.037056-443.300476z'/%3E%3C/g%3E%3Cpath d='m534.991853 370.991853c86.156421 0 156 69.843579 156 156s-69.843579 156-156 156-156-69.843579-156-156 69.843579-156 156-156zm0 35c-66.826455 0-121 54.173545-121 121s54.173545 121 121 121 121-54.173545 121-121-54.173545-121-121-121z' fill='%23fff'/%3E%3Cpath d='m535.947441 478.991853 74.023116 90.999216-20.023116 27h-108l-19.978704-27z' fill='%23fff' transform='matrix(-1 0 0 -1 1071.9392 1075.983)'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E" alt="Hanani logo" class="logo">
|
178
|
+
<h1>Welcome to Hanami</h1>
|
179
|
+
</div>
|
180
|
+
<div class="grid">
|
181
|
+
<a href="https://guides.hanamirb.org" class="card">
|
182
|
+
<h2>Guides</h2>
|
183
|
+
<p>Get started with the Hanami guides</p>
|
184
|
+
</a>
|
185
|
+
<a href="https://docs.hanamirb.org/" class="card">
|
186
|
+
<h2>API docs</h2>
|
187
|
+
<p>Learn more through the API docs</p>
|
188
|
+
</a>
|
189
|
+
<a href="http://github.com/hanami" class="card">
|
190
|
+
<h2>Code</h2>
|
191
|
+
<p>Contribute to the source code</p>
|
192
|
+
</a>
|
193
|
+
<a href="https://discourse.hanamirb.org" class="card">
|
194
|
+
<h2>Forum</h2>
|
195
|
+
<p>Join the conversation on the forum</p>
|
196
|
+
</a>
|
197
|
+
</div>
|
198
|
+
<p class="meta">
|
199
|
+
Hanami version: <%= hanami_version %> • Ruby version: <%= ruby_version %>
|
200
|
+
</p>
|
201
|
+
</main>
|
202
|
+
</body>
|
203
|
+
</html>
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "erb"
|
4
|
+
|
5
|
+
module Hanami
|
6
|
+
# @api private
|
7
|
+
module Web
|
8
|
+
# Middleware that renders a welcome view in fresh Hanami apps.
|
9
|
+
#
|
10
|
+
# @api private
|
11
|
+
# @since 2.1.0
|
12
|
+
class Welcome
|
13
|
+
# @api private
|
14
|
+
# @since 2.1.0
|
15
|
+
def initialize(app)
|
16
|
+
@app = app
|
17
|
+
end
|
18
|
+
|
19
|
+
# @api private
|
20
|
+
# @since 2.1.0
|
21
|
+
def call(env)
|
22
|
+
request_path = env["REQUEST_PATH"] || ""
|
23
|
+
request_host = env["HTTP_HOST"] || ""
|
24
|
+
|
25
|
+
template_path = File.join(__dir__, "welcome.html.erb")
|
26
|
+
body = [ERB.new(File.read(template_path)).result(binding)]
|
27
|
+
|
28
|
+
[200, {}, body]
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
# @api private
|
34
|
+
# @since 2.1.0
|
35
|
+
def hanami_version
|
36
|
+
Hanami::VERSION
|
37
|
+
end
|
38
|
+
|
39
|
+
# @api private
|
40
|
+
# @since 2.1.0
|
41
|
+
def ruby_version
|
42
|
+
RUBY_DESCRIPTION
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -18,6 +18,19 @@ RSpec.describe "Assets", :app_integration do
|
|
18
18
|
end
|
19
19
|
RUBY
|
20
20
|
|
21
|
+
write "config/assets.mjs", <<~JS
|
22
|
+
import * as assets from "hanami-assets";
|
23
|
+
await assets.run();
|
24
|
+
JS
|
25
|
+
|
26
|
+
write "package.json", <<~JSON
|
27
|
+
{
|
28
|
+
"scripts": {
|
29
|
+
"assets": "node config/assets.mjs"
|
30
|
+
}
|
31
|
+
}
|
32
|
+
JSON
|
33
|
+
|
21
34
|
write "config/routes.rb", <<~RUBY
|
22
35
|
module TestApp
|
23
36
|
class Routes < Hanami::Routes
|
@@ -62,10 +75,8 @@ RSpec.describe "Assets", :app_integration do
|
|
62
75
|
RUBY
|
63
76
|
|
64
77
|
write "app/templates/posts/show.html.erb", <<~ERB
|
65
|
-
<%=
|
66
|
-
<%= css("app") %>
|
78
|
+
<%= stylesheet_tag("app") %>
|
67
79
|
<%= javascript_tag("app") %>
|
68
|
-
<%= js("app") %>
|
69
80
|
ERB
|
70
81
|
|
71
82
|
write "app/assets/js/app.ts", <<~TS
|
@@ -11,6 +11,8 @@ RSpec.describe "Logging / Request logging", :app_integration do
|
|
11
11
|
|
12
12
|
let(:logger_stream) { StringIO.new }
|
13
13
|
|
14
|
+
let(:root) { make_tmp_directory }
|
15
|
+
|
14
16
|
def configure_logger
|
15
17
|
Hanami.app.config.logger.stream = logger_stream
|
16
18
|
end
|
@@ -19,14 +21,18 @@ RSpec.describe "Logging / Request logging", :app_integration do
|
|
19
21
|
@logs ||= (logger_stream.rewind and logger_stream.read)
|
20
22
|
end
|
21
23
|
|
24
|
+
def generate_app
|
25
|
+
write "config/app.rb", <<~RUBY
|
26
|
+
module TestApp
|
27
|
+
class App < Hanami::App
|
28
|
+
end
|
29
|
+
end
|
30
|
+
RUBY
|
31
|
+
end
|
32
|
+
|
22
33
|
before do
|
23
|
-
with_directory(
|
24
|
-
|
25
|
-
module TestApp
|
26
|
-
class App < Hanami::App
|
27
|
-
end
|
28
|
-
end
|
29
|
-
RUBY
|
34
|
+
with_directory(root) do
|
35
|
+
generate_app
|
30
36
|
|
31
37
|
require "hanami/setup"
|
32
38
|
configure_logger
|
@@ -125,4 +131,56 @@ RSpec.describe "Logging / Request logging", :app_integration do
|
|
125
131
|
end
|
126
132
|
end
|
127
133
|
end
|
134
|
+
|
135
|
+
context "when using ::Logger from Ruby stdlib" do
|
136
|
+
def generate_app
|
137
|
+
write "config/app.rb", <<~RUBY
|
138
|
+
require "logger"
|
139
|
+
require "pathname"
|
140
|
+
|
141
|
+
module TestApp
|
142
|
+
class App < Hanami::App
|
143
|
+
stream = Pathname.new(#{root.to_s.inspect}).join("log").tap(&:mkpath).join("test.log").to_s
|
144
|
+
config.logger = ::Logger.new(stream, progname: "custom-logger-app")
|
145
|
+
end
|
146
|
+
end
|
147
|
+
RUBY
|
148
|
+
end
|
149
|
+
|
150
|
+
def before_prepare
|
151
|
+
with_directory(root) do
|
152
|
+
write "config/routes.rb", <<~RUBY
|
153
|
+
module TestApp
|
154
|
+
class Routes < Hanami::Routes
|
155
|
+
root to: ->(env) { [200, {}, ["OK"]] }
|
156
|
+
end
|
157
|
+
end
|
158
|
+
RUBY
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
let(:logs) do
|
163
|
+
Pathname.new(root).join("log", "test.log").readlines
|
164
|
+
end
|
165
|
+
|
166
|
+
it "logs the requests with the payload serialized as JSON" do
|
167
|
+
get "/"
|
168
|
+
|
169
|
+
request_log = logs.last
|
170
|
+
|
171
|
+
# Expected log line follows the standard Logger structure:
|
172
|
+
#
|
173
|
+
# I, [2023-10-14T14:55:16.638753 #94836] INFO -- custom-logger-app: {"verb":"GET", ...}
|
174
|
+
expect(request_log).to match(%r{INFO -- custom-logger-app:})
|
175
|
+
|
176
|
+
# The log message should be JSON, after the progname
|
177
|
+
log_message = request_log.split("custom-logger-app: ").last
|
178
|
+
log_payload = JSON.parse(log_message, symbolize_names: true)
|
179
|
+
|
180
|
+
expect(log_payload).to include(
|
181
|
+
verb: "GET",
|
182
|
+
status: 200
|
183
|
+
)
|
184
|
+
end
|
185
|
+
end
|
128
186
|
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rack/test"
|
4
|
+
require "stringio"
|
5
|
+
|
6
|
+
RSpec.describe "Hanami web app: Method Override", :app_integration do
|
7
|
+
include Rack::Test::Methods
|
8
|
+
|
9
|
+
let(:app) { Hanami.app }
|
10
|
+
|
11
|
+
around do |example|
|
12
|
+
with_tmp_directory(Dir.mktmpdir, &example)
|
13
|
+
end
|
14
|
+
|
15
|
+
context "enabled by default" do
|
16
|
+
before do
|
17
|
+
write "config/app.rb", <<~RUBY
|
18
|
+
require "hanami"
|
19
|
+
|
20
|
+
module TestApp
|
21
|
+
class App < Hanami::App
|
22
|
+
config.logger.stream = StringIO.new
|
23
|
+
end
|
24
|
+
end
|
25
|
+
RUBY
|
26
|
+
|
27
|
+
generate_app_code
|
28
|
+
end
|
29
|
+
|
30
|
+
it "overrides the original HTTP method" do
|
31
|
+
post(
|
32
|
+
"/users/:id",
|
33
|
+
{"_method" => "PUT"},
|
34
|
+
"CONTENT_TYPE" => "multipart/form-data"
|
35
|
+
)
|
36
|
+
|
37
|
+
expect(last_response).to be_successful
|
38
|
+
expect(last_response.body).to eq("PUT")
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
context "when disabled" do
|
43
|
+
before do
|
44
|
+
write "config/app.rb", <<~RUBY
|
45
|
+
require "hanami"
|
46
|
+
|
47
|
+
module TestApp
|
48
|
+
class App < Hanami::App
|
49
|
+
config.logger.stream = StringIO.new
|
50
|
+
config.actions.method_override = false
|
51
|
+
end
|
52
|
+
end
|
53
|
+
RUBY
|
54
|
+
|
55
|
+
generate_app_code
|
56
|
+
end
|
57
|
+
|
58
|
+
it "overrides the original HTTP method" do
|
59
|
+
post(
|
60
|
+
"/users/:id",
|
61
|
+
{"_method" => "PUT"},
|
62
|
+
"CONTENT_TYPE" => "multipart/form-data"
|
63
|
+
)
|
64
|
+
|
65
|
+
expect(last_response).to_not be_successful
|
66
|
+
expect(last_response.status).to be(404)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def generate_app_code
|
73
|
+
write "config/routes.rb", <<~RUBY
|
74
|
+
module TestApp
|
75
|
+
class Routes < Hanami::Routes
|
76
|
+
put "/users/:id", to: "users.update"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
RUBY
|
80
|
+
|
81
|
+
write "app/actions/users/update.rb", <<~RUBY
|
82
|
+
module TestApp
|
83
|
+
module Actions
|
84
|
+
module Users
|
85
|
+
class Update < Hanami::Action
|
86
|
+
def handle(req, res)
|
87
|
+
res.body = req.env.fetch("REQUEST_METHOD")
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
RUBY
|
94
|
+
|
95
|
+
require "hanami/boot"
|
96
|
+
end
|
97
|
+
end
|