Monkey patching Devise (или любой камень Rails) - программирование
Подтвердить что ты не робот

Monkey patching Devise (или любой камень Rails)

В моем проекте Rails я использую Devise, и я хочу изменить ключи, которые он использует в флэш-предупреждениях. (Devise использует: уведомление и: предупреждает флеш-ключи, но я хочу изменить их на: success и: error, чтобы я мог отображать красивые зеленые/красные поля с Bootstrap.)

Итак, я хочу, чтобы как-то переопределить метод set_flash_message в 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

Но я просто не знаю, куда его поместить.


UPDATE:

На основе ответа я создал файл config/initializers/overrides.rb со следующим кодом:

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

Но это вызывает ошибку при каждом действии Devise:

Ошибка маршрутизации: undefined метод 'prepend_before_filter' для Разрабатывают:: SessionsController: Класс

4b9b3361

Ответ 1

Если вы попытаетесь снова открыть класс, то тот же синтаксис, что и объявление нового класса:

class DeviseController
end

Если этот код выполняется перед объявлением реального класса, он наследует от Object вместо расширения класса, объявленного Devise. Вместо этого я пытаюсь использовать следующие

DeviseController.class_eval do
  # Your new methods here
end

Таким образом, вы получите сообщение об ошибке, если DeviseController не было объявлено. В результате вы, вероятно, закончите с

require 'devise/app/controllers/devise_controller'

DeviseController.class_eval do
  # Your new methods here
end

Ответ 2

Использование ответа Rails 4 @aceofspades не помогло мне.

Мне все время требовалось: cannot load such file -- devise/app/controllers/devise_controller (LoadError)

Вместо того, чтобы прикручиваться с порядком загрузки инициализаторов, я использовал крючок события to_prepare без инструкции require. Это гарантирует, что исправление обезьян происходит до первого запроса. Этот эффект похож на after_initialize hook, но гарантирует перезагрузку обезьяны в режиме разработки после перезагрузки (в режиме prod результат идентичен).

Rails.application.config.to_prepare do
  DeviseController.class_eval do
    # Your new methods here
  end
end

N.B. документация на рельсы на to_prepare по-прежнему неверна: см. проблема Github

Ответ 3

Как насчет добавления в инициализатор переопределения и псевдонима для атрибутов хэша flash, например:

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

Это должно позволить вашему приложению читать flash [: notice] или flash [: success] (flash.notice и flash.success)

Ответ 4

В файле инициализации:

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)

Это довольно жестоко, но будет делать то, что вы хотите. Mixin удалит предыдущий метод set_flash_message, заставляя подклассы возвращаться к методу mixin.

Изменить: self.included называется, когда mixin включен в класс. Параметр klass - это класс, к которому был включен mixin. В этом случае klass является DeviseController, и мы вызываем remove_method на нем.

Ответ 5

Вам нужно перезаписать DeviseController, поддерживая его суперкласс в инициализаторе.

Что-то вроде:

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

Ответ 6

Это то, что вам нужно будет инициализировать папку rails, потому что это настраиваемая конфигурация для этого приложения, в частности, вы должны использовать так:

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

тогда вы должны получить ожидаемое поведение. надеюсь, что это помогает, так как я не тестировал, а не PLS дать обратную связь, и я помогу вам попробовать что-то другое.

Ответ 7

Я знаю, что это старый поток, но это может быть полезно. Вы должны иметь возможность потребовать файл из каталога gem, используя путь named_from engine.

  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