-
Notifications
You must be signed in to change notification settings - Fork 204
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Respond_with uri extensions path.[json|xml|text|txt|html] #367
Changes from 11 commits
27b082c
6c26a1c
a4b04e9
f9eae91
d391c70
d8fbfdf
dce4065
cfe11a1
606f906
d2eac6f
b037006
a03ed80
1452b8b
25b972e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -83,6 +83,44 @@ module Amber::Controller | |
context.request.headers["Accept"] = "text/plain" | ||
ResponsesController.new(context).index.should eq expected_result | ||
end | ||
|
||
it "responds with json for path.json" do | ||
expected_result = %({"type":"json","name":"Amberator"}) | ||
context.request.path = "/response/1.json" | ||
ResponsesController.new(context).index.should eq expected_result | ||
end | ||
|
||
it "responds with xml for path.xml" do | ||
expected_result = "<xml><body><h1>Sort of xml</h1></body></xml>" | ||
context.request.path = "/response/1.xml" | ||
ResponsesController.new(context).index.should eq expected_result | ||
end | ||
|
||
it "responds with text for path.txt" do | ||
expected_result = "Hello I'm text!" | ||
context.request.path = "/response/1.txt" | ||
ResponsesController.new(context).index.should eq expected_result | ||
end | ||
|
||
it "responds with text for path.text" do | ||
expected_result = "Hello I'm text!" | ||
context.request.path = "/response/1.text" | ||
ResponsesController.new(context).index.should eq expected_result | ||
end | ||
|
||
it "responds with 406 for path.text when text hasn't been defined" do | ||
expected_result = "Response Not Acceptable." | ||
context.request.path = "/response/1.text" | ||
ResponsesController.new(context).show.should eq expected_result | ||
context.response.status_code.should eq 406 | ||
end | ||
|
||
it "respond_with default or accept header request if extension is invalid" do | ||
expected_result = "<html><body><h1>Elorest <3 Amber</h1></body></html>" | ||
context.request.path = "/response/1.texas" | ||
context.request.headers["Accept"] = "text/html" | ||
ResponsesController.new(context).index.should eq expected_result | ||
end | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there a way to check the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think so. I'll check it I guess. Since they're all set at the some time I didn't think it was important to set that since the response was right. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For some reason I'm getting an error that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I can set it though. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In base_spec we should be verifying the context context.response.body.should
context.response.status_code.should
context.response.headers["Content-Type"].should I think it is important that is done this way because something else could be changing the response. This is how I found the issue with the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unless you are covering testing status code and content-type in the unit test for Responders::Content context.response.status_code.should There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good point. I was put off by the fact that there was no getter for content_type. Makes sense that it's getting written to the header though. I'll test that. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
end | ||
|
||
describe "#render" do | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,17 @@ | ||
module Amber::Controller::Helpers | ||
module Responders | ||
class Content | ||
TYPE = {html: "text/html", json: "application/json", text: "text/plain", xml: "application/xml"} | ||
TYPE = { | ||
html: "text/html", | ||
json: "application/json", | ||
txt: "text/plain", | ||
text: "text/plain", | ||
xml: "application/xml", | ||
} | ||
|
||
TYPE_EXT_REGEX = /\.(#{TYPE.keys.join("|")})$/ | ||
ACCEPT_SEPARATOR_REGEX = /,|,\s/ | ||
|
||
@requested_responses : Array(String) | ||
@available_responses = Hash(String, String).new | ||
@type : String? = nil | ||
|
@@ -31,40 +41,46 @@ module Amber::Controller::Helpers | |
end | ||
|
||
def type | ||
select_type.to_s | ||
(@type ||= select_type).to_s | ||
end | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we move def type
@type ||= select_type.to_s
end I like the convention of def property_name
@property_name ||= calculate_property_name
end Of course, 'calculate' can be 'select' or 'generate' - what ever verb makes sense. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would have done that but I needed to return @type.to_s so that @type wouldn't have the possibility of being Nil. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I did come up with a solution here that I think we'll both like. |
||
|
||
def body | ||
@available_responses[type] | ||
@available_responses[type]? | ||
end | ||
|
||
private def select_type | ||
@type ||= begin | ||
raise "You must define at least one response_type." if @available_responses.empty? | ||
@requested_responses << @available_responses.keys.first | ||
@requested_responses.find do |resp| | ||
@available_responses.keys.includes?(resp) | ||
end | ||
raise "You must define at least one response_type." if @available_responses.empty? | ||
# NOTE: If only one response is requested don't return something else. | ||
@requested_responses << @available_responses.keys.first if @requested_responses.size != 1 | ||
@requested_responses.find do |resp| | ||
@available_responses.keys.includes?(resp) | ||
end | ||
end | ||
end | ||
|
||
private def requested_responses | ||
req_responses = Array(String).new | ||
private def extension_request_type | ||
path_ext = request.path.match(Content::TYPE_EXT_REGEX).try(&.[1]) | ||
return [Content::TYPE[path_ext]] if path_ext | ||
end | ||
|
||
if (accept = context.request.headers["Accept"]?) && !accept.empty? | ||
accepts = accept.split(";").first?.try(&.split(/,|,\s/)) | ||
req_responses.concat(accepts) if accepts.is_a?(Array) && accepts.any? | ||
private def accepts_request_type | ||
accept = context.request.headers["Accept"]? | ||
if accept && !accept.empty? | ||
accepts = accept.split(";").first?.try(&.split(Content::ACCEPT_SEPARATOR_REGEX)) | ||
return accepts if !accepts.nil? && accepts.any? | ||
end | ||
req_responses | ||
end | ||
|
||
private def requested_responses | ||
extension_request_type || accepts_request_type || [] of String | ||
end | ||
|
||
protected def respond_with(&block) | ||
content = with Content.new(requested_responses) yield | ||
if content.body | ||
set_response(body: content.body, status_code: 200, content_type: content.type) | ||
else | ||
set_response(body: "Response unexceptable.", status_code: 406, content_type: Content::TYPE[:text]) | ||
set_response(body: "Response Not Acceptable.", status_code: 406, content_type: Content::TYPE[:text]) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hi @elorest I didn't see it 😅 Why not There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was under the impression that we didn't want to raise exceptions because it was slower or something. I remember @eliasjpr saying that was the reason he made halt instead of just raising and exception to handle later. Also since I'm setting a response anyone it sort of seems natural to set the other response and message. |
||
end | ||
end | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -78,7 +78,18 @@ module Amber::Router | |
end | ||
|
||
def merge_route_params | ||
route_params.each { |k, v| params[k] = v } | ||
route_params_without_ext.each do |k, v| | ||
params[k] = v | ||
end | ||
end | ||
|
||
def route_params_without_ext | ||
rparams = route.params | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. you can use Thanks for splitting it into 2 separate methods. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I just wanted something short and temporary what wouldn't overwrite anything else. To me rparams looked better than _params since it was still more descriptive. WDYT? |
||
unless rparams.empty? | ||
key = rparams.keys.last | ||
rparams[key] = rparams[key].sub(Controller::Base::Content::TYPE_EXT_REGEX, "") | ||
end | ||
route.params | ||
end | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For line 90, And changing the value at |
||
|
||
def route_params | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add a test for an invalid (unsupported) extension?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah. I'll commit that in a second. I already have a test for a supported but undefined extension.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.