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

週刊Railsウォッチ: Ruby 3.2.0 Preview 1リリース、Rails向けDocker環境ジェネレータ、scientist gemほか(20220404前編)

こんにちは、hachi8833です。昨日付けで早くもRuby 3.2.0 Preview 1がリリースされました。WASIベースのWebAssemblyサポートが目玉です。個人的には正規表現のタイムアウト機能が楽しみです。

週刊Railsウォッチについて

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

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

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

以下のChangelogを中心に見繕いました。コミットの大半がスペル修正でした。

🔗 importmapを使わない場合にbin/setupでyarnをインストールする

はい皆さん👋
このプルリクが有効かどうかはわからないけど(IDK: I don't know)、何か気づいたら教えて欲しい。
af7428c以降bin/setupからyarnインストールがなくなっている。自分はRails 7でプロジェクトを作成してesbuildを使っているが、依存関係を更新したかったのでbin/setupに自分でyarnを追加した。
このコミットは、importmapを使わない場合にsetupスクリプトにyarnのインストールを追加する。
同PRより


つっつきボイス:「コードを見るとesbuildやwebpackやrolloutが有効な場合はyarnをインストールするようになった↓」「このセットアップならyarnは必要ですよね」

# railties/lib/rails/generators/rails/app/templates/bin/setup.tt#L18
+<% if ["webpack", "esbuild", "rollup"].include?(options.javascript) -%>
+
+  # Install JavaScript dependencies
+  system("yarn check --check-files") || system!("yarn install")
+<% end -%>
+

Rails 7: importmap-rails gem README(翻訳)

🔗 sanitize_sql_likeのパフォーマンス改善

# activerecord/lib/active_record/sanitization.rb#L109
      def sanitize_sql_like(string, escape_character = "\\")
-       pattern = Regexp.union(escape_character, "%", "_")
-       string.gsub(pattern) { |x| [escape_character, x].join }
+       if string.include?(escape_character) && escape_character != "%" && escape_character != "_"
+         string = string.gsub(escape_character, '\0\0')
+       end
+
+       string.gsub(/(?=[%_])/, escape_character)
      end

つっつきボイス:「エスケープ文字そのもののエスケープ処理と_の処理を分けた感じ」「いきなり正規表現の置き換えをせずにinclude?で確認してから行う」「正規表現じゃない方が速い、なるほど」「かなり速くなってますね」「sanitize_sql_likeはお世話になることが多いのでありがたい👍」

Ruby: 文字列マッチは正規表現より先に専用メソッドを使おう

🔗 db:mysql:buildでユーザー作成とGRANTを自動化


つっつきボイス:「お、これは地味にありがたそう」「今まで手動でやっていたけどそろそろ自動化しようという感じかしら」「.devcontainerにあったということは↓、今までテストには入っていたのをdb:mysql:buildというRakeタスクでできるようにしたんでしょうね」

# .devcontainer/boot.sh#L16
# Create MySQL database and databases
-MYSQL_PWD=root sudo mysql -uroot <<SQL
-CREATE USER 'rails'@'localhost';
-CREATE DATABASE activerecord_unittest  DEFAULT CHARACTER SET utf8mb4;
-CREATE DATABASE activerecord_unittest2 DEFAULT CHARACTER SET utf8mb4;
-GRANT ALL PRIVILEGES ON activerecord_unittest.* to 'rails'@'localhost';
-GRANT ALL PRIVILEGES ON activerecord_unittest2.* to 'rails'@'localhost';
-GRANT ALL PRIVILEGES ON inexistent_activerecord_unittest.* to 'rails'@'localhost';
-SQL
+cd activerecord
+MYSQL_CODESPACES=1 bundle exec rake db:mysql:build

「昨今はDocker環境が普通なのでローカル開発ではわざわざユーザーを作らなくてもrootでいいとやいう気持ちはありますね: 本番環境はいずれにしろセッティングは変えますし」「たしかに」

「Dockerでデータベースのコンテナを気軽に生成できるようになったおかげで、昔のようにローカルPCで複数のデータベースアカウントをわざわざ作ったりポート番号をずらしたりすることもめっきりなくなりましたね」「ローカルでMySQLの5.6と5.7を共存させたりしてたな〜」「10年前にあったらとても嬉しかった機能かも」

🔗 NoMethodErrorRails::Railtieオブジェクト全体を出力しないよう修正

# railties/lib/rails/railtie.rb#L250
+   def inspect # :nodoc:
+     "#<#{self.class.name}>"
+   end

つっつきボイス:「なるほど、Rails::Railtie.class.nameだけを出力するようにした↓」「全部をドバっと出されるよりいいですよね」

class A
  def initialize(session)
    @A = session
  end
end

test = A.new("*" * 36)
test.dsad # undefined method `dsad' for #<A:0x00007f847d8494b0 @A="************************************"> (NoMethodError)
# Note that the "#<A:0x00007f847d8494b0 @A="************************************">" part is 65 chars long.

test = test = A.new("*" * 37)
test.dsad # undefined method `dsad' for #<A:0x00007fa8c38299c0> (NoMethodError)

🔗 ローカル開発ガイドの更新


つっつきボイス:「Shopifyの人によるプルリクです」「Railsガイドの更新は地味にありがたい👍」「古い記述があるとハマったりしますよね」「RailsガイドにGitHub CodespacesやVS Code Remote Pluginのことが書かれるようになった↓」

# guides/source/development_dependencies_install.md#L14
-The Easy Way
- ------------
+Other Ways to Set Up Your Environment
+-------------------------------------

-The easiest and recommended way to get a development environment ready to hack is to use the [Rails development box](https://github.com/rails/rails-dev-box).
+If you don't want to set up Rails for development on your local machine you can use Codespaces, the VS Code Remote Plugin, or rails-dev-box. Learn more about these options [here](https://guides.rubyonrails.org/contributing_to_ruby_on_rails.html#setting-up-a-development-environment).

参考: Development Dependencies Install — Ruby on Rails Guides
参考: Rails コア開発環境の構築方法 - Railsガイド

「GitHub Codespacesが実用的に使えるならその方がいいかも」「環境作りに慣れてない人が即作業できるのがいいですよね」

参考: Codespaces | GitHub

🔗Rails

🔗 Turbo Frame


つっつきボイス:「今日のWebチーム内発表で触れられていたドキュメントです」「Turbo Frameについてとりあえず概要を知っておくにはよさそう」「記事にもあるように、特定の専用タグで宣言した部分をライブラリで動的に更新していくという昨今のフロントエンドでお馴染みのパターン↓:」「以前のTurbolinksよりはずっと読みやすいし、個人的にはビューを生で使うよりも好き👍」

参考: Turbo Handbook

<!-- GET /posts/1 -->
<body>
  <h1>記事詳細</h1>

  <turbo-frame id="post_1">
    <form action="/posts/1" method="post">
      <div><input name="title" type="text" value="記事タイトルが入ります" /></div>
      <div><textarea name="body">記事内容が入ります
(編集をクリックすると id="post_1" の部分が書き換わります)</textarea></div>
      <button type="submit">更新</button>
    </form>
  </turbo-frame>

  <turbo-frame id="comments" src="/posts/1/comments">
    読み込み中。ここに記事コメントが入ります。(遅延読み込み)
  </turbo-frame>
</body>

hotwired/turbo-rails - GitHub

turbolinks/turbolinks - GitHub

HotwireはRailsを「ゼロJavaScript」でリアクティブにできるか?前編(翻訳)

🔗 ruby-on-whales: Rails開発用Docker環境ジェネレータ

evilmartians/ruby-on-whales - GitHub


つっつきボイス:「先週のウォッチで紹介したEvil MartiansのDocker+Railsの更新記事↓(ウォッチ20220328)で紹介されていたEvil Martians製のツールです」

参考: Ruby on Whales: Dockerizing Ruby and Rails development — Martian Chronicles, Evil Martians’ team blog

「なるほど、Docker環境のオプションを対話的に入力するとDocker関連ファイルを生成してくれるのね↓」「まだ動かせてませんが今度試してみます」

「こういう対話型ジェネレータでいいんじゃないかという気持ち」「カスタマイズするときの手間は一緒かもしれないけど、最初の一歩を踏み出しやすくするのはいいですね👍」

つっつき後に手元で動かしてみました↓。まだRedisのオプションなどは含まれていないので、ruby-on-whalesには今後も手を加えそうな気がしています。

$ bundle exec rails app:template LOCATION='https://railsbytes.com/script/z5OsoB'
👋 Welcome to interactive Ruby on Whales installer 🐳.
Make sure you've read the guide: https://evilmartians.com/chronicles/ruby-on-whales-docker-for-ruby-rails-development
Which Ruby version would you like to use? (Press ENTER to use 3.1.0)
Which system package do you want to install? (Press ENTER to continue)
Which database adapter do you use? (Press ENTER to use sqlite3) postgresql
Which PostgreSQL version do you want to install? (Press ENTER to use 14) 14
Which Node version do you want to install? (Press ENTER to use 16, type 'n/no' to skip installing Node) 16
Which Yarn version do you want to install? (Press ENTER to install the latest one)
      create  .dockerdev/Dockerfile
      create  .dockerdev/compose.yml
      create  dip.yml
      create  .dockerdev/.bashrc
      create  .dockerdev/.psqlrc
      create  .dockerdev/README.md
        warn  📝  Important things to take care of:
                - Make sure you have `ENV["RAILS_ENV"] = "test"` (not `ENV["RAILS_ENV"] ||= "test"`) in your test helper.
                - Don't forget to add `url:  ENV["DATABASE_URL"]` to your database.yml
        info  ✅  You're ready to sail! Check out .dockerdev/README.md or run `dip provision && dip up web` 🚀

「お、ruby-on-whalesのリポジトリにterraforming-railsというツール↓のリンクもある」「あ、それはHashiCorpのTerraformではなくてEvil Martiansの各種ツールコレクションとサンプルの置き場のようです」「惜しい、Terraformのtfファイルのジェネレータとかではないのね」「少々紛らわしいですが、Evil Martiansが2019年にRailsConfで登壇したときのタイトル↓をもじったのかなと想像してます」「本来の意味のテラフォーミングか」

evilmartians/terraforming-rails - GitHub

terraform: {他動} : 天体の地表と大気を地球と同じように変化させ、居住可能にする。

🔗 HerokuでRails起動時間を半分にするコツ(Ruby Weeklyより)


つっつきボイス:「Heroku向けの小ネタ記事です」「labs:enable build-in-app-dirを指定するとBootsnapがHerokuで効くようになる、なるほど」「Bootsnapが効けば速いでしょうね」「自分はローカルのDocker環境でBootsnap止めることが多いけど」

heroku labs:enable build-in-app-dir -a <APP_NAME>

Shopify/bootsnap - GitHub

🔗 scientist gemでクリティカルなコードパスをリファクタリングする(Ruby Weeklyより)


つっつきボイス:「このscientistというgemはずうっと前のウォッチでちょっとだけ触れました」「記事ではHEY.comで使ったとありますね」

github/scientist - GitHub

「何をするgemですか?」「Rubyのクリティカルパスのリファクタリングとありますね」「そうそう、scientistはGitHub公式でした」

usetryにそれぞれ変更前と変更後のコードを渡してrunで実行すると、両者が食い違うときにエラーになるということみたい↓」

# 同リポジトリより
require "scientist"

class MyWidget
  def allows?(user)
    experiment = Scientist::Default.new "widget-permissions"
    experiment.use { model.check_user?(user).valid? } # old way
    experiment.try { user.can?(:read, model) } # new way

    experiment.run
  end
end

「コンテキストを指定したりできるのね↓」「実際のデータとコードで動作を比較できるということか」

# 同リポジトリより
science "widget-permissions" do |e|
  e.context :user => user

  e.use { model.check_user(user).valid? }
  e.try { user.can?(:read, model) }
end

「以下の記事あたりを見ると↓、大規模なリファクタリングとかをしたときに本当に振る舞いが変わらないのかをproductionのリクエストを流してチェックできるようですね」「GitHubのような大規模なサービスだとscientistのようなツールが欲しくなりそう」

参考: Scientist gemについて調べた - Qiita

参考: Scientist: Measure Twice, Cut Once | The GitHub Blog


Scientist: Measure Twice, Cut Once | The GitHub Blogより

🔗 その他Rails

つっつきボイス:「よくある記事かなと思いつつ拾ってみました」「Rubyのアップグレードは大変ではないけど、結局gemのアップグレードが大変なんですよね」「そうそう」


前編は以上です。

バックナンバー(2022年度第1四半期)

週刊Railsウォッチ: RubyKaigi 2022開催情報、RDB参考資料、DNSのHTTPSレコードほか(20220329後編)

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

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

Rails公式ニュース

Ruby Weekly

RubyFlow

160928_1638_XvIP4h


CONTACT

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