Search code examples
ruby-on-railsrubydevisecancanrolify

Setting up different User models and registration paths for Devise on Ruby on Rails


I am very new to ruby and I have been really struggling with this for months. I searched extensively and tried what the answers said but still no luck. (I tried Multiple user models with Ruby On Rails and devise to have separate registration routes but one common login route but didnt work)

I currently have a user.rb model and it is connected to devise and works fine.

1- On the sign-up page, I would like to have 3 buttons that would lead to separate registration forms (one each for business, manager and the already existing user). Do I set this up in routes.rb? 2- The forms will have different attributes that will populate their respective databases. 3- After completion of the form they will be directed to their respective routes. User to the current default route while business to the business dashboard and manager to the manager dashboard. Is this again in routes.rb or devise?

I would greatly appreciate any guidance!

I've read through the documentations for devise, cancan and rolify but I can't seem to bring it all together to work for me.

I am very new to ruby and I have been really struggling with this for months. I searched extensively and tried what the answers said but still no luck. (I tried Multiple user models with Ruby On Rails and devise to have separate registration routes but one common login route but didnt work)

I currently have a user.rb model and it is connected to devise and works fine.

1- On the sign-up page, I would like to have 3 buttons that would lead to separate registration forms (one each for business, manager and the already existing user). Do I set this up in routes.rb? 2- The forms will have different attributes that will populate their respective databases. 3- After completion of the form they will be directed to their respective routes. User to the current default route while business to the business dashboard and manager to the manager dashboard. Is this again in routes.rb or devise?

I would greatly appreciate any guidance!

I've read through the documentations for devise, cancan and rolify but I can't seem to bring it all together to work for me.

#user.rb
class User < ActiveRecord::Base
has_many :contibutions

rolify
# Include default devise modules. Others available are:
# :lockable, :timeoutable
devise :database_authenticatable, :registerable, :confirmable,
     :recoverable, :rememberable, :trackable, :validatable, :omniauthable

validates_format_of :email, :without => TEMP_EMAIL_REGEX, on: :update

def admin?
  has_role?(:admin)
end

def self.find_for_oauth(auth, signed_in_resource = nil)

# Get the identity and user if they exist
identity = Identity.find_for_oauth(auth)
user = identity.user
if user.nil?

  # Get the existing user from email if the OAuth provider gives us an email
  user = User.where(:email => auth.info.email).first if auth.info.email

  # Create the user if it is a new registration
  if user.nil?
    user = User.new(
      name: auth.extra.raw_info.name,
      #username: auth.info.nickname || auth.uid,
      email: auth.info.email.blank? ? TEMP_EMAIL : auth.info.email,
      password: Devise.friendly_token[0,20]
    )
    user.skip_confirmation!
    user.save!
  end

  # Associate the identity with the user if not already
  if identity.user != user
    identity.user = user
    identity.save!
  end
end
user
end
end

Solution

  • I'd go with one User model and a two stage signup. First they would click on their desired button, each one passing a unique 'role' param in the URL and going to the devise signup page. Here they would enter only their email/password and we would pass the param from the URL to a simple 'role' hidden field in the form.

    Then as step 2, after technically registering, they are directed to a separate edit account type page (each user having a different account, outlined below) to fill in the rest of their details.

    The models:

    models/user.rb

    class User < ActiveRecord::Base
      has_one :account
      has_one :business_account
      has_one :manager_account
    end
    

    models/account.rb

    class Account
      belongs_to :user
    

    models/business_account.rb

    class BusinessAccount
      belongs_to :user
    

    models/manager_account.rb

    class ManagerAccount
      belongs_to :user
    

    Then, using devise, I'd override the registrations_controller to add a role based on a hidden field in the first step simple registration form (which would just be email/password/role).

    In that file, I'd also override the after_signup_path method, to redirect to an edit_account type page for the relevant account we create for them during signup.

    First the routes:

    devise_for :users, :controllers => {:registrations => "registrations"}
    
    resources :users do 
      resource :account
      resource :business_account
      resource :manager_account
    end
    

    Then the controller (see comments within code):

    controllers/registrations_controller.rb

    class RegistrationsController < Devise::RegistrationsController
    
      def create
        build_resource(sign_up_params)
    
        if resource.save
    
          # you will name the following param. make sure it's in devise strong_params
          # also the == will depend on how you pass the role - string, integer etc
    
          if sign_up_params[:role] == "1"
            user.add_role :standard
            resource.build_account(user_id: resource.id) # code to create user account
          elsif sign_up_params[:role] == "2"
            user.add_role :manager
            resource.build_manager_account(user_id: resource.id) # code to create user account
          elsif sign_up_params[:role] == "2"
            user.add_role :business
            resource.build_business_account(user_id: resource.id) # code to create user account
          end
    
          if resource.active_for_authentication?
            set_flash_message :notice, :signed_up if is_navigational_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_navigational_format?
            expire_session_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
    
      protected
    
      # override the after signup path to your desired route, e.g
      def after_sign_up_path_for(resource)
        if sign_up_params[:role] == "1"
          edit_user_account_path(resource.id)
        elsif sign_up_params[:role] == "2"
          edit_user_manager_account_path(resource.id)
        elsif sign_up_params[:role] == "2"
          edit_user_business_account_path(resource.id)
        end 
      end
    end
    

    The above would redirect them to a separate accounts controller/view depending on the account type. This solution would save you a lot of headaches down the line.