Is It Possible in Rails to Check Whether a Redirect or Render Had Already Been Issued

Is it possible in Rails to check whether a redirect or render had already been issued?

You can call performed? in your controller to check if render or redirect_to has been called already:

performed?               # => false
redirect_to(login_path)
performed? # => true

Read more about performed? in the Rails docs.

How can I determine whether a controller action has rendered (rather than redirected?)

Look at @performed_render. :) You don't explain why you need to detect this, so I am unable to suggest alternative solutions.

How do I check if a controller action is already redirecting?

I am assuming that the login_required method performs a redirect if the user is not logged in. In which case:

Your before filter should return false after calling redirect. This will prevent the new action from ever being called. Later versions of rails automatically do this if you call render or redirect in a before_filter, so maybe you are using an older version.

Also you should return after the call to redirect in the new handler, unless you want to always create a new Payment object.

Detect in Rails after_filter whether we're rendering or redirecting

You could look at the status code. 200 is a render, 302 is a redirect.

after_filter :what_happened

protected

def what_happened
was_redirect = self.status == 302
was_render = self.status == 200
end

Testing requests that have redirects in RSpec

You can check that a post was created and that a user was redirected, if params are valid. And if you have any validation in the Post model, it is good idea to test invalid params:

RSpec.describe 'PostsController', type: :request do
describe 'POST #create' do
context 'with valid params' do
it 'creates a new post' do
expect { post posts_path, params: { post: valid_attributes } }.to change(Post, :count).by(1)
expect(response).to redirect_to post_path(Post.last)
end
end

context 'with invalid params' do
it 'does not create a new post' do
expect { post posts_path, params: { post: invalid_attributes } }.not_to change(Post, :count)
expect(response).to have_http_status 200
end
end
end
end

how to check whether newly entering value is already present in database or not in rails

You can simply do it as,

image_present? = MImage.where(image_name: params[:image_name]).present?

Mostly Rails-uniqueness validation is used in model to add error if attribute already present for another object

Ruby on Rails 5 - Resource gives error when checking if it already exists - whether it does or not

Your conditional is checking the wrong thing. This:

if @product.product_code.present?

only checks if product_code is present in the @product you just made, it does't check if there's anything already in the database with that product code.

I think you want something more like this:

@existing_product = Product.find_by(product_code: @product.product_code)
if @existing_product
flash.now[:error] = ...
render 'new'
elsif @product.save
redirect_to @product
else
render 'new'
end

You can use find_by to find the thing you're looking for, that will either give you the product or nil if there isn't a matching product.

You might want to look into a couple other things:

  1. Using the URL helper rather than manually building a link would be better.
  2. Your model should be checking the uniqueness of the product codes rather than the controller.
  3. As Dorian mentions in the comments, HTML in the flash messages isn't the best idea. You'd be better off checking @existing_product in the view and then putting up the link and whatever "complex" error message you need entirely within the view.

How do I check to see if a user will be redirected to his/her profile after login (Rails Tutorial Ch. 9)?

I'm way late in answering this question, so it might be a moot point now, but I'm working on this exercise in the tutorial right now myself and had to struggle to come up with the correct answer, but I finally did. So I thought I'd share what I did to get the tests to pass in case you're still seeking an answer. First I'll point out where the flaw in your current logic is (because I made the same mistake initially and had to process the logic flow of the code to figure out what went wrong).

In your test, you're testing for equality between with assert_equal session[:forwarding_url], user_path(@user). The problem is, the user's user_path is never set as the forwarding url. In the sessions_helper.rb file, the redirect_back_or() method has this line: redirect_to(session[:forwarding_url] || default) (the default argument passed to this method is user when it's called in the create action of the sessions controller). So this line in the redirect_back_or() method can be read in plain English as

Redirect to the forwarding url if one has been set by the store_session method. If the forwarding url is nil, redirect to the default page, which in this case is the user page.

In order to test for equality as you're trying to do (and as I initially tried), it requires that at some point user_path(@user) or user would have to be set as the session's forwarding url. store_session takes no parameters to do this, so the only way user_path(@user) could be set as the forwarding url is if an unauthorized user were attempting to access that page and were first forced to log in then be redirected there (as best I can tell, this text does not require a person to be logged in to view a single user's page). So, user_path(@user) would never conceivably be set as the forwarding url in the store_session method, and thusly, assert_equal session[:forwarding_url], user_path(@user) would never evaluate to true.

At this point, the error message you cited in your original question is extremely helpful in solving your problem.

Expected: nil
Actual: "/users/762146111"

If you recall from the early chapters of Hartl's text (or from the Rails API) assert_equal tests take two required parameters: the expected value and the actual value and then compare the two. This is reflected in your error message: session[:forwarding_url] evaluates to nil while user_path(@user) evaluates to a specific user's url. Since, like I stated earlier, the user's profile page should never conceivably be set as the forwarding url, we can eliminate user_path(@user) entirely from our test.

Fortunately for us, the exercise in the textbook doesn't require that we user assert_equal to get the correct result. We just need to test that session[:forwarding_url] has the right value. That should be pretty easy. Let me walk through it step by step:

  1. User is not logged in and trying to access a page served via the index, edit, update, or destroy controller actions.
  2. Before filter catches this and runs the logged_in_user method. unless logged_in? evaluates to false, so the code in this method is run.
  3. store_location is called inside logged_in_user and the unauthorized user's get request is stored as the forwarding url. Simultaneously, the user is redirected to the log in page.
  4. Assuming the user enters valid credentials to successfully log in, this calls the session controller's create method, to create a new session. This action logs the user in and calls redirect_back_or().
  5. redirect_back_or() checks to see if session[:forwarding_url] has a value associated with it -- we know it was because store_location assigned a value to this key in step #3. So the first half of redirect_back_or()'s || statement is executed and the now-authenticated user gets redirected to the page they were initially trying to access before they logged in.
  6. redirect_back_or() then deletes the value associated with the forwarding url, since the user has been redirected there already.
  7. Now we try to log in again, this time by directly accessing the log-in page. This happens successfully, and the session controller's create action is called again.
  8. This time, we weren't trying to access a page we didn't have authorization to access, so the store_session method never gets called. Therefore, the forwarding_url is currently nil.
  9. When the session is created and redirect_back_or() gets called, session[:forwarding_url] evaluates to nil, so the second half of the || statement gets executed, namely redirect_to user (since user is the parameter passed to the method as its default).

To summarize, the correct value of session[:forwarding_url] for subsequent login attempts should be nil. You can write this test one of two ways:

  1. You can still use assert_equal to test for equality:

    assert_equal session[:forwarding_url], nil

  2. You can refactor your test to be more succinct and direct:

    assert_nil session[:forwarding_url]

I prefer test #2, but I tested them both and it worked just fine both ways.

Though not explicitly required in the original exercise, I also added a test after this one to ensure all subsequent redirects are sent to the user_path(@user):

assert_nil session[:forwarding_url]
assert_redirected_to @user



TL;DR: session[:forwarding_url] will never equal user_path(@user). If it hasn't been stored with store_location (which is the case with all subsequent login attempts), it will evaluate to nil, so a passing test for this exercise must be written one of two ways:

assert_nil session[:forwarding_url]
OR
assert_equal session[:forwarding_url], nil

How to use Rails instance public method performed? in if-else

In a controller action, when we type render or redirect_to those are not immediately executed, but they are queued and will be executed after the completion of the method. So this allows to have double renders or redirect_to in a controller action, and this will generate an error (because then rails has no idea which to execute). So that is why in rails they have added a method performed? which will indicate if a render or redirect_to has already been called (queued) or not.

In most cases this is not really needed because normally your controller code is pretty simple.

To clarify: performed? does not actually test the redirect_to has been done, it just tests a render or redirect-to was called/queued. Furthermore the redirect_to does not return a boolean indicating whether it was done or not.

So your code should be like this:

if condition
does something
else
redirect_to(records_path)
end
if performed?
# the redirect-to was executed
does another thing # but not a render or redirect
else
yet another thing
# and could be a render or redirect
# if not the normal view will be rendered
end

Please not that in this simple example the performed? is just the negative of condition so you could easily squash those together.



Related Topics



Leave a reply



Submit