Tag: Devise

  • This is an update of an old post of similar name but for a newer version of Devise and with better design decisions. The old post was for Devise 1.0.8, this one covers 4.0.0

    I was trying to have a single page with both sign in and sign up forms with Devise 4.0.0 but this applies to whenever you want to show log in or registering individually or together anywhere on your site other than the views and controllers Devise creates for you.

    For my task, I created a custom controller for it with a single new action as the create actions would be in the respective already existing Devise controllers. Something like this:

    class Users::SessionsOrRegistrationsController < ApplicationController
      def new
      end
    end

    And then I created a new.html.erb (actually, new.html.haml, but I digress) that contained both log in and sign up one after the other. Something like this:

    <h2>Sign up</h2>
    
    <%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
      <%= devise_error_messages! %>
    
      <div class="field">
        <%= f.label :email %>
        <%= f.email_field :email, autofocus: true %>
      </div>
    
      <div class="field">
        <%= f.label :password %>
        <% if @minimum_password_length %>
          <em>(<%= @minimum_password_length %> characters minimum)</em>
        <% end %>
        <%= f.password_field :password, autocomplete: "off" %>
      </div>
    
      <div class="field">
        <%= f.label :password_confirmation %>
        <%= f.password_field :password_confirmation, autocomplete: "off" %>
      </div>
    
      <div class="actions">
        <%= f.submit "Sign up" %>
      </div>
    <% end %>
    
    <hr/>
    
    <h2>Log in</h2>
    
    <%= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %>
      <div class="field">
        <%= f.label :email %>
        <%= f.email_field :email, autofocus: true %>
      </div>
    
      <div class="field">
        <%= f.label :password %>
        <%= f.password_field :password, autocomplete: "off" %>
      </div>
    
      <% if devise_mapping.rememberable? %>
        <div class="field">
          <%= f.check_box :remember_me %>
          <%= f.label :remember_me %>
        </div>
      <% end %>
    
      <div class="actions">
        <%= f.submit "Log in" %>
      </div>
    <% end %>

    I actually ended up creating two _form partials and including them. In either case, when you try to render those views, you’ll get errors about some missing methods. You need to provide those as helper methods so my controller actually looks like this:

    class Users::SessionsOrRegistrationsController < ApplicationController
      def new
      end
    
      private
    
      def resource_name
        :user
      end
      helper_method :resource_name
    
      def resource
        @resource ||= User.new
      end
      helper_method :resource
    
      def devise_mapping
        @devise_mapping ||= Devise.mappings[:user]
      end
      helper_method :devise_mapping
    
      def resource_class
        User
      end
      helper_method :resource_class
    end

    And now it works.

  • If you are getting this error:

    ActionView::Template::Error: undefined method `authenticate?' for nil:NilClass
    

    in your call to Devise’s user_signed_in? or similar, you probably forgot to add this:

    class ActionController::TestCase
      include Devise::TestHelpers
    end

    at the bottom of the test_helper.rb file. Not that that would ever happen to me…

  • Update: there’s a new version of this post covering Devise 4.0.0: Show a devise log in or sign up forms in another page

    Devise create various forms, among them one for signing up and one for logging in of course. These are the forms as they are generated in Devise 1.0.8:

    <h2>Sign up</h2>
    
    <%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
      <%= f.error_messages %>
    
      <div>
        <%= f.label :email %><br>
        <%= f.text_field :email %>
      </div>
    
      <div>
        <%= f.label :password %><br>
        <%= f.password_field :password %>
      </div>
    
      <div>
        <%= f.label :password_confirmation %><br>
        <%= f.password_field :password_confirmation %>
      </div>
    
      <div>
        <%= f.submit "Sign up" %>
      </div>
    <% end %>
    
    <%= render partial: "shared/devise_links" %>

    and

    <h2>Sign in</h2>
    
    <%= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %>
      <div>
        <%= f.label :email %><br>
        <%= f.text_field :email %>
      </div>
    
      <div>
        <%= f.label :password %><br>
        <%= f.password_field :password %>
      </div>
    
      <% if devise_mapping.rememberable? %>
        <div>
          <%= f.check_box :remember_me %>
          <%= f.label :remember_me %>
        </div>
      <% end %>
    
      <div>
        <%= f.submit "Sign in" %>
      </div>
    <% end %>
    
    <%= render partial: "shared/devise_links" %>

    If you try to put them somewhere else you’ll run into some problem. There are some variables/methods those forms use that you’ll be lacking, specifically: resource_name, resource and for logging in also devise_mapping. I’ve recently tried to put them both in the homepage for an upcoming project of mine and this is how I’ve solved it:

    module ContentHelper
      def resource_name
        :user
      end
    
      def resource
        @resource ||= User.new
      end
    
      def devise_mapping
        @devise_mapping ||= Devise.mappings[:user]
      end
    end