Question

I am struggling here quiet a bit, having tried googling a solution for the past few hours, I thought its worth posting this question - new to rails, but willing to learn.

I have implemented a user authentication on rails 4 using devise. There are two different types of users who can sign up to the site. Each type requires different information to be stored (i.e.: Type A requires a phone number, whilst Type B doesn't)

Long story short - I settled for Polymorphic associations. So I now have the usual User table and two other tables storing additional information for each type. To realize this I followed these steps:

http://astockwell.com/blog/2014/03/polymorphic-associations-in-rails-4-devise/

It's all working well.

However, when a user of type A is signing up to the site (Controller/Views & Models of User A are used) the users are not signed in upon success. What do I have to do that user A is signed in automatically after signing up ?

Here my code so far (I have a user table & a chef table)

controllers:

users_controller.rb

class UsersController < ApplicationController
  before_filter :set_user, only: [:show, :edit, :update]
  before_filter :validate_authorization_for_user, only: [:edit, :update]


  # GET /users/1
  def show
  end

  # GET /users/1/edit
  def edit
  end


  # PATCH/PUT /users/1
  def update
    if @user.update_attributes(user_params)
      #debugger
      redirect_to @user, notice: 'User was successfully updated.'
    else
      render action: 'edit'
    end
  end


  private
    # Use callbacks to share common setup or constraints between actions.
    def set_user
      @user = User.find(params[:id])
    end

    def validate_authorization_for_user
       redirect_to root_path unless @user == current_user
    end

    private

    def user_params
      params.require(:user).permit(:name, :about)
    end
  end

chefs_controller.rb

class ChefsController < ApplicationController

  before_action :set_chef, only: [:show, :edit, :update, :destroy]
  #before_filter :authenticate_user!

  # GET /chefs
  # GET /chefs.json
  def index
    @chefs = Chef.all
  end

  # GET /chefs/1
  # GET /chefs/1.json
  def show
  end

  # GET /chefs/new
  def new
    @chef = Chef.new
    render "chefs/registrations/new"
  end

  # GET /chefs/1/edit
  def edit
  end

  # POST /chefs
  # POST /chefs.json
  def create
    @chef = Chef.new(chef_params)

    respond_to do |format|
      if @chef.save
        format.html { redirect_to @chef, notice: 'Chef was successfully created.' }
        format.json { render action: 'show', status: :created, location: @chef }
      else
        format.html { render action: 'new' }
        format.json { render json: @chef.errors, status: :unprocessable_entity }
      end
    end
  end

  # PATCH/PUT /chefs/1
  # PATCH/PUT /chefs/1.json
  def update
    respond_to do |format|
      if @chef.update(chef_params)
        format.html { redirect_to @chef, notice: 'Chef was successfully updated.' }
        format.json { head :no_content }
      else
        format.html { render action: 'edit' }
        format.json { render json: @chef.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /chefs/1
  # DELETE /chefs/1.json
  def destroy
    @chef.destroy
    respond_to do |format|
      format.html { redirect_to chefs_url }
      format.json { head :no_content }
    end
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_chef
      @chef = Chef.find(params[:id])
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def chef_params
      #params[:chef]
      params.require(:chef).permit!
    end
end

models:

user.rb

class User < ActiveRecord::Base
  rolify
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable and :omniauthable

  devise :database_authenticatable, :registerable,
      :recoverable, :rememberable, :trackable, :validatable, :omniauthable
  validates_presence_of :email
  mount_uploader :image, ImageUploader
  has_many :authorizations
  before_create :set_role

  belongs_to :meta, polymorphic: true

  def self.new_with_session(params,session)
    if session["devise.user_attributes"]
      new(session["devise.user_attributes"],without_protection: true) do |user|
        user.attributes = params
        user.valid?
      end
    else
      super
    end
  end

  def self.from_omniauth(auth, current_user)
    authorization = Authorization.where(:provider => auth.provider, :uid => auth.uid.to_s, :token => auth.credentials.token, :secret => auth.credentials.secret).first_or_initialize
    if authorization.user.blank?
      user = current_user.nil? ? User.where('email = ?', auth["info"]["email"]).first : current_user
      if user.blank?
       user = User.new
       user.password = Devise.friendly_token[0,10]
       user.name = auth.info.name
       user.email = auth.info.email
       auth.provider == "twitter" ?  user.save(:validate => false) :  user.save
     end
     authorization.username = auth.info.nickname
     authorization.user_id = user.id
     authorization.save
   end
   authorization.user
 end

    private

        def set_role
            case meta_type
            when 'Chef'
                add_role :Chef
            else
                add_role :Baer
            end
        end
end

chef.rb

class Chef < ActiveRecord::Base
    has_one :user, as: :meta, dependent: :destroy
    accepts_nested_attributes_for :user
end

Any help is appreciated! Thank you

Was it helpful?

Solution

Nice post -

The standard way to use this is to override the Devise Sessions Controller, with the after_sign_in_path_for method:

 #app/controllers/application_controller.rb
 class ApplicationController < ActionController::Base
    def after_sign_in_path_for(resource)
         your_path
    end
 end

However, your question is not about this; it's how to sign in the chef?

I'd need to sit down for some more time to work this out, but from my observations - the problem seems to be an issue with how your chef record is being created -- you're basically sending the data through to Devise, which I think will bypass the standard Devise mechanisms


def create

When Devise creates a new user, it uses the create method in the Devise Registrations controller to create the user & sign them in:

  def create
    build_resource(sign_up_params)

    if resource.save
      yield resource if block_given?
      if resource.active_for_authentication?
        set_flash_message :notice, :signed_up if is_flashing_format?
        sign_up(resource_name, resource)
        respond_with resource, location: after_sign_up_path_for(resource)
      else
        set_flash_message :notice, :"signed_up_but_#{resource.inactive_message}" if is_flashing_format?
        expire_data_after_sign_in!
        respond_with resource, location: after_inactive_sign_up_path_for(resource)
      end
    else
      clean_up_passwords resource
      respond_with resource
    end
  end

  def sign_up(resource_name, resource)
     sign_in(resource_name, resource)
  end

Personally, I wouldn't use the polymorphic association with Devise. I'd create a single account and use a role-based structure to authorize them for different things (kind of like how admin / moderators etc works)

I appreciate this won't solve your issue, but may help some way

OTHER TIPS

For those who wish to go down the polymorphic user model route take a look at this - I just tested this and it works. Multiple user models with Ruby On Rails and devise to have separate registration routes but one common login route

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top