benoitr
benoitr

Reputation: 6105

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

Upvotes: 1

Views: 2210

Answers (2)

Peter Ehrlich
Peter Ehrlich

Reputation: 7135

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.

Upvotes: 3

Srdjan Pejic
Srdjan Pejic

Reputation: 8202

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?

Upvotes: 0

Related Questions