active_delivery gemがいい感じ。

active_deliveryはActionMailer 的な処理 をまとめるWrapperです。

例によってWeb上にはREADMEと作者の方のブログエントリーしか見当たらなかったので、使い方を知るにはソース読んだ方が早いです。僕が代わりに読んでおいたのでちょっとしたプラス情報を書いておきます。

使い方

def after_signup
  ActivityMailer.user_signup.deliver_later if user.receive_emails?
  DiscordNotifier.user_signup.notify_later if user.receive_discord?
end

こんな感じに書いてたのを

def after_signup
  ActivityDelivery.notify(:user_signup, user)
end

こんな感じに描けるようになります。

ActivityDelivery.notify!(:user_signup, user)

また、デフォルトでは非同期の方を呼び出しますが、notify!の方だと同期版を呼んでくれます。

ActivityDelivery.with(user: user).notify(:user_signup)

そして、ActionMailerのようにParameterizedな呼び方もできます。

概要

直接使うDeliveryクラスと実際に動くMailerクラス(やその他の独自のDeliveryクラス)、その間をつなぐLineクラスがあります。

Image from Gyazo

デフォルトで使われるActiveDelivery::Lines::Mailerを見るとどうやって間を繋いでいるのかがわかります。

実装

これを用意しておくだけで既に、

# app/deliveries/activity_delivery
class ActivityDelivery < ActiveDelivery::Base
end

下記が呼べてメールが送信されます。

ActivityDelivery.notify(:user_signup, user)

何故かとういうと、デフォルトでこういうクラスのメソッドを探してメソッドを呼んでくれるからです。

class_name.gsub(/Delivery$/, "Mailer")

Mailerはデフォルトで呼ぶんですね。

class ActivityDelivery < ActiveDelivery::Base
  unregister_line :mailer
end

こうしておけばデフォルトのMailerを呼ぶのもなしにできます。

独自のDeliveryクラスを使うには自分でLineクラスを書く必要があります。

しかし、abstract_notifierを使って作った通知クラスがあれば、そちらにActiveDelivery::Lines::Notiferクラスが同伴されているので自分で書く必要はないです。便利!

abstract_notifierについてはこちらのエントリーを見てください。

abstract_notifierで通知を実装する - komagataのブログ

class ActivityDelivery < ActiveDelivery::Base
  register_line :discord,
                ActiveDelivery::Lines::Notifier,
                resolver: ->(_) { DiscordNotifier }
end

これでActivityMailerとDiscordNotifierのメソッドを一緒に呼んでくれるようになります。

テスト

テストはこんな感じで同期版・非同期版、普通版・Parameterized版をそれぞれテストしておけば安心だと思います。

require 'test_helper'

class ActivityDeliveryTest < ActiveSupport::TestCase
  test '.notify(:user_signup)' do
    params = {
      body: 'user signup!'
      user: users(:foo)
    }

    assert_difference -> { AbstractNotifier::Testing::Driver.deliveries.count }, 2 do
      ActivityDelivery.notify!(:user_signup, **params)
    end

    assert_difference -> { AbstractNotifier::Testing::Driver.enqueued_deliveries.count }, 2 do
      ActivityDelivery.notify(:user_signup, **params)
    end

    assert_difference -> { AbstractNotifier::Testing::Driver.deliveries.count }, 2 do
      ActivityDelivery.with(**params).notify!(:user_signup)
    end

    assert_difference -> { AbstractNotifier::Testing::Driver.enqueued_deliveries.count }, 2 do
      ActivityDelivery.with(**params).notify(:user_signup)
    end
  end
end

Comments


Option