Tech Racho エンジニアの「?」を「!」に。
  • Ruby / Rails関連

週刊Railsウォッチ: 7.1でバリデーションメッセージのアポストロフィ->カーリー置き換えが取り消しほか(20230928後編)

こんにちは、hachi8833です。つっつきで一同がこのコードにどよめきました。

週刊Railsウォッチについて

  • 各記事冒頭には🔗でパーマリンクを置いてあります: 社内やTwitterでの議論などにどうぞ
  • 「つっつきボイス」はRailsウォッチ公開前ドラフトを(鍋のように)社内有志でつっついたときの会話の再構成です👄
  • お気づきの点がありましたら@hachi8833までメンションをいただければ確認・対応いたします🙏

TechRachoではRubyやRailsなどの最新情報記事を平日に公開しています。TechRacho記事をいち早くお読みになりたい方はTwitterにて@techrachoのフォローをお願いします。また、タグやカテゴリごとにRSSフィードを購読することもできます(例:週刊Railsウォッチタグ)

🔗Rails

🔗 Railsのミドルウェアで知っておくべき(ほぼ)すべてのこと(Ruby Weeklyより)


同記事より


つっつきボイス:「Railsを支えているRackミドルウェアの解説記事です」「Rackとミドルウェアはずっと前からこういう仕組みですね: 他にもいろいろ記事がありますし、このあたりは知っておいて損はない👍」

rack/rack - GitHub

参考: Rails と Rack - Railsガイド

🔗 スクリーンキャスト: Rails 7.1の機能で認証をゼロから構築する(Ruby Weeklyより)


つっつきボイス:「5:15のあたりで、RailsのCurrentAttributesを継承したCurrentクラスを作って実装している」

参考: § 1.12 SecurePasswordモジュール -- Active Model の基礎 - Railsガイド
参考: Rails API ActiveSupport::CurrentAttributes

「ここでは任意の場所からログイン中のユーザー情報を取得できるようにするためにCurrentAttributesを使っているんですね: こうすると引数渡しとかを経由せずに任意の場所(モデルやヘルパーも含む)からグローバルコンテキストとして呼び出すことができるので、使いようによっては便利です」「なるほど」

「ただし、任意の場所から呼び出せるグローバルなコンテキストにメソッドの振る舞いが依存してしまうと、ユニットテスト時にも都度考慮してMockしてあげる必要があるなど副作用を生みやすいので、最近はあまり見ない実装方法かもしれませんね(昔はこういうフレームワークも結構ありましたが)」

「このスクリーンキャストに関連している7.1のプルリクを貼っておきました」「has_secure_passwordはRails標準の機能ですね」「7.1ではパスワードチャレンジの機能なども追加されているのね」

参考: Enhance has_secure_password to also generate a password_salt method by lazaronixon · Pull Request #47490 · rails/rails
参考: Add authenticate_by when using has_secure_password by jonathanhefner · Pull Request #43765 · rails/rails
参考: Add ActiveRecord::Base::generates_token_for by jonathanhefner · Pull Request #44189 · rails/rails
参考: Add ActiveRecord::Base::normalizes by jonathanhefner · Pull Request #43945 · rails/rails
参考: Support password challenge via has_secure_password by jonathanhefner · Pull Request #43688 · rails/rails

🔗 Brotli圧縮でRailsのキャッシュ効率を改善(Ruby Weeklyより)


つっつきボイス:「Brotliって圧縮アルゴリズムなんですね」「Brotliのリポジトリを見ると、LZ77アルゴリズムのバリアントなのね: rails-brotli-cache gemを使ってRailsのキャッシュアルゴリズムをGzipからこのBrotliに差し替えて圧縮率を高めたという記事ですね」

# 同記事より
require 'json'
require 'brotli'
json = File.read("sample.json")
puts json.size # => 1127380 bytes ~ 1127kb
brotlied = ::Brotli.deflate(json, quality: 6)
puts brotlied.size # => 145056 bytes ~ 145kb

google/brotli - GitHub

pawurb/rails-brotli-cache - GitHub

参考: Brotli - MDN Web Docs 用語集: ウェブ関連用語の定義 | MDN
参考: LZ77 - Wikipedia

🔗 feature specをsystem specで書き直す(Ruby Weeklyより)

# 同記事より
# OLD: feature spec type
RSpec.feature "name", type: :feature do

# NEW: system spec type
RSpec.describe "name", type: :system do

つっつきボイス:「RSpecのfeature specを頑張って書き直した記事だそうです」「今はCPUリソースが豊富になったのでsystem specもやりやすくなりましたね」「そういえばfeature specがRSpecの標準でなくなって随分経ってる」「記事によると、RSpec 3.7で"今後はsystem specを使ってね"とアナウンスされていたそうです↓」「Rails本体はもっと前からシステムテストが主流ですね」

参考: RSpec 3.7 has been released!
参考: § 6 システムテスト -- Rails テスティングガイド - Railsガイド

🔗 Rails 7.1.0の「バリデーションメッセージのアポストロフィをカーリーに置き換える」プルリク(その後取り消し)

元記事: Rails v7.1.0 で can't be blankcan’t be blank に変わる - アジャイルSEの憂鬱


つっつきボイス:「Rails 7.1.0beta1のChangelogを読んでて自分も気になっていましたが、神速さんが早速記事にしていました」「これね〜、日本語圏だと'を意識して使い分けないことが多いので、従来のメッセージを想定して手打ちしたテストが失敗したときに原因がすぐにわからない、なんてことになりそうだし、ちょっとどうかなという気持ち」「英語圏の人はいいのかもしれないけど」「実際、英語圏ではストレートの""''はあくまで間に合わせで、カーリーの“”‘’の方が正式という意識があるんですよね」

参考: Improve typography of user facing validation messages by jdufresne · Pull Request #45463 · rails/rails
参考: 引用符 - Wikipedia

「ドキュメントでのカーリー置き換えはともかく、デフォルトのバリデーションメッセージ↓については、i18nで定義されているデフォルトのen.ymlの内容を上書きする設定を組み込めば元の挙動に戻せると思いますけどね」「#45463に反論も寄せられているし、取り消しになってくれるといいんですけどね」

# activemodel/lib/active_model/locale/en.yml#L10
      inclusion: "is not included in the list"
      exclusion: "is reserved"
      invalid: "is invalid"
-     confirmation: "doesn't match %{attribute}"
+     confirmation: "doesn’t match %{attribute}"
      accepted: "must be accepted"
-     empty: "can't be empty"
-     blank: "can't be blank"
+     empty: "can’t be empty"
+     blank: "can’t be blank"
      present: "must be blank"

なお、この種の変更は英語でよく"cosmetic change"と呼ばれます。本質的でない変更というニュアンスもあります。


つっつきの後、昨日#45463が取り消されました↓。昨日リリースされたv7.1.0.rc1に反映されています🎉。

参考: Revert typography change in user facing errors · rails/rails@acfa045

🔗Ruby

🔗 YJITベンチマーク対象にLobste.rsのソースコードも追加(Ruby Weeklyより)


つっつきボイス:「Lobste.rsは実際に動いているSNSサイトだそうです」「Lobste.rsってロブスターなのね」「.rsで一瞬Rustの拡張子かと思った」

参考: Lobsters

「このソースコードがベンチマークに追加されたんですね↓」

参考: yjit-bench/benchmarks/lobsters at main · Shopify/yjit-bench

🔗 Rubyの行カウントの効率改善(Ruby Weeklyより)


つっつきボイス:「記事によると、str.count($/)と書くとstr.lines.countより1.5倍速いんだそうです」「何この書き方?」「見たことない」「メモリアロケーションも減るんですって」「Cの内部実装を利用しているらしいけど、正規表現...じゃないのかな?」「Cのコードを読むしかないか」

# 同記事より
Warming up --------------------------------------
                size    31.000  i/100ms
              length    75.000  i/100ms
               count    77.000  i/100ms
   each_line + count    81.000  i/100ms
           count($/)   196.000  i/100ms
Calculating -------------------------------------
                size      1.529k (±33.9%) i/s -      4.774k in   5.015361s
              length      1.434k (±38.8%) i/s -      5.025k in   5.139834s
               count      1.335k (±40.7%) i/s -      4.697k in   5.079353s
   each_line + count      1.411k (±39.5%) i/s -      5.022k in   5.110146s
           count($/)      2.231k (± 2.6%) i/s -     11.172k in   5.012323s

Comparison:
           count($/):     2230.5 i/s
                size:     1529.0 i/s - 1.46x  (± 0.00) slower
              length:     1434.2 i/s - 1.56x  (± 0.00) slower
   each_line + count:     1411.0 i/s - 1.58x  (± 0.00) slower
               count:     1334.9 i/s - 1.67x  (± 0.00) slower

参考: String#count (Ruby 3.2 リファレンスマニュアル)

🔗 re2: Google正規表現エンジンのRubyラッパー(Ruby Weeklyより)

mudge/re2 - GitHub


つっつきボイス:「re2はバックトラック機能がない代わりに高速でReDoS脆弱性が発生しないという特徴があります」「re2 gemは、Googleの正規表現エンジンであるre2のラッパーなのね」

google/re2 - GitHub

「このre2を見て、以下の記事で言及されているのを思い出しました↓」「Rubyのメイン正規表現エンジンをOnigmoからre2に変える試みがあったんですね」「Onigmo正規表現エンジンはRegexp以外でもStringなどあちこちで使われているので、たしかに差し替えは難しそう」

参考: 正規表現とは何なのか、makenowjustが正規表現に興味を持ったきっかけ。深掘りRubyKaigi 2023 with spikeolaf & makenowjust 文字起こしレポート vol.1 - STORES Product Blog

shyouhei:(Rubyで)RE2を使ってみようという話を実は前に言ってた人がいて、誰だっけな。Sam Saffronかなぁ。Sam SaffronはDiscourseを作っている人ですけど、ともかく正規表現が遅いのは困るから早くしてくれみたいな感じで、早い正規表現エンジンがここにあるじゃん、これ使えばいいじゃんみたいな話をして。それでさっき話が出ていた遠藤さんが実際ちょっとやってみようかなぁみたいな感じでちょっとだけ手を出して、生半可な気持ちでは手が出せませんねということが明らかになって撤退していった感じですね。
(中略)
shyouhei:Rubyの言語の処理系の中から今のRubyの正規表現エンジンを抜くっていう話になると、多言語対応(M17N)が超大変。Rubyの成立過程として先に正規表現の方が多言語対応して、そこに後からRuby本体が乗ったので、正規表現の方だけ抜こうと思うとちょっとテクニカルに大変ですね。
同記事より

k-takata/Onigmo - GitHub

「Go言語の標準の正規表現ライブラリであるregexpパッケージでもre2が使われていて、以前はバックトラックできないのが貧弱に思えたので使わなかったんですが、今思えば速度とセキュリティを優先していたのかもしれない」

参考: regexp package - regexp - Go Packages

はじめての正規表現とベストプラクティス10: 危険な「Catastrophic Backtracking」前編

🔗 その他Ruby


つっつきボイス:「大江戸Ruby会議10が浅草で開催決定🎉」「アフターパーティの会場は浅草花やしきですって」「会場は現在手配中だそうです」「花やしきって貸し切りサービスやってるんですね」

参考: 浅草花やしき貸切料金表|浅草花やしき

🔗DB

🔗 PostgreSQL 16がリリース


つっつきボイス:「BPS社内Slackに貼っていただいた記事です」「そうそう、まだ詳しく見てないけど新機能の中ではLogical replicationがとりあえず気になっているかな: Row Filters Ruleというものを使うと、特定の条件に一致するデータだけを論理レプリケーション可能になるみたいですね」「なるほど」「従来でも論理レプリケーションするgemが使えたけど、標準機能でできるのはいいですね👍」

参考: PostgreSQL: Documentation: 16: 31.3. Row Filters
参考: 第31章 論理レプリケーション

shayonj/pg_easy_replicate - GitHub

🔗 PostgreSQLに保存した大量のBase64文字列データを移行した話(Ruby Weeklyより)


つっつきボイス:「メールの添付ファイルをBase64文字列でデータベースに保存してたらものすごく大きくなった、あるある」「PostgreSQLにバイナリで保存すればもう少しましだったかもしれないけど、そもそもこの種のデータをデータベースに保存することが減っていますね」「今はファイルがめちゃくちゃ大きくなってますしね」「記事ではActive JobとActive Storageを使ってデータをRailsのオブジェクトストアに移動したのね」

# 同記事より
class SaveAttachmentsToCellarJob < ApplicationJob
  queue_as :default

  def perform(email_id)
    email = OutgoingEmail.find(email_id)

    email.attachments.each do |attachment|
      next if attachment.content == nil # Skip if already moved

      decoded_content = Base64.decode64(attachment.content)
      # We use at tempfile here to avoid using memory for large attachments
      file = Tempfile.new
      file.binmode
      file.write(decoded_content)
      file.rewind
      attachment.file.attach(io: file, filename: attachment.name,
                             content_type: attachment.content_type, identify: false) # This is what stores the file in the object store, we set identify to false as the users provide teh Content-Type themselves

      attachment.update(content: nil) # Remove the base64 string from the database
    end
  end
end

🔗JavaScript

🔗 Turboのソースコードを読む(Ruby Weeklyより)


つっつきボイス:「Turboのソースコードを読みながら、リンクをクリックしてから動くまでの流れを追っていく記事です」

「ところで最近のReactを触っていると、ブラウザで動きを追える統合ツールがかなりよくできていると感じるんですが、Turboはまだデバッグツールが少ないですね」「Turboにも優秀なツール欲しいですね」「うんとリッチなWeb UIを構築するなら最初からReactでやる方がいいかな」

// 同記事より
export class LinkClickObserver {
  started = false

  constructor(delegate, eventTarget) {
    this.delegate = delegate        // set to 'session'
    this.eventTarget = eventTarget  // set to 'window'
  }

  start() {
    if (!this.started) {
      this.eventTarget.addEventListener("click", this.clickCaptured, true)
      this.started = true
    }
  }
}

Railsの技: "プログレッシブエンハンスメント"でHotwire的思考を身につける(翻訳)

Rails 7とReactによるCRUDアプリ作成チュートリアル(翻訳)


後編は以上です。

バックナンバー(2023年度第3四半期)

週刊Railsウォッチ: Rails 7.1がJavaScriptでBunサポートを追加ほか(20230926前編)

ソースの表記されていない項目は独自ルート(TwitterやはてブやRSSやruby-jp SlackやRedditなど)です。

Ruby Weekly


CONTACT

TechRachoでは、パートナーシップをご検討いただける方からの
ご連絡をお待ちしております。ぜひお気軽にご意見・ご相談ください。