ActionMailer default_url_options: как автоматически извлекать порт из DefaultOptions?

У меня часто есть много приложений Rails, работающих параллельно, поэтому я настроил их на использование разных портов:

# Change default port of development server, see http://stackoverflow.com/info/18103316
require 'rails/commands/server'
module DefaultOptions
  def default_options
    super.merge!(Port: 3001)
  end
end

Теперь важно, чтобы это также работало для ActionMailer в разработке env:

config.action_mailer.default_url_options = {host: 'localhost:3001'}

Но вместо hardcoding это, я хотел бы установить его непосредственно из DefaultOptions. Как это возможно?

Ответ 1

Вот небольшое отклонение от вашего кода:

# config/boot.rb
require 'rails/commands/server'
module DefaultOptions
  PORT = 3001

  def default_options
    super.merge!(Port: PORT)
  end
end

# This line was part of the Qaru answer you quoted, and is important
# With newer Ruby versions, you can call `prepend` directly
Rails::Server.prepend(DefaultOptions)

Это следует за ответом на переполнение стека, за исключением того, что вы извлекаете порт в константу DefaultOptions::PORT. Обратите внимание, что нет ничего волшебного в имени модуля DefaultOptions, он просто определяет простой модуль, который затем добавляется к Rails::Server. Вы могли бы назвать его, как хотите. Когда запускается сервер разработки, создается новый объект Rails::Server, и в какой-то момент на этот объект вызывается метод default_options. Из-за использования prepend поиск метода сначала достигнет метода, который вы определили в DefaultOptions. super в этом методе просто вызывает исходный un-prepended default_options, определенный в Rails::Server.

Причина, по которой "трудно" получить значения в default_options, заключается в том, что это метод экземпляра, то есть вы можете получить к нему доступ только в экземпляре класса Rails::Server, и мы обычно не имеем удерживайте объект сервера. Вы можете получить доступ к нему следующим образом:

# config/development.config
require 'rails/commands/server'
Rails::Server.new.default_options[:Port]

Но я думаю, что это ненужная зависимость и создание объекта. Имя Rails::Server также подразумевает, что мы можем иметь только один объект этого класса, и я бы не создавал экземпляр объектов сервера, чтобы получить хэш-конфигурацию конфигурации. Поэтому, извлекая порт из константы, которую вы можете удерживать, независимо от того, есть ли у вас ссылка на объект сервера - DefaultOptions::PORT - яснее в моем сознании.

Итак, теперь, когда мы получили константу DefaultOptions::PORT, вы можете использовать ее в своей почтовой программе:

# config/development.rb
  config.action_mailer.smtp_settings = {
  :port => DefaultOptions::PORT,
  :address => '...',
  :user_name => '...',
  :password => '...',
  :domain => '...',
  :authentication => :plain
}

Вы также можете рассмотреть возможность определения почтовой программы и порта в файле yaml, так что вам не нужно "разбрызгивать" части вашей конфигурации в разных местах - это может сэкономить вам немного на голове.

Если вы хотите сделать это, вы можете создать класс оболочки самостоятельно, который использует YAML.load_file для загрузки вашего нового файла конфигурации yaml в хэш. Кроме того, проверьте Figaro gem, он дает удобный способ разместить все конфигурации Rails в одном файле - application.yml - и получить к ним доступ везде, используя ENV.

Если бы вы использовали Figaro, например, и имели ключ PORT в application.yml, тогда ваш код может выглядеть следующим образом:

# config/boot.rb
require 'rails/commands/server'
module DefaultOptions
  def default_options
    super.merge!(Port: ENV['PORT'])
  end
end

Rails::Server.prepend(DefaultOptions)

# config/development.rb
config.action_mailer.smtp_settings = {
  :port => ENV['PORT'],
  :address => ENV['SMTP_SERVER'],
  :user_name => ENV['SMTP_LOGIN'],
  :password => ENV['SMTP_PASSWORD'],
  :domain => ENV['MAILER_DOMAIN'],
  :authentication => :plain
}

Ответ 2

Вы можете использовать Rails:: Server:: Параметры, чтобы получить необходимую информацию, а затем соответствующим образом настройте ActionMailer:

config.action_mailer.default_url_options = { host: Rails::Server.new.options[:Host], port: Rails::Server.new.options[:Port] }