Monkey patching Devise (or any Rails gem)

后端 未结 7 1362
甜味超标
甜味超标 2020-12-07 23:11

I\'m using the Devise authentication gem in my Rails project, and I want to change the keys it\'s using in flash alerts. (Devise uses :notice and :alert flash keys, but I wa

相关标签:
7条回答
  • 2020-12-07 23:32

    If you try to reopen a class, it's the same syntax as declaring a new class:

    class DeviseController
    end
    

    If this code is executed before the real class declaration, it inherits from Object instead of extending the class declared by Devise. Instead I try to use the following

    DeviseController.class_eval do
      # Your new methods here
    end
    

    This way, you'll get an error if DeviseController has not been declared. As a result, you'll probably end up with

    require 'devise/app/controllers/devise_controller'
    
    DeviseController.class_eval do
      # Your new methods here
    end
    
    0 讨论(0)
  • 2020-12-07 23:32

    I know this is an old thread but this might still be helpful. You should be able to require the file from the gem directory using the engine called_from path.

      require File.expand_path('../../app/helpers/devise_helper',Devise::Engine.called_from)
      require File.expand_path('../../app/controllers/devise_controller',Devise::Engine.called_from)
    
      DeviseController.class_eval do
        # Your new methods here
      end
    
    0 讨论(0)
  • 2020-12-07 23:33

    What about adding in the override initializer and alias for the attributes of the flash hash, like this:

    class ActionDispatch::Flash::FlashHash
      alias_attribute :success, :notice
      alias_attribute :error, :alert
    end
    

    This should allow your application to read flash[:notice] or flash[:success](flash.notice and flash.success)

    0 讨论(0)
  • 2020-12-07 23:46

    You need to overwrite DeviseController while keeping around its superclass, in your initializer.

    Something like:

    class DeviseController < Devise.parent_controller.constantize
        def set_flash_message(key, kind, options = {})
           if key == 'alert'
               key = 'error'
           elsif key == 'notice'
               key = 'success'
           end
           message = find_message(kind, options)
           flash[key] = message if message.present?
        end
    end
    
    0 讨论(0)
  • 2020-12-07 23:54

    This is the kind of thing that you will want to put on initialize rails folder, because it's a custom config for this application in particular, second you should use like so:

    class DeviseController
        def set_flash_message(key, kind, options = {})
           if key == 'alert'
              key = 'error'
           elsif key == 'notice'
              key = 'success'
           end
           message = find_message(kind, options)
           flash[key] = message if message.present?
        end
    end
    

    then you should get the expected behavior. hope it helps since i dont tested, of not pls give a feedback and i will help you try something diferent.

    0 讨论(0)
  • 2020-12-07 23:55

    In your initializer file :

    module DeviseControllerFlashMessage
      # This method is called when this mixin is included
      def self.included klass
        # klass here is our DeviseController
    
        klass.class_eval do
          remove_method :set_flash_message
        end
      end
    
      protected 
      def set_flash_message(key, kind, options = {})
        if key == 'alert'
          key = 'error'
        elsif key == 'notice'
          key = 'success'
        end
        message = find_message(kind, options)
        flash[key] = message if message.present?
      end
    end
    
    DeviseController.send(:include, DeviseControllerFlashMessage)
    

    This is pretty brutal but will do what you want. The mixin will delete the previous set_flash_message method forcing the subclasses to fall back to the mixin method.

    Edit: self.included is called when the mixin is included in a class. The klass parameter is the Class to which the mixin has been included. In this case, klass is DeviseController, and we call remove_method on it.

    0 讨论(0)
提交回复
热议问题