Devise Authentication + Hotwire Support

Adds user authentication to your app using the Devise gem. Supports Hotwire.
Icons/chart bar
Used 770 times
Created by
A Andrea Fomera

Usage

Run this command in your Rails app directory in the terminal:

rails app:template LOCATION="https://railsbytes.com/script/zOvsyy"
Template Source

Review the code before running this template on your machine.

def do_bundle
  # Custom bundle command ensures dependencies are correctly installed
  Bundler.with_unbundled_env { run "bundle install" }
end

def find_and_replace_in_file(file_name, old_content, new_content)
  text = File.read(file_name)
  new_contents = text.gsub(old_content, new_content)
  File.open(file_name, 'w') { |file| file.write new_contents }
end

run "bundle add devise"
do_bundle

rails_command "generate devise:install"

model_name = ask("What do you want to call your Devise model?")
attributes = ""
if yes?("Do you want to any extra attributes to #{model_name}? [y/n]")
  attributes = ask("What attributes?")
end

# We don't use rails_command here to avoid accidentally having RAILS_ENV=development as an attribute
run "rails generate devise #{model_name} #{attributes}"

FileUtils.mkdir_p("app/controllers/users")

create_file "app/controllers/users/devise_controller.rb", <<-END_CONTROLLER
class Users::DeviseController < ApplicationController
  class Responder < ActionController::Responder
    def to_turbo_stream
      controller.render(options.merge(formats: :html))
    rescue ActionView::MissingTemplate => error
      if get?
        raise error
      elsif has_errors? && default_action
        render rendering_options.merge(formats: :html, status: :unprocessable_entity)
      else
        redirect_to navigation_location
      end
    end
  end

  self.responder = Responder
  respond_to :html, :turbo_stream
end
END_CONTROLLER

inject_into_file 'config/initializers/devise.rb', after: "# frozen_string_literal: true\n" do <<~EOF
class TurboFailureApp < Devise::FailureApp
  def respond
    if request_format == :turbo_stream
      redirect
    else
      super
    end
  end

  def skip_format?
    %w(html turbo_stream */*).include? request_format.to_s
  end
end
  EOF
end

inject_into_file 'config/initializers/devise.rb', after: "# ==> Warden configuration\n" do <<-EOF
  config.warden do |manager|
    manager.failure_app = TurboFailureApp
  end
  EOF
end
# Update devise configs
find_and_replace_in_file("config/initializers/devise.rb", "# config.parent_controller = 'DeviseController'", "config.parent_controller = 'Users::DeviseController'")
# uncomment navigational_formats and add :turbo_stream
find_and_replace_in_file("config/initializers/devise.rb", "# config.navigational_formats = ['*/*', :html]", "config.navigational_formats = ['*/*', :html, :turbo_stream]")
Comments
Fajarullah
Hi, thank you for created this template.
You should also add `:turbo_stream` into `config.navigational_formats`, because without that it doesn't re-render the view in all my case.
i got it from https://gorails.com/episodes/devise-hotwire-turbo
Andrea Fomera
Thank you  I've updated that to fix that case too!
Travis Smith
Hi, Andrea, I'm using this for your course. Have the Devise issues been resolved in the latest version? Is there an updated Railsbyte, if so?
Karl Keller
Note: The TurboFailureApp Class is not needed anymore since Devise version 4.9.x plays nice with Rails 7.
(The "Learn Hotwire by Building a Forum" tutorial brought me here - I used vanilla Devise 4.9.2 with Rails 7.05)