Skip to content

Commit 02375fa

Browse files
committed
Add response_content_type plugin for more easily setting content-type for responses
1 parent c24c007 commit 02375fa

File tree

4 files changed

+151
-0
lines changed

4 files changed

+151
-0
lines changed

CHANGELOG

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
=== master
2+
3+
* Add response_content_type plugin for more easily setting content-type for responses (jeremyevans)
4+
15
=== 3.94.0 (2025-07-14)
26

37
* Add view_subdir_leading_slash plugin for using view subdirectory unless template name starts with slash (jeremyevans) (#395)
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# frozen-string-literal: true
2+
3+
#
4+
class Roda
5+
module RodaPlugins
6+
# The response_content_type extension adds response.content_type
7+
# and response.content_type= methods for getting and setting the
8+
# response content-type.
9+
#
10+
# When setting the content-type, you can pass either a string, which
11+
# is used directly:
12+
#
13+
# response.content_type = "text/html"
14+
#
15+
# Or, if you have registered mime types when loading the plugin:
16+
#
17+
# plugin :response_content_type, mime_types: {
18+
# plain: "text/plain",
19+
# html: "text/html",
20+
# pdf: "application/pdf"
21+
# }
22+
#
23+
# You can use a symbol:
24+
#
25+
# response.content_type = :html
26+
#
27+
# If you would like to load all mime types supported by rack/mime,
28+
# you can use the <tt>mime_types: :from_rack_mime</tt> option:
29+
#
30+
# plugin :response_content_type, mime_types: :from_rack_mime
31+
#
32+
# Note that you are unlikely to be using all of these mime types,
33+
# so doing this will likely result in unnecessary memory usage. It
34+
# is recommended to use a hash with only the mime types your
35+
# application actually uses.
36+
#
37+
# To prevent silent failures, if you attempt to set the response
38+
# type with a symbol, and the symbol is not recognized, a KeyError
39+
# is raised.
40+
module ResponseContentType
41+
def self.configure(app, opts=OPTS)
42+
if mime_types = opts[:mime_types]
43+
mime_types = if mime_types == :from_rack_mime
44+
require "rack/mime"
45+
h = {}
46+
Rack::Mime::MIME_TYPES.each do |k, v|
47+
h[k.slice(1,100).to_sym] = v
48+
end
49+
h
50+
else
51+
mime_types.dup
52+
end
53+
app.opts[:repsonse_content_types] = mime_types.freeze
54+
else
55+
app.opts[:repsonse_content_types] ||= {}
56+
end
57+
end
58+
59+
module ResponseMethods
60+
# Return the content-type of the response. Will be nil if it has
61+
# not yet been explicitly set.
62+
def content_type
63+
@headers[RodaResponseHeaders::CONTENT_TYPE]
64+
end
65+
66+
# Set the content-type of the response. If given a string,
67+
# it is used directly. If given a symbol, looks up the mime
68+
# type with the given file extension. If the symbol is not
69+
# a recognized mime type, raises KeyError.
70+
def content_type=(mime_type)
71+
mime_type = roda_class.opts[:repsonse_content_types].fetch(mime_type) if mime_type.is_a?(Symbol)
72+
@headers[RodaResponseHeaders::CONTENT_TYPE] = mime_type
73+
end
74+
end
75+
end
76+
77+
register_plugin(:response_content_type, ResponseContentType)
78+
end
79+
end
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
require_relative "../spec_helper"
2+
3+
describe "response_content_type plugin" do
4+
it "allows getting and setting content-type" do
5+
app(:response_content_type) do |r|
6+
r.get "a" do
7+
response.content_type = "text/plain"
8+
"a:#{response.content_type}"
9+
end
10+
":#{response.content_type}"
11+
end
12+
13+
_, h, b = req
14+
h[RodaResponseHeaders::CONTENT_TYPE].must_equal 'text/html'
15+
b.must_equal [":"]
16+
17+
_, h, b = req("/a")
18+
h[RodaResponseHeaders::CONTENT_TYPE].must_equal 'text/plain'
19+
b.must_equal ["a:text/plain"]
20+
end
21+
22+
it "supports using symbols with plugin :mime_types option, and raises if an invalid symbol is provided" do
23+
app(:bare) do
24+
plugin :response_content_type, mime_types: {:txt => "text/plain"}
25+
route do |r|
26+
r.get "a" do
27+
response.content_type = :bad
28+
end
29+
response.content_type = :txt
30+
response.content_type
31+
end
32+
end
33+
34+
_, h, b = req
35+
h[RodaResponseHeaders::CONTENT_TYPE].must_equal 'text/plain'
36+
b.must_equal ["text/plain"]
37+
38+
proc{req("/a")}.must_raise KeyError
39+
end
40+
41+
it "supports plugin mime_types: :from_rack_mime option" do
42+
app(:bare) do
43+
plugin :response_content_type, mime_types: :from_rack_mime
44+
route do |r|
45+
r.get String do |s|
46+
response.content_type = s.to_sym
47+
""
48+
end
49+
response.content_type = :txt
50+
response.content_type
51+
end
52+
end
53+
54+
2.times do
55+
_, h, b = req
56+
h[RodaResponseHeaders::CONTENT_TYPE].must_equal 'text/plain'
57+
b.must_equal ["text/plain"]
58+
59+
header(RodaResponseHeaders::CONTENT_TYPE, "/pdf").must_equal "application/pdf"
60+
61+
proc{req("/invalid-mime-type")}.must_raise KeyError
62+
63+
# test when loading the plugin more than once
64+
app.plugin :response_content_type
65+
end
66+
end
67+
end

www/pages/documentation.erb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@
121121
<li><a href="rdoc/classes/Roda/RodaPlugins/RedirectHttpToHttps.html">redirect_http_to_https</a>: Adds request method to redirect HTTP requests to the same location using HTTPS.</li>
122122
<li><a href="rdoc/classes/Roda/RodaPlugins/RequestAref.html">request_aref</a>: Adds configurable handling for [] and []= request methods.</li>
123123
<li><a href="rdoc/classes/Roda/RodaPlugins/RequestHeaders.html">request_headers</a>: Adds a headers method to the request object, for easier access to request headers.</li>
124+
<li><a href="rdoc/classes/Roda/RodaPlugins/ResponseContentType.html">response_content_type</a>: More easily set content-type header for responses.</li>
124125
<li><a href="rdoc/classes/Roda/RodaPlugins/ResponseRequest.html">response_request</a>: Gives response object access to request object.</li>
125126
<li><a href="rdoc/classes/Roda/RodaPlugins/SinatraHelpers.html">sinatra_helpers</a>: Port of Sinatra::Helpers methods not covered by other plugins.</li>
126127
<li><a href="rdoc/classes/Roda/RodaPlugins/Status303.html">status_303</a>: Uses 303 as the default redirect status for non-GET requests by HTTP 1.1 clients.</li>

0 commit comments

Comments
 (0)