abstract_notifier gemがいい感じ。

abstract_notifierはPush Notificationのようなテキストベースの通知をActionMailerと同じような感じに実装するためのgemです。色々やってくれるというより、名前の通り枠組みを提供する感じです。

実装

Image from Gyazo

要するにabstract_notifierに従って実装しておくと、

DiscordNotifier.user_signup(user).notify_now

DiscordNotifier.user_signup(user).notify_later

こんな感じにActionMailerのように同期版、非同期版を選んで実行できたり、

DiscordNotifier.with(user).user_signup.notify_later

こんな感じにActionMailerのようにParametarizedな呼び方ができます。

弊社のフィヨルドブートキャンプアプリではサイト内通知とDiscord通知をこれを使うものに置き換えました。

class DiscordNotifier < AbstractNotifier::Base
  self.driver = DiscordDriver.new
  self.async_adapter = DiscordAsyncAdapter.new
end

実装は、AbstractNotifier::Baseを継承したクラスで同期処理用のdriverと非同期用のasync_adapterを実装してセットしておくとOKです。

普通のRubyクラスでOKですが、それぞれcallメソッドとenqueueメソッドを実装している必要があります。両方ともAbstractNotifier::Baseが持ってるnotificationを経由して呼び出されます。

普通は通知でやりたいことは共通だと思うので、同期用のdriverは普通に書いて、非同期用のasync_adapterはActiveJob経由でdriverを呼び出すように書いておけば実装が共通にできていいと思います。

driverはこんな感じ。

class DiscordDriver
  def call(params)
    Discord::Notifier.message(
      params[:body],
      username: params[:name],
      url: params[:webhook_url]
    )
  end
end

async_adapterはこんな感じ。 Parameterized版はコンストラクタが呼ばれるのでenqueueでもそれが使われるように書いておく必要があります。

class DiscordAsyncAdapter
  def initialize(params = {})
    @params = params
  end

  def enqueue(_, params = {})
    params.merge!(@params)
    DiscordJob.perform_later(params)
  end
end

jobはこんな感じにしておくだけで非同期にできて便利ですね。

class DiscordJob < ApplicationJob
  queue_as :default

  def perform(params)
    DiscordDriver.new.call(params)
  end
end

テスト

テストは同期版、非同期版、Parameterized版をそれぞれテストしておくと安心です。gemにはrspec向けのhelperしかないですが、AbstractNotifier::Testing::Driver.deliveriesAbstractNotifier::Testing::Driver.enqueued_deliveriesにそれぞれdeliverしたものが入るようになっていたのでminitestではこれらを直接見る様に書くといいんじゃないかと思います。

長いのでassert_deliveryとかあった方が便利そうなのでminitest_helperを後でPRしようかなと思います。

2022年06年04日:マージされました。

abstract_notifierをminitestでテストする - komagataのブログ

require 'test_helper'

class DiscordNotifierTest < ActiveSupport::TestCase
  setup do
    @params = {
      body: 'new user signup!',
      sender: users(:bob),
      name: 'bob',
      webhook_url: 'https://discord.com/api/webhooks/0123456789/xxxxxxxx'
    }
  end

  test '.user_signup' do
    notification = DiscordNotifier.user_signup(@params)

    assert_difference -> { AbstractNotifier::Testing::Driver.deliveries.count }, 1 do
      notification.notify_now
    end

    assert_difference -> { AbstractNotifier::Testing::Driver.enqueued_deliveries.count }, 1 do
      notification.notify_later
    end

    # Parameterized
    notification = DiscordNotifier.with(@params).user_signup

    assert_difference -> { AbstractNotifier::Testing::Driver.deliveries.count }, 1 do
      notification.notify_now
    end

    assert_difference -> { AbstractNotifier::Testing::Driver.enqueued_deliveries.count }, 1 do
      notification.notify_later
    end
  end
end

ちなみにこのgemに関する情報は作者本人のブログエントリーとREADMEぐらいしか見当たらなかったのでコード読んだ方が早いです。

その後

「別に同期・非同期切り替える必要ないし、似たような実装で今動いてるから置き換えるまではいかないかな〜」

と思うかもですね。

しかし僕はこれで通知という視点で抽象化するというアイデアを知るまではDiscord通知とメール通知を同じものだと思ってなかったので共通化してスッキリしました。他のメンバーに通知を実装してもらう時もオリジナル実装よりもこういうgemになっている方がルールが明確でよさそうに思います。

そして真の目的はこれに対応させておくと後でエントリーを書こうと思っているactive_deliveryでシュッと使えるようになる点です。

つづく

active_deliveryで通知をまとめる - komagataのブログ

Image from Gyazo

PiroさんからITエンジニア1年生のためのまんがでわかるLinuxをご献本いただきました。ありがとうございます!

こちらは僕らがやっているフィヨルドブートキャンプでも参考書籍にさせていただいている「まんがでわかるLinux シス管系女子」にLinuxの本当の入門者用の内容を追加して新しくした感じの本になっています。(今から買うなら今回の新しい方一択です)

完全に「新人教育する人の目線」になっちゃうんですが、Linuxに入門する人にとってより入りやすくなっていて素晴らしかったです。

弊社スクールでもLinuxを入門するときに、

「Terminalをなぜ使うのかわからない」 「どういうシチュエーションでみんな使うのかわからない」

などイメージがしづらいという部分でつまづく人が多くいます。

新人が入社したところから仕事で必要になって一つづつ学んでいくというストーリーがまんがで描かれることによってその辺りのイメージがとっても掴みやすくなっていると思います。

Image from Gyazo

Next.jsと組み合わせる永続化のAPIって何が一般的でしょう?

僕が開発するなら何でもいいんですが、フィヨルドブートキャンプのJavascriptコース最終盤のカリキュラムで生徒の方もスクラムで開発するものになるので、そこで使う技術はカリキュラムとしても用意する必要があります。

考え方としては、Javascriptのプログラマーとして就職したら遭遇するよくある構成がいいなと思っています。

Next.jsを使うというのは決定していて、DB・API部分の選択肢が豊富で迷っちゃいます。

  • Firebase(ちょっとしたサイトならいいが、しっかりしたサービスだと避けるイメージ)
  • OpenAPI(サーバーサイドがガッツリし過ぎ感)
  • GraphQL(最有力か?)

みなさんは何を使ってますかね〜?

ヘッドホンを伸ばしたらバキッと一部壊れちゃいました。

妻が目立たないテープで縛ってくれたので騙し騙し使っていきます…。

Image from Gyazo

最近のrailsでのパスワード認証gemって何使ってます?

The Ruby Tool BoxのWeb Authentiationカテゴリーを参照。

devise

やってくれることは多いがカスタマイズしづらいので嫌う人も多い。俺はこれ使ってる。 しかし登場から時間が経ち、railsの仕様に追従していくにつれて初期の設計に無理がきてる感。特にrails7からはさらに。

sorcery

やってくれることは少ないが、カスタマイズしやすい。bootcampのアプリはこれを使ってる。(確か @hrysd が入れた) リニューアル版sorcery(sorcery-rework)が別リポジトリで進んでるが、当分完成しそうにな。

authlogic

使ったことないけどちょっと古い感?

clearance

使ったことないけどちょっと古い感2

railsの基本機能を使って自作

これが多いのかも?

個人的には認証などのセキュリティが大きく絡むところはなるべくgemなどみんなが見ている(セキュリティパッチがすぐ入りやすい)ところのを使いたいな〜と思っております。

みなさんのプロジェクトではどんな感じでしょう? 温度感が知りたい感。

railsの6系最新(6.1.4.4)とrubyの最新(3.1.0)にアップグレードするときに対応してないgemにPRを送る作業をやっています。

bootcampのrails 6.1.4.4

https://github.com/fjordllc/bootcamp/pull/4101

pgのwarningが残ってる。(こういうところも粘り強く対応できるようになっていきたい)

bootcampのruby 3.1.0

https://github.com/fjordllc/bootcamp/pull/4108

@ima1zumiさんが3.0.2にアップデートする作業を初めてくれて、そこに乗る形でやりとりさせていただいてました。3.0.3だと起きるけど3.1.0では起きない問題があったので一気に上げちゃう方針に。こういうところも粘り強く(略

おかげでCI通ったようなのであと一歩。

sorcery-jwt

https://github.com/hayfever/sorcery-jwt/pull/14

sorcery-jwtはdependencyの設定を変えないと最新のsorceryで使うことができない。sorcery-jwt自体、リニューアル後(予定)の新sorceryではcore pluginになる予定だそうで、こちらのgemをもうupdateする気はないのかもしれない。とりあえずforkして対応してるけど、メールを送ってみる予定。

ActiveFlag

https://github.com/kenn/active_flag/pull/19

これはbundlerのバージョンによるものだった。

今後

ruby 3.1.0にできたら次はrails 7.0.1に上げる作業をやる予定。こっちも色々PRに必要になるでしょう。

ActiveFlagのテストでrequire 'set'が必要かもという問題。

人によって結果が違うのなぜかな〜という状態だったんですが、(実行していただいた方々ありがとうございます)

bundler 2.2.8でrequire 'set'がなくなったのが原因だそうです。

https://github.com/kenn/active_flag/pull/19#issuecomment-1025093171

Image from Gyazo

この行。

https://github.com/rubygems/rubygems/pull/4297/files#diff-04ae823e98259f697c78d2d0b4eab0ced6a83a84a986578703eb2837d6db1a32L4

ほぼ標準といえるようなgemから依存がなくなるとこういうことが起きるんですね。setについて他のライブラリでも似たようなこと起きてるかもしれません。環境を示すときはbundlerのバージョンも示した方がいいんだなと勉強になりました。

ボランティア開発者が「反乱」。もっとオープンソースに還元されるべき?(山口健太) - 個人 - Yahoo!ニュース

これいろんな意見がありますよね。オープンソース・オープンソースコミュニティ大好きなので開発者・ユーザーにとって良い方向に進むといいなと思います。

それとは別に、オープンソース開発者の義務について思うこと。

OSSに好き勝手に要望を出すことは良いと思う。

「だったら自分でやれよ」って言っちゃうとフィードバックが無くなっちゃうので。

それと同時にOSSコミッターが持つ最大の権利が、

「要望を無視すること」

かな〜と思いました。(要望を「反論する」「却下する」ではなく「無視する」)

「好き勝手に要望を出すのはOK」ただしコミッタは「スルーする権利」を持つ。

好き勝手な要望に全部向き合わないといけないのではコミッターの体が持たないんじゃないかな〜と思います。

この「スルーする」っていうのっていろんな場面で強力な選択肢だと思ってます。

重要なOSSのコミッターの方々には利益を得て欲しいと思うと同時に、負担が減ればいいな〜と切に思います。

先週作ったPRにコメント欄でリマインド。

Update requirements for sorcery by komagata · Pull Request #14 · hayfever/sorcery-jwt

railsアプリをruby3.0.3にアップデートする作業で、ActiveFlagのto_humanメソッドが落ちる。ActiveFlagにPRを作ろうかと思いgemのrake testをするとエラーが起きるのでまずそれを治すPRを作った。

'require' is needed by komagata · Pull Request #19 · kenn/active_flag

railsアプリのその部分、ruby3.1.0にすると起きないので調査する気力がダウン…。

その調査の途中で見つけたfixtureデータの不正をPRして時間切れ。

テストデータが不正だったのを修正 by komagata · Pull Request #4033 · fjordllc/bootcamp

引越しのための物の整理をしていたら昔のデータのCD-ROMを見つけました。そこに消失したとおもていた昔のこのブログ(昔はホームページの日記だった)の画像データが残っていたので、いくつか画像を復旧してみました。

OSを大インストール - komagataのブログ

Redhat9のインストール方法なんて誰も欲してないと思いますが…。