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

週刊Railsウォッチ:(20211108前編)RubyKaigiの過去講演動画が多数公開、Active ResourceとHerほか

こんにちは、hachi8833です。

週刊Railsウォッチについて

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

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

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

今回は以下の公式更新情報から見繕いました。件数が多いので残りは次回に。

🔗 ActiveRecord::Base.prohibit_shard_swappingを追加

シャーディング化したデータベースをリクエストのライフサイクル全体で用いる場合、データベースのシャードが意図せず変更されないことが望ましいことがよくある。
このコミットはActiveRecord::Base.prohibit_shard_swappingを導入する。これはブロックを受け取って、ブロックが継続中にシャードのスワップが発生しないようにするというもの。
同PRより


つっつきボイス:「シャーディング(sharding: 水平分散)でスワップが発生しないようにするとは?」「データベースのシャーディングでは、複数あるシャードのどれにデータを置くかをハッシュ関数などで決定します」「ちょっとRAID 5みたいですね」

参考: DBのテーブル水平分割技術「シャーディング」とは | 若手エンジニアのブログ
参考: RAID - Wikipedia
参考: ハッシュ関数 - Wikipedia

「で、シャーディングのキーとなるカラムが更新されるとブロックの置き場所が変わるんですよ」「あ、そうか」「おそらくシャードのスワッピングはそのことを指しているんでしょうね: この改修は、prohibit_shard_swappingブロック内ではそういうシャードの移動が発生しないようにすることだと思います」「なるほど」

🔗 schema/structureダンプのファイルパスを指定可能に

従来のRailsでは、schema/structureダンプの生成がデータベースの設定名に基づいていた。discuss.rubyonrails.orgでの議論にもあるように、これはシャード間でdumpファイルを共有するうえで問題がある。
このプルリクによる機能は、スキーマダンプのファイル名を設定するカスタムソリューションを既に書いていて、それを使いたい場合に便利。スキーマキャッシュのパスを指定できるのだから、スキーマダンプでもそうしない手はないだろう。
そのために、古いschema_fileデータベースタスクを非推奨にして、代わりに新しい方法でdb_configを渡せるようにした。db_configからファイル名を決定するコードはここで重複することになるが、将来はこれがファイル名決定の正しい方法になる。これらはコンフィグに設定されるのでアプリのコンフィグでもアクセス可能なはず。
なおschema_dumpはアルファ版以外のどのリリースにも含まれていないので、schema_dumpの振る舞いを非推奨化の手続きに乗せる必要はなかった。ただしfalsenilの場合の振る舞いは同じ。
* #43173をクローズ
* #43240に取って代わる
共著: Ryan Kerr leboshi@gmail.com
同PRより


つっつきボイス:「なるほど、schema/structureのダンプ先を指定できるようになったんですね: これはいいことだと思う👍」「ありがたい🙏」「今まではできなかったとは」「スキーマダンプのコマンドはrails db:schema:dumpでしたね」

🔗 Rails 7でselenium-webdriver 4以上とwebdrivers 5をサポート

新しいRails 7.0のジェネレータが生成するGemfileでは、Ruby 3.0をサポートするselenium-webdriver 4.0.0以上(#43270)と、それを必要とする最新のwebdrivers 5.0.0が使われる。

https://rubygems.org/gems/webdrivers/versions/5.0.0
そろそろRails 7.0でselenium-webdriver 3.xのサポートを廃止するとき。
同PRより


つっつきボイス:「selenium-webdriverのバージョンがRails 7から上がる: 既存アプリによってはRails 7にアップグレードするときのシステムテストの更新がちょっと大変になるかも」「システムテストをみっちり書いているアプリほどつらそう...」「自分はそこまでシステムテストをびっちり書かないかな」

🔗 monotonic_timeをRubyネイティブの機能に置き換え


つっつきボイス:「これはリファクタリングか」「Ruby公式のProcess.clock_gettimeがあるならそれを使おうという流れのようですね」

# activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb#L530
          newly_checked_out = []
-         timeout_time      = Concurrent.monotonic_time + (@checkout_timeout * 2)
+         timeout_time      = Process.clock_gettime(Process::CLOCK_MONOTONIC) + (@checkout_timeout * 2)

          @available.with_a_bias_for(Thread.current) do
            loop do
              synchronize do
                return if collected_conns.size == @connections.size && @now_connecting == 0
-               remaining_timeout = timeout_time - Concurrent.monotonic_time
+               remaining_timeout = timeout_time - Process.clock_gettime(Process::CLOCK_MONOTONIC)
                remaining_timeout = 0 if remaining_timeout < 0
                conn = checkout_for_exclusive_access(remaining_timeout)
                collected_conns   << conn
                newly_checked_out << conn
              end
            end

参考: Process::CLOCK_MONOTONIC (Ruby 3.0.0 リファレンスマニュアル)

「従来のmonotonic_timeはconcurrent-rubyの機能なのかな?」「ググって出てきた過去記事↓を見ると、Rails 6でconcurrent-rubyがActive Supportに導入されているので(#32034)そのようですね: インターフェイスが少し変わるのでバックポートまではしなさそうですが」

RailsアプリでConcurrent Rubyを使う(翻訳)

ruby-concurrency/concurrent-ruby - GitHub

🔗 CI環境でデフォルトのeager loadingをサポート

このパターンはShopifyで長年使われている。
テストをローカル実行するときはほとんどの場合テストの一部しか実行しないので、最初のテスト結果が出るまでの時間を短縮するには読み出すコードができるだけ少ない方がよい。
CIの場合はいずれにしろすべてのテストコードを読み込むのだし、テストスイートが複数のランナーに分散する場合でも副作用のあるコードファイルを確実に読み込むために、アプリケーションをeager loadingする方が望ましい。
これにより、オートロードされた定数がCI上で適切にテストされていない場合であっても、少なくともデプロイ時より前にCIで読み込まれてSyntaxErrorなどのエラーが明示的に出るようになる。
同PRより


つっつきボイス:「config.eager_load = ENV["CI"].present?がコンフィグにデフォルトで追加されるのね」「CIという環境変数は使ったことなかったな〜」

🔗 Classic->Zeitwerkガイド


つっつきボイス:「お〜、Zeitwerkへの移行ガイドがedgeに上がった」「本家英語版ガイドではまだですが、日本語版Railsガイド向けに日本語訳をWIPで準備中です↓」「Railsガイドってこういうふうに翻訳してるのか〜」「なるほど、英語版のmarkdownファイルと別にjpディレクトリに日本語版markdownファイルを置いているんですね」「そうしないと自分がバージョンを追えなくなってしまいそうなので😅」

参考: [WIP]Zeitwerkアップグレードガイド by hachi8833 · Pull Request #1093 · yasslab/railsguides.jp

🔗Rails

🔗 RubyKaigiの過去動画が多数公開


つっつきボイス:「先週の大江戸Ruby会議09出前Editionで、RubyKaigiの過去の講演動画アーカイブが多数お披露目されましたね(イベント後に公開されました)」「2006〜2008年でこれだけの数の動画スゴい」「貴重な動画を掘り起こしてくれてありがたいです🙏」「紹介されていた動画がどれもすんごくよかった」「全部見る時間あるかな...」「YouTubeなら英語字幕も付くので気楽に見てみるといいと思います」「今見るとさらに増えているみたい」「あ、ホントだ」

🔗 DHHによるRails解説「One controller, many ins, many outs」

「圧巻は、冒頭でみんなで視聴した若き日のDHHによるRails解説↓」「このときのDHHは20代だったのが信じがたい」「CRUDやRESTとの関連など、今見てもまったく色あせない内容なのがホントすごい」

🔗 Matzを説得する方法

「このHow to persuade matzという動画もとてもおもしろかった↓」

🔗 The Island of Ruby

「Dave Thomas氏がRubyへの愛を熱烈に語った動画もよかった↓」「Rubyは今後こういうことをしなければならないという話をこの時代にしていたのが改めてすごいと思いました」「大江戸Ruby会議09は、他にも自分の知らない時代のRubyの話が山盛り」「そうそう、知らない話が続々出てきました」

参考: #8 達人プログラマー Dave Thomas(前編) RubyにはMatzの受けた教育,宗教とかすべてが反映されている:小飼弾のアルファギークに逢いたい♥|gihyo.jp … 技術評論社

🔗 HTTPClient gem

「こちらはVimeoに上がっている動画ですが、自分が大好きなHTTPClient gemの作者である@nahiさんの発表が個人的にとても刺さりました↓」「そういえば以前からHTTPClientを推してましたね」「HTTPClientで自分のやりたいことが全部できるのは、この方が既存のHTTPクライアントで何がサポートされて何がサポートされていないかをこんなに詳しくサーベイしていたからなんだなというのがとてもよくわかる動画」「お〜これは細かい!」「この動画を見て、改めて今後もHTTPClientを使おうという気持ちになりました: 自分でRubyのHTTPクライアントライブラリを選定したことのある人には響くんじゃないかな」

大江戸HTTPクライアント絵巻 / @nahi from ogi on Vimeo.

nahi/httpclient - GitHub

「視聴中に寝落ちしてこのあたりを見られませんでした...」「自分も懇親会の記憶があまりない😆」

🔗 Active ResourceとHer

「ところで、DHHの講演の中でActive ResourceというものがRailsの機能として登場していて"こんな機能あったかな?"と思ったら、v3.2.6あたりを最後にgemに切り出されていたんですね↓」「Active Resourceは、Railsが描いた未来の姿のひとつとしてかなり昔からあります」

rails/activeresource - GitHub

なお、apidock.comにもActive Resourceはありませんでした。

「ちなみに、Active Resourceのような機能を今やるのであれば、herというライブラリがかなりメジャーです↓」「her、そういえば以前もお話しされてましたね」「自分は今もよくherを使っています」

remi/her - GitHub

# remi/herより
User.all
# GET "https://api.example.com/users" and return an array of User objects

User.find(1)
# GET "https://api.example.com/users/1" and return a User object

@user = User.create(fullname: "Tobias Fünke")
# POST "https://api.example.com/users" with `fullname=Tobias+Fünke` and return the saved User object

@user = User.new(fullname: "Tobias Fünke")
@user.occupation = "actor"
@user.save
# POST "https://api.example.com/users" with `fullname=Tobias+Fünke&occupation=actor` and return the saved User object

@user = User.find(1)
@user.fullname = "Lindsay Fünke"
@user.save
# PUT "https://api.example.com/users/1" with `fullname=Lindsay+Fünke` and return the updated User object

「RESTfulに実装されているAPIサーバーであれば、あたかもActive Recordのようなインターフェースでデータの取得・更新ができて、たとえばfind(1)してsaveすると"https://api.example.com/users/1をPUTしたりする」「お〜、そういうことができるんですか、これいいな〜」「ただしwhereのようなメソッドはAPI側が対応する必要がありますし、当然orjoinみたいなことはできないので、Active Recordとすべて同じというわけにはいきませんが、idで取ってくるような処理なら十分使えます」「なるほど」

「Active Resourceはほとんど更新されなくなっていますね、もっともherも直近のリリースは2019年ですが」「たしかに」「herは以下のようにアダプタがモジュラブルになっていて使いやすいですし↓、Active Recordを継承しているわけではないのでRailsへの依存も小さいはず: 今Active Resourceのようなことをするならherだと自分は思っています」

# remi/herより
# config/initializers/her.rb
Her::API.setup url: "https://api.example.com" do |c|
  # Request
  c.use Faraday::Request::UrlEncoded

  # Response
  c.use Her::Middleware::DefaultParseJSON

  # Adapter
  c.use Faraday::Adapter::NetHttp
end

「Active Resourceという概念は今でも好きなので、それもあって今もherを使ってます」「マイクロサービスが流行っている今Active Resourceがカムバックしてもよかったんじゃないかというコメントを見かけましたけど、それもちょっとわかるかも」「Active Resourceは、ローカルのリソースも外部リソースも同じインターフェイスで扱えるところが強みなんですよ: おそらくDHHも自分が作ったActive Resourceという概念が念頭にあるから今もRESTfulにこだわっているのかなという気持ちに少しなりました」「ふーむ」

参考: Representational State Transfer(REST)- Wikipedia

🔗 Railsのトランザクション入門(Ruby Weeklyより)


つっつきボイス:「Railsのデータベーストランザクションの基本的な解説のようです」「トランザクションとは何か、トランザクションの作成、ロールバック、失敗したトランザクションのキャッチ、まさに入門の導入部という感じですね」

🔗 ルーティングをRESTfulにする(Ruby Weeklyより)


つっつきボイス:「ネステッドなルーティングに関する記事のようですね」

# 同記事より: RESTfulでない例
# config/routes.rb
Rails.application.routes.draw do
  resources :repositories, only: %i[index show] do
    resources :collaborators, only: %i[index] do
      post :accept_invite
      post :decline_invite
      post :invite
      get :show_invite
    end
  end
end

「上はrepositoriesの下にcollaboratorsがあるけど、なんちゃら_inviteに対応する中間モデルがないので、以下のようにinvitationsという概念を設けることでRESTfulになる、という流れですね」「なるほど」

# 同記事より: RESTfulな例
# config/routes.rb
Rails.application.routes.draw do
  resources :repositories, only: %i[index show] do
    resources :collaborators, only: %i[index]
    resources :invitations, only: %i[show create update destroy]
  end
end

「ルーティングがRESTfulになっていないということは、中間モデルが概念化されていないということ: さっきのDHHの講演動画でもまさに同じことを説明してます😆」「お〜そこにもつながるとは」

🔗 その他Rails

つっつきボイス:「Rubyのprotectedを使うときがあったとは」「正しく使える気がしない機能」「Javaを最初に勉強するとRubyのprotectedの意味がJavaと違いすぎて頭抱えますよね」「そういえばjnchitoさんの大昔の記事でも頭抱えてました↓」「今でもわからないレベル」

参考: 呼び出し制限 -- クラス/メソッドの定義 (Ruby 3.0.0 リファレンスマニュアル)

参考: JavaやC#の常識が通用しないRubyのprivateメソッド - give IT a try


前編は以上です。

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

週刊Railsウォッチ: 2021年度Rubyアソシエーション開発助成、Rails REST APIレベルで楽観的ロックほか(20211102後編)

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

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

Rails公式ニュース

Ruby Weekly


CONTACT

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