Skip to content

Create a role

caibiwsq edited this page Jul 5, 2019 · 3 revisions

Generate role model

In the following command you will replace MODEL with the class name used for the application’s users (it’s frequently User but could also be Admin). This will create a model (if one does not exist) and configure it with the default Devise modules. The generator also configures your config/routes.rb file to point to the Devise controller.

$ rails generate devise MODEL

Next, check the MODEL for any additional configuration options you might want to add, such as confirmable or lockable. If you add an option, be sure to inspect the migration file (created by the generator if your ORM supports them) and uncomment the appropriate section. For example, if you add the confirmable option in the model, you'll need to uncomment the Confirmable section in the migration.

Then run rails db:migrate

You should restart your application after changing Devise's configuration options (this includes stopping spring). Otherwise, you will run into strange errors, for example, users being unable to login and route helpers being undefined.

Configuring controllers

you can customize each controller by following these steps:

  1. Create your custom controllers using the generator which requires a scope:

    $ rails generate devise:controllers [scope]

    If you specify users as the scope, controllers will be created in app/controllers/users/. And the sessions controller will look like this:

    class Users::SessionsController < Devise::SessionsController
      # GET /resource/sign_in
      # def new
      #   super
      # end
      ...
    end

    (Use the -c flag to specify a controller, for example: rails generate devise:controllers users -c=sessions)

  2. Tell the router to use this controller:

    devise_for :users, controllers: { sessions: 'users/sessions' }
  3. Finally, change or extend the desired controller actions.

    You can completely override a controller action:

    class Users::SessionsController < Devise::SessionsController
      def create
        # custom sign-in code
      end
    end

    Or you can simply add new behaviour to it:

    class Users::SessionsController < Devise::SessionsController
      def create
        super do |resource|
          BackgroundWorker.trigger(resource)
        end
      end
    end

    This is useful for triggering background jobs or logging events during certain actions.

And you can create your custom controllers using the generator which requires a scope:

$ rails generate devise:controllers [scope]

Tell the router to use this controller, you can add devise:controllers you want.

devise_for :users, controllers: { sessions: 'users/sessions' }

You can completely override a controller action:

class Users::SessionsController < Devise::SessionsController
  def create
    # custom sign-in code
  end
end

Or you can simply add new behaviour to it:

class Users::SessionsController < Devise::SessionsController
  def create
    super do |resource|
      BackgroundWorker.trigger(resource)
    end
  end
end

You should restart your application after changing Devise's configuration options (this includes stopping spring). Otherwise, you will run into strange errors, for example, users being unable to login and route helpers being undefined.

Configuring routes

Devise also ships with default routes. If you need to customize them, you should probably be able to do it through the devise_for method. It accepts several options like :class_name, :path_prefix and so on, including the possibility to change path names for I18n:

devise_for :users, path: 'auth', path_names: { sign_in: 'login', sign_out: 'logout', password: 'secret', confirmation: 'verification', unlock: 'unblock', registration: 'register', sign_up: 'cmon_let_me_in' }

If you have the need for more deep customization, for instance to also allow "/sign_in" besides "/users/sign_in", all you need to do is create your routes normally and wrap them in a devise_scope block in the router:

devise_scope :user do
  get 'sign_in', to: 'devise/sessions#new'
end

This way, you tell Devise to use the scope :user when "/sign_in" is accessed. Notice devise_scope is also aliased as as in your router.

Please note: You will still need to add devise_for in your routes in order to use helper methods such as current_user.

devise_for :users, skip: :all

Controller filters and helpers

Devise will create some helpers to use inside your controllers and views. To set up a controller with user authentication, just add this before_action (assuming your devise model is 'User'):

before_action :authenticate_user!

For Rails 5, note that protect_from_forgery is no longer prepended to the before_action chain, so if you have set authenticate_user before protect_from_forgery, your request will result in "Can't verify CSRF token authenticity." To resolve this, either change the order in which you call them, or use protect_from_forgery prepend: true.

If your devise model is something other than User, replace "_user" with "_yourmodel". The same logic applies to the instructions below.

To verify if a user is signed in, use the following helper:

user_signed_in?

For the current signed-in user, this helper is available:

current_user

Controller tests

You can use the sign_in and sign_out methods on your controller tests:

sign_in @user
sign_in @user, scope: :admin

If you are testing Devise internal controllers or a controller that inherits from Devise's, you need to tell Devise which mapping should be used before a request. This is necessary because Devise gets this information from the router, but since controller tests do not pass through the router, it needs to be stated explicitly. For example, if you are testing the user scope, simply use:

it 'GET new' do
  # Mimic the router behavior of setting the Devise scope through the env.
  @request.env['devise.mapping'] = Devise.mappings[:user]

  # Use the sign_in helper to sign in a fixture `User` record.
  sign_in users(:alice)

  get :new

  # assert something
end