hanami 2.1.0.beta2.1 → 2.1.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|