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

週刊Railsウォッチ: Rails公式の"rails-new"ツールでRailsプロジェクトをセットアップほか(20240409前編)

こんにちは、hachi8833です。

週刊Railsウォッチについて

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

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

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

🔗 Action Viewとヘルパーのドキュメントが一般プレビュー開始

  • コード例を追加し、既存のコード例も更新。
  • パーシャルに関連するトピックをこのガイドに統合する。
  • 文の流れと読みやすさ向上のため、セクションを再構成。
  • Action Viewがどのように適合するかを明確にする形で序文を書き換え。
    #51435 Detailより
  • AssetTagHelperでは多数の*_tagヘルパーについて説明しているが、*_urlヘルパーや*_pathヘルパー(これらはAssetUrlHelperにある)については言及していたりいなかったりしている。こうしたヘルパーは知っていれば役には立つが利用頻度は低いと思われ、言及されている場合やされていない場合もあって不完全なので(*_tagヘルパーには対応するものがあるか、さもなければ基本的なasset_pathasset_urlを利用する)、これらのバリアントをすべて網羅する価値はないと思われ、むしろ削除したい。言及したいのであれば、同じサブセクション内にまとめるか、関連する*_tagセクションの例としてまとめることも可能。
  • キャッシュヘルパーでは他にもいくつかの情報を利用できる。フラグメントAPIにリンクする価値はなく、むしろキャッシュガイドのフラグメントキャッシュの項で詳しく説明されているので、そちらにリンクする方が価値があると思う。その例を拡張すれば少なくともレコードを取得する例を示すことも可能。
  • content_forではcontent_for?の例も何らかの形で示す方がよいかもしれない。
  • このガイドではフォームヘルパーについては言及せず、別ガイドへのリンクを示すにとどめたいが、そのパラグラフをもっと明確にできるかもしれない。
  • escape_javascriptにコード例を付けてもよい(APIにあるものはjQueryとのつながりが強そうなのでそれではないものにしたい: JSにRubyを埋め込む他のコード例なのかもしれない)。
  • rails-html-sanitizer gemへの言及はリンクに変えてもいいかもしれない。
  • メモ: エスケープなしの'<'、'>'、'&' が出力に含まれてブラウザが混乱する可能性があることについても言及すべきだろうか?
  • UrlHelperにはcurrent_page?mail_toなどよく使われているものが他にもある(全部のメソッドを解説する必要はなく、最もよく使われるものだけでよい)。
  • このガイドではCsrfHelperについて説明する必要はなさそう(CsrfHelperのような必要かどうかがよくわからないものについては追加していない、たぶんスキップしてもよさそう)。
  • TextHelperのセクションを追加して、simple_formattruncateexcerptあたりの説明を加えるといいかもしれない(繰り返すが全部を網羅するよりも最もよく使われるものだけでよい)。
  • TagHelperのセクションを追加して、content_tagtagについてはそこで説明してもよさそう。
  • 冒頭の「このガイドの内容:」を更新。
  • 全セクションの形式を同じような形に揃える: ヘルパーの下に何らかの説明文を置き、それからメソッドについて書く。
  • (markdownの)改行位置をスタイルガイドの通りにする。
  • 非推奨のベンチマークを削除。
  • 言い回しが明確になるよう調整。
  • capturecontent_forの説明をわかりやすくした。
  • 必要に応じてコード例を追加。
  • このガイドのWIP表示を削除。
  • セクションを並べ替えた。

#51432 Detailより抜粋


つっつきボイス:「Action Viewとヘルパーのドキュメント更新に対して一般からのフィードバックが募集されています」「セクションの順序も含めて大幅に変えようとしていますね」「ちなみに更新に含まれているdocuments.yamlはガイドにWIP(work-in-progress:作業中)を表示するかどうかの制御にも使われています」

「すべてのヘルパーを見たければAPIドキュメント↓で参照すればいいので、ガイドのドキュメントに何もかも盛り込む必要はないというのはわかる: ガイドのドキュメントは、その名の通りどんな機能・使い方・注意点があるのかという概要を示す方に注力する方がいいでしょうね👍」

参考: Ruby on Rails API

🔗 config.active_record.permanent_connection_checkoutが追加

参考: #50793

ActiveRecord::Base.connectionは「エラーを発生させる」「非推奨警告を表示する」「どちらも行わない」のどれにするかを制御する。

ActiveRecord::Base.connectionは、そのコネクションのプールからデータベースコネクションをチェックアウトして、リクエストやジョブが完了するまでリース中(貸出中、専有中)のままにする。この振る舞いは、利用可能なコネクション数よりも多くのスレッドやfiberが使われる環境では望ましくない場合がある。

このActiveRecord::Base.connectionコンフィグを使うと、ActiveRecord::Base.connectionをトラッキングして削除し、ActiveRecord::Base.with_connectionを代わりに使う形で移行しようと思えばできるようになる。

このコンフィグのデフォルトの振る舞いは変更されておらず、現時点ではデフォルトの振る舞いを変更する予定はない。

まだ足りないのは「.with_connectionに似ているが.connectionがチェックアウトを永続化しない形で、レガシーコードをラップする方法」かもしれない。たぶん.with_connectionキーワード引数の形にすべきだろうか?

@matthewd、何か意見があればよろしく。
同PRより


つっつきボイス:「この#51349は、この間からbyrootさん(PR作成者の別名)が取り組んでいたコネクションのリース周りの改修でも言及されていたプルリクです(ウォッチ20240402)」「アプリ開発者がActiveRecord::Base.connectionの扱いについて制御可能にするコンフィグが追加されたという話ですね」

🔗 ActiveSupport::BacktraceCleanerでdeep copyされていない項目があったのを修正

ActiveSupport::BacktraceCleanerを拡張して、複製やクローンのときにfilterssilencersを複製するよう修正。

従来は、複製によって内部のfilterssilencersの配列が共有され、ステートが漏洩していた。

Jean Boussier
同Changelogより


つっつきボイス:「BacktraceCleanerdupしたときにfilterssilencersdupされていなかったので、意図したインスタンス変数と違うものが参照されていたんですね: これはバグ」「シンプルにdupを追加して修正していますね↓」「BacktraceCleanerは使った覚えがあまりないかも」

# activesupport/lib/active_support/backtrace_cleaner.rb#L113
+     def initialize_copy(_other)
+       @filters = @filters.dup
+       @silencers = @silencers.dup
+     end

参考: Object#clone (Ruby 3.3 リファレンスマニュアル) -- dupはエイリアス
参考: 8.3 Quieter Backtrace -- Ruby on Rails 2.3 リリースノート - Railsガイド

🔗 エラーメッセージで関連付けのオプション候補がすべて表示されるよう修正

動機/背景

このプルリクを作成した理由は、関連付けのthroughオプションをタイプミスしたときに、そのことがエラーメッセージに表示されなかったため。

たとえば、以下のような単純なタイプミスtroughをしたとする。

class User < ApplicationRecord
  has_many :courses
  has_many :assignments, trough: :courses
end

Rails起動時に以下のメッセージが表示されるが、これは誤解を招く。

Unknown key: :trough. Valid keys are: :class_name, :anonymous_class, :primary_key, :foreign_key, :dependent, :validate, :inverse_of, :strict_loading, :query_constraints, :autosave, :before_add, :after_add, :before_remove, :after_remove, :extend, :counter_cache, :join_table, :index_errors (ArgumentError)

注目すべき肝心のthroughがエラーメッセージに表示されていない。

詳細

このプルリクでは、関連付けのHasOneビルダーやHasManyビルダーでこれらの問題をすべて修正する。

その理由は、現在のActiveRecord::Associations::Builder::HasMany.valid_optionsは、指定されたオプションにthroughが存在する場合にのみthroughを追加しているため。

def self.valid_options(options)
  ...
  valid += [:through, :source, :source_type] if options[:through]
  ...
end

このロジックはsourcesource_typeの場合にのみ適切と思われるが、throughをエラーメッセージのリストに無条件に追加できない理由がわからない。これはActiveRecord::Associations::Builder::HasOne.valid_optionsの略である。

パッチを適用することで、問題は以下のように修正される。

Unknown key: :trough. Valid keys are: :class_name, :anonymous_class, :primary_key, :foreign_key, :dependent, :validate, :inverse_of, :strict_loading, :query_constraints, :autosave, :before_add, :after_add, :before_remove, :after_remove, :extend, :counter_cache, :join_table, :index_errors, :through (ArgumentError)

追加情報

自分が何か見落としておらず、これが実際に問題であるとするなら、他にも似たような問題が発生している可能性がある。
同PRより


つっつきボイス:「Unknown keyの関連付け予約語候補リストにthroughが含まれていなくて困ったのがきっかけで改修したんですね: 修正は地味ですが、よくぞ見つけたという感じ👍」

# activerecord/lib/active_record/associations/builder/has_many.rb#L9
    def self.valid_options(options)
-     valid = super + [:counter_cache, :join_table, :index_errors]
-     valid += [:as, :foreign_type] if options[:as]
-     valid += [:through, :source, :source_type] if options[:through]
+     valid = super + [:counter_cache, :join_table, :index_errors, :as, :through]
+     valid += [:foreign_type] if options[:as]
+     valid += [:source, :source_type, :disable_joins] if options[:through]
      valid += [:ensuring_owner_was] if options[:dependent] == :destroy_async
-     valid += [:disable_joins] if options[:disable_joins] && options[:through]
      valid
    end
# activerecord/lib/active_record/associations/builder/has_one.rb#L9
    def self.valid_options(options)
-     valid = super
-     valid += [:as, :foreign_type] if options[:as]
+     valid = super + [:as, :through]
+     valid += [:foreign_type] if options[:as]
      valid += [:ensuring_owner_was] if options[:dependent] == :destroy_async
-     valid += [:through, :source, :source_type] if options[:through]
-     valid += [:disable_joins] if options[:disable_joins] && options[:through]
+     valid += [:source, :source_type, :disable_joins] if options[:through]
      valid
    end

🔗 特定のクエリメソッドやArelでallow_retryオプションを指定可能になった

コネクションに関連する例外で、冪等(べきとう: idempotent)であることがわかっているSELECTクエリをリトライできるようになった。

自分たち開発者がArelツリーをたどってビルドしたSELECTクエリや、モデルの既知の属性を用いてビルドしたSELECTクエリは冪等であり、コネクションエラー時でも安全にリトライできる。従来は、リクエスト中にコネクションエラーが発生するとTrilogyAdapterActiveRecord::ConnectionFailed: Trilogy::EOFErrorが発生することがあった。

Adrianna Chang
同Changelogより

動機/背景

#51166の再チャレンジだが、#selectメソッドによるSQLクエリは安全にリトライ可能という前提を置かず、自分たちがビルドして冪等であるとわかっているクエリだけをリトライする。

詳細

このプルリクでは、オプションのallow_retryフラグを使うことで2種類のクエリをリトライ可能にする。

  1. 開発者が#to_sql_and_bindsでArelツリーをたどって構築したSQLクエリ。
    コレクタクラスで新しいretryable属性を利用する。この属性は、ほとんどのArelノード種別ではデフォルトでtrueに設定されるが、冪等でないノード種別(関数、SQLリテラル、UPDATE/DELETE/INSERTなど)ではfalseに設定される。retryable値は#to_sql_and_bindsから返されて#select_allで利用され、コールスタックに渡されて最終的にアダプタの#internal_exec_queryメソッドに到達する。

  2. 既知の属性を用いる#findクエリや#find_byクエリ。
    #cached_find_byallow_retry: trueを設定してから、これを#find_by_sql#_query_by_sqlに渡す。

これらの変更によって、安全にリトライ可能であることが開発者側でわかっているクエリについては自動的にリトライできるようになる。

追加情報

検証用:

  • テストのカバレッジは十分か?
    特定のSQLクエリを生じる可能性のあるActive Record APIをすべてテストするのは困難なので、最もよく使われるものをカバーしてみた。UPDATE/DELETE/INSERTがリトライ不可能であることをテストしようとしたが、これらはトランザクションでラップされているため、最終的にBEGINで再接続される。

  • 冪等でない可能性のあるArelノードを見逃した可能性はあるか?
    なお自分たちは、NamedFunctionSqlLiteralは冪等ではないと考えているので、本当はリトライ可能にできるクエリの多くが除外されている可能性もあるが、現時点ではこれが最も安全なオプションとなる。

  • cached_find_byが常に冪等なクエリを生成することは保証されているのか?
    これは、Post.findPost.find_by(title: "foo")をリトライ可能にする唯一の方法であり、この種のクエリはリトライ可能にすべきだと考えている。SQLのフラグメントを含むfind_byが引数として渡されると、フラグはリトライ不可に設定される。

cc @matthewd
同PRより


つっつきボイス:「冪等であることがわかっているselect系メソッドやfind系メソッドなどにallow_retryオプションを付けられるようになったそうです」「TrilogyAdapterの場合だけかと思ったらPostgreSQLやMySQLやSQLite3のアダプタでも使えるようになってるのか」「31ファイルも修正されていますね」

「言われてみれば関数とかはまったく同じクエリを実行したときにも冪等にならない可能性はたしかにありますね: 機能追加だから既存の振る舞いは変わらないけど、RDBの種類に依存する部分もあるだろうし、結構複雑なことをやろうとしている感じだけど、冪等にしても大丈夫と思われるクエリをホワイトリスト化することで行う分には大丈夫そう👍」

参考: サーバーレスが気になる開発者に捧ぐ「べき等性」ことはじめ 第一回〜べき等性 (冪等性/idempotency) ってなんだ!? - builders.flash☆ - 変化を求めるデベロッパーを応援するウェブマガジン | AWS

🔗Rails

🔗 Rails公式の"rails-new"ツール(Ruby Weeklyより)

rails/rails-new - GitHub


つっつきボイス:「名前がrails-newなのが面白い」「少し前からGitHubのRailsリポジトリに公式に置かれているツールですが、これまで知りませんでした: Rustで書かれていて、Dockerさえ使えればRubyがインストールされていない環境でも新規Railsアプリを速攻で作れるようです」「なるほど、やっていることはシンプルそう」

「とりあえずこのツールがどんな方向を目指しているかにもよるけど、最近のRailsがサポートしているdevcontainer(ウォッチ20240228)の活用例を模索している感じなのかな」「そういえばREADMEのインストール方法がまだ空欄ですし、まだ作り中っぽい気もしますね」「devcontainerが使えるとGitHub上ですべての作業が完結するようになるので、教育方面とかでRails環境のセットアップを簡略化したりするのにもよさそう👍」「ブラウザ環境だけでRails開発できるようになるヤツですね」


インストール方法がREADMEに書かれていなかったので、gh repo clone rails/rails-newしてcargo buildしたデバッグ版バイナリで動かしてみましたが、私の環境ではコケたのでDockerfileを取り急ぎ修正して動かしてみました↓。

▶インストールログ(クリックで展開)
rails$ ~/deve/rust/rails-new/target/release/rails-new myapp
[+] Building 11.4s (7/7) FINISHED                                                                       docker:desktop-linux
 => [internal] load build definition from Dockerfile                                                                    0.0s
 => => transferring dockerfile: 489B                                                                                    0.0s
 => [internal] load metadata for docker.io/library/ruby:3.3.0                                                           2.2s
 => [internal] load .dockerignore                                                                                       0.0s
 => => transferring context: 2B                                                                                         0.0s
 => CACHED [1/3] FROM docker.io/library/ruby:3.3.0@sha256:bf1afb92b390d90815b83ebf175c243b21e6e5204e040d3ca03a2f4dce3f  0.0s
 => [2/3] RUN groupadd myapp -g 1000 -o &&     useradd myapp -u 1000 -g 1000 -o --create-home --shell /bin/bash         0.2s
 => [3/3] RUN if [ -z "7.1.3" ] ; then gem install rails ; else gem install rails -v 7.1.3 ; fi                         8.8s
 => exporting to image                                                                                                  0.2s
 => => exporting layers                                                                                                 0.2s
 => => writing image sha256:0633a814d33d1294d2a2c5b30525e5c62654e040a70b78dffc54551523d12a17                            0.0s
 => => naming to docker.io/library/rails-new-3.3.0-7.1.3                                                                0.0s

View build details: docker-desktop://dashboard/build/desktop-linux/desktop-linux/tu0c80vtgqgxw8o5nngguxvva

What's Next?
  View a summary of image vulnerabilities and recommendations → docker scout quickview
      create
      create  README.md
      create  Rakefile
      create  .ruby-version
      create  config.ru
      create  .gitignore
      create  .gitattributes
      create  Gemfile
         run  git init -b main from "."
Initialized empty Git repository in /Users/hachi8833/deve/rails/myapp/.git/
      create  app
      create  app/assets/config/manifest.js
      create  app/assets/stylesheets/application.css
      create  app/channels/application_cable/channel.rb
      create  app/channels/application_cable/connection.rb
      create  app/controllers/application_controller.rb
      create  app/helpers/application_helper.rb
      create  app/jobs/application_job.rb
      create  app/mailers/application_mailer.rb
      create  app/models/application_record.rb
      create  app/views/layouts/application.html.erb
      create  app/views/layouts/mailer.html.erb
      create  app/views/layouts/mailer.text.erb
      create  app/assets/images
      create  app/assets/images/.keep
      create  app/controllers/concerns/.keep
      create  app/models/concerns/.keep
      create  bin
      create  bin/rails
      create  bin/rake
      create  bin/setup
      create  Dockerfile
      create  .dockerignore
      create  bin/docker-entrypoint
      create  config
      create  config/routes.rb
      create  config/application.rb
      create  config/environment.rb
      create  config/cable.yml
      create  config/puma.rb
      create  config/storage.yml
      create  config/environments
      create  config/environments/development.rb
      create  config/environments/production.rb
      create  config/environments/test.rb
      create  config/initializers
      create  config/initializers/assets.rb
      create  config/initializers/content_security_policy.rb
      create  config/initializers/cors.rb
      create  config/initializers/filter_parameter_logging.rb
      create  config/initializers/inflections.rb
      create  config/initializers/new_framework_defaults_7_1.rb
      create  config/initializers/permissions_policy.rb
      create  config/locales
      create  config/locales/en.yml
      create  config/master.key
      append  .gitignore
      create  config/boot.rb
      create  config/database.yml
      create  db
      create  db/seeds.rb
      create  lib
      create  lib/tasks
      create  lib/tasks/.keep
      create  lib/assets
      create  lib/assets/.keep
      create  log
      create  log/.keep
      create  public
      create  public/404.html
      create  public/422.html
      create  public/500.html
      create  public/apple-touch-icon-precomposed.png
      create  public/apple-touch-icon.png
      create  public/favicon.ico
      create  public/robots.txt
      create  tmp
      create  tmp/.keep
      create  tmp/pids
      create  tmp/pids/.keep
      create  tmp/cache
      create  tmp/cache/assets
      create  vendor
      create  vendor/.keep
      create  test/fixtures/files
      create  test/fixtures/files/.keep
      create  test/controllers
      create  test/controllers/.keep
      create  test/mailers
      create  test/mailers/.keep
      create  test/models
      create  test/models/.keep
      create  test/helpers
      create  test/helpers/.keep
      create  test/integration
      create  test/integration/.keep
      create  test/channels/application_cable/connection_test.rb
      create  test/test_helper.rb
      create  test/system
      create  test/system/.keep
      create  test/application_system_test_case.rb
      create  storage
      create  storage/.keep
      create  tmp/storage
      create  tmp/storage/.keep
      remove  config/initializers/cors.rb
      remove  config/initializers/new_framework_defaults_7_1.rb
         run  bundle install
Fetching gem metadata from https://rubygems.org/...........
Resolving dependencies...
Fetching rake 13.2.0
Installing rake 13.2.0
Fetching bigdecimal 3.1.7
Fetching drb 2.2.1
Fetching public_suffix 5.0.5
Fetching bindex 0.8.1
Fetching msgpack 1.7.2
Fetching regexp_parser 2.9.0
Fetching io-console 0.7.2
Fetching rubyzip 2.3.2
Installing bigdecimal 3.1.7 with native extensions
Installing drb 2.2.1
Fetching websocket 1.2.10
Installing io-console 0.7.2 with native extensions
Installing bindex 0.8.1 with native extensions
Installing rubyzip 2.3.2
Fetching sqlite3 1.7.3 (aarch64-linux)
Installing regexp_parser 2.9.0
Fetching puma 6.4.2
Installing public_suffix 5.0.5
Installing msgpack 1.7.2 with native extensions
Fetching sprockets 4.2.1
Installing websocket 1.2.10
Fetching net-imap 0.4.10
Fetching net-smtp 0.5.0
Installing puma 6.4.2 with native extensions
Installing sprockets 4.2.1
Installing net-smtp 0.5.0
Installing net-imap 0.4.10
Fetching xpath 3.2.0
Installing xpath 3.2.0
Fetching addressable 2.8.6
Installing addressable 2.8.6
Installing sqlite3 1.7.3 (aarch64-linux)
Fetching rdoc 6.6.3.1
Fetching selenium-webdriver 4.19.0
Fetching capybara 3.40.0
Installing rdoc 6.6.3.1
Fetching reline 0.5.0
Installing capybara 3.40.0
Installing reline 0.5.0
Fetching irb 1.12.0
Installing irb 1.12.0
Fetching debug 1.9.2
Installing debug 1.9.2 with native extensions
Installing selenium-webdriver 4.19.0
Fetching bootsnap 1.18.3
Installing bootsnap 1.18.3 with native extensions
Fetching activesupport 7.1.3.2
Installing activesupport 7.1.3.2
Fetching activemodel 7.1.3.2
Fetching actionview 7.1.3.2
Fetching activejob 7.1.3.2
Installing activemodel 7.1.3.2
Fetching activerecord 7.1.3.2
Installing actionview 7.1.3.2
Fetching actionpack 7.1.3.2
Fetching jbuilder 2.11.5
Installing activejob 7.1.3.2
Installing activerecord 7.1.3.2
Installing actionpack 7.1.3.2
Installing jbuilder 2.11.5
Fetching actioncable 7.1.3.2
Fetching activestorage 7.1.3.2
Fetching actionmailer 7.1.3.2
Fetching railties 7.1.3.2
Fetching sprockets-rails 3.4.2
Installing actioncable 7.1.3.2
Installing activestorage 7.1.3.2
Fetching actionmailbox 7.1.3.2
Fetching actiontext 7.1.3.2
Installing actionmailer 7.1.3.2
Installing railties 7.1.3.2
Installing sprockets-rails 3.4.2
Installing actionmailbox 7.1.3.2
Installing actiontext 7.1.3.2
Fetching importmap-rails 2.0.1
Fetching stimulus-rails 1.3.3
Fetching turbo-rails 2.0.5
Fetching web-console 4.2.1
Fetching rails 7.1.3.2
Installing importmap-rails 2.0.1
Installing stimulus-rails 1.3.3
Installing turbo-rails 2.0.5
Installing web-console 4.2.1
Installing rails 7.1.3.2
Bundle complete! 14 Gemfile dependencies, 81 gems now installed.
Use `bundle info [gemname]` to see where a bundled gem is installed.
         run  bundle lock --add-platform=x86_64-linux
Writing lockfile to /Users/hachi8833/deve/rails/myapp/Gemfile.lock
         run  bundle binstubs bundler
       rails  importmap:install
       apply  /usr/local/bundle/gems/importmap-rails-2.0.1/lib/install/install.rb
  Add Importmap include tags in application layout
      insert    app/views/layouts/application.html.erb
  Create application.js module as entrypoint
      create    app/javascript/application.js
  Use vendor/javascript for downloaded pins
      create    vendor/javascript
      create    vendor/javascript/.keep
  Ensure JavaScript files are in the Sprocket manifest
      append    app/assets/config/manifest.js
  Configure importmap paths in config/importmap.rb
      create    config/importmap.rb
  Copying binstub
      create    bin/importmap
         run  bundle install
Bundle complete! 14 Gemfile dependencies, 81 gems now installed.
Use `bundle info [gemname]` to see where a bundled gem is installed.
       rails  turbo:install stimulus:install
       apply  /usr/local/bundle/gems/turbo-rails-2.0.5/lib/install/turbo_with_importmap.rb
  Import Turbo
      append    app/javascript/application.js
  Pin Turbo
      append    config/importmap.rb
         run  bundle install
Bundle complete! 14 Gemfile dependencies, 81 gems now installed.
Use `bundle info [gemname]` to see where a bundled gem is installed.
Run turbo:install:redis to switch on Redis and use it in development for turbo streams
       apply  /usr/local/bundle/gems/stimulus-rails-1.3.3/lib/install/stimulus_with_importmap.rb
  Create controllers directory
      create    app/javascript/controllers
      create    app/javascript/controllers/index.js
      create    app/javascript/controllers/application.js
      create    app/javascript/controllers/hello_controller.js
  Import Stimulus controllers
      append    app/javascript/application.js
  Pin Stimulus
  Appending: pin "@hotwired/stimulus", to: "stimulus.min.js"
      append    config/importmap.rb
  Appending: pin "@hotwired/stimulus-loading", to: "stimulus-loading.js"
      append    config/importmap.rb
  Pin all controllers
  Appending: pin_all_from "app/javascript/controllers", under: "controllers"
      append    config/importmap.rb
         run  bundle install
Bundle complete! 14 Gemfile dependencies, 81 gems now installed.
Use `bundle info [gemname]` to see where a bundled gem is installed.

🔗 Ruby開発者が愛する14のツールとgem(Ruby Weeklyより)


つっつきボイス:「以前翻訳したEvil Martiansのgem記事↓は、Railsアプリ本体の機能強化に使うものが中心という感じでしたが、上のtestdoubleの記事は、どちらかというとツール寄りのgemを紹介している感じですね」

Rails: Evil Martiansが使って選び抜いた夢のgem(翻訳)

rack-miniprofilerとかは定番の便利ツールだけど、それと連携してパフォーマンスチェックするdebugbarは初めて見た」「SQLを整形する系のツールもいくつかありますね」「http://localhost:3000/rails/info/routesがちゃんとツールとして紹介されているのはエラい」「Rails標準の機能ですよね」

参考: 6.1 既存のルールを一覧表示する -- Rails のルーティング - Railsガイド

ルーティングといえばこちらも標準の機能ですね↓。

Rails: ルーティングを動的にビジュアル表示する方法

🔗 Active Supportの宣言的testヘルパーでテスト間のステートを共有するcontextヘルパーを実装するならこうかもしれない(Ruby Weeklyより)


つっつきボイス:「RSpec作者のSteven Bakerさんによるgistだそうです」「RSpecのcontext的なことをminitestでやれるようにするということなのかな?気持ちはわからなくもないけど、minitestの並列性や速度が落ちそうだしテストがもろくなりそうなのであんまりやりたくないかも」「あら、gistの冒頭にこんなことが書かれているのでジョークっぽいですね↓」

Active Supportの宣言型スタイルを利用するときに、Minitestの宣言型スタイルでコンテキストヘルパーを実装する方法のデモンストレーション。

これはRubyで私が好きなお楽しみハックだが、おそらく使うべきではない。

いや、むしろ絶対に使わないこと。しかし私が20年前にRSpecをリリースしたときにも同じことを言っても聞いてもらえなかったのに、今になって聞いてもらえるだろうか。

こういうのを賢いと思う人や、私が作ったものを気に入ってくれた形はぜひメールにてご一報を。テストスイートの回復力を高めてコードを改善する作業を引き受けます。ファンメールはsteven@stevenrbaker.comまで。

このコードが不安定な理由などについて楽しく話し合いたい。 私はRubyが大好きである。
同gistより

RSpecの作者が振り返る歴史(翻訳)

🔗 その他Rails


つっつきボイス:「今回も公式の#RailsTipから見繕いました」「お〜、selectで文字列にいちいちASを書かなくても、こうやってハッシュ形式で渡すとArelがASを補ってくれるのか!」

# 以下のように書かなくても
Post.select(
  'posts.id AS post_id, posts, title AS post_title'
)

# 今は以下のように書ける
Post.select(
  posts: {
    id: :post_id,
    title: :post_title
  }
)

前編は以上です。

バックナンバー(2024年度第2四半期)

週刊Railsウォッチ: solid_queueとmission_control-jobsが正式にRailsのgemに、Rubyの"チルド"文字列ほか(20240402)

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

Rails公式ニュース

Ruby Weekly


CONTACT

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