how to display error information onto another page? - ruby-on-rails-3

how to display error onto another page?
I have a form under user folder
And in another place, my_account folder, i render the form from user folder inside the index page of my_account. so that i can reuse the form,to allow user to update their user information.
So when user click update, update method in UserController will be invoked.
My question is, if faild to update user object, how can i display error message on to my_account index page, and still have the field highlight, and error message??
e.g invalid format of email address
User
-- new.html.erb
-- _form
my_account
-- index.html.erb
I tried to do following, but not sure how can i print the "error" in my_account page:
// try to update user information
// if failed, redirect to my account page
format.html { redirect_to my_account_path, :error => #user.errors }

I'm not sure about it but might work for you.
In update method of user_controller when you get any error just redirect to the index page of my_account and pass the error details with it to be displayed on index page. ie.:
def update
#user = User.find(params[:id])
## update process and if fails
redirect_to :action=> 'index', :status=>:unprocessable_entity, :error=>#user.errors
end

You need the errors to be set on the user object. This is used by form helper to display error messages when calling <%= f.error_messages %> inside your <% form_for %> block.
The Rails best-practice is to call render here, not redirect, so that we ensure we pass the #user object that has errors properly assigned.
To render the action, you can simply call:
if !#user.save
render :action => '../my_account/index'
end
This should easily solve your problem; just make sure to have all of your #member-variables set that the index.html.erb view expects.

Related

Rails goes to show action when I want custom action of open or expired

I am basically trying to do a custom index
In my routes.rb I have:
resources :tokens do
resources :visits
collection do
get :open
get :expired
end
end
My routes show as :
open_tokens GET /tokens/open(.:format) tokens#open
expired_tokens GET /tokens/expired(.:format) tokens#expired
I have open and expired defined in my tokens controller
def open
#q = Token.open_token.includes(:households, :visits).search(params[:q])
#tokens = #q.result
respond_to do |format|
format.html {render template: 'tokens/open'}
end
end
def expired
#q = Token.expired_token.includes(:households, :visits).search(params[:q])
#tokens = #q.result
respond_to do |format|
format.html {render template: 'tokens/expired'}
end
end
But it goes to my show controller with the following error:
Couldn't find Token with id=open
and it crashes at my Show action:
Better Errors shows the request as:
{"action"=>"show", "controller"=>"tokens", "id"=>"open"}
It seems to me that the action should be open with no parameters
What am I doing wrong?
I think I may have answered my question since it appears that Rails can only have one index per controller so I couldn't define another view that acts as an index.
So now what I want to do is pass a parameter so I can apply either an open scope or an expired scope

Template missing error in case of CSV format

I am generating a csv file as a report. It works fine if model validations are passed. In case of failed validations I want to show errors on the "new" action view and keep user there until they correct errors, just like any HTML form. However since I am using format=>'csv' with my form I am getting template missing error when the user input fails model validations.
Missing template /new.html with {:formats=>[:csv],
I tried the following render in my controler but it did not work:
format.csv { render :template=>"new.html.erb"}
I even tried to add a handler in an initializer but did not work:
ActionController::Renderers.add :csv do |csv, options|
self.content_type ||= Mime::CSV
self.response_body = csv.respond_to?(:to_csv) ? csv.to_csv : csv
end
Can you redirect_to you new path in the controller?
redirect_to :action => 'new', :format => 'html'

How to render validation errors on the page that submitted the form?

I've got a comments form in the article/show page. In this page, it displays the article and has a comments form.
When I submit a comment that has validation errors, I need it to go back to the article/show page, and display the errors there.
Should I change render :action => 'new' to something else?
In the Comment controller, I tried:
def create
...
if #comment.save?
redirect_to article_path(#comment.article), :notice => "Posted comment!"
else
# render :action => 'new'
render 'articles/show"
end
end
But this will complain, since the app won't know which article to show based on the ID.
EDIT: I found this solution. The approach would be to use a session to pass the errors instead. Is this the right way to go with this?
Try this
def create
...
if #comment.save?
redirect_to article_path(#comment.article), :notice => "Posted comment!"
else
# render :action => 'new'
#article = #comment.article
render 'articles/show"
end
end`
So fix your routing so the app does know what article to show based on the ID.

How to redirect page after confirmation in Devise

Say a user clicks a link to a page that is protected. They are then redirected to a sign in screen where they can log in. If they do, then are successfully redirected to that page. But if they don't have an account they have to sign up. This is where things get tricky because I'm doing an email confirmation.
By clicking a link it creates a new session can I can't automatically redirect the user to that protected page. I'm trying to change this by putting in a reference to the redirect inside the confirmation link. I would like to do:
<%= link_to 'Confirm my account', confirmation_url(#resource, :confirmation_token => #resource.confirmation_token, :redirect_to => stored_location_for(#resource)) %>
But I can't figure out how to get access to stored_location_for (or if that is even the right location to get). It is defined in Devise::Controllers::Helpers, but it is an instance method so I can't do Devise::Controllers::Helpers.stored_location_for(…).
How do I get access to stored_location_for or what is the better way of doing this?
My goal is to do that and then in my custom ConfirmationsController define:
def show
if params[:redirect_to]
session["user_return_to"] = params[:redirect_to]
end
super
end
That should work right?
I figured it out. I'm not sure if this changes with the update Devise did yesterday in making Devise::Mailer put most of its functionality into a module. (See the code and ticket for more information).
Basically it boils down to not being able to access the session inside of a mailer view. Therefore you have to pass the redirect as a variable. Devise uses an after_create method on your resource (User in my case) which then sends the confirmation email. This meant I couldn't just pass the session variable directly to the mailer. Thus I feel like this is a pretty nasty work-around in order to get this functionality, but here is the code:
To get the redirect_to variable into the mailer you have to add a variable to the user, thus:
class User < ActiveRecord::Base
…
attr_accessor :return_to
…
end
Then you have to set that variable when you create the user for the first time.
I already had a custom controller setup for registration. (See Devise' Readme on how to set this up, or see #ramc's answer for direction). But it was relatively easy to do this part, I just added it to the parameters and let the rest take care of itself.
class RegistrationsController < Devise::RegistrationsController
def create
params[:user][:return_to] = session[:user_return_to] if session[:user_return_to]
…
super
end
end
Now the user has a variable return_to which is set. We just need to access that in the confirmation_instructions email. I've already rewritten part of confirmation_instructions.html.erb so inside there I just added:
<% if #resource.return_to %>
<%= link_to 'Confirm my account', confirmation_url(#resource, :confirmation_token => #resource.confirmation_token, :redirect_to => #resource.return_to) %>
<% else %>
<%= link_to 'Confirm my account', confirmation_url(#resource, :confirmation_token => #resource.confirmation_token) %>
<% end %>
(For those who are new to this, #resource is the variable Devise uses to define your user).
Now once the user clicks on that link we need to redirect them. #ramc's before filter works well for this:
class ConfirmationsController < Devise::ConfirmationsController
before_filter :set_redirect_location, :only => :show
def set_redirect_location
session[:user_return_to] = params[:redirect_to] if params[:redirect_to]
end
end
That will take care of the case where a new user goes to a protected page then signs up, clicks on the confirmation link and is properly redirected to the protected page.
Now we just need to take care of the case where a user does the above, but instead of clicking on the link, they try to go back to the protected page. In this case they are asked to sign-up/sign-in. They sign-in and then are asked to confirm their email and are given the option of resending the confirmation email. They put in their email and now we need to put the redirect_to variable in that new confirmation email.
To do this we need to modify the ConfirmationController, similarly to how we did the RegistrationController. This time we need to modify the create method. The way it works out of the box is to call a class method on the user called send_confirmation_instructions. We want to rewrite that method so we can pass the return_to variable into it.
class ConfirmationsController < Devise::ConfirmationsController
def create
self.resource = resource_class.send_confirmation_instructions(params[resource_name],session[:user_return_to])
if resource.errors.empty?
set_flash_message(:notice, :send_instructions) if is_navigational_format?
respond_with resource, :location => after_resending_confirmation_instructions_path_for(resource_name)
else
respond_with_navigational(resource){ render_with_scope :new }
end
end
end
The only thing different than what comes with Devise is that first line of create, we pass two variables in. Now we need to rewrite that method:
class User < ActiveRecord::Base
def self.send_confirmation_instructions(attributes={},redirect=nil)
confirmable = find_or_initialize_with_errors(confirmation_keys, attributes, :not_found)
confirmable.return_to = redirect if confirmable.persisted?
confirmable.resend_confirmation_token if confirmable.persisted?
confirmable
end
end
confirmable becomes an instance of User (the current user based on email). So we just need to set return_to.
That's it.
Looking at the way stored_location_for has been implemented in lib/devise/controllers/helpers.rb
def stored_location_for(resource_or_scope)
scope = Devise::Mapping.find_scope!(resource_or_scope)
session.delete("#{scope}_return_to")
end
It is possible to otherwise access it using session['user_return_to']. In your case, you would lose that session object because when the user clicks on the link from the confirmation mail, it might be a new session that is spawned.
You can implement whatever you have suggested as a before filter:
class Users::ConfirmationsController < Devise::ConfirmationsController
before_filter :set_redirect_location, :only => :show
def set_redirect_location
session["user_return_to"] = params[:redirect_to] if params[:redirect_to]
end
end
In addition to this, you will have to modify the route to make devise call your controller instead of its own confirmation controller.
devise_for :users,
:controllers => { :confirmations => 'users/confirmations'}
Hope this helps :)
Note: The code snippets are not complete and only contain relevant details.
From what I can see from the comments in the devise source code, all you need to do is implement the following in your registrations_controller.rb:
def after_inactive_sign_up_path_for(resource_or_scope)
session["user_return_to"]
end

How to Pass incoming params with redirect_to?

I'm a neophyte to Ruby On Rails (3). Using Get and Post, I'm trying to create a simple login functionality.
After the login credentials are SUBMITed, I've written the following code in my controller,
def login
#title = "Test Login"
if request.post? and params[:user]
#user = User.new(params[:user])
user = User.find_by_username_and_password(#user.username , #user.password)
if user
session[:user_id] = user.id
flash[:notice] = "User #{user.username} logged in!"
redirect_to :action => "index" , :username => params[:username]
The problem lies here, I want to print the name that was entered in the username textfield.
Hence, I'm redirecting it to index along with params. Please help me, get my syntax right, inorder to transfer the username parameter to index with redirect_to.
That should work.
Is the code giving you an error?
Updated from data.
Think I got what is happening. It is how you are trying to display it.
First inside your index method you need a line such as
#username = params[:username]
the #username will make the variable available to your index.html.erb file as it becomes an instance variable for the controller.
In index.html.erb
Welcome Back <%= #usename %>
That should do it.