current_page? wrong after POST - ruby-on-rails

I'm displaying certain items in my navigation bar depending on the current page. When I go to my sign in page the correct items are displayed. If I sign in with an incorrect password the items change and are incorrect.
In my html I check if (current_page?(new_user_session_path))
After the incorrect password is submitted and the page reloads this condition isn't returning true and it's displaying the wrong items in the navbar. I looked through the requests on the server logs and I'm guessing it's because the second time around the page loads after a POST (the unsuccessful password submission). Is there a different path I need to check for the second time?

Expanding on Scott's answer, you could create a helper in app/helpers/navigation_helper.rb for instance, like so:
module NavigationHelper
def current_location?(*args)
options = args.extract_options!
options.each do |key, val|
return false unless eval("controller.#{key.to_s}_name") == val
end
true
end
end
And use it this way:
current_location?(controller: 'my_controller', action: 'new')
current_location?(controller: 'my_controller')
current_location?(action: 'new')
In your view you can then do something like:
# Change this according what your really need
if current_location?(controller: 'sessions', action: 'new')
Hope it helps ; )

If you look at the source code of current_page?, it always returns false if the request's HTTP mode is anything other than GET or HEAD:
http://api.rubyonrails.org/classes/ActionView/Helpers/UrlHelper.html#method-i-current_page-3F
def current_page?(options)
unless request
raise "You cannot use helpers that need to determine the current " "page unless your view context provides a Request object " "in a #request method"
end
return false unless request.get? || request.head?
...
So even if your incorrect form is at exactly the same path as new_user_session_path, your logic won't match.
You may want to consider comparing controller.controller_name and controller.action_name directly instead. Not exactly elegant, but it's going to be more reliable.

Related

Rails: Conditional redirect in rails app controller

I have an action in a controller that I call from two different views. In each case, I want the action to redirect back to the page on which the link was clicked. At the moment I am doing this...
In this view I am passing a parameter like this...
%a.showtooltip#wprofile{:href => idea_vote_up_path(#idea, :source => 'idea'), :title => 'Awesome idea - vote up !', }
and in the controller...
if params[:source] == 'idea'
redirect_to idea
else
redirect_to ideas_path
end
This works fine, but does not feel elegant, especially as it ends up being in a few actions. Is there a better way?
You can rewrite it in following way:
redirect_to params[:source] == 'idea' ? idea : ideas_path
If you want to redirect back to the page (refresh current page)
redirect_to request.referer
Store the referrer in the session like so session[:previous] ||= request.referer and use it as redirect_to session.delete(:previous)
I find that a good way is to have a hidden input with the value you'd like to be as the return url. Seems like an easily manageable solution and has worked for me. This way you can create the hidden input in 1 or 1000 views and have a single line of code in the controller to do the redirects. I can't immediately think of what the cons to this approach would be.
In form
hidden_field_tag(:redirect_to, params[:redirect_to]) # in the form that is to be submitted, value determined by a query string
hidden_field_tag(:redirect_to, "/a/direct/value") # in the form, value specified directly
In controller
redirect_to params[:redirect_to].presence || idea_path(#idea)
Didn't test the code and don't know ruby sups well so double check but the logic should stand. The ".presence" takes care of situations where you don't want a custom redirect to and have no hidden input to specify.

conditionally contain arguments in url path in rails

I have a search function which allows a user to search for items. From the generated list, the user can add items to a list. After adding an item from the list, the page reloads and the user is returned to their current search list. However, currently, if a user has not searched for anything, and just adds an item of the top of the list (starting at 'A, for example), when the page reloads it includes a blank search query in the url. I know why, it is because of this line
redirect_to admin_job_job_products_path(#job, search_term: params[:search_term])
I want to make the last argument in the redirect path a conditional one so that, if say the :search_term == nil || == "" || " " then the redirect_to only includes the first argument.
How would I best achieve this?
Thanks in advance!
You can do:
if params[:search_term].present?
redirect_to admin_job_job_products_path(#job, search_term: params[:search_term])
else
redirect_to admin_job_job_products_path(#job)
end
Or
redirect_to admin_job_job_products_path(#job, search_term: params[:search_term].presence)
References:
.present? method documentation
.blank? method documentation
The second example of MrYoshiji probably will not work, but you can write it short and simple this why:
redirect_to admin_job_job_products_path(#job, search_term: params[:search_term].present? ? params[:search_term] : nil)
So if the result will be nil rails will not include that param in the url

What is the use of request.referer?

I want to know what the following code does. What is the use of request.referer?
#board = request.referer['dashboard'] if request.referer
request.referer gives you the previous URL or / if none. It is usually used to redirect the user back to the previous page (link)
More information here
Regarding your question, it is simply returning 'dashboard' if found in request.referer. Look at the following example:
> str = "hello world!"
=> "hello world!"
> str['hello']
=> "hello"
> str['lo wo']
=> "lo wo"
> str['foo']
=> nil
However, you should not depend on this method to redirect your user back. You can do this in your controller instead:
redirect_to :back
request.referer gives you the previous URL or / if none
In library you can see:
def referer
#env['HTTP_REFERER'] || '/'
end
You can use the referer technique for this, but you'll have to capture it when entering the form instead of when the form is submitted. Something like this:
<%= hidden_field_tag :referer, (params[:referer] || request.env['HTTP_REFERER']) %>
Then you can use params[:referer] in the controller to redirect back.

Rails - How to create a 'previous page' object/method?

How can I obtain the path of the previous page so I can use it in an if-statement like so:
if previous_page == example_path
...
else
...
end
Edit:
For clarification, I'm setting custom redirects for User Updates with Devise. The redirects go through a method called after_update_path_for(resource) and I'm adding playing around with that.
You could work with the referer from the request object in your controllers.
request.referer
=> http://localhost:3000/contacts
Use the session to store the current page, so that you can check later (on the next page) which one it was.

Checking for nil in view in Ruby on Rails

I've been working with Rails for a while now and one thing I find myself constantly doing is checking to see if some attribute or object is nil in my view code before I display it. I'm starting to wonder if this is always the best idea.
My rationale so far has been that since my application(s) rely on user input unexpected things can occur. If I've learned one thing from programming in general it's that users inputting things the programmer didn't think of is one of the biggest sources of run-time errors. By checking for nil values I'm hoping to sidestep that and have my views gracefully handle the problem.
The thing is though I typically for various reasons have similar nil or invalid value checks in either my model or controller code. I wouldn't call it code duplication in the strictest sense, but it just doesn't seem very DRY. If I've already checked for nil objects in my controller is it okay if my view just assumes the object truly isn't nil? For attributes that can be nil that are displayed it makes sense to me to check every time, but for the objects themselves I'm not sure what is the best practice.
Here's a simplified, but typical example of what I'm talking about:
controller code
def show
#item = Item.find_by_id(params[:id])
#folders = Folder.find(:all, :order => 'display_order')
if #item == nil or #item.folder == nil
redirect_to(root_url) and return
end
end
view code
<% if #item != nil %>
display the item's attributes here
<% if #item.folder != nil %>
<%= link_to #item.folder.name, folder_path(#item.folder) %>
<% end %>
<% else %>
Oops! Looks like something went horribly wrong!
<% end %>
Is this a good idea or is it just silly?
No you should use
<% if #item.nil? %>
for example
#item1=nil
if #item1.nil? ### true
#item2 = ""
if #item2.nil? ### false
#item3 = []
if #item3.nil? ### false
#item4 = {}
if #item4.nil? ### false
To check An object is blank if it‘s false, empty, or a whitespace string.
use
<% if #item.blank? %>
ref:- this
for example
#item1=nil
if #item1.blank? #### true
#item2 = ""
if #item2.blank? #### true
#item3 = []
if #item3.blank? #### true
#item4 = {}
if #item4.blank? #### true
Your example code remade:
controller code. ( I assume this is ItemsController )
def show
# This will fail with 404 if item is not found
# You can config rails to pretty much render anything on Error 404
#item = Item.find(params[:id])
# doesn't seem to be used in the view
# #folders = Folder.find(:all, :order => 'display_order')
# this is not needed anymore, or should be in the Error 404 handler
#if #item == nil or #item.folder == nil
# redirect_to(root_url) and return
#end
end
view code, since the controller made sure we have #item
#display the item's attributes here
<%= item_folder_link(#item) %>
helper code:
# display link if the item has a folder
def item_folder_link(item)
# I assume folder.name should be a non-blank string
# You should properly validate this in folder model
link_to( item.folder.name, folder_path(item.folder) ) if item.folder
end
Anyway, I try to keep view very very simple. Usually if I see loops and conditionals in views, I try to refactor them into helpers.
Don't forget .try, which was added in Rails 2.3. This means that you can call something like the following:
#object.try(:name)
And if #object is nil, nothing will be returned. This is perhaps the built-in solution for sameera207's idea.
Ideally, you shouldn't be sending nil objects through to the view - however it's not always possible to avoid.
I personally think that if you are checking nil in your views (and I think since the view is the ultimate presentation layer nil should be checked in that level), you don't want to check it in the controller. (but this will not apply to all the places)
I would recommend you to create a method to check nil (to make it little DRY) and pass your object and check if it is nil or not
something like:
def is_nil(object)
object.nil? ? '':object
end
and add it in the application controller and make it a helper (so that you can use it in both controllers and views)
(helper_method :is_nil - add this line to your application controller)
and now you can pass the object you want to check if it is nil or not.
cheers,
sameera
Your controller is responsible for deciding which view is going to be rendered. If you can verify that your controller will never render this particular view without an item or item_folder then you do not need to check for nil values.
By can verify I mean that you have tests/specs that check which view is rendered for nil items and item_folders.

Resources