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

週刊Railsウォッチ: Rails 7がRuby 3.1のClass#descendantsに対応、GitHub Issue風ファイルアップローダほか(20211115前編)

こんにちは、hachi8833です。つっつきの後でこの番組見ました↓。

週刊Railsウォッチについて

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

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

お知らせ

来週の週刊Railsウォッチは祝日のためお休みをいただきます🙇。

🔗Rails: 先週の改修(Rails公式ニュースより)

更新情報にまた追い抜かれ中…ハロウィンエディションの残りと次の更新情報から見繕いました。

🔗 uuid_namespaceのパラメータが未定義文字列の場合のUUID生成を修正

Digest::UUIDで定義されているのと異なるnamespace IDの場合のDigest::UUID.uuid_from_hashの振る舞いを修正した。
新しい振る舞いは、コンフィグのconfig.active_support.use_rfc4122_namespaced_uuidsオプションをtrueに設定すると有効になる(新規アプリではこれがデフォルト)。
アップグレードしたアプリでは古い振る舞いがデフォルトになり、Digest::UUIDでnamespace IDとして定義されている定数と異なる場合に毎回deprecation warningを出力する。
Alex Robbin, Erich Soares Machado, Eugene Kenny
同Changelogより


つっつきボイス:「Digest::UUIDってあるんですね」「UUIDの名前空間とは…?ググってみるとPythonライブラリのドキュメントが出てきた↓」「これは知らなかった」「コードのDNS_NAMESPACEURL_NAMESPACEなどもこのドキュメントに載っているので、UUIDには名前空間の仕様もあるということなのか、へ〜!」

参考: uuid --- RFC 4122 に基づくUUID オブジェクト — Python 3.10.0b2 ドキュメント

# activesupport/lib/active_support/core_ext/digest/uuid.rb#58
+   def self.pack_uuid_namespace(namespace)
+     if [DNS_NAMESPACE, OID_NAMESPACE, URL_NAMESPACE, X500_NAMESPACE].include?(namespace)
+       namespace
+     elsif use_rfc4122_namespaced_uuids == true
+       match_data = namespace.match(/\A(\h{8})-(\h{4})-(\h{4})-(\h{4})-(\h{4})(\h{8})\z/)
+
+       raise ArgumentError, "Only UUIDs are valid namespace identifiers" unless match_data.present?
+
+       match_data.captures.map { |s| s.to_i(16) }.pack("NnnnnN")
+     else
+       ActiveSupport::Deprecation.warn <<~WARNING.squish
+         Providing a namespace ID that is not one of the constants defined on Digest::UUID generates an incorrect UUID value according to RFC 4122.
+         To enable the correct behavior, set the Rails.application.config.active_support.use_rfc4122_namespaced_uuids configuration option to true.
+       WARNING
+
+       namespace
+     end
+   end

参考: Rails API Digest::UUID

🔗 Ruby 3.1のClass#descendantsに対応

Ref: https://bugs.ruby-lang.org/issues/14394
Ref: ruby/ruby#4974
ObjectSpaceをイテレーションする必要のないClass#descendantsのネイティブ実装がRuby 3.1に入る公算が高いので、可能ならこれを使うべき。
Rubyのプルリクがマージされるのを待ってからこちらをマージするつもり。DescendantsTrackerのほとんどをスキップする必要もあるが、検出方法がまだわからないので、利用可能かどうかをRUBY_VERSIONでチェックする方法で回避した。

# activesupport/lib/active_support/core_ext/class/subclasses.rb#L17
  def descendants
    ObjectSpace.each_object(singleton_class).reject do |k|
      k.singleton_class? || k == self
    end
- end
+ end unless method_defined?(:descendants) # RUBY_VERSION >= "3.1"

つっつきボイス:「ActiveSupport::DescendantsTrackerにあるdescendantsメソッドが、Ruby 3.1にネイティブのClass#descendantsが入ったのを機にRubyのバージョンを見て切り替えるようにしたんですね」「Active SupportにあるメソッドがこうやってRuby本家に取り入れられることはときどきありますよね」「descendantsもとうとう出世メソッドになった」「出世おめでとうございます🎉」

🔗 テストごとにExecutor#wrapを呼び出すオプションが追加

  • Rails.application.executorフックがすべてのテストの前後で呼ばれるようになった。
    これにより、リクエストやジョブのローカルなステートがリセットされるのを適切にシミュレートするようになり、テストとテストの間でステートが漏れるのを防止できる。
    ただし、test環境で実行されるexecutorフックはリエントラントであることが求められる。
    Jean Boussier
    同Changelogより

つっつきボイス:「変更が割と多いかも」「テスト間でステートが漏れ出さないようにする改修: active_support.executor_around_test_case = trueというコンフィグで設定できるのね」

🔗 date_selectヘルパーにday_formatが追加

:year_formatオプションと同様の:day_formatオプションをdate_selectに追加。
ロケールをja(日本語)にしたRailsアプリケーションとrails-i18nではdate_selectで以下が表示される。

日本では2021-10-29という日付を2021年10月29日と表示するのが普通(年はyear、月はmonth、日はdayの意味)。
しかし現在のdate_selectにはdayの書式をカスタマイズするオプションがないのでday_formatを追加した。

form.date_select :published_date, year_format: ->(year) { "#{year}年" }, day_format: ->(day) { "#{day}日" }

これにより、上のように書くと以下が出力される。

form.date_select :published_date, order: %i[day month year], day_format: ->(day) { day.ordinalize }

また、上のように書くと以下のように出力できる。

同PRより


つっつきボイス:「お〜なるほど、今までyear_formatは指定できたけどday_formatはできなかったのをできるようにしたんですね」「最近RailsでAPIとバッチを書くことが多くてビューを書く機会があまりなかったけど、今までできなかったのか〜」「month_formatがないと思ったらmonth_format_stringがあった」

🔗 delegated_typeaccepts_nested_attributes_forをサポート

このプルリクは、delegated_typeaccepts_nested_attributes_forのサポートを追加する。これで以下のようなメソッドを書かずにレコードを手軽に作成や更新できるようになる。

class Entry < ApplicationRecord
  delegated_type :entryable, types: %w[ Message Comment ]

  def self.create_with_comment(content, creator: Current.user)
    create! entryable: Comment.new(content: content), creator: creator
  end
end

accepts_nested_attributes_forが使えると以下のように書ける。

class Entry < ApplicationRecord
  delegated_type :entryable, types: %w[ Message Comment ]
  accepts_nested_attributes_for :entryable
end

params = { entry: { entryable_type: 'Comment', entryable_attributes: { content: 'Smiling' } } }
entry = Entry.create(params[:entry])

作った理由
accepts_nested_attributes_forによるネステッドフォームは非常に強力。これはDelegated Typeでも使えるようにするために足りなかった最後のピースとなる。
疑問
これはポリモーフィックなbelongs_toリレーションシップなので、他にどんなテストがあるとよいだろうか?(TestNestedAttributesOnABelongsToAssociationで既にテストされているので)
同PRより


つっつきボイス:「Delegated Typeといえば以前話題になりましたね(ウォッチ20200601)」「Delegated Typeでaccepts_nested_attributes_forが使えるようになったのね: 自分はaccepts_nested_attributes_forは使わないけど」「このメソッドで苦しんだ人を割とよく見かける印象あります」「Rails wayなフォームですべてできるならいいんですが、そこからはみ出したときにちょっとね…」

参考 【Rails】accepts_nested_attributes_forを使用しない方が良いワケとその代替方法

「そういえばDelegated TypeのAPIドキュメント↓を翻訳したので近々公開しようと思います」

🔗Rails

🔗 StimulusとActive StorageでGitHub Issue風のファイルアップローダを作る(Ruby Weeklyより)


つっつきボイス:「これはたしかにGitHub issue風↓」「こんなふうに作れるんですね、いいな〜」


同記事より

「今日ちょうど社内勉強会で話題になりましたけど、Active Storageはダイレクトアップロードにも対応していますね」「そうそう、意外に知られてないのかも」「サーバーに負担をかけずにクラウドにアップロードできるダイレクトアップロード機能はあってうれしい機能ですね: 要するにでかいファイルをサーバーで受け取りたくない」「ほんとに」

参考: 9 ダイレクトアップロード — Active Storage の概要 - Railsガイド

🔗 minitest-spec-rails(Ruby Weeklyより)

metaskills/minitest-spec-rails - GitHub

何だか懐かしい風味のロゴと東洋風フォントですね↓。


同リポジトリより


つっつきボイス:「minitestでspecが使いたいのかな?」「普通にminitestで書けばよさそうですけどね」

「お、ActiveSupport::TestCaseを使いながらMiniTest::Spec::DSLにあるitでも書けるのね↓」「なるほど」「ActiveSupport::TestCaseだけどRSpec風にitでも書きたい人向けという感じかな」

# 同リポジトリより
require 'test_helper'
class UserTest < ActiveSupport::TestCase
  let(:user_ken)   { User.create! :email => 'ken@metaskills.net' }
  it 'works' do
    user_ken.must_be_instance_of User
  end
end

ActiveSupport::TestCaseを継承しないとテスト用拡張モジュールを使えないようなgemは、実は割とあるんですよ」「あ〜」「ズバリRailsがそうだったりします」「そういえばRailsフレームワークのテストコードはRSpecじゃないんですよね」「minitestですね」

参考: library minitest/unit (Ruby 3.0.0 リファレンスマニュアル)

🔗 Railsコアメンバーによるプルリクレビュー実況動画


つっつきボイス:「ruby-jp SlackのEnglishチャンネルで見かけた、RailsコアメンバーのKasperさんがRailsフレームワークへのプルリクをレビューする様子の実況中継です」「お〜、RubyKaigiなどでやりそうな企画ですね: ヒアリングの練習がてら見てみるといいかも」

🔗 その他Rails


つっつきボイス:「そうそう、@koicさんが11/19(金)の銀座Rails#39の招待講演枠に登壇されます: まだ一般の発表枠はあるので皆さんぜひお気軽に応募お願いします↓」


前編は以上です。

バックナンバー(2021年度第4四半期)

週刊Railsウォッチ: JSON.parseの機能、Opal 1.3、async gem、Linuxコマンドチートシートほか(20211110後編)

今週の主なニュースソース

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

Rails公式ニュース

Ruby Weekly


CONTACT

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