Railtieを使ってActionMailerのdelivery_methodを拡張してみました。 サンプルで作ったgemはこちら↓
- http_action_mailer | RubyGems.org | your community gem host
- tzmfreedom/http_action_mailer: ActionMailer debugger with HTTP
ということで今回は作り方の備忘として残します。
作り方
Rails::Railtieのサブクラスで定義したinitializerのブロックをRailsが自動で呼び出す仕組みになっています。このinitializerブロック内に拡張する処理を入れていきます。http_action_mailerの例だと以下のようにActionMailer::BaseがロードされたタイミングでActionMailer::Base#add_delivery_methodでdelivery_methodを追加しています。
# lib/http_action_mailer/delivery_method.rb
require 'http_action_mailer/delivery_method'
module HttpActionMailer
class Railtie < Rails::Railtie
initializer 'http_action_mailer.add_delivery_method', before: 'action_mailer.set_configs' do
ActiveSupport.on_load :action_mailer do
ActionMailer::Base.add_delivery_method(:http, HttpActionMailer::DeliveryMethod)
end
end
end
end
delivery_methodに追加するクラスはこんな感じで定義します。基本的にinitializeとdelivery!メソッドを定義すれば良いです。
require 'faraday'
require 'json'
module HttpActionMailer
class DeliveryMethod
def initialize(settings)
@settings = settings
end
def deliver!(mail)
http_post(mail)
end
private
def http_post(mail)
conn = Faraday.new(url: @settings[:url])
conn.post do |r|
r.path = "#{@settings[:path]}/#{Array(mail.to).first}"
r.headers = @settings[:headers] if @settings[:headers]
r.body = {
from: mail.from,
to: mail.to,
cc: mail.cc,
subject: mail.subject,
text: mail.text_part.body.raw_source,
html: mail.html_part.body.raw_source,
}.to_json
end
end
end
end
deliver!の引数にはMail::Messageのオブジェクトが渡されます。コンストラクタの引数にはconfig.xxx_settingsで設定したハッシュが渡されます。xxxの部分にはadd_delivery_methodの第一引数のシンボルの名前が入ります。http_action_mailerの例だとconfig.http_settings = { … }
といった感じで設定できます。xxx_settingsのアクセサはadd_delivery_method呼び出し時に自動的に生成されます。add_delivery_methodの定義はこんな感じです↓
module ActionMailer
module DeliveryMethods
module ClassMethods
def add_delivery_method(symbol, klass, default_options = {})
class_attribute(:"#{symbol}_settings") unless respond_to?(:"#{symbol}_settings")
send(:"#{symbol}_settings=", default_options)
self.delivery_methods = delivery_methods.merge(symbol.to_sym => klass).freeze
end
...
あとはrequire ‘xxx’でRailtieがロードされるように調整します
# lib/http_action_mailer.rb
require 'http_action_mailer/railtie' if defined?(Rails)
gem化する場合は、generatorを使ってスケルトン作って、lib直下にRailtieを書けばOK
$ rails plugin new http_action_mailer --skip-bundle
実際のRailsアプリでテストをしたい場合はtest/dummy配下にRailsアプリが作られているので、コントローラやルーティングを設定してアプリを立ち上げれば、アプリの手動テストができます。
テストコードにrspecを使いたい場合はこちらを参照すると良いです↓
before: ‘action_mailer.set_configs’について
Railtieのinitializerのオプションでbefore: ‘action_mailer.set_configs’
を入れないと、config.xxx_settings での設定ができません。set_configsでもActionSupport.on_loadを使ってActionMailer::Baseのロードをフックしていますが、add_delivery_methodをする前にset_configsのhookブロックが呼び出されるとxxx_settingsのメソッドがまだ定義されていないためエラーになります。そのためset_configsの前にadd_delivery_methodを呼び出すブロックをon_loadで渡してあげることで、xxx_settingsのアクセサ定義 → send(“xxx_settings”)呼び出しという流れでdelivery_methodの設定をすることができます。
module ActionMailer
class Railtie < Rails::Railtie # :nodoc:
...
initializer "action_mailer.set_configs" do |app|
...
ActiveSupport.on_load(:action_mailer) do
...
options.each { |k, v| send("#{k}=", v) }
end
...
end