RoR pattern for a registration page - ruby-on-rails

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

Related

What does redirecting to an instance mean in Ruby on Rails

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).

Rails Actions for Form Posting

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

redirecting from another page taking info

So after the user signs up, i redirect them to my additional info page where i collect some more information. However, something is wrong with my design/implementation as rails is saying im missing users/create template
this is my users controller
class UsersController < ApplicationController
def show
#user = User.find(params[:id])
end
def new
#user = User.new
end
def additional_info
#user = User.new(user_addinfo)
if #user.save
redirect_to show_path
end
end
def create
#user = User.new(user_params)
if #user.save
# UserMailer.welcome_email(#user).deliver
sign_in #user
redirect_to additional_info_path
flash[:success] = "Welcome to InYourShoes!"
#return #user
else
render'new'
end
end
private
def user_params
params.require(:user).permit(:name, :email, :password, :password_confirmation)
end
def user_addinfo
params.permit(:year)
end
end
def show is the user profile page i want to show after redirecting to the additional_info page
def additional_info is just take additional info from the private method def user_addinfo
def create is the sign up process.
After entering the basic user info, it gets redirected to additional which is fine. but after the additional, it says im missing the users/create template, but my code i attempted to redirect to show_path and #usersshow, still doesnt work
any suggestions? sorry if this seems intuitive but Im new to rails.
I think your problem is in the additional_info method, as i said in the comment. What you're doing is:
creating a user
creating a session for the user (sign_in #user) - storing somewhere the user_id in the session
redirecting to your additional_info page
And here comes the problem. As the user is already signed in you don't have any need to create a new user with additional params. You should have some helper to retrieve the current signed in user (like current_user) and in additional_info method, just update it.
So your additional_info method would become something like:
def additional_info
user = User.find session[:user_id]
user.update params[:user]
redirect_to user_path #show action
end

Devise - Authenticate user (after validations) on a create action

Using Devise, I know how to protect controller actions from non-signed-in users through:
before_filter :authenticate_user!
In order to illustrate what I am trying to achieve, please see an example:
I have the following controller: (a project belongs to a user)
projects_controller.rb
def create
#project = current_user.projects.new(params[:project])
if #project.save
redirect_to #project
else
render :action => 'new'
end
end
What I am looking for is a way that users can interact more with the website before having to sign up/sign in. Something like:
after_validation :authenticate_user!
if the user is not signed in, and redirect him after success (sign up/sign in) to the "project" show page.
Things I thought:
1.) Change the controller in order to accept a project object without user_id, ask for authentication if the user is not signed in, then update attributes with the user_id
I try to do it like this first and it results to a very ugly code. (Moreover authenticate_user! doesn't redirect to the #project which lead to more customization)
2.) Create a wizard with nested_attributes (project form and nested new registration form and session form)
3.) Something better? (a custom method?)
It seems authologic manages this more easily. I'm not sure it is a reason to switch so I would like to have your idea/answer on this. Thanks!
EDIT
references: Peter Ehrlich answer comment
CONTROLLER WITH VALIDATIONS LOGIC
projects_controller.rb
def create
unless current_user
#project = Project.new(params[:project]) # create a project variable used only for testing validation (this variable will change in resume project method just before being saved)
if #project.valid? # test if validations pass
session['new_project'] = params[:project]
redirect_to '/users/sign_up'
else
render :action => 'new'
end
else
#project = current_user.projects.new(params[:project])
if #project.save
redirect_to #project
else
render :action => 'new'
end
end
end
def resume_project
#project = current_user.projects.new(session.delete('new_project')) # changes the #project variable
#project.save
redirect_to #project
end
routes
get "/resume_project", :controller => 'projects', :action => 'resume_project'
application_controller.rb
class ApplicationController < ActionController::Base
protect_from_forgery
def after_sign_in_path_for(resource)
return '/resume_project' if session['new_project'].present?
super
end
Something like this should work:
def create
unless current_user
session['new_project'] = params[:project]
redirect_to '/register'
return
end
# and on to normal stuff
# in your devise controller
def after_sign_in_path
return '/resume_project' if session['new_project'].present?
super
end
# back in projects_controller now
def resume_project
#project.create(session.delete('new_project'))
# you know the drill from here
# I'd also put in a check to make an error if the session is not set- in case they reload or some such
Keep in mind that session is a cookie in the browser, and thus has a size limit (4kb). If you're posting images or other media, you'll have to store them temporarily server-side.
Another option would be to create a userless project, and use a similar technique to allow them to claim it as their own. This would be nice if you wanted unclaimed projects displayed to all to be available as a flow.
I haven't tested it out, but it should be possible to store the action the user was going to, I.e. create, with the params hash that was submitted and redirect to it upon successful login. It would then handle the error cases as normal.
Have you tried that?

Devise restrict sign up to Admin

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!

Resources