I am using Rails 4 and moved from CakePHP.
I have a User Model and to create a new record it uses two Actions - New and Create.
Now when i want to over ride the default for my app. i would like the users to go to Signup action to create a new user. Now when i have a Server side validation and it fails i am posting the form to lets say "create" action the user is shown in the url
'app.com/user/create' instead of 'app.com/user/signup'
Is there any way to keep the user in the same action instead of have multiple action just to display form and save the form?
# GET /users/new
def new
#user = User.new
end
# POST /users
def create
#user = User.new(user_params)
if #user.save
redirect_to #user, notice: 'User was successfully created.'
else
render :new
end
end
You should simply add a redirect in your create action when the user creation fails.
redirect_to :back, #user
I would not recommend using :back all the time but this is going to be helpful for now as by understanding the scenario you have mentioned.
By default, action new just initialize model with-or-without params. Action create save model to database. app.com/user/create is not RESTful and "Rails Way".
users_path #=> app.com/users
new_user_path #=> app.com/users/new
user_path(:id) #=> app.com/user/:id
edit_user_path(:id) #=> app.com/user/:id/edit
# and so on
In controllers you can define redirections for every action. For example:
def create
if #user.save
redirect_to user_path(#user)
else
redirect_to :back # return to previous page
end
end
More information about routing here: http://guides.rubyonrails.org/routing.html
I would stick with rails conventions but you should be able to do this if you really wanted
Routes.rb
get 'signup', to: 'users#signup'
post 'signup', to: 'users#signup'
Controller
class UsersController < ApplicationController
def signup
if request.get?
#user = User.new
elsif request.post?
#user = User.new(user_params)
if #user.save
redirect_to root_url, notice: 'Signed In'
else
#should just render signup as it's signup action
end
end
end
end
Related
What does redirecting to a particular instance mean? I am aware of how the redirecting works.
class UsersController < ApplicationController
def show
#user = User.find(params[:id])
end
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
log_in #user
flash[:success] = "Welcome to the Sample App!"
redirect_to #user
else
render 'new'
end
end
def edit
#user = User.find(params[:id])
end
private
def user_params
params.require(:user).permit(:name, :email, :password,
:password_confirmation)
end
end
I understand the following ways of redirecting:
redirect_to :new (redirect to new method and displaying the new.html.erb file)
redirect_to "show" (redirect to show.html.erb file or the path for show method)
but what does redirect_to #user mean? Which method and path are we are redirecting to?
If you check the redirect_to documentation, you will find this.
Record - The URL will be generated by calling url_for with the options, which will reference a named URL for that record.
It's Rails "magic" for redirecting to the #show action for that #user using GET. You'll find similar things in default Rails forms as well, but for actions like POST.
According to section 7.4.1 from Michael Hartl's The Rails Tutorial:
redirect_to #user
can be written instead of
redirect_to user_url(#user)
Quoting Michael Hartl:
This is because Rails automatically infers from redirect_to #user that
we want to redirect to user_url(#user).
Preface: I'm using devise for authentication.
I'm trying to catch unauthorized users from being able to see, edit, or update another user's information. My biggest concern is a user modifying the form in the DOM to another user's ID, filling out the form, and clicking update. I've read specifically on SO that something like below should work, but it doesn't. A post on SO recommended moving the validate_current_user method into the public realm, but that didn't work either.
Is there something obvious I'm doing wrong? Or is there a better approach to what I'm trying to do, either using devise or something else?
My UsersController looks like this:
class UsersController < ApplicationController
before_filter :authenticate_admin!, :only => [:new, :create, :destroy]
before_filter :redirect_guests
def index
redirect_to current_user unless current_user.try(:admin?)
if params[:approved] == "false"
#users = User.find_all_by_approved(false)
else
#users = User.all
end
end
def show
#user = User.find(params[:id])
validate_current_user
#user
end
def new
#user = User.new
end
def edit
#user = User.find(params[:id])
validate_current_user
#user
end
def create
#user = User.new(params[:user])
respond_to do |format|
if #user.save
format.html { redirect_to #user, :notice => 'User was successfully created.' }
else
format.html { render :action => "new" }
end
end
end
def update
#user = User.find(params[:id])
validate_current_user
respond_to do |format|
if #user.update_attributes(params[:user])
format.html { redirect_to #user, :notice => 'User was successfully updated.' }
else
format.html { render :action => "edit" }
end
end
end
private
def redirect_guests
redirect_to new_user_session_path if current_user.nil?
end
def validate_current_user
if current_user && current_user != #user && !current_user.try(:admin?)
return redirect_to(current_user)
end
end
end
The authenticate_admin! method looks like this:
def authenticate_admin!
return redirect_to new_user_session_path if current_user.nil?
unless current_user.try(:admin?)
flash[:error] = "Unauthorized access!"
redirect_to root_path
end
end
EDIT -- What do you mean "it doesn't work?"
To help clarify, I get this error when I try to "hack" another user's account:
Render and/or redirect were called multiple times in this action.
Please note that you may only call render OR redirect, and at most
once per action. Also note that neither redirect nor render terminate
execution of the action, so if you want to exit an action after
redirecting, you need to do something like "redirect_to(...) and
return".
If I put the method code inline in the individual controller actions, they do work. But, I don't want to do that because it isn't DRY.
I should also specify I've tried:
def validate_current_user
if current_user && current_user != #user && !current_user.try(:admin?)
redirect_to(current_user) and return
end
end
If you think about it, return in the private method just exits the method and passes control back to the controller - it doesn't quit the action. If you want to quit the action you have to return again
For example, you could have something like this:
class PostsController < ApplicationController
def show
return if redirect_guest_posts(params[:guest], params[:id])
...
end
private
def redirect_guest_post(author_is_guest, post_id)
redirect_to special_guest_post_path(post_id) if author_is_guest
end
end
If params[:guest] is present and not false, the private method returns something truthy and the #show action quits. If the condition fails then it returns nil, and the action continues.
You are trying and you want to authorize users before every action. I would suggest you to use standard gems like CanCan or declarative_authorization.
Going ahead with this approach you might end up reinventing the wheel.
In case you decide on using cancan, all you have to do is add permissions in the ability.rb file(generated by rails cancan:install)
can [:read,:write,:destroy], :role => "admin"
And in the controller just add load_and_authorize_resource (cancan filter). It will check if the user has permissions for the current action. If the user doesnt have persmissions, then it will throw a 403 forbidden expection, which can be caught in the ApplicationController and handled appropriately.
Try,
before_filter :redirect_guests, :except => [:new, :create, :destroy]
should work.
This is because you are using redirect twice, in authenticate_admin! and redirect_guests for new, create and destroy actions.
"Render and/or redirect were called multiple times in this action. Please note that you may only call render OR redirect, and at most once per action."
That's the reason of the error. In show method, if you are neither the owner of this account nor the admin, you are facing two actions: redirect_to and render
My suggestion is to put all of the redirect logic into before_filter
1 - My controller is this , but this not call the view whit this name is login.html.erb, im not understand beacause what happen this , is to show the form of the login.
class FinancesController < ApplicationController
# GET /finances
# GET /finances.json
def login
#user = User.find_by_name(params[:user])
if #user
session[:user_id] = #user.id
redirect_to :action => 'index'
else
redirect_to login_url
end
end
def index
#finances = Finance.all(:order => "created_at")
respond_to do |format|
format.html # index.html.erb
format.json { render json: #finances }
end
end
2 - Someone can talk to me to use the authenticate method to validate a sample login whit user and password forms some, can show to me how use this method , the find_by_name_and_password dont works.
I thanks by the answers, many.
Routes.rb
Controle::Application.routes.draw do
match 'login' => 'finances#login'
resources :finances
end
check for the #user object.
because, as of your code it will redirect to your login method if #user is not available.
To render login.html.erb from login method, there is no need for any manual redirections.
I am working on a Rails App that uses Devise as the authentication module, however I want to customize it so that CanCan will only permit Administrators to create a new user. I am having a hard time understanding how to customize the controller for Devise so that this can be done. Any help would be appreciated.
You don't need to customize anything :D
Remove :registerable from your Devise model.
Create your Users CRUD* (just scaffold users)
Use CanCan for user permission on your Users
Controller.
*Check Devise's wiki on how to create a Users CRUD, there is a routing trick you need to do
You can create a "User" controller that will manage users and then simply set permissions for it. So in your new User controller you can have something like:
class UserController < ApplicationController
load_and_authorize_resource
def index
end
def new
end
def show
end
def create
if #user.save
flash[:notice] = "Successfully created User."
redirect_to root_path
else
render :action => 'new'
end
end
def edit
end
def update
params[:user].delete(:password) if params[:user][:password].blank?
params[:user].delete(:password_confirmation) if params[:user][:password].blank? and params[:user][:password_confirmation].blank?
if #user.update_attributes(params[:user])
flash[:notice] = "Successfully updated User."
redirect_to user_index_path
else
render :action => 'edit'
end
end
def destroy
if #user.destroy
flash[:notice] = "Successfully deleted User."
redirect_to user_index_path
end
end
end
Assuming you have administrators set to:
can :manage, :all
You should be good to go.
In your routes file you'll need to set up your routes:
resources :user, :controller => "user"
Hope this helps!
I have a action/view named: join
And I post the the 'create' action.
What should my create action look like, I want to pre-populate some fields if the creation process had an error in it (like say the email address).
So far I have:
def create
#user = User.new(params[:user])
if #user.save
end
end
If you have a new action for registration, do this:
def new
#user = User.new
end
def create
#user = User.new(params[:user])
if #user.save
redirect_to success_page
else
render :action => "new"
end
end
+1 for #PeterWong but if possible why not use authentication, authorization plugin. There are many for rails, most famous ones would be
1 - Authlogic - http://github.com/binarylogic/authlogic
2 - Devise - http://github.com/plataformatec/devise
3 - REstful Authentication - http://github.com/technoweenie/restful-authentication
cheers
sameera