Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to properly extend Devise Recoverable?

I want Recoverable module to send "invite" emails with reset password links for my users (the app is on invite only), I partially declare methods in initializer:

module Devise
  class Mailer
    def invite_new_user(record)
      devise_mail(record, :invitation_instructions)
    end
  end
end

module Devise
  module Models
    module Recoverable
      def send_invite_user_instructions!
        return unless status == User::STATUS_PENDING
        generate_reset_password_token!
        ::Devise.mailer.invite_new_user(self).deliver
      end
    end
  end
end

And recoverable is extended nicely, but it says that my mailer does not have invite_new_user method (rails console output):

1.9.2p290 :002 > user.send_invite_user_instructions! 
  User Load (1.4ms)  SELECT "users".* FROM "users" WHERE "users"."reset_password_token" = 'zMQK1CEXYupjNKpH8dph' LIMIT 1
   (0.3ms)  BEGIN
   (15.0ms)  UPDATE "users" SET "reset_password_token" = 'zMQK1CEXYupjNKpH8dph', "updated_at" = '2012-05-01 17:40:32.085256' WHERE "users"."id" = 59
   (4.5ms)  COMMIT
NoMethodError: undefined method `invite_new_user' for Devise::Mailer:Class

but calling has method in the same console session:

1.9.2p290 :003 > ::Devise.mailer.method_defined? 'invite_new_user'
 => true 

What am I missing?

like image 577
Grzegorz Avatar asked May 01 '12 17:05

Grzegorz


1 Answers

Devise can be set-up for what you need:

1- Create a Mailer class in app/mailers/auth_mailer.rb file and make it inherit from Devise::Mailer

class AuthMailer < Devise::Mailer
  def invite_new_user(record)
    devise_mail(record, :invitation_instructions)
  end
end

2- Instruct Devise to use your class by editing config/initializers/devise.rb file and adding

config.mailer = 'AuthMailer'

3- (optional) If (and only if) you use a delay email sending such as SideKiq or DelayedJob you may need to eager load in development, or the delayed job may not find your AuthMailer class. In config/environments/development.rb

config.eager_load = true

4- I personally would define your send_invite_user_instructions! method in my User class instead of patching Devise class


Side note: I'm not a big fan or doing a partial declaration of a class in Rails initializer, because depending on how the gem is designed you can have trouble with autoload : There is a gem (Monologue) that reload objects during run time, without running the initializers, so the monkey patch works well on the first call, but not on the next calls.

like image 83
Benj Avatar answered Sep 30 '22 12:09

Benj