The problem I have is very simple. Basically, I have a Rails action for my videos page. It makes a HTTP request and gets some response back. But I am trying to add error checking right now. My problem is it will NOT LEAVE this action after entering the if block. It appears to continue to try to run all the code after the if block... Which is bad because if it enters the if block, it means we didn't get a 200 OK response. There's nothing to run. Just throw an error message and yeet!
It is entering the if block (Gyazo link here).
def videos
# get current_user's wistia_project_id & authorization token
#current_user = current_user
project_id = #current_user.wistia_project_id
auth_token = "blah"
request = "https://api.wistia.com/v1/projects/#{project_id}.json?api_password=#{auth_token}"
#response = HTTP.get(request)
puts "BEFORE"
# handle errors (4xx & 5xx)
# catches client errors and server errors
# should print out and then LEAVE this entire action. (return)
if #response.status.client_error? || #response.status.server_error?
puts "INSIDE"
render text: "Sorry, error"
return
end
#response = HTTP.get(request).body
# get embed code for each video using the hashed_id, put in list
# BUT!!! for some reason it gets here and there's an error
# because there is nothing in the response (errors)
#video_iframe_urls = JSON.parse(#response)['medias'].map do |p|
"https://fast.wistia.com/embed/iframe/#{p["hashed_id"]}?version=v1&controlsVisibleOnLoad=true&playerColor=aae3d8"
end
end
render text: "Sorry, error"
change this line to plain
render plain: "Sorry, error"
the problem is not
it will NOT LEAVE this action after entering the if block.
because render is not support text options.you code actually is
if #response.status.client_error? || #response.status.server_error?
puts "INSIDE"
render
return
end
the render method render template name by action name as default
Related
Edit - I misunderstood the exact nature of the problem, have reposted here with a better grasp of the issue: Rails / Devise - updating session variables between controller actions
I am using a session variable to pass data between three form submission controller actions in my Rails app, but I am having an issue where the process works the first time I run through but fails the second time because the session variable does not pass to a subsequent controller action - however, if I clear my browser history, it works fine (but will fail if I try to repeat without clearing history, etc.). The app uses Devise for user log in, and the problem seems to be related to the session being destroyed on log out.
The session variable in question here is session[:ids], and it seems that it is not being passed from the submission action to the approval action without first clearing the browser history.
Here are the routes:
post 'report/submission'
get 'report/approval'
get 'report/summary' => 'report#summary'
And the associated controller actions (added a few groups of puts to check the variable values in the console):
def submission
#incomingReport = ActiveSupport::JSON.decode(params[:report])
#newReportIDArray = Array.new
#incomingReport.each do |x|
#DATA PROCESSING STUFF HERE
#newReportIDArray.push(#new_report.id)
end
session[:ids] = #newReportIDArray
puts "submission_marker"
puts #newReportIDArray
puts "submission_marker_end"
respond_to do |format|
format.json do
render json: {
success: 200
}.to_json
end
end
end
#USER LOGIN OCCURS HERE
def approval
#reportIDs = session[:ids]
puts "approval_marker"
puts #reportIDs
puts "approval_marker_end"
#reportIDs.each do |x|
#new_report = Report.find(x)
#new_report.user_id = current_user.id
#new_report.save
end
redirect_to report_summary_path
end
def summary
#reportIDs = session[:ids]
puts "summary_marker"
puts #reportIDs
puts "summary_marker_end"
end
#USER LOGS OUT AFTER SUMMARY
So if I run this with clean history, my console looks like this:
submission_marker
766
submission_marker_end
approval_marker
766
approval_marker_end
summary_marker
766
summary_marker_end
but if I log out after the first summary and then I try to run through the process without clearing the history, I end up with
submission_marker
767
submission_marker_end
approval_marker
approval_marker_end
and the error undefined method 'each' for nil:NilClass, referring to #reportIDs.each in the approval action.
What am I missing here? Why is it that with a clean history, everything runs ok, but with a 'dirty' history, the session variable sets for the submission action but is dropped by/on its way to the approval action? I see that this has something to do with Devise and the destruction of the session, but I don't understand why I can't create a new session variable.
I want to capture all the errors in a form and also ensure that the operation is atomic that is the form goes through if there are no errors else it just captures the error and no other correct fields are saved.
ActiveRecord::Base.transaction do
#var_types.each_with_index do |var,index|
begin
var.save!
puts "saving"
rescue
puts "rescued"
end
end
I am using the above block, although this captures all the errors in but atomicity is not guarenteed. Any way to correct this?
EDIT:
Example
An example would be say something like a form where a user has to enter multiple fields and multiple fields may not conform to the db rules, so i expect all the errors to be shown at once so the save as to keep happening to get all the errors but at the same time ensure that no correct changes get saved if there is even one error.
You have to catch the exception outside the transaction.
The transactions are rollbacked when an exception goes out the transaction block
begin
ActiveRecord::Base.transaction do
#var_types.each_with_index do |var,index|
var.save!
puts "saving"
end
end
rescue
puts "rescued"
end
UPDATE after reading your comment:
ActiveRecord::Base.transaction do
raise ActiveRecord::Rollback unless #var_types.map(&:save).all? #passing here a block like { |res| res == true } is redundant.
redirect_to some_index_path, notice: "Everything saved"
end
render action: 'edit' # something didn't pass the validation, re-render the view
Things to note here:
If you raise ActiveRecord::Rollback inside a transaction block, you don't need a rescue for it (read the docs at http://api.rubyonrails.org/classes/ActiveRecord/Rollback.html)
Someone might say that you should not drive the flow based in exception-throwing, if you don't feel comfortable with it, you can do something like this:
all_saved = false # need to define this var outside, or it will a block-level variable, visible only in the block
ActiveRecord::Base.transaction do
all_saved = #var_types.map(&:save).all?
raise ActiveRecord::Rollback unless all_saved
end
if all_saved
redirect_to some_index_path, notice: "Everything saved"
else
render action: 'edit' # something didn't pass the validation, re-render the view
end
I have action check_status in instances_controller, and I want to check on status code of URL before redirect_to it.
if status_code is 200 redirect_to it, else go to view page.
This is the pseudo-code of check_status action:
def check_status
if "http://www.web.com".status_code == 200
redirect_to "http://www.web.com"
else
#DO Nothing, and go to its view
end
end
For example get '/about' => 'instances#check_status', and i want to check if (web.com) get status=200 visit (web.com)
You can do this - but beware:
Your user will have to wait for your response check to complete before they get redirected. If you're checking a slow server, that could be up to 30 seconds before they get sent somewhere else.
There's no guarantee that the user will get the same result you got when you checked.
Here's some code that uses Ruby's Net::HTTP module to perform that web request:
require 'net/http'
def check_status(url)
uri = URI(url)
Net::HTTP.start(uri.host, uri.port) do |http|
request = Net::HTTP::Head.new uri.request_uri
response = http.request request
if response == Net::HTTPSuccess
redirect_to url and return
end
end
end
Make sure you're passing in full URLs to this method, complete with an http:// or https:// prefix, or this won't work.
If you were worried about that performance hit, you could cache the results for a short while and check those before returning. That is, when you look up a URL you can save the time of the lookup & the status code retrieved. Then, on the next lookup, if you've checked that domain in the past 24 hours, return the redirect immediately rather than checking it again.
For me just this one is working to check the response
The == one gets no match...
response.is_a? Net::HTTPSuccess
In addition to Alex's answer, you can use curl tool instead of Net::HTTP module.
system('curl www.google.com') # => true
system('curl www.google_not_valid.com') # => false
I built a json view to return json in one of ajax call in rails4 app. I have used the idea suggested here
https://stackoverflow.com/a/12832116/1560470
But I always keep getting status code as 200, even if I enforce other status code.
My jbuilder view in view/managers/create.json.jbuilder looks as follows:
if #manager.errors.messages.any?
envelope(json, :unprocessable_entity, #manager.errors.messages) do
json.success false
end
else
envelope(json, :created) do
json.success true
end
end
My application helper lloks as follows:
module ApplicationHelper
def envelope json, status, errors
json.status status
json.data do
yield if block_given?
end
json.errors errors
end
end
My controller is as follows:
def create
#manager = Manager.new manager_params
#saved = ( #manager.valid? && #manager.save )
end
You can see even I am passing status params value as :unprocessable_entity in my jbuilder view, still response comes back as 200 every time. Even I use any status code, it always return 200. Status codes are defined at http://guides.rubyonrails.org/layouts_and_rendering.html
I had the same issue, I found success with a call to render followed by whatever status you want to issue. At the bottom of create put the following
render status: 400
Reference: https://stackoverflow.com/a/28144206/3826642
You can move the logic from that envelope method to the jbuilder template so you're passing status directly from controller to the view.
That status you have in the envelope method will only be in the json that is rendered, it's not a http response status code sent by the server whereas as the one in the render method is
I am making an activeresource call to a service, and I'd like some custom error messages as feedback. I have some validations that aren't normal model validations, so I can't just return #object.errors.
So, for instance, one of my validations is this. Two objects have a many to many relationship, but I want to restrict one object to only have a limited number (say 2) of relationships to other objects. Here's some code:
In the client:
response = Customer.find(customer_id).put(:add_user, :user_id => user_id)
This puts a request to add a user to the customer. Then in the service I want to check that this addition is valid.
def add_user
#user = User.find(params[:user_id])
#customer = Customer.find(params[:id])
if #customer.users.length > 2
render :xml => "ERR_only_2_users_allowed", :status => :unprocessable_entity
end
end
Here's my problem. In active resource, if the return status is an error, the client side completely fails. I could change the status to 200 and I get back the body err msg fine, but this seems to defeat the purpose of having error reponse codes.
I can put the whole request call from the client in a begin/rescue block
begin
response = Customer.find(customer_id).put(:add_user, :user_id => user_id)
rescue ActiveResource::ResourceInvalid => e
#return error code
end
but when I catch the 422 (unprocessable_entity) response, I get nothing of the body back, so I don't get my custom error message. response = nil
Does anyone know how I can achieve these custom error message with the proper response codes?
This may or may not be your problem, but both of ours seem very close. I'm using a custom put method, but his should work for you too. What's going on is that the code that does this:
rescue ResourceInvalid => error
errors.from_xml(error.response.body)
end
Is only working with the standard save method. If you want errors added when other methods are called it looks like you need to do it yourself.
I had to add it to
vendor/rails/activeresource/lib/active_resource/custom_methods.rb
Here is what my diff from git looks like:
old code:
def put(method_name, options = {}, body = '')
connection.put(custom_method_element_url(method_name, options), body, self.class.headers)
end
new code:
def put(method_name, options = {}, body = '')
begin
connection.put(custom_method_element_url(method_name, options), body, self.class.headers)
rescue ResourceInvalid => error
errors.from_xml(error.response.body)
end
self
end
So look at the stack trace when get the exception thrown for the 422 and see which method it's calling exactly. Then add something like what I have and you should be good to go.
Don't ask me why the activeresource folks thought validations should only work with their save method. the save method does a create or update, but calling 'put or post' is the exact same thing, IMO. If we want validations to work on save we want them to work on put and post...anyway give it a shot.
I'm not sure if i need the self at the end...i may not. I'm not totally done with this as I just figured out how to make it work.
Erik
I think that your problem might be the response isn't an xml document but just a plain string. Try changing your render statement to something like:
render :xml => { :error => "ERR_only_2_users_allowed" }, :status => :unprocessable_entity