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

週刊Railsウォッチ: The Rails Foundation発足、Ruby 3.2.0 Preview 3リリース、Ruby演算子クイズほか(20221122)

こんにちは、hachi8833です。

週刊Railsウォッチについて

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

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

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

🔗 Enumerable#many?を修正

再現手順
追加のブロックパラメータを渡す別の関数(each_with_indexなど)にmany?をチェインし、ブロックを渡して呼び出す。このブロックは要素だけを受け取り、追加のブロックパラメータがforwardされない。これはすべてのブロックパラメータをforwardするany?と振る舞いが異なる。

# frozen_string_literal: true
require "bundler/inline"

gemfile(true) do
  source "https://rubygems.org"

  git_source(:github) { |repo| "https://github.com/#{repo}.git" }

  # Activate the gem you are reporting the issue against.
  gem "activesupport", "~> 6.1.0"
end

require "active_support"
require "active_support/core_ext/object/blank"
require "active_support/core_ext/enumerable"
require "minitest/autorun"

class BugTest < Minitest::Test
  def test_stuff
    [1, 2, 3].each_with_index.many? do |element, index|
      assert index.present?
      assert index == element - 1
    end
  end
end

期待される動作
呼び出し側のブロックがすべてのブロックパラメータを受け取る。
実際の動作
呼び出し側のブロックが要素だけを受け取る。
システム構成
Rails: 6.1.6.1
Ruby: 2.7.5p203
#46445より


つっつきボイス:「Active SupportのEnumerable拡張にmany?というメソッドがあるって知らなかった」「要素が2個以上ならtrueを返すんでしょうね」「このプルリクでは、many?メソッドをチェインしたときにブロックがうまく転送されていなかったのが修正されたようですね」

🔗 ActiveRecord::PendingMigrationErrorで未実行のマイグレーションファイル名をフルパスで出力するようになった

filenameはパスを提供するがbasenameは提供しない。マルチDBアプリではマイグレーションがさまざまなディレクトリに保存されるので、ファイル名をフルパスで表示する方がよい。
同PRより


つっつきボイス:「マイグレーションを忘れたときに出るエラーですね」「basenamefilenameに変更することで、どのパスにあるマイグレーションファイルが未実行かが表示されるようになったのはいい👍」「developmentモードでしか表示されないメッセージだからフルパス表示でも問題ないでしょうね」

# activerecord/lib/active_record/migration.rb#L162
        pending_migrations.each do |pending_migration|
-         message += "#{pending_migration.basename}\n"
+         message += "#{pending_migration.filename}\n"
        end

🔗 ダークモードのエラーハイライト表示を改善

動機/背景
#45818のフォローアップ。今はダークモードで以下のように表示される。

濃い黄色で見づらくない色合いを試行錯誤した結果、以下にしてみた。

この色合いについて遠慮なくサジェスチョン求む。
同PRより


つっつきボイス:「何を修正したかったかがひと目でわかる」「ダークモードで白い文字の背景が明るいとたしかに読みにくい」「ダークモードって作業がいろいろ増えがちですよね」

🔗 MissingExactTemplateエラーページにコントローラ名とアクション名も表示

動機/背景
このプルリクは、コントローラ名とアクション名をエラーページにパススルーしてはどうかという@jhawthornの以下の提案↓を実装したもの。@olivierlacanの素晴らしいプルリク#46342にエラーページの改善を加える形で構築する。
同PRより


これはとてもいい!ところでそれとは別に、期待されるコントローラとアクションもパススルーすることが可能なら、実際に作成したいメッセージを暗黙のレンダリングでこのメッセージに含められるのでは?
#46342コメント(by jhawthorn)より


つっつきボイス:「エラーページにクラス名とアクション名も表示するようになったんですね」「これもdevelopmentモードのみなので、情報は多い方がいい👍」

# actionpack/lib/action_controller/metal/implicit_render.rb#L43
      elsif interactive_browser_request?
        message = "#{self.class.name}\##{action_name} is missing a template for request formats: #{request.formats.map(&:to_s).join(',')}"
-       raise ActionController::MissingExactTemplate, message
+       raise ActionController::MissingExactTemplate.new(message, self.class, action_name)
      else

🔗Rails

🔗 The Rails Doctrineの日本語訳が進行中


つっつきボイス:「日本Rubyの会代表の@takahashimさんが "The Rails Doctrine" の日本語化を進めているのを最近になって知りました」「この翻訳自体は昨年3月に投稿されていたんですね」「元の英語版は割と前からあったはずで以前読んだ覚えもありますが、この翻訳が公式へのマージを目指しているのはよさそう👍: そのうち久しぶりに読み返してみようかな」

後でarchive.orgで見るとThe Rails Doctrineは2016年からありました。

「この気持わかる↓」「exitしたいことがわかっているならexitして欲しいですよね」「Pythonは関数呼び出しの()を省略できないから、()なしのexitを実行できたら逆にびっくりするかもしれないけど、Rubyは省略できるので、()なしのexitquitをメソッドとして問題なく実行できますよね」「こういう細かい部分の気遣いがRubyらしい点」

# The Rails Doctrineより
$ irb
irb(main):001:0> exit
$ irb
irb(main):001:0> quit

$ python
>>> exit
Use exit() or Ctrl-D (i.e. EOF) to exit

「ところで、このvalidates_presence_of :nameという書き方↓は割と古いかも(今も一応使えるけど)」「今はpresence:をオプションで渡すんでしたっけ」「そうそう、validates :name, presence: trueみたいに書くのが主流ですね」

# The Rails Doctrineより
class Project < ApplicationRecord
  belongs_to :account
  has_many :participants, class_name: 'Person'
  validates_presence_of :name
end

参考: validates -- ActiveModel::Validations::ClassMethods

「ところで、以前に比べてRailsに対する批判を見かけることが減った気がしますね」「それはあるかも」「新規プロジェクトを何でもかんでもRailsで作り始めることが以前より減ったからかもしれませんね」


後ほど私も日本語訳にいくつか編集リクエストを投げてマージいただきました🙏。

🔗 GitHub元CTOのツイート


つっつきボイス:「以下のScrapbox記事で詳しくまとめられていますが、GitHub元CTOのJason Warnerによるマイクロサービスについての上の発言が議論になっていたそうです」「最後まで読まずに反応する人はどうしても一定数いますよね」「定番の話がいろいろまとまってていい👍」

参考: Jason Warnerとマイクロサービス - 西尾泰和のScrapbox

「まとめには、"スピードとリスク"のようなThe Rails Doctrineに通じる話もいくつかありますね」「マイクロサービスは、ドメイン境界の設計やサービスごとの役割の切り分けを行う人がものすごく有能でないと、一般には難しい面があると思います」「マイクロサービス化が進むほどRPCの頻度がものすごく高まったりログの量も増えたりしますよね」「そういえばGitHubも部分的にマイクロサービスを導入したという話を見かけた覚えがあります」「GitHubの規模なら避けられないでしょうね」

参考: GitHubのモノリスからマイクロサービスへのジャーニー

「まとめにある"できるだけ長くモノリスであること"というのは本当にそのとおりだと思います: マネタイズの見極めがつくとかそういうときが来るまでは、なるべくモノリスのままにしておきたい」「そうですね」「もちろんアプリの中には切り離しやすい部分もあれば切り離しにくい部分もあるので、切り離しやすい部分をしっかり見極められるなら必要に応じてマイクロサービス化を検討するのはありだと思います」

「"モノリスから脱却する場合小さなサービスではなく大きなアプリに分割する"も前から言われていて、これも同感」「そうそう、マイクロサービスは機能で分割するのではなく業務ドメインで分割しようみたいな話ですね」「そうしないと何をするにも大量のAPI呼び出しが発生したりしがち」

「これも昔から言われていますね↓: たいていのアプリはこうした伝統的な3レイヤアーキテクチャでまかなえると思います」

世界中の企業の90%は、プライマリDBクラスタとDBバックアップ、キャッシュ、プロキシで動作するモノリスで済ますことができるだろう。
Jason Warnerとマイクロサービス - 西尾泰和のScrapboxより

「ちなみに上のツイートは以下の発表を視聴していて知りました↓」「お〜、こんなミートアップが開催されていたんですね」

参考: 【iCARE Dev Meetup #36】Rejected CFP 供養会 2022 - connpass

🔗 The Rails Foundationが発足(Rails公式ニュースより)


つっつきボイス:「The Rails Foundationという公式な団体が、1企業がすべてを采配しない形で立ち上げられたのはいいことだと思います👍」

参考: Ruby on Railsを推進する「The Rails Foundation」発足、理事長にDHH氏が就任。Cookpad、GitHub、37signals、Shopifyなどが創立 - Publickey

In alphabetical order, the eight founding core members of The Rails Foundation are: Cookpad, Doximity, Fleetio, GitHub, Intercom, Procore, Shopify, and 37signals.
Ruby on Rails — The Rails Foundation kicks off with one million dollarsより

「ところで本筋と関係ないんですが、創立メンバーリスト↑がalphabetical orderと書かれているのに37signalsが最後尾にあるのがちょっと面白い」「アルファベット順なら数字が最初に来そうですよね」「推測ですが、英語圏の慣習では37みたいなアラビア数字よりもthirty-sevenのようにスペルアウトした形が正式という意識が強いからじゃないかなと思いました」「へ〜」「英語圏では特に文章がアラビア数字で始まることを嫌う傾向があるんですが、おそらく箇条書きと紛らわしいからだろうと思います」

参考: 【ビジネス英語】数の表記は数字?アルファベット?使い分けのルールを知ろう | みんなの仕事Lab-シゴ・ラボ-

🔗 その他Rails


つっつきボイス:「小ネタ記事です」「たしか従来のオートローダーがRailsに密結合しすぎていたからというのZeitwerkが開発された動機だったと思うので、単体で使えてもおかしくないでしょうね: こういうのをやってみるのは勉強にいいですね👍」

fxn/zeitwerk - GitHub

参考: Classic から Zeitwerk への移行 - Railsガイド

「Zeitwerkがオプショナルな形でRuby本体に入る可能性はあるでしょうか?」「Rubyには前からModule#autoloadがありますし↓、オートローダーのパフォーマンスが問題になるほど大規模なアプリ以外でZeitwerkが欲しくなることは少ないと思うので、Ruby本体に入る可能性はあまりないんじゃないかな」「なるほど」

参考: Module#autoload (Ruby 3.1 リファレンスマニュアル)

🔗 Ruby

🔗 Ruby 3.2.0 Preview 3リリース(Ruby公式ニュースより)


つっつきボイス:「Preview 3がリリースされる季節になったか〜」「3.2.0最終リリースももうじきですね」

「koicさんの以下の記事も貼りました↓」「Bisonのバージョンも気をつけないといけないのね」

参考: Ruby 3.2 (dev) のビルドに Bison 3 以上が必要になる - koicの日記

「ちなみに自分は3.2.0-devのビルドが未だに成功していません🥲」「マジですか」「M1環境なのと、どこかで設定をしくじっているような気もするんですが、BisonをアップデートしたりYJITをオフにしたりいろいろやってもビルドできず...」


その後オプションをいろいろ変えて、最新の3.2.0-dev(v20221121)と以下のオプションでやっとビルドに成功しました。まだM1で--enable-yjit=devはできないようです。

$ export RUBY_CONFIGURE_OPTS="--disable-yjit --with-opt-dir="$(brew --prefix openssl):$(brew --prefix readline):$(brew --prefix libyaml)" --disable-install-doc --disable-install-rdoc"
$ rbenv install 3.2.0-dev
To follow progress, use 'tail -f /var/folders/6_/412tm5md0svdq66fxwwgn0tw0000gn/T/ruby-build.20221122140346.89512.log' or pass --verbose
Downloading openssl-3.0.7.tar.gz...
-> https://dqw8nmjcqpjn7.cloudfront.net/83049d042a260e696f62406ac5c08bf706fd84383f945cf21bd61e9ed95c396e
Installing openssl-3.0.7...
Installed openssl-3.0.7 to /Users/hachi8833/.anyenv/envs/rbenv/versions/3.2.0-dev

Cloning https://github.com/ruby/ruby.git...
Installing ruby-master...
ruby-build: using readline from homebrew
ruby-build: using gmp from homebrew
Installed ruby-master to /Users/hachi8833/.anyenv/envs/rbenv/versions/3.2.0-dev
$ ruby -v
ruby 3.2.0dev (2022-11-22T02:32:28Z master 36f297e621) [arm64-darwin22]

参考: Bug #18912: Build failure with Xcode 14 and macOS 13 (Ventura) Beta - Ruby master - Ruby Issue Tracking System

🔗 Rubyの演算子クイズ(Ruby Weeklyより)


つっつきボイス:「Rubyのクイズサイトです」「お、面白そう〜」「全部で12問で制限時間があるのね」(しばし全員でつっつく)

「4択とはいえ、制限時間内に答えるの結構むずい」「andor&&||の優先順位の違いとか」「<<ってビットシフトだったか」「irbで動かせば一発だけど、やったら負けなんでしょうね...」「普段使わないような書き方が多いですよね」「終わったら解説が表示されるんですね」「知らない挙動がいくつもあった」「Ruby技術者認定試験でこんなの出たらつらそう」「Ruby技術者認定試験の問題にはこんな感じのが出ることもありますね」「これは楽しい👍」

参考: Ruby技術者認定試験

🔗 Rubyのメモリ使用量を減らす方法(Ruby Weeklyより)


つっつきボイス:「一口メモ的な記事です」「MALLOC_ARENA_MAX=2はどこかで見たと思ったら以下のNate Berkopecさんの記事にもありました↓」「MALLOC_ARENA_MAXということはglibc方面の話ですね」

Ruby: mallocでマルチスレッドプログラムのメモリが倍増する理由(翻訳)

参考: The GNU C Library -- glibc

その他Ruby


つっつきボイス:「RubyWorld Conference 2022のMatzのキーノートスピーチのまとめですね(ウォッチ20221116)」

🔗DB

🔗 HEYでページネーションを高速化(Ruby Weeklyより)


つっつきボイス:「HEY.comを手掛けている37signalsの記事です」「MySQLのEXPLAINで結果を確かめつつ、複合インデックスを作ってからFORCE INDEXするという定番の手法ですね: MySQLはデフォルトでインデックスが統計情報に応じて動的に選択されてしまうので、明示的に使って欲しい特定のインデックスがある場合にはFORCE INDEXを使わないと想定通りに動かないことがよくあります」

参考: HEY - Email at its best, new from 37signals.

-- 同記事より
mysql> EXPLAIN
    -> SELECT `topics`.*
    -> FROM `topics` FORCE INDEX(index_topics_on_account_id_and_id_and_status_and_dates)
    -> INNER JOIN `accesses` ON `accesses`.`topic_id` = `topics`.`id`
    -> WHERE `accesses`.`contact_id` IN (
    ->     SELECT `contacts`.`id`
    ->     FROM `contacts`
    ->     INNER JOIN `users` ON `contacts`.`contactable_id` = `users`.`id`
    ->     WHERE `users`.`identity_id` = 280304
    ->       AND `contacts`.`contactable_type` = 'User'
    ->   )
    ->   AND `topics`.`status` = 5
    ->   AND `accesses`.`account_id` = `topics`.`account_id`
    -> ORDER BY `topics`.`active_at` DESC, `topics`.`id` DESC
    -> LIMIT 15;

参考: MySQL :: MySQL 8.0 リファレンスマニュアル :: 8.9.4 インデックスヒント -- FORCE INDEX

「ところで37signalsとBasecampってどういう関係でしたっけ?」「最近社名が変わったみたいな話を見かけた気がするけどどうだったかな🤔」


後で調べると、2022/05/03にBasecampが社名を37signalsに改名したというアナウンスが出ていたことを今になって知りました↓。37signalsという名前はBasecampの初期の社名だったそうです。
Wikipedia英語版によると「(1999創立)37signals -> (2014)Basecamp -> (2022)37signals」のように社名が変遷していたんですね。なおbasecamp.comのサービス名はこれまでどおりBasecampです。

参考: 37signals: Hello again
参考: 37signals - Wikipedia
参考: Basecamp: Project management software, online collaboration

🔗 設計・セキュリティ

🔗 素のRailsは十分に豊かである(Ruby Weeklyより)

既に翻訳許可をいただいたので、近々公開いたします。


つっつきボイス:「YassLabの安川さんから教えていただいた記事で、これも37signalsです」「Vanilla Railsって素のRailsですか」

バニラ(英語:Vanilla)とは、コンピュータソフトウェア、まれにコンピュータのハードウェアまたはアルゴリズムなどで、改変・改修・カスタマイズなどが一切行われていない、提供された状態のまま(原型を留めたままの状態)を指す。
バニラという表現は、業界でもデ・ファクト・スタンダードとして標準的に企業や個人で広く利用されている。語源は、アイスクリームの標準的な風味であるバニラ味からきている。エリック・レイモンドのジャーゴンファイルによると、バニラは "default" (デフォルト)よりは "ordinary" (普通の・平凡な)の意に近いとしている。
バニラ (ソフトウェア) - Wikipediaより

「境界を仕切るようなgemに頼らずに、concernsやPORO(Plain Old Ruby Object)を使いこなす形でやっていくのをVanilla Railsと呼んでいるようですね: クリーンアーキテクチャのような大掛かりなものを使わなくてもここまでやれるという感じ」「なるほど」「安易にgemを入れたりせずに解決するやり方はいいと思います👍」

参考: 多層アーキテクチャ - Wikipedia

「記事の最後に"37signalsに入社してみてコードの品質がめちゃくちゃ高いことに驚いた"、"DDD(ドメイン駆動設計)を使ってないのにDDDを見事に体現している"みたいなことが書かれてますね」「HeyやBasecampのように情報だけを扱うタイプの自社開発アプリは、素のRailsで比較的きれいに設計しやすいという側面はあるでしょうね」「それもそうですね」

参考: ドメイン駆動設計 - Wikipedia

「対照的に、たとえばShopifyのようなサービスはものすごく複雑な現実を相手にしているので、そういうところではPackwerk(ウォッチ20220920)のような境界を仕切るgemが欲しくなるだろうということも想像できます」「なるほど」「国ごとの通貨や法律の違いや外部連携や頻繁な仕様変更といった複雑な現実を相手にするようになると、きれいに抽象化できないとか例外的な扱いをせざるを得なくなることが発生しやすいんですよ」「そうなんですよね...」「そういう複雑な現実をマイクロサービスで切り離そうとしてもあまりうまくいく気がしない」「結局敵は現実世界か...」

Shopify/packwerk - GitHub

🔗JavaScript

🔗 Symfony UXでHotwire技術が導入されている


つっつきボイス:「今日のWebチーム内発表で触れられていたヤツです」「SymfonyはPHPのWebフレームワークで自分も以前使ったことがありますが、このSymfony UXではHotwireのStimulusとTurboを使っている」「curated by Symfonyと書かれていますね」「名前空間もsymfonyになっているので公式のものですね」「HotwireはRailsがなくても使えるんですよね」「結局みんなだいたい同じようなところで悩んだり詰まったりするので、RailsやSymfonyなどで欲しいものが似通ってくるというのはあるでしょうね」

速報: Basecampがリリースした「Hotwire」の概要


今週は以上です。

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

週刊Railsウォッチ: Rubyを使っている企業の時価総額リスト、irbのshow_source、GitHub Codespacesほか(20221116後編)

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

Ruby 公式ニュース

Rails公式ニュース

Ruby Weekly


CONTACT

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