Set response status before render in rails 4 - ruby-on-rails

I'd like to set the response status value in particular action methods before the render method is called. Is this not possible?
Many of my methods in controllers render JSON API views for action methods like #destroy, #update, #create and those actions simply invoke #show or #index as is appropriate. However, I'd like to also return the appropriate HTTP Response status value, like 201, 202, etc, without having to pass arguments to these methods. Essentially, I am looking for something like this:
def destroy
# code that kills
status :accepted # ArgumentError, status= silently fails
index
end

if you just call
response.status = ###
in a controller, and then don't add the :status argument at render, it should get you what you need.

you can use the method render by passing the parameter :status, example :
render nothing: true, status: 201

Related

Rails RABL: how to respond with specified http status code?

Basically I have the following controller method:
def create
begin
#check_in = create_check_in()
rescue => exception
render json: { message: exception }, status: 500
end
end
and the following json.rabl file:
object #check_in => :event_check_in
attributes :id
What I try to achieve is to set manually the HTTP status code of the response. It currently responds with 200, and I need it to be 201 instead.
I saw very few similar question and the answer was generally to render / respond_with from the controller action, so I tried something like this:
def create
begin
#check_in = create_check_in()
render #check_in, status: 201
rescue => exception
render json: { message: exception }, status: 500
end
end
but all my attempts failed, throwing various errors.
Is there a way I could set the status code?
The issue is you're passing in #check_in as the first argument of the render method when it expects the first argument to be a hash of options, including the status option.
Your status: 201 option is being passed in as a hash to the methods second argument and being ignored.
Typicallya render call will look something like:
render json: #check_in.as_json, status: 201
# or more commonly something like
render action: :create, status: 201 # the #check_in variable is already accessible to the view and doesn't need to be passed in
# or just
render status: 201
# since by default it will render the view with the same name as the action - which is `create`.
There are lots of ways to call render, see the docs for more.
-- EDIT --
Max has a great comment - I'd strongly advise against rescuing from all exceptions and also against doing it in a specific controller action. In addition to his suggestion, Rails 5+ supports :api formatting for exceptions out of the box or, if you need more, I'd look at a guide like this one.

Calling a ControllerA Action from Another ControllerB Action passing some params for ControllerA Action from ControllerB Action in Ruby on Rails

I would like to call a TestController#actiona from another ResultController#actionb passing the parameters for the actiona along with the call in rails.
Does anyone know what the most efficient way to achieve this is?
I have tried just calling the method like the code below but getting an error. What am I doing wrong?
Sample Pseudocode:
class TestController < ApplicationController
def actiona
# #value = do_something_with(params) // we are doing something with params
# render resource: #value, status: 201
end
end
class ResultController < ApplicationController
def actionb
# new_params = params + some_other_things // building new params
# #new_value = TestController.new.actiona(new_params) // we are passing the new_params
# render resource: #new_value, status: 201
end
end
I expect that a call to the ResultController#actionb calls the TestController#actiona with the params and renders the same #value that TestController#actiona would have returned if we called it directly.
actiona is an instance method. You can't call actiona on the class TestController.
Furthermore, if it would work, you will execute both call to render, from actiona and from actionb which make no sense. A call to a controller action must call render only once.
If you want to execute the actiona from the actionb, you can trigger a redirection using redirect_to with your params.
If you just want to render the view of the TestController, from the actionb, you can specify the expected view when using render method.
If you just want to reuse some code, you can extract that part of the code into a concern.

Why is yield not passing the result to block (Rails)?

I know there are several SO questions as well as online articles on using yield in Rails. But I'm still having trouble understanding what's wrong with my code below, and would appreciate any advice.
In my app, I have:
A controller that passes data to the command class's run method, and returns the request status based on the result of the Command.run (true/false)
A command class that deals with the actual meat of the process, then yields true if it succeeded, or false if it failed
However, the command class seems to be failing to yield the results to my controller. According to the error messages when I run my tests, it seems like my block in the controller isn't being recognized as a block:
# If I use "yield result":
LocalJumpError: no block given (yield)
# If I use "yield result if block_given?":
# (This is because I have "assert_response :success" in my tests)
Expected response to be a <2XX: success>, but was a <400: Bad Request>
How should I rewrite the block (do ... end part in the controller below) so that yield works correctly? Or if the issue lies elsewhere, what am I doing wrong?
I've provided a simplified version of my code below. Thank you in advance!
# controller
def create
Command.run(params) do
render json: { message: 'Successfully processed request' }
return
end
render json: { message: 'Encountered an error' }, status: :bad_request
end
# command class
def run(params)
# Do some stuff, then send HTTP request
# "result" below returns true or false
result = send_http_request.parsed_response == 'ok'
yield result
end
def self.run(params)
new.run(params)
end
Note: This code works if I use if true... else... in the controller instead of a block, and just return the boolean result instead of yielding it. But here I'd like to know how to make yield work.
In your controller you need to have a variable for the result.
def create
Command.run(params) do |result|
if result
render json: { message: 'Successfully processed request' }, status: :success
else
render json: { message: 'Encountered an error' }, status: :bad_request
end
return
end
render json: { message: 'Encountered an error' }, status: :bad_request
end
(EDIT)
Also, you are calling the class method which call the instance method. You have to pass the block from the calling code to the instance method you are calling.
def self.run(params, &block)
new.run(params, &block)
end
EDIT: ah, so you have a class method run and instance method run.
Either do as Marlin has suggested and supply the block explicitly from class method to the instance method.
Or use only the class method as I've initially suggested (it doesn't
seem like there's any reason to instantiate Command in your case):
def self.run(params, &block)
result = send_http_request.parsed_response == 'ok'
block.yield(result)
end

respond_with ArgumentError (Nil location provided. Can't build URI.):

I have a controller that responds_with JSON for all of the RESTful actions, index, create, update etc,
class QuestionsController
respond_to :json
def index
respond_with Question.all
end
end
However, I also have other actions in the controller. For example, in one method, it checks whether a response was correct and then tries to return a variable with a boolean true or false
respond_with correct_response #either true or false
However, this is giving me the error
ArgumentError (Nil location provided. Can't build URI.):
There will also be other methods that I'll wish to respond with multiple values. In Sinatra, you can do this to respond with json
{:word => session[:word], :correct_guess => correct_guess, :incorrect_guesses => session[:incorrect_guesses], :win => win}.to_json
How would I do that in Rails?
So, two questions, what's the proper way to write this
respond_with correct_response
and how to respond_with multiple values in a way similar to the example I showed from a Sinatra app.
Thanks for your help.
You want ActionController::Base#render, not respond_with. The proper way to do what you're trying to achieve here is:
render json: {word: session[:word], correct_guess: correct_guess, incorrect_guesses: session[:incorrect_guesses], win: win}
respond_with is actually OK for this scenario--it just happens to do some magic for you and relies on having access to info it needs; take a look at Rails 4.1.9's actionpack/lib/action_controller/metal/responder.rb.
In your case, ArgumentError (Nil location provided. Can't build URI.) is actually telling the truth--it's trying to determine a URL to use from the location header setting but isn't able to figure it out. I'd wager you could get your code working if you gave it one:
class QuestionsController
respond_to :json
def index
respond_with Question.all, location: questions_url
end
end

Rails 3 Custom Method Location

I am currently trying to add some parsing methods to a controller method in a Rails 3 application.
I have a controller action as follows:
def control
#device = Device.find(params[:id])
<do things>
parse_return(#returned_data)
end
and I added a custom method to the controller as below (this method would not have any routes and would only be accessible to controller actions):
def parse_return
<parse data>
end
but this does not appear to allow the parse_return method to be used. Is there somewhere else in the Rails app that I can put re-usable methods?
Thanks!
At a first glance it seems that you fail to render a response. Is it true that control action doesn't have an associated view?
In this case you have to manually call render in your action. For example, to render JSON response you can do this:
def control
# ...
render :json => parse_return(#returned_data),
:content_type => 'application/json',
:layout => false
end
You should include what the errors are.
What happens if you try this?
def parse_return(returned_data)
<parse data>
end
Perhaps the method is not expecting an parameter to be passed along with it.

Resources