TechRacho Ruby on Rails システム開発情報サイト - BPS株式会社 2026-01-14T08:41:40Z https://techracho.bpsinc.jp/feed/atom WordPress https://techracho.bpsinc.jp/wp-content/uploads/2017/09/cropped-techracho_official_icon-1-32x32.png hachi8833 <![CDATA[ChatGPTのしくみとAI理論の根源に迫る(15/16)意味文法と「計算言語」のパワー(翻訳)]]> https://techracho.bpsinc.jp/?p=153872 2026-01-14T01:43:22Z 2026-01-15T08:30:52Z <![CDATA[

概要 原文サイトのCreative Commons BY-NC-SA 4.0を継承する形で翻訳・公開いたします。 英語記事: What Is ChatGPT Doing … and Why Does It Work?—Stephen Wolfram Writings 原文公開日: 2023/02/14 原著者: Stephen WolframMathematicaやWolframAlphaの開発者として知られる計算科学者・理論物理学者です 日本語タイトルは内容に即したものにしました。原文が長大なので、章ごとに16分割して公開します。 スタイルについては、かっこ書きを注釈にする、図をblockquoteにするなどフ […]

The post ChatGPTのしくみとAI理論の根源に迫る(15/16)意味文法と「計算言語」のパワー(翻訳) first appeared on TechRacho.

]]>
<![CDATA[

概要

原文サイトのCreative Commons BY-NC-SA 4.0を継承する形で翻訳・公開いたします。

日本語タイトルは内容に即したものにしました。原文が長大なので、章ごとに16分割して公開します。
スタイルについては、かっこ書きを注釈にする、図をblockquoteにするなどフォーマットを適宜改善し、文面に適宜強調も加えています。

元記事は、2023年2月の公開時点における、ChatGPTを題材とした生成AIの基本概念について解説したものです。実際の商用AIでは有害コンテンツのフィルタなどさまざまな制御も加えられているため、そうした商用の生成AIが確率をベースとしつつ、確率以外の制御も加わっていることを知っておいてください。

本記事の原文を開いて、そこに掲載されている図版をクリックすると、自分のコンピュータでもすぐに実行して試せるWolfram言語コードが自動的にクリップボードにコピーされるようになっています。

コモンズ証 - 表示 - 非営利 - 継承 4.0 国際 - Creative Commons

前回: (14/16)意味空間と「意味論的な」運動法則

ChatGPTのしくみとAI理論の根源に迫る(15/16)意味文法と「計算言語」のパワー(翻訳)

人間の「意味を持つ言語」を生成するには、何が必要なのでしょうか?かつては、人間の脳がなければ到底無理だろうと考えられていた時代もあります。しかし今や、ChatGPTのニューラルネットを用いることで、相当なレベルで実現できることがわかってきました。

とはいうものの、もしかしたらこれが限界かもしれませんし、人間が理解可能な技術としてはこれ以上シンプルなものは存在しないかもしれないという可能性もあります。しかし私は、ChatGPTの成功は、ある重要な「科学的」事実を暗黙のうちに明らかにしたのだと私は強く信じています。それは、人間の「意味を持つ言語」には、人間がこれまで見落としていたような構造やシンプルさがまだ潜んでいる可能性があること、そして最終的には、そのような「意味を持つ言語」を構成する方法を記述できる、現在よりさらにシンプルな規則が存在する可能性すらありうる、という信念です。


これまでに述べたように、構文文法(syntactic grammar)は、さまざまな品詞に対応する語(word)を人間の言語でどのように組立てるかについてのルールを与えます。しかしそれはあくまで形の上での構文であり、言語で意味を扱うには、そこからさらに踏み込む必要があります。

その方法の1つが、構文文法に加えて意味文法(semantic grammar)、すなわち意味を表現するための文法についても考察することです。

私たちは、構文上の目的のために、名詞や動詞などの品詞を識別しますが、意味上の目的のためには、それよりも「もっと細かいグラデーション」が必要です。

たとえば私たちは、「動く」という概念を識別することも、位置にかかわらず同一性を維持する「物体(object)」という概念を識別することもできます。このような「意味概念」の具体例をリストアップしていたらきりがありませんが、意味文法という観点からは、基本的に「"物体"は"動く"ことがある」という何らかの一般的なルールがあれば十分です。これが機能するしくみについては別記事でもある程度説明しましたが、ここでは、今後の道筋を示すいくつかの考察を述べるにとどめておきます。

ここで指摘しておきたいのは、ある文が、たとえ文法的には完璧だったとしても、意味文法に照らし合わせたときに、それが現実に起きたことと一致する(あるいは起きる可能性がある)とは限らないことです。
「そのゾウは月に旅をした(The elephant traveled to the Moon)」という文章は、私たちの意味文法に照らし合わせれば文句無しに「合格」しますが、それが現実世界で(少なくとも過去に)起きたことを意味していないのは明らかです(もちろんフィクションの世界であれば何の問題もありません)。

こういう「意味文法」の話を始めると、たちまち「その意味文法を支えているものは一体何なのか?」「そこではどんな"世界モデル"を仮定しているのか?」という疑問が持ち上がります。構文文法は、あくまで語の組み立てについてのルールを述べているに過ぎませんが、意味文法では、どうしても何らかの「世界モデル」(つまり実際の語を積み重ねたものを土台としてその上に構築される「骨格」として存在するもの)と関わりを持つことになります。


ごく最近まで、そうした「世界モデル」を記述する一般的な手段は、(人間の)言語以外には存在しないと思われてきたことでしょう。ほんの数世紀前に、特に数学の特定の分野では「形式化(formalization)」が開始されていましたが、現在の私たちは、それよりもずっと汎用性の高い形式化である計算言語(computational language)を手にしているのです。

そしてこの計算言語こそ、私が40年以上に渡って取り組んできたビッグプロジェクトであり、それがWolfram言語に結実しています。Wolfram言語の目的は、世界で起きているあまたの事象と、人間が関心を寄せる抽象的な事象をできる限り精密に記述できる記号表現を開発することです。実際、Wolfram言語では、「都市 」「分子」「画像」「 ニューラルネットワーク」などの記号化表現が用意されており、それらについて計算を行うのに必要な知識もそこに組み込まれています。

私たちは数十年におよび同プロジェクトの取り組みの結果、多くの分野をカバーしてきましたが、「日常の会話」についてはカバーしていません。たとえば「私は2ポンドのリンゴを買った(I bought two pounds of apples)」という文を例にすると、「2ポンドのリンゴ」の部分についてはそのための記号化表現が既に用意されていますが、「私は買った(I bought)」を表す記号表現は(今のところ)ありません。

今の話は、どれも意味文法というアイデア、すなわち概念を汎用的に表現する記号の「組み立てキット」を提供するという目標と結びついています。この記号組み立てキットは、そこから「何と何をどう組み合わせれば意味が適切になるか」を示すルールや、ひいてはそれが人間の言語に変換されるときの「フロー」のルールを得られることが期待されます。


しかし、仮にそうした「記号化された会話言語」というものが手に入ったとして、それで何ができるでしょうか?最初のうちは「局所的に意味のあるテキスト」を生成して満足していても、いずれ「よりグローバルに意味のあるテキスト」が欲しくなるものです。つまり、現実世界(もしくは辻褄の合うフィクションの世界)で実際に起き得ることをさらに深く計算しなければならなくなるということです。

現時点のWolfram言語には、さまざまな事物を対象とする膨大な計算知識が組み込まれています。しかし会話言語の記号化を完全なものにするには、現実世界に存在する一般的な事物を扱うための「計算方法」を新たに構築しなければならなくなるでしょう(例: ある物体をAからBに移動し、次にBからCに移動すると、最終的にAからCに移動したことになる)。

仮に記号化された会話言語が手に入れば、私たちはそれを用いて「単独の文面」を記述するだけでなく、あるときは私の「Wolram Alpha」のようなスタイルで世界について質問することも可能でしょうし1、またあるときは何らかの外部駆動メカニズムを用いて「こうしたい、ああしたい」と指示することも可能でしょう。またあるときは、現実世界であろうとフィクションの世界などであろうと構わずに何らかの主張を行うことも可能でしょう。


そもそも人間の言語は、根本的に精度が低いものです。その理由は、人間の言語は特定の計算処理実装に「縛られている」ものではなく、基本的に、言語を使う人間たちの間で暗黙のうちに交わされている「社会的契約」によってのみ定義されているからです。

しかし逆に、計算言語は根本的にある程度高精度なものです。その理由は、計算言語は常に「コンピュータ上で曖昧さなしに実行される」ものだからです。

人間の言語は、適度に曖昧さを含んでいるのが普通です(「惑星」と言ったときに、太陽系外惑星を含むのか含まないのか、など)が、計算言語では、人間普段行っているあらゆる区別を厳密かつ明確な形で行わなければならなくなります。

計算言語でも、変数名や関数名などを人間の言語(ここでは英語)で命名できると便利なことがよくあります。しかし計算言語における命名は、必然的に厳密にならざるを得ません。そして、それが人間の言語で典型的に見られるような意味上の「含み」「含意」までカバーしていることもあれば、そうでないこともあるでしょう。

一般的な「記号化された会話言語」にふさわしい「オントロジー(ontology: ここでは知識を体系化するための仕様)」を果たして見いだせるかというと、まあ普通に難しいでしょう。二千年以上も昔に哲学者アリストテレスが手がけた初期のオントロジー研究がほとんど進んでいない理由も、おそらくそのせいです。しかし現代では、世界を「計算的に」考える方法は非常に役立っています(だからこそ、私たちが手掛けているWolfram物理プロジェクトや私が提唱するRuliadという概念2から得た「基礎的な形而上学」も決して無駄ではありません)。

しかし、これらはChatGPTの文脈において一体何を意味しているのでしょうか?
ChatGPTは、トレーニングによって、ある程度の(というよりかなりの)量の意味文法的なものを効果的に「組み立てて」きました。しかしChatGPTの成功そのものが、そうしたものを計算言語らしい形式で、より完全に構築できてもおかしくないと考える根拠を与えてくれています。そして、これまでChatGPTの内部について解明されてきた結果と異なる、人間にとって理解しやすい計算言語を設計できることが期待できそうです。


さて、意味文法について、いわゆる三段論法(syllogistic logic)とのアナロジーが考えられます。
古代ギリシャで三段論法が最初に登場したときは、本質的に人間の言語で表現される文(statements)の規則の集合でした。しかし(もちろん二千年後)形式論理(formal logic)が開発されたことで、三段論法にもともとあった基本要素を用いて、たとえば現代のデジタル回路の操作といった壮大な「形式の塔(formal tower)」を構築できるようになったのです。

であれば、三段論法よりずっと一般性の高い意味文法でも同じことが期待できるでしょう。最初のうちは、たとえばテキストで表現されたシンプルなパターンを扱うのが精一杯かもしれません。しかし計算言語のフレームワーク全体が構築されれば、それを用いて「汎化意味論ロジック(generalized semantic logic)」の巨大な塔を建設することも可能となり、それによって、従来ならどうやっても曖昧にならざるを得ない人間の言語を介して「基礎の基礎レベル」でアクセスするしかなかったあらゆる事物を、厳密かつ形式的に扱うことが可能になるでしょう。


計算言語(と意味文法)の構築は、言ってみれば事物を表現するときの究極の圧縮とみなすことも可能です。それが実現できれば、たとえば普通の人間の言語に存在する「ありとあらゆる凝った言い回し」を扱わなくても、何らかの可能性の本質だけをズバリ表現できるようになります。

そして、ChatGPTの強みも、それと少し似ている面があります。ChatGPTはある意味で「突き抜けている」ので、どんな凝った言い回しがありうるかという可能性を気にすることなく、「意味のある形で言語を組み立てられる」からです。

では、それらの基礎となる計算言語にChatGPTを適用したら3、何が起きるでしょうか?
計算言語は、「何が可能であるか?」を記述できますが、Web上のあらゆるコンテンツに基づいて「人気があるのはどれか?」という感覚をChatGPTに付け加えることも可能になります。

しかし、ChatGPTの背後で計算言語を扱うようになれば、潜在的に縮約不可能な計算を利用するための究極ツールに、即座に、かつ本質的にアクセス可能になります。すなわち、ChatGPTが単に「適切なテキストを生成する」だけのツールにとどまらず、テキストが世界について(あるいはテキストが語るべきあらゆる内容について)本当に「正しい」ことを述べているかどうかを、厳密かつ計算的に判断できることが期待できます。

次回: (16/16)結局ChatGPTはなぜ、どのように動くのか?

訳注

近年のGPT-5 Proは、数学的な発見まで行うようになっているそうです。

関連記事

ChatGPTのしくみとAI理論の根源に迫る:(1/16)実は語を1個ずつ後ろに追加しているだけ(翻訳)

ChatGPTのしくみとAI理論の根源に迫る:(2/16)その確率は「どこから」来たのか?(翻訳)

ChatGPTのしくみとAI理論の根源に迫る:(3/16)AIの「モデル」とは何か(翻訳)


  1. 訳注: Wolram Alphaでたとえば「地震 日本 2011年」を検索してみるとよいでしょう。 
  2. 訳注: 推測止まりですが、Ruliadはおそらくルール(rule)とイーリアス(illiad)を組み合わせた造語と思われます。 
  3. 訳注: これは、ChatGPTからWolfram Alpha(およびWolfram言語)に今で言うMCP的に連携させることを指していると考えられます(元記事執筆当時はMCPはありませんでした)。 

The post ChatGPTのしくみとAI理論の根源に迫る(15/16)意味文法と「計算言語」のパワー(翻訳) first appeared on TechRacho.

]]>
0
baba <![CDATA[ページ内検索したテキストはWebサイト運営者にバレる]]> https://techracho.bpsinc.jp/?p=155955 2026-01-14T08:41:40Z 2026-01-14T08:30:03Z <![CDATA[

はじめに 突然ですがこのページを開いてCtrl+Fのページ内検索を開き、 x y z のいずれかの文字を検索してみてください。 https://codepen.io/babatakao/full/bNeBOwr このように、検索した結果がalertで表示されます。 言いたかったこと このように、ページ内検索したテキストはコンテンツJavaScriptから検知できます。 選択したテキストや、フォームに入力したテキストが検知できるのは当然なのですが、ページ内検索テキストは今まで知りませんでした。例えば得体のしれない怪しいウェブサイトで「自分のメールアドレスが漏洩していないかな」とページ内検索、などはやっては駄目なこと […]

The post ページ内検索したテキストはWebサイト運営者にバレる first appeared on TechRacho.

]]>
<![CDATA[

はじめに

突然ですがこのページを開いてCtrl+Fのページ内検索を開き、 x y z のいずれかの文字を検索してみてください。

このように、検索した結果がalertで表示されます。

言いたかったこと

このように、ページ内検索したテキストはコンテンツJavaScriptから検知できます。

選択したテキストや、フォームに入力したテキストが検知できるのは当然なのですが、ページ内検索テキストは今まで知りませんでした。例えば得体のしれない怪しいウェブサイトで「自分のメールアドレスが漏洩していないかな」とページ内検索、などはやっては駄目なことになりますね。気をつけましょう。

仕組み

こんなHTMLとJavaScriptを仕込んでいました。

<p hidden="until-found" id="x">x</p>
<p hidden="until-found" id="y">y</p>
<p hidden="until-found" id="z">z</p>
const list = { x: false, y: false, z: false };

setInterval(() => {
  Object.keys(list).forEach(word => {
    if (list[word]) { return }
    if (getComputedStyle(document.getElementById(word)).contentVisibility === 'visible') {
      list[word] = true;
      alert(`${word}が検索されました`);
    }
  });
}, 100);

似たようなことをやる方法はいくつかありそうです。

::search-text 疑似要素

Chrome 144でリリースされた新しい機能で、ページ内検索でヒットした文字列の装飾を変更できます。

::search-text {
  /* Styles for all search results */
}
::search-text:current {
  /* Styles for the active search result */
}

詳しい仕様はこちらに https://drafts.csswg.org/css-pseudo-4/#selectordef-search-text

普通に使う分には、ヒット色が見づらくなってしまうデザインのWebサイトなどで役立ちそうですね。

背景色やシャドウなど一部の要素しか変更できないので、単に background-image: url(tracking url) は使えないし、element.pseudo('::search-text') でヒットするわけでもない(これで取得できるのは ::after ::before ::marker のみ)ですが、多分何か工夫すればJSで検知できるような気はします。

details

<details>を折りたたんでいる場合、検索にヒットすると自動で展開されます。このイベントを監視することで、検索されたことを検出できます。

hidden="until-found"

今回使った方法です。こんな属性あったんですね。ヒットするまでは Content-Visibility: hidden 扱いになります。

参考: HTML hidden グローバル属性 - HTML | MDN

雑記

最初に ::search-text の機能を知って、あれこれ検索テキストバレるじゃんspecのPrivacy Consideration書いてないの良いの?と思って調べたら、もとから出来るということを知った流れでした。皆さん賢い。

上記の素朴な実装では1文字ずつしか検知できませんし面倒ですが、やれば出来るということはそのうちに気合入ったGoogle Analytics的なやつが実装しないとも限らないわけで、エンドユーザとしてはブラウザの使い方に気をつけていこうと思います。

関連記事

HTMLDialogElement: showModal() はHTMLの長年の課題を解決する期待の星かと思ったら実際はだめな子

スクロールコンテナに指定したpaddingは適用されるべきか

The post ページ内検索したテキストはWebサイト運営者にバレる first appeared on TechRacho.

]]>
0
hachi8833 <![CDATA[Ruby 4.0.1がリリースされました]]> https://techracho.bpsinc.jp/?p=155928 2026-01-14T02:43:26Z 2026-01-13T06:49:48Z <![CDATA[

Ruby 4.0.1がリリースされました。バグ修正を含む定期リリースです。 Ruby 4.0.1 Released https://t.co/czUG6J0sia This release includes a bugfix for Kernel#sleep, along with other bugfixes. We recommend upgrading your Ruby version at your earliest convenience. — k0kubun (@k0kubun) January 13, 2026 リリース情報: Ruby 4.0.1 Released | Ruby 詳し […]

The post Ruby 4.0.1がリリースされました first appeared on TechRacho.

]]>
<![CDATA[

Ruby 4.0.1がリリースされました。バグ修正を含む定期リリースです。

詳しくはリリース情報をご覧ください。

参考: 今後の定期リリース予定

予定は上記リリース情報記載のものであり、変更の可能性があります(臨時で重要なリリースが公開される場合、以後の定期リリースの時期がシフトする可能性もあり)。

  • Ruby 4.0.1: 2026年1月(今回のリリース)
  • Ruby 4.0.2: 2026年3月
  • Ruby 4.0.3: 2026年5月
  • Ruby 4.0.4: 2026年7月
  • Ruby 4.0.5: 2026年9月
  • Ruby 4.0.6: 2026年11月

メモ: メンテナンス終了が近いRubyバージョン

  • Ruby 3.2: 2026年3月いっぱいでメンテナンス終了(予定)
  • Ruby 3.3: 2027年3月いっぱいでメンテナンス終了(予定)

参考: Ruby ブランチごとのメンテナンス状況

🔗 更新の概要

詳しい変更内容は以下で参照できます。

gem update --systemでRubyGems(gemコマンド)とbundlerを更新できます。

🔗 関連する更新

🔗 gems

default gemsとbundled gemはしばしば変更・更新されます。詳しくは以下をご覧ください。

参考: standard librariesとdefault gemsとbundled gemsの違い - ESM アジャイル事業部 開発者ブログ

Ruby 4.0.x公式のライブラリ情報(gemも含む)は以下で参照できます。

参考: Documentation for Ruby 4.0

Rubyバージョンごとのdefault gemsとbundled gem編成については以下のstdgems.orgが便利です。

参考: Standard Gems -- stdgems.org -- default gemsとbundled gemのリストをRubyバージョンごとに確認できます(現時点では4.0.0まで)。

🔗 rbenv

rbenvでは既にRuby 4.0.1が利用可能になっています。

参考: Release ruby-build 20260113 · rbenv/ruby-build

rbenv/ruby-build - GitHub

🔗 Docker Hub

追記2026/01/14: その後利用可能になりました。

現時点のDocker HubではRuby 4.0.1はまだ利用可能ではありません(現時点では4.0.0まで)。

🔗 Heroku

追記2026/01/14: その後利用可能になりました。

現時点のHerokuランタイムでは、Ruby 4.0.1はまだサポートされていません(現時点では4.0.0まで)。

参考: Heroku Ruby Support | Heroku Dev Center

heroku/heroku-buildpack-ruby - GitHub

🔗 devcontainer

現時点のRails devcontainerは、まだRuby 4.0.1に対応していません(現時点では4.0.0まで)。

参考: Package devcontainer/images/ruby

rails/devcontainer - GitHub

🔗 GitHub Actionsのsetup-ruby

追記2026/01/14: その後利用可能になりました。

GitHub Actions向けのsetup-rubyは、現時点ではまだRuby 4.0.1に対応していません(現時点では4.0.0まで)。

ruby/setup-ruby - GitHub

関連記事

Ruby 4.0.0がリリースされました

M1/M3 MacでRuby 2.4〜4.0をrbenvでビルドする最小限のセットアップを全部調べた

The post Ruby 4.0.1がリリースされました first appeared on TechRacho.

]]>
0
hachi8833 <![CDATA[Rails 8.1.2がリリースされました]]> https://techracho.bpsinc.jp/?p=155895 2026-01-09T05:47:09Z 2026-01-09T05:47:09Z <![CDATA[

Ruby on Rails 8.1.2がリリースされました。内容は主にバグ修正です。 英語版Changelogをまとめて見るにはGItHubのリリースタグ↓が便利です。v8.1.2タグの日付は日本時間の2026/01/09 05:17でした。 Release 8.1.2 · rails/rails 詳しくは以下のコミットリストをご覧ください。 コミットリスト: Comparing v8.1.1...v8.1.2 · rails/rails 🔗 参考: Minitest 6のサポート Changelogには含まれていませんが、Rails 8.1.2には最近リリースされたMinitest 6をサポートするための修正も […]

The post Rails 8.1.2がリリースされました first appeared on TechRacho.

]]>
<![CDATA[

Ruby on Rails 8.1.2がリリースされました。内容は主にバグ修正です。

英語版Changelogをまとめて見るにはGItHubのリリースタグ↓が便利です。v8.1.2タグの日付は日本時間の2026/01/09 05:17でした。

詳しくは以下のコミットリストをご覧ください。

🔗 参考: Minitest 6のサポート

Changelogには含まれていませんが、Rails 8.1.2には最近リリースされたMinitest 6をサポートするための修正も含まれています。

私がRails 8.1.1でrails newしたときにminitestが動かなくなったのに気づき、上のissueを知りました。8.1.2ではこの問題は修正されました。

🔗 最新のサポート期限

現在のメンテナンスポリシー↓ではRails 8.1.x8.0.xがバグ修正サポート対象、Rails 8.1.x8.0.x7.2.xがセキュリティ修正の対象となっています。

  • バグ修正:
    • Rails 8.1.x: 2026/10/10までサポート
    • Rails 8.0.x: 2026/05/07までサポート
  • セキュリティ修正:
    • Rails 8.1.x: 2027/10/10までサポート
    • Rails 8.0.x: 2026/11/07までサポート
    • Rails 7.2.x: 2026/08/10までサポート

Rails 7.1.x以下はセキュリティ修正も含めサポートが終了しました。

参考: Ruby on Rails — Maintenance policy

🔗 参考情報

アップグレード方法については以下の手順をどうぞ。

参考: § 1.3 アップグレード手順 -- Rails アップグレードガイド - Railsガイド
参考: Railsアップグレードガイド - ruby-jp

フレームワークそのもののdiffではなく、生成されるアプリのdiffをチェックするには、railsdiff.orgが便利です↓。

Railsバージョン間のgemの互換性をチェックするには、以下のrailsbump.orgが便利です。GemfileやGemfile.lockを貼り付けてチェックすることも可能です。

🔗 更新の概要

本記事では、GitHubリリースタグに掲載されているChangelogに対応するプルリクやコミットへのリンクを取り急ぎ貼りました。

🔗 Active Support

🔗 Make delegate and delegate_missing_to work in BasicObject subclasses by rafaelfranca · Pull Request #56353 · rails/rails

delegatedelegate_missing_toがBasicObjectのサブクラスで動作するよう修正。

Rafael Mendonça França
同Changelogより

🔗 Fix Inflections.instance_or_fallback to properly find :en fallback by Saidbek · Pull Request #56344 · rails/rails

:enにフォールバックするロケールを使った場合にpluralize:enにフォールバックしなくなっていたのを修正。

Said Kaldybaev

同PRより

🔗 Ensure TimeWithZone#as_json always return an UTF-8 string by byroot · Pull Request #56242 · rails/rails

ActiveSupport::TimeWithZone#as_jsonがUTF-8文字列を返さない場合があったのを修正。

従来は返される文字列がUS-ASCIIになる場合があり、一部で問題が生じる可能性があった。

修正後は常にUTF-8文字列を返すようになった。

Jean Boussier
同Changelogより

🔗 Fix TimeWithZone#xmlschema when wrapping DateTime local time by drymar · Pull Request #56216 · rails/rails

DateTimeインスタンスをローカル時間でラップした場合にTimeWithZone#xmlschemaが生成するISO-8601文字列が不正になる問題を修正。

従来は不正な時刻が返されていた。

Dmytro Rymar

同Changelogより

🔗 Implement LocalCache strategy on MemoryStore by mikeygough · Pull Request #56152 · rails/rails

ActiveSupport::Cache::MemoryStoreにLocalCache戦略を実装した。

このMemoryStoreは他のキャッシュストア(ActiveSupport::NullStoreなど)と同じインターフェイスに応答する必要がある。

Mikey Gough
同Changelogより

🔗 ActiveSupport: humanize handle international chars by jlduran · Pull Request #56077 · rails/rails

ActiveSupport::Inflector.humanizeが英語以外のアルファベット文字の大文字小文字を適切に変換できるよう修正。

ActiveSupport::Inflector.humanize("áÉÍÓÚ")  # => "Áéíóú"
ActiveSupport::Inflector.humanize("аБВГДЕ") # => "Абвгде"

Jose Luis Duran
同Changelogより

🔗 Active Record

🔗 fix cached_queries_count on ActiveRecord RuntimeRegistry by alanoliveira · Pull Request #56138 · rails/rails

ActiveRecord::RuntimeRegistrycached_queries_countが、クエリがキャッシュにヒットしたときにもカウントアップしていたのを修正。

fatkodima
同Changelogより

🔗 Fix merging relations with arel equality predicates with null relations by fatkodima · Pull Request #56482 · rails/rails

Arelで述語リレーションに.nonemergeするとエラーになる場合があったのを修正。

fatkodima
同Changelogより

🔗 Fix SQLite3 schema dump for non-autoincrement integer primary keys by khasinski · Pull Request #56534 · rails/rails

テーブルにAUTOINCREMENTなしの整数主キーを含むSQLite3ダンプをリストアした場合の問題を修正。

修正前は、テーブルをリストアしたときに誤って主キーのAUTOINCREMENTが有効になっていた。

Chris Hasiński
同Changelogより

🔗 [Backport 8-1-stable] Fix PostgreSQL schema_search_path after reconnect and reset by eglitobias · Pull Request #56535 · rails/rails

PostgreSQLのschema_search_pathreset!reconnect!の実行後に再適用されないよう修正。

これにより、database.ymlschema_search_pathがPostgreSQLのデフォルトにフォールバックせずに適切に再適用されるようになった。

Tobias Egli
同Changelogより

🔗 Fix enum validation to allow Float values by Saidbek · Pull Request #56447 · rails/rails

Rails 8.1.1のenumでFloat値を指定できなくなっていたのを修正。

enum :rating, { low: 0.0, medium: 0.5, high: 1.0 },

Rails 8.1.0でenum値のバリデーションが追加されたときに、浮動小数点が想定されていなかった。

Said Kaldybaev
同Changelogより

🔗 Ensure batched preloaded associations accounts for klass when grouping to avoid issues with STI by zzak · Pull Request #56415 · rails/rails

STIでの問題を回避するため、関連付けのバッチプリロードでグループ化時のklassが確実に識別されるよう修正。

zzak, Stjepan Hadjic
同Changelogより

🔗 Fix ActiveRecord::SoleRecordExceeded#record to return the relation by byroot · Pull Request #56287 · rails/rails

ActiveRecord::SoleRecordExceeded#recordがリレーションを返していなかったのを修正。

Rails 7.2まではリレーションを返していたが、Rails 8.0から誤ってモデルクラスを返すようになっていた。

Jean Boussier
同Changelogより

🔗 Handle asynchronous raise in all of reconnect! by byroot · Pull Request #56231 · rails/rails

PostgreSQLAdapterのTimeout.timeoutへの対応を改善した。

reconnect!メソッド内でスローされる非同期な例外の扱いを改善したことで、以下のような深い位置でのエラーを修正。

undefined method `key?' for nil:NilClass (NoMethodError)
          if !type_map.key?(oid)

Jean Boussier
同Changelogより

🔗 Emit structured events for Active Record work on v8.1 by y-yagi · Pull Request #56119 · rails/rails

Active Recordの構造化イベントがRails 8.1で発火していなかったのを修正。

Yuji Yaginuma
同Changelogより

🔗 Prevent duplicates when eager-loading models with a composite primary key by Martin-Alexander · Pull Request #56088 · rails/rails

複合主キーを持つhas_many関連付けを読み込むときのeager_loadを修正。

修正前は一部のレコードが複数回読み込まれることがあった。

Martin-Alexander
同Changelogより

🔗 Action View

🔗 [Bug] Fix input[type=file] accept attribute array value separator as , by bogdan · Pull Request #56389 · rails/rails

file_fieldにMIMEタイプのリストを以下のように配列で渡しても正常に処理されるよう修正(修正前はHTMLでaccept属性の値がスペース区切りになっていたが、これはHTMLの仕様に沿っていない)。

file_field(:article, :image, accept: ['image/png', 'image/gif', 'image/jpeg'])

修正後は以下を渡した場合と同様に正常に動作する。

file_field(:article, :image, accept: 'image/png,image/gif,image/jpeg')

Bogdan Gusiev
同Changelogより

🔗 Fix ERB strict locals parsing when comment spans multiple lines by Saidbek · Pull Request #56270 · rails/rails

strict localsアノテーションで複数行にわたる定義を処理できるよう修正。

<%# locals: (arg_1:,
             arg_2: nil,
             arg_3: []) %>

Said Kaldybaev
同Changelogより

🔗 Fix content_security_policy_nonce error in mailers when using content_security_policy_nonce_auto setting by jclusso · Pull Request #56050 · rails/rails

content_security_policy_nonce_auto設定を使うとメーラーでcontent_security_policy_nonceでエラーが発生していたのを修正。

content_security_policy_nonceヘルパーはActionController::ContentSecurityPolicyで提供されており、これはrequest.content_security_policy_nonceに依存している。メーラーにはこのモジュールもリクエストオブジェクトも存在しない。

Jarrett Lusso
同Changelogより

🔗 Action Pack

🔗 Implement exclude keys for ActionController::Live execution sharing by eileencodes · Pull Request #56393 · rails/rails

ActionController::Liveで共有される実行ステートを制御するためのconfig.action_controller.live_streaming_excluded_keysを追加。

ActionController::Liveを利用すると、コントローラのアクションは個別のスレッド内で実行されるが、ステートは親スレッドで共有される。この新しいコンフィグによって、アプリケーションで特定のステートキーを共有しないよう除外できるようになる。

これは、ストリーミングスレッドで独自のデータベースコネクションコンテキストを利用する必要があるconnected_toブロック内でストリーミングを行う場合に有用。

# config/application.rb
config.action_controller.live_streaming_excluded_keys = [:active_record_connected_to_stack]

デフォルトではすべてのキーが共有される。

Eileen M. Uchitelle
同Changelogより

🔗 Include HTTP_FORWARDED header in IpSpoofAttackError message if available by zzak · Pull Request #56256 · rails/rails

IpSpoofAttackErrorメッセージにForwardedヘッダーの内容が含まれていなかったのを修正。

この情報がないとエラーメッセージが誤解を招く可能性がある。

zzak
同Changelogより

🔗 Active Job

🔗 ActiveJob.perform_all_later should respect job_class.enqueue_after_transaction_commit by byroot · Pull Request #56264 · rails/rails

ActiveJob.perform_all_laterjob_class.enqueue_after_transaction_commitが反映されていなかったのを修正。

修正前は、enqueue_after_transaction_commit = trueを指定してもperform_all_laterですべてのジョブが即座にエンキューされていた。修正後は、この設定を行えばトランザクションがコミットされるまでジョブが適切に先延ばしされるようになり、perform_laterの振る舞いとも整合するようになった。

OuYangJinTing
同Changelogより

🔗 Fix Arguments#serialize missing custom serializers by skipkayhil · Pull Request #56093 · rails/rails

Rails 8.1でActiveJob::Baseが読み込まれていない状態だとActiveJob::Arguments.serializeを実行できなかったのを修正。

Hartley McGuire
同Changelogより

🔗 Active Storage

🔗 Memoize IAM client and set authorization to ADC for GCS Active Storage service by jmalcic · Pull Request #56012 · rails/rails

GCS(Google Cloud Storage)でURLをIAM(Identity and Access Management)で署名するときにADC(デフォルト認証情報)を利用する振る舞いを復活させた。

従来はIAMでURLに署名する際の自動認証にADCが使われていた。
ADCは現在も使われているが、認証クライアントがメモ化されているため、現在の認証情報が期限切れにならないと新しい認証情報がリクエストされない。ActiveStorage::Service::GCSService#iam_clientで認証を設定することで、他の認証方法を利用可能になった。

ActiveStorage::Blob.service.iam_client.authorization = Google::Auth::ImpersonatedServiceAccountCredentials.new(options)

この方法はActive Storageにのみ適用され、他のGoogle APIクライアントに影響しないため、Google::Apis::RequestOptions.default.authorizationよりも安全。

Justin Malčić
同Changelogより

🔗 Railties

🔗 Skip all system test files on app generation by eileencodes · Pull Request #56272 · rails/rails

Railsアプリ生成時にシステムテストのファイル生成をすべてスキップするよう修正。

Eileen M. Uchitelle
同Changelogより

🔗 Fix db:system:change to correctly update Dockerfile base packages by jcsmithf22 · Pull Request #56102 · rails/rails

db:system:changeでDockerfileのベースパッケージが適切に更新されるよう修正。

Josiah Smith
同Changelogより

🔗 Fix devcontainer volume mount when app name differs from folder name by rafaelfranca · Pull Request #56029 · rails/rails

アプリ名とフォルダ名が異なる場合にdevcontainerのボリュームマウントがフォルダ名ベースになっていたのを修正。

Rafael Mendonça França
同Changelogより

🔗 Fixed bin/rails notes command to work with CSS /**/ comments by davidrhyswhite · Pull Request #56412 · rails/rails

rails notesコマンドがCSSファイル内のコメントも適切に抽出するよう修正。

David White
同Changelogより

🔗 Fix default Dockerfile to properly copy the vendor directory · rails/rails@1bf1f40

デフォルトのDockerfileで、bundle install実行時にvendor/ディレクトリが適切に対象に含まれるよう修正。

Zhong Sheng
同Changelogより


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

関連記事

Rails 8.1.1/8.0.4/7.2.3/7.1.6/7.0.10がリリースされました

速報: Rails 8.1.0がリリースされました

The post Rails 8.1.2がリリースされました first appeared on TechRacho.

]]>
0
hachi8833 <![CDATA[Rails: Tailwind CSSはコンポーネントに向いているCSSか?]]> https://techracho.bpsinc.jp/?p=155617 2026-01-08T01:47:29Z 2026-01-08T08:30:19Z <![CDATA[

皆さん、Tailwind CSS(以後Tailwindと書きます)、使ってますか? 参考: Tailwind CSS - Rapidly build modern websites without ever leaving your HTML. 私はオレオレRailsアプリで数年前にノリ一発でViewComponentとTailwindを導入したはいいものの、Tailwindのコンセプトの理解が不十分だったため、ViewComponentとどう溶け込ませるかで、ああでもないこうでもないと相当悩んでしまいました🫠 個人的には、Tailwindのカラー設計の美しさに惚れ込んだのがきっかけとして大きかったと思います。個 […]

The post Rails: Tailwind CSSはコンポーネントに向いているCSSか? first appeared on TechRacho.

]]>
<![CDATA[

皆さん、Tailwind CSS(以後Tailwindと書きます)、使ってますか?

参考: Tailwind CSS - Rapidly build modern websites without ever leaving your HTML.

tailwindlabs/tailwindcss - GitHub

私はオレオレRailsアプリで数年前にノリ一発でViewComponentとTailwindを導入したはいいものの、Tailwindのコンセプトの理解が不十分だったため、ViewComponentとどう溶け込ませるかで、ああでもないこうでもないと相当悩んでしまいました🫠

個人的には、Tailwindのカラー設計の美しさに惚れ込んだのがきっかけとして大きかったと思います。個人的に#00ffffのシアンという色が大嫌いで、Windowsのデフォルト色として見かけるたびにがっかりしていたのですが、Tailwindのシアンは私の先入観を覆してくれました❤


Colors - Core concepts - Tailwind CSSより

また、Tailwindの命名規則を見ていてどことなくRISC CPUのインストラクションにも似たシンメトリーを感じて、そこが好きになったのだと思います。


flex-basis - Flexbox & Grid - Tailwind CSSより

参考: 命令セット - Wikipedia

🔗 一般的なTailwindの印象と私の印象

  • ⭕ユーティリティクラスが多数定義されていて、自分でクラス名を考えないで済むのがいいよね」
  • ❌style=""属性に書いているのと大差ないんじゃないの?」

前者は私の第一印象でもあります。しかしstyle=""とどう違うのかと言われても長らく答えられない有り様でした。

  • ⭕「ユーティリティクラスの命名規則がシンメトリックかつ一貫していて覚えやすいよね」
  • ❌「また別のCSS記法を覚えないといけないの?」

私も長らく知りませんでしたが、ユーティリティファーストを採用しているCSSシステムはTailwind CSS以外にもあります。

なお、既存のCSSシステムからTailwindに移行するのは変更量が多すぎるので、よほどの理由がなければ既存のものを移行する意味はなさそうです。Tailwindを導入するのであれば、新規プロジェクトのなるべく初期段階がよいと思います。

🔗 Tailwindは何を目指しているのか

Tailwindの公式ドキュメントを読んでも今ひとつピンとこないままだったのですが、RailsでTailwind入りのコンポーネントを作ったり壊したりしながら、ChatGPTなどのAIと質疑応答を繰り返すうちに、やっとTailwindのやりたいことが見えてきました。ReactのコンポーネントでTailwindを使っている人たちからすると今さらかもしれませんが😅

🔗 1: すべてをユーティリティクラス化したのは「詳細度を揃えて制御可能にする」ため

詳細度(specificity)は、CSSの複雑さとつらみをもたらすものとして有名かと思います。

参考: 詳細度 - CSS | MDN

【社内勉強会】特濃!CSS講座 #2: セレクタ、カスケード、継承をがっつり理解する

詳しくは上の記事に譲りますが、詳細度(specificity)の算出は複雑で、セレクタやカスケードなど周りのさまざまな文脈ですぐ変わるので、それに足を取られてCSSが思うように効かないという話はいくらでもあります。

そして!importantという怨霊退散の御札を貼って強引に言うことを聞かせようとすると、今度はたちまち!importantだらけになってしまったりします。誰もが通る道かもしれません。

Tailwindがすべてをユーティリティクラス化したのは、単にシンメトリックなクラス名にリネームするだけではなく、それによってユーティリティクラスの詳細度を揃える、そして詳細度への影響をできるだけ小さくすることが重要だと理解しています。

🔗 2: @themeでデザイントークンを設定し、3つの@layerで優先順位を制御する

そのうえで、Tailwindには以下のようなしくみがあります。

@theme変数はTailwind固有の拡張です。@themeは優先順位の階層には含まれず、Tailwindにデフォルトの設定(フォントやカラーパレット、ブレークポイントなど)を上書きしたり、カラーパレットに色を追加したりできます。

  • @theme {}
    カラーパレット、基本フォント、ブレークポイントなどのデザインシステム(デザイントークン)はここに配置する

そのうえで、Tailwindは以下の3つの階層を使っています。

  • @layer base {} -- 最も優先順位が低い
    h1aなどの基本的なHTML要素のスタイルはここに配置する
  • @layer components {}
    コンポーネント固有のスタイルはここに配置する
  • @layer utilities {} -- 最も優先順位が高い
/* app/assets/tailwind/application.css */
@import "tailwindcss";

@theme {

}
@layer base {

}
@layer components {

}
@layer utilities {

}

@layerはTailwindではなくCSS自体の記法です。
Tailwindでは、生成されるCSSが「base」->「components」->「utilities」の順になるように構成されていて、結果としてその順で上書き関係が整理されます。

もちろん、各@layer{ }の中に書いたスタイルは、CSSの特性に従ってファイルの末尾(下)に近いものほど優先されます。
「同一レイヤ内では後勝ち」「レイヤが違う場合はレイヤ順」と覚えておくとよいでしょう。

そして、Tailwindのユーティリティクラスのビルド結果は、常に優先度が最も高い@layer utilities {}に配置されます(つまりTailwindのユーティリティクラスは、まさに@layer utilities {}に配置されていることにやっと自分も気づきました😅)。

/* app/assets/tailwind/application.css */
@import "tailwindcss";

@theme {
  --font-sans: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  --color-primary-50: #f0f9ff;
}
@layer base {
  h1, h2, h3, h4 { ... };
}
@layer components {
  .nav { ... };
}
@layer utilities {
  /* HTMLに書いたユーティリティクラスは全部ここに出力されるのと同じ */
}

階層化がこのように設計されていることで、baseに書いたものは常にcomponentsutilitiesで上書き可能になり、componentsに書いたものは常にutilitiesで上書き可能になります。

これによって、

  • !importantを使わずに済むようになる
    (ただしTailwindにも!をサフィックスする記法が一応ありますが使ったことはありません)
  • 同一要素のclass=" "内の並び順は基本的に結果に影響しなくなる(同じ詳細度のルール同士なら生成CSS側の順序で決まる)
    一方、親要素の指定が子に効く設計(例: text-slate-*などの色やfont-*の継承)は当然あります。

つまり、「class=" "に書いたものが確実に効く」世界を目指しているということです。

Tailwindはユーティリティクラス中心なので、CSSのidをJavaScriptのフックとして使いやすいのもありがたい点です。

逆に、h2 li aセレクタのように文書構造に依存する従来の書き方は、Tailwindの考え方にはそぐわないものです。Tailwindでは、効かせたい場所にclass=" "でユーティリティクラスを書くという考え方です。

🔗 参考: style属性に書いても効かないCSSがある

HTMLのstyle=""属性によるインラインスタイルは強力ですが、疑似要素や疑似クラスなどを直接書けないという制約があります。また、レスポンシブやダークモードのような「条件付き」の記述もHTMLで扱いにくくなります。

すべてではありませんが、Tailwindのユーティリティクラスでは、style属性では書けない疑似クラスやメディアクエリなども書けます。

<!-- 擬似クラスの例 -->
<button class="hover:bg-red-500 focus:ring-2">
<!-- メディアクエリの例 -->
<div class="sm:hidden md:block">
<!-- ダークモードの例 -->
<div class="dark:text-white">
<!-- アニメーションの例 -->
<div class="transition hover:scale-105 duration-200">

Tailwindでも書けないような複雑なスタイルももちろんありますが、それこそ@layer base {}@layer components {}に通常のCSSとして書けばよいということです。

詳しくは今後の記事に譲りますが、Tailwindではユーティリティクラスばかり使うのではなく、@themeや3つの@layerもうまく使い分けることが大事だとわかってきました。


まとめ: Tailwindは、CSSのカスケードや詳細度のトラブルを減らして、コンポーネントの表示上の境界を定めるのに役立ちます。


🔗 Tailwindとコンポーネントの相性

ここまで書いてみて、ReactなどのコンポーネントでTailwindを採用しているところが目立つ理由が、これで腑に落ちました。

RailsのViewComponentでも、Tailwindのスタイルをコンポーネントに閉じ込めることで、コンポーネント内で確実にスタイルが効くようになり、baseやcomponentsのスタイルを確実に上書きできるのは、コンポーネントの独立性を固めるうえでたしかに嬉しいポイントだと思います。

参考: ViewComponent

viewcomponent/view_component - GitHub

Lookbookでコンポーネントをプレビューするときも、周囲の親要素から思わぬ影響を受けるリスクの少ないTailwindはありがたいものだと思います。特にWebデザイナーとコラボするときのメリットは大きいでしょう。

参考: Lookbook Demo

lookbook-hq/lookbook - GitHub

その代わりと言っていいのかどうかわかりませんが、Bootstrapなどのような既成の無料スタイルをダウンロードして使ったり、有料のスタイルを購入するというのはTailwindだと少々やりにくそうに思えました。

参考: Bootstrap · 世界で最も利用されているHTML、CSS、JSのライブラリです。

個人の感想ですが、Tailwindは既製品のスタイルを適当に当てはめて作るよりも、Webデザイナーと共同で作り込んでいくのに向いていそうです。Webデザイナーにも感想を聞いてみたいところです。何となくですが、Tailwindの発想はデザイナー寄りな気がしています。

以下の記事で「Tailwindはデザインシステムを確立しておかないと失敗しやすい」と指摘していますが、実はよく考えれば、TailwindでなくたってWebシステムはデザインシステムを確立しておく方がよいはずです。Tailwindは、デザインシステムの必要性に直面させる効果があるとも言えそうです。

Tailwind CSSをカオスにしないための5つのベストプラクティス(翻訳)

フロントエンド開発者がデザインに向き合うための厳選ベストプラクティス7選(翻訳)

もちろんBootstrapでもScssでもPostCSSでも、既存のCSSシステムに慣れていて知見が積まれているのであれば、全然その方がよいと思います(チームの同意がなければTailwindを導入しようもありませんし)。

🔗 最後に: Tailwind v4のbreaking changes

以下の記事にも書きましたが、Tailwindはv4で大きな変更がありました。

Tailwindcss 4.0の変更点がtailwindcss-railsに与える影響

  • JavaScriptへの依存を減らし、高速化した
  • tailwind.config.jsをデフォルトで使わなくなり、基本的に素のapplication.cssでできるだけ普通のCSSの機能を使うようになった
  • ユーティリティクラスの一部がリネームされた

変更の主な目的は「ゼロコンフィグを目指す」ことにあるようです。

しかしv3からv4の変化は結構大きいので、v3に乗っていた人たちは大変だったと思います。

素のapplication.cssになったために、いわゆるGlob記法(../*)で複数ファイルをパス指定できなくなりましたし、プラグインもnodeなどで外から導入するしかなくなったようです(個人的にはTailwindにプラグインが必要とはあまり思えないのですが)。

ともあれ、今後はこのような大きな変更はないと思いたいです🙏🙏
そういえばViewComponentも、v4で長期サポートに入り、機能はひととおり完成したと宣言しています↓。

As of version 4, ViewComponent is in Long-Term Support and considered feature-complete.
ViewComponentより

自分の中では、Railsでコンポーネントするための機がやっと熟したということにしています。
コンポーネントがどうか盛り上がりますように⛩
YouTube動画も貼っておきます↓

関連記事

tailwindcss-rails README(翻訳)

Tailwind CSS: 開発者が知っておきたいデリケートなUIデザインの便利技11種(翻訳)

The post Rails: Tailwind CSSはコンポーネントに向いているCSSか? first appeared on TechRacho.

]]>
0
hachi8833 <![CDATA[ChatGPTのしくみとAI理論の根源に迫る:(14/16)意味空間と「意味論的な」運動法則(翻訳)]]> https://techracho.bpsinc.jp/?p=153804 2026-01-13T01:18:29Z 2026-01-07T08:30:21Z <![CDATA[

概要 原文サイトのCreative Commons BY-NC-SA 4.0を継承する形で翻訳・公開いたします。 英語記事: What Is ChatGPT Doing … and Why Does It Work?—Stephen Wolfram Writings 原文公開日: 2023/02/14 原著者: Stephen WolframMathematicaやWolframAlphaの開発者として知られる計算科学者・理論物理学者です 日本語タイトルは内容に即したものにしました。原文が長大なので、章ごとに16分割して公開します。 スタイルについては、かっこ書きを注釈にする、図をblockquoteにするなどフ […]

The post ChatGPTのしくみとAI理論の根源に迫る:(14/16)意味空間と「意味論的な」運動法則(翻訳) first appeared on TechRacho.

]]>
<![CDATA[

概要

原文サイトのCreative Commons BY-NC-SA 4.0を継承する形で翻訳・公開いたします。

日本語タイトルは内容に即したものにしました。原文が長大なので、章ごとに16分割して公開します。
スタイルについては、かっこ書きを注釈にする、図をblockquoteにするなどフォーマットを適宜改善し、文面に適宜強調も加えています。

元記事は、2023年2月の公開時点における、ChatGPTを題材とした生成AIの基本概念について解説したものです。実際の商用AIでは有害コンテンツのフィルタなどさまざまな制御も加えられているため、そうした商用の生成AIが確率をベースとしつつ、確率以外の制御も加わっていることを知っておいてください。

本記事の原文を開いて、そこに掲載されている図版をクリックすると、自分のコンピュータでもすぐに実行して試せるWolfram言語コードが自動的にクリップボードにコピーされるようになっています。

コモンズ証 - 表示 - 非営利 - 継承 4.0 国際 - Creative Commons

以下の解説記事は、本章の内容に関連する新しい成果のひとつです。

参考: ‘Analogies Explained’ … Explained | Carl Allen: Homepage

前回: (13/16)ChatGPTは実際にはどう動いているのか

🔗 ChatGPTのしくみとAI理論の根源に迫る:(14/16)意味空間と「意味論的な」運動法則(翻訳)

これまでの章で、ChatGPTの内部では、あらゆるテキストが実質的に数値の配列で表現されていることと、それらは「言語特徴空間(linguistic feature space)」における点の座標のようなものとみなせることを説明してきました。つまり、ChatGPTがテキストの続きを生成するということは、この言語特徴空間の中で描かれる軌跡(tragectory)をたどることに相当するわけです。

しかしここで、「私たちにとって意味のあるテキストが、その軌跡と一致するのはなぜなのか?」という疑問が浮かび上がります。そして、言語特徴空間内の点が「意味性」を維持しながら移動する方法を定義(少なくとも制約)する、いわば「運動法則の意味論版」的な法則が存在する可能性はあるのでしょうか?

では、この言語特徴空間とやらは一体何なのでしょうか?以下に、そのような特徴空間を2次元に投影した場合に、複合語でない単独の語(ここでは一般名詞)がどのように配置されるかを図示します。

第9章では動物や植物を表す語で同じような二次元投影図をお見せしましたが、どちらの場合も「意味的に近い語」同士が互いに近い場所に配置されていることが重要です。

別の例として、さまざまな品詞(part of speech)に対応する語がどのように配置されるかを以下に図示します。


noun(名詞)、verb(動詞)、adjective(形容詞)、adverb(副詞)、pronoun(代名詞)

もちろん、1個の語が「意味を1つしか持たない」(ひいては1種類の品詞にしか対応しない)などということは、一般には起きません。そして、ある語を含む文が特徴空間内でどのように配置されるかを観察してみると、さまざまな意味が「あぶり出される」ことがよくあります。

ここでは、crane(鳥類のツルと、工作機械のクレーン)という語の例を見てみましょう。


図の上左「...200年以上に渡ってクロヅル(common crane)が非常に重要な役割を...」「...希少なカナダヅル(sandhill crane、学名Antigone canadensis)キューバサンドヒル...」「…Xeno-canto におけるクロヅルの音声記録の地図…」「...コーネル大学 eBird でハゴロモヅル(Blue crane)の種を探す...」
図の上右「2016年にウェールズで野生のツルが生まれ…」
図の下「…クレーンが魔法の石を運んでいるという話は事実ではなく…」「そこでのクレーンは…として描写されている」「映画『真昼の決闘(High Noon)』には有名なクレーン撮影があった」「…ノアの方舟を建設するのにクレーンが必要だと神が…」「プラハでは二重車輪式の踏み車クレーンが使われている…」「キャリーデッキクレーンは小型の四輪…」「そのクレーンはパナマに売却された…」「...クレーンで炉に注がれた高温の金属はその後圧延され…」「...イギリスで唯一の二重車輪式踏み車クレーン...」「...事故復旧用のSO-80型鉄道クレーンが保存されている…」

さて、この特徴空間では「意味の近い語」がこの空間上で近い位置に配置されていると考えることについては、少なくとも妥当でしょう。
しかし、この空間に他の構造を見出すことは可能でしょうか?つまり、たとえば数学で言う空間の「平坦性(flatness)」を反映した「平行移動(parallel transport)」のような概念はあるでしょうか?これを理解する方法の1つは、類推(analogy)を観察することです。

はい、2次元に投影した場合でも、少なくとも「平坦性」を匂わせるものはよく見つかります。しかしそれが普遍的であるとは限りません。

では軌跡についてはどうでしょうか?ChatGPTのプロンプトが特徴空間でどのような軌跡をたどるかを観察してみると、ChatGPTは以下のように文を継続している様子がわかります。

この軌跡には、「幾何学的に明らかな」運動法則と呼べそうなものは残念ながら見当たりません。しかし、私が別記事で述べたように、これが相当複雑な話になることは十分予測できていたことなので、法則が見当たらないのも無理はありません。
それに、仮に「意味論上の運動法則」が見つかったとしても、それをどんな種類の埋め込みで(具体的にはどんな「変数」で)表現するのが最も自然であるかについては、到底見当を付けられそうにありません。

上の図では、「軌跡」をいくつかのステップで表示していますが、ここではどのステップでもChatGPTが最も可能性が高いと判断した語だけを選択しています(つまり温度ゼロの状態)。

ただし、最も可能性の高い語ばかりを選ぶ代わりに、以下のように特定のステップで「次にどの語が来る可能性があるか」をその都度尋ねることも可能です。

これを観察すると、次に来る可能性の高いいくつかの語が、まるで「細長い扇形」のように特徴空間内でほぼ同じ方向に向かっていることがわかります。その調子で続きの語を追いかけてみると、行く先々で以下のような「扇形」が出現します。

以下は、合計40ステップを3次元空間で表示したものです。

これでは何のことやらわかりません。これをいくら見つめていても、「ChatGPTの内部で行われていること」を経験的に研究すれば「数学や物理学のように確かな」「意味論上の運動法則」を特定できることは期待できそうにありません。

しかしおそらく、私たちは「間違った変数」(さもなければ間違った座標系)を観察しているだけなのでしょう。正しい変数ないしは正しい座標系で観察すれば、たとえば「ChatGPTはこれこれこういう測地線をたどっている」といった「数学や物理学のように確かな」法則を見出せるかもしれません。

しかし現時点の私たちには、ChatGPTが「人間の言語の組み立て方」についてどんなことを発見したのかを、「ChatGPT内部の振る舞い」から「経験的に読み解く」ための準備が不足しています。

次回: (15/16)意味文法と「計算言語」のパワー

関連記事

ChatGPTのしくみとAI理論の根源に迫る:(1/16)実は語を1個ずつ後ろに追加しているだけ(翻訳)

ChatGPTのしくみとAI理論の根源に迫る:(2/16)その確率は「どこから」来たのか?(翻訳)

ChatGPTのしくみとAI理論の根源に迫る:(3/16)AIの「モデル」とは何か(翻訳)

The post ChatGPTのしくみとAI理論の根源に迫る:(14/16)意味空間と「意味論的な」運動法則(翻訳) first appeared on TechRacho.

]]>
0
hachi8833 <![CDATA[Rails: パーシャルよりもViewComponentを選ぶべき理由(翻訳)]]> https://techracho.bpsinc.jp/?p=155344 2026-01-08T00:55:58Z 2026-01-06T08:30:20Z <![CDATA[

概要 元サイトの許諾を得て翻訳・公開いたします。 英語記事: Why choose ViewComponent over Rails partials | Rails Designer 原文更新: 2024/11/18 原著者: Rails Designer -- Railsフロントエンド関連記事に加えて、ViewComponentとTailwind CSSを用いた美しいUIコンポーネントなどさまざまな製品を販売しています 日本語タイトルは内容に即したものにしました。 Rails: パーシャルよりもViewComponentを選ぶべき理由(翻訳) Reactにインスパイアされて生まれたViewComponentが […]

The post Rails: パーシャルよりもViewComponentを選ぶべき理由(翻訳) first appeared on TechRacho.

]]>
<![CDATA[

概要

元サイトの許諾を得て翻訳・公開いたします。

日本語タイトルは内容に即したものにしました。

Rails: パーシャルよりもViewComponentを選ぶべき理由(翻訳)

viewcomponent/view_component - GitHub

Reactにインスパイアされて生まれたViewComponentが紹介されたのは、RailsConf 2019でした。ViewComponentはデータフローを改善してビューをテストしやすくし、ビューのコードを美しく整えられます。

類似のソリューションはViewComponentの前後にもいくつか登場していましたが、その中から私はGitHubの支援を受けたViewComponentを選びました(私のRails Designerでもトップチョイスになっているほどです)。

ViewComponentを導入するうえで気がかりな点はあるでしょうか?Railsにはパーシャルもヘルパーも揃っているのに、わざわざ依存関係を増やす必要はあるのでしょうか?

では最初にViewComponentの長所と短所をリストアップしておきましょう。

長所

  • コード編成が改善される
  • パフォーマンスが向上する
  • 拡張やコンポジションがはかどる

短所

  • 依存関係が増える
  • 「やりすぎ」のワナに陥りがち
  • 学習コストが増える

🔗 パーシャルやヘルパーからViewComponentに乗り換える理由

パーシャルやヘルパーは、Railsアプリの中核となる機能であり、Rails開発者が長年使いこなして慣れ親しんでいます。

ここで申し上げておきたいのですが、パーシャルやヘルパーを使うこと自体は一向に構わないと思うのです。
実際、私はパーシャルをデフォルトで使っていますし、ViewComponentを使うのは必要なデータ処理が複雑になってきたときだけに限っています(Railsの規約に沿うならばヘルパーを使うのが普通ですし、さもなければより高度なDecoratorパターンやPresenterパターンを使うでしょう)。

Railsのビューヘルパーの最大の問題は、グローバルであることです。たとえばUserHelperでnameメソッドを定義すると、あらゆるビューやパーシャルから呼び出し可能になってしまいます。都合よくuserオブジェクトだけで呼び出し可能になってくれたりはしません1。ヘルパーメソッドの命名を工夫すればある程度しのげるかもしれませんが、それでも理想からはほど遠いしろものです。

もちろん私だってRailsのヘルパーは使っていますよ!以下のようにアプリ全体でグローバルに使えるメソッドなら、それに適したヘルパーに配置しています。

  • component "global_hotkeys"
    render GlobalHotKeysComponent.new呼び出しを置き換えるヘルパー)
  • stream_notification "Saved"
    turbo_stream.replace "notification" { NotificationComponent.new(message: "Saved") }を置き換えるヘルパー)
  • その他の日付/時刻のフォーマット用ヘルパー(custom_format(user.created_at)など)

詳しくは以下の記事をどうぞ。

参考: From Partials (and Helpers) to Embracing ViewComponent in Rails | Rails Designer

🔗 ViewComponentはパフォーマンスを改善する

ViewComponentは、パーシャルと比べて著しく高速です。主な理由は、ViewComponentのテンプレートはパーシャルのような実行時ではなく、アプリケーションの起動時にすべてプリコンパイルされるからです。ERBが多用されていればいるほど、速度が目に見えて改善されます。

ViewComponentのドキュメントによると、現実のユースケースでは最大でパーシャルの10倍2高速です。
また、コンポーネントのテストも高速です(Railsのビューのテストが伝統的に遅いのかもしれませんが)。

ただし、小〜中規模のRailsアプリでは実際にはそこまで速くならないことはここで触れておく必要があります。

🔗 ViewComponentのしくみ

コンポーネントは通常app/components/ディレクトリに配置され、以下のようなコードになります(ViewComponentドキュメントより抜粋)。

class MessageComponent < ViewComponent::Base
  erb_template <<-ERB
    <h1>Hello, !</h1>
  ERB

  def initialize(name:)
    @name = name
  end
end

このコンポーネントを以下のようにERBでインスタンス化して使います。

<⁠%= render(MessageComponent.new(name: "World"))

テストコードは以下のような感じになります。

require "test_helper"

class MessageComponentTest < ViewComponent::TestCase
  def test_render_component
    render_inline(ExampleComponent.new(name: "Hello, World!"))

    assert_text("Hello, World!")
  end
end

🔗 高度なUIコンポーネント

ViewComponentそのものは単なるRubyオブジェクトなので、拡張(継承)やコンポジションで機能を追加できます。ビューのロジックを純粋なRubyで書けるので、再利用しやすいDRYなコードにできます。

ViewComponentには、「スロット」「コレクション」「条件付きレンダリング」などの機能もあります。

🔗 1: スロット

最近の私はすっかりスロットを多用するようになりました。一度使い所がわかってしまえば、いくらでも使い場所が見つかるはずです(私のRails Designerで販売しているコンポーネントをご覧いただければおわかりいただけるでしょう)。

コンポーネントの例:

  • PageHeadingComponent: "Create"や"View"などのページアクションをオプションで指定可能
  • ModalComponent: オプションで見出し要素を指定可能

ViewComponentではrenders_onerenders_manyという2つのフレーバーを利用できます。以下のサンプルコードをご覧ください。

# blog_component.rb
class BlogComponent < ViewComponent::Base
  renders_one :⁠header
  renders_many :⁠posts
end
<%# blog_component.html.erb %>
<h1><%= header %></h1>

<% posts.each do |post| %>
  <%= post %>
<% end %>
<%= render BlogComponent.new do |component| %>
  <% component.with_header do %>
    <%= link_to "My blog", root_path %>
  <% end %>

  <% BlogPost.all.each do |blog_post| %>
    <% component.with_post do %>
      <%= link_to blog_post.name, blog_post.url %>
    <% end %>
  <% end %>
<% end %>

これは本当にシンプルな例なので、詳しくはViewComponentのスロットのドキュメントを参照してください。

🔗 2: コレクション

Railsのパーシャルでコレクションをレンダリングできるのと同様に、ViewComponentでもコレクションをサポートしています。以下のサンプルをご覧ください。

<%= render(ProductComponent.with_collection(@products)) %>
class ProductComponent < ViewComponent::Base
  def initialize(product:)
    @product = product
  end
end

私はコレクションを使いすぎないようにする傾向があります。
上のProductComponentクラスに、以下のテンプレートを適用した場合を考えてみましょう。

<li>
  <%= @product.name %>
</li>

これではHTMLとして有効にならないうえに、コンポーネントを<ul>で囲むことを忘れないように気をつけなければならなくなります。重要なCSSクラスが脱落する可能性もあります。この書き方はよくありません。

私は、コンポーネントのRubyコードの内部でループを手動で回す方法が、コードを整理するうえで好ましいと思います。

🔗 3: 条件付きレンダリング

条件付きレンダリングは多用しています。
以下のようにERBの中でパーシャルを条件文で囲むのではなく、

<% unless Current.user.subscribed? %>
  <%= render partial: "subscribe_form", locals: { user: Current.user} %>
<% end %>

以下のようにコンポーネントをインスタンス化します。

<%= render SubscribeFormComponent.new(user: Current.user) %>

コンポーネントのRubyクラスには以下のようにrender?メソッドも追加します。

class SubscribeFormComponent < ViewComponent::Base
  # ...
  def render?
    !Current.user.subscribed?
  end
  #...
end

そしてこのrender?の条件に基づいて、コンポーネントをレンダリングするかどうかを決めるようにします。こうすることで、ビューのERBにロジックを書かずに済むので、ビューがかなりすっきりしますよね?

以上は、私がパーシャルの代わりにViewComponentを使うようになったことで得られたメリットの一部です。
当然ながら、ViewComponentを使うならパーシャルは使わないといった2つに1つみたいな話ではまったくありません。私のアプリでは今もパーシャルが使われていますが、パーシャルはシンプルにすべきであり、ロジックは不要なはずです。そしてそうでない場合にこそ、ViewComponentのコンポーネントに移行するようにしています。

関連記事

Railsのコンポーネントを「gemなしで」シンプルに構築する(翻訳)


  1. 訳注: config.action_controller.include_all_helpersfalseに設定すると、ヘルパーの呼び出しは対応するコントローラのスコープ内に限定されます。 
  2. 訳注: ViewComponent 4.1.1ドキュメントでは最大で2.5倍と改められています。 

The post Rails: パーシャルよりもViewComponentを選ぶべき理由(翻訳) first appeared on TechRacho.

]]>
0
hachi8833 <![CDATA[Rails: 最新のブラウザ機能で「JavaScriptなし」のスタイル付き確認ダイアログを構築する(翻訳)]]> https://techracho.bpsinc.jp/?p=155712 2025-12-26T01:54:16Z 2026-01-05T08:30:15Z <![CDATA[

概要 元サイトの許諾を得て翻訳・公開いたします。 英語記事: Beautiful Rails confirmation dialogs (with zero JavaScript) | Boring Rails: Skip the bullshit and ship fast 原文公開日: 2025/12/15 原著者: Matt Swanson サイト: Boring Rails 日本語タイトルは内容に即したものにしました。 以下は本記事を元にしたスタイル・アニメーション付き確認ダイアログです。ダイアログの外側をクリックしてもEscキーを押してもダイアログが閉じます。これらの機能はすべてJavaScriptなし […]

The post Rails: 最新のブラウザ機能で「JavaScriptなし」のスタイル付き確認ダイアログを構築する(翻訳) first appeared on TechRacho.

]]>
<![CDATA[

概要

元サイトの許諾を得て翻訳・公開いたします。

日本語タイトルは内容に即したものにしました。

以下は本記事を元にしたスタイル・アニメーション付き確認ダイアログです。ダイアログの外側をクリックしてもEscキーを押してもダイアログが閉じます。これらの機能はすべてJavaScriptなしで実現できています。

Rails: 最新のブラウザ機能で「JavaScriptなし」のスタイル付き確認ダイアログを構築する(翻訳)

原注

これはStephen Margheimとのコラボ記事です。StephenはHigh Leverage Railsの作者であり、同サイトではシンプルなSQLite・HTML・CSSのパワーを駆使して高品質なRailsアプリケーションを構築する方法を動画で学べます。

Turboのdata-turbo-confirm属性は、Webアプリで確認ダイアログをさくっと作るのに便利ですが、この属性がトリガーするブラウザネイティブのconfirm()のダイアログは、さすがに古臭くて浮いている印象があります。

アプリの最新デザインにふさわしい確認ダイアログを表示しようとする場合、従来推奨されてきたのは以下のようにJavaScriptをどっさり使うアプローチばかりで、ダイアログの表示・非表示切り替えや、イベントリスナーによるキーボード入力の処理、トリガーやモーダルダイアログの振る舞いの細かな調整などをStimulusコントローラであれこれ制御するのが通例でした。

しかし最近のブラウザのアップデートで状況が激変しました。Chrome 131やSafari 18.4で導入されたInvoker(呼び出し)コマンドcommand="コマンド名")のおかげで、ダイアログの制御を宣言的に書けるようになったのです。

参考: 呼び出しコマンド API - Web API | MDN

そこに@starting-styleのアニメーションを組み合わせれば、JavaScript一切不要のアニメーション付きの美麗な確認ダイアログを構築できます。

機能 実現方法
ダイアログを表示する ボタンにcommand="show-modal"を書くだけ
ダイアログを閉じる キャンセルボタンにcommand="close"を書くだけ
エスケープキーでダイアログを閉じる (ブラウザ組み込みの機能)
ダイアログ外のクリックでダイアログを閉じる closedby="any"属性を指定するだけ
アニメーション開始 CSSの@starting-styleルールで指定するだけ
アニメーション終了 displayトランジションでallow-discreteを指定するだけ

アプリでユーザーが項目を削除しようとしたときに確認ダイアログを表示したいとします。最新のブラウザ機能を活用して確認ダイアログをJavaScriptなしで構築するには、以下のように書きます。

<button type="button" commandfor="delete-item-dialog" command="show-modal">
  Delete this item
</button>

<dialog id="delete-item-dialog" closedby="any" role="alertdialog"
        aria-labelledby="dialog-title" aria-describedby="dialog-desc">
  <header>
    <hgroup>
      <h3 id="dialog-title">Delete this item?</h3>
      <p id="dialog-desc">Are you sure you want to permanently delete this item?</p>
    </hgroup>
  </header>

  <footer>
    <button type="button" commandfor="delete-item-dialog" command="close">
      Cancel
    </button>
    <%= button_to item_path(item), method: :delete do %>
      Delete item
    <% end %>
  </footer>
</dialog>

最新のcommand属性を使うと、実行すべき操作をブラウザに指示できます。操作の対象となる要素はcommandfor属性にidで指定します。

<button type="button" commandfor="delete-item-dialog" command="show-modal">
  Delete this item
</button>

たとえばボタンにcommand="show-modal"という属性を追加しておけば、ユーザーがボタンをクリックしたときに表示対象のダイアログでshowModal()メソッドが呼び出されます。

キャンセルボタンにもcommand="close"属性を追加しておけば、クリックしたときにcloseメソッドが呼び出されます。

    <button type="button" commandfor="delete-item-dialog" command="close">
      Cancel
    </button>

このキャンセルボタンにはtype="submit"ではなく、type="button"が設定されていることにご注意ください。つまり、このキャンセルボタンをクリックしても、フォームが送信される心配はまったくないということです。

showModal()メソッドで表示されたモーダルダイアログは、何も指定しなくてもEscキーを押すだけでブラウザが自動的に閉じてくれます。

さらにclosedby="any"属性も指定しておけば、ダイアログの外側の背景のどこをクリックしても自動的に閉じるという待望の機能が実現できます。

<dialog id="delete-item-dialog" closedby="any" role="alertdialog"
        aria-labelledby="dialog-title" aria-describedby="dialog-desc">

aria-labelledby属性はダイアログの見出し要素のidを、aria-describedby属性はダイアログの説明文要素のidをそれぞれ指定できます。これらで指定した見出しや本文はスクリーンリーダーによって即座に読み上げられるので、ユーザーはその音声を元にダイアログに対応できるようになります。

さらに、ダイアログ要素にrole="alertdialog"属性を指定しておけば、そのダイアログがユーザーが必ず対応しなければならない重要なものであることをブラウザに伝えられます(訳注: ブラウザは基本的にOSのアクセシビリティAPIとしてダイアログを表示し、スクリーンリーダーがそれを受けてダイアログの内容を読み上げます)。

🔗 デモ

このダイアログをさまざまな方法で閉じられることを、以下のデモで試せます(訳注: 原文のデモをCodePenに移植しました)。

以下のどの方法でもダイアログが閉じます。

  • [Cancel]ボタンをクリックする
  • [Delete Item]ボタンをクリックする
  • Escキーを押す
  • ダイアログの外側の領域をクリックする

ここでご注目いただきたいのは、returnValueの値が "confirm"になるのは、[Delete Item]ボタンの場合のみであり、それ以外の場合は決して"confirm"にならないことです。

宣言的なHTMLだけでこれほど充実した機能が使えるようになるので、とても気に入っています。

🔗 @starting-styleでアニメーションも追加できる

確認ダイアログのデザインを洗練させるために、スムーズな表示・非表示のトランジションを追加しましょう。
CSSの@starting-styleアットルール)とallow-discrete(キーワード値)を使えば、CSSだけでダイアログの表示・非表示をアニメーション化できます。

@starting-styleルールは、要素が最初に表示されるときの初期状態を定義します。ダイアログにルールが指定されていない場合は、ブラウザで即座に最終的な状態が表示されます。
ダイアログでたとえば以下のようにルールを指定すると、ブラウザ上でopacity: 0; scale: 0.95からopacity: 1; scale: 1へトランジションが行われます。

  @starting-style {
    opacity: 0;
    scale: 0.95;
  }

ダイアログを閉じるときのアニメーションでは、displayoverlayのトランジションにallow-discreteを指定する必要があります。

  transition:
    opacity 0.2s ease-out,
    scale 0.2s ease-out,
    overlay 0.2s ease-out allow-discrete,
    display 0.2s ease-out allow-discrete;

CSSプロパティは、opacity(透明度)を0.5にしたり、色をブレンドしたりできることからもわかるように、ほとんどの場合、中間値も指定可能です。

しかしCSSのdisplayプロパティは、たとえばnoneblockの中間値が存在しないことからわかるように、離散的なプロパティです。つまり従来のdisplayは、歴史的にアニメーションを指定できなかったのです。

CSSでallow-discreteを指定すると、そうした離散的なプロパティにもトランジションのタイミングを適用可能になります。

ダイアログを閉じるときのアニメーションであれば、要素が表示された状態からアニメーションを実行すると、アニメーション中は要素を表示したままにし、アニメーションが完了したときにはじめてdisplay: none に切り替わります。

これはCSSのoverlayプロパティについても同様です。overlayプロパティのトランジションにease-out allow-discreteを指定すると、アニメーションが終わるまではダイアログをtop layer(最上位レイヤ)に維持するようになります。

dialog {
  opacity: 1;
  scale: 1;

  transition:
    opacity 0.2s ease-out,
    scale 0.2s ease-out,
    overlay 0.2s ease-out allow-discrete,
    display 0.2s ease-out allow-discrete;

  @starting-style {
    opacity: 0;
    scale: 0.95;
  }
}

dialog:not([open]) {
  opacity: 0;
  scale: 0.95;
}

dialog::backdrop {
  background-color: rgb(0 0 0 / 0.5);
  transition:
    background-color 0.2s ease-out,
    overlay 0.2s ease-out allow-discrete,
    display 0.2s ease-out allow-discrete;

  @starting-style {
    background-color: rgb(0 0 0 / 0);
  }
}

dialog:not([open])::backdrop {
  background-color: rgb(0 0 0 / 0);
}

🔗 ブラウザでのサポート状況

機能 Chrome Safari Firefox Can I Use?
command 135以降 26.2以降 144以降 リンク
commandfor 135以降 26.2以降 144以降 リンク
@starting-style 117以降 17.5以降 129以降 リンク
closedby 134以降 未対応 141以降 リンク

Safariでのclosedbyサポートはまだ保留中の状態です。production環境でSafariに対応したい場合は、dialog-closedby-polyfillポリフィルを追加してください。

fractaledmind/dialog-closedby-polyfill - GitHub

古いブラウザでinvokerコマンドのサポートが必要な場合は、それ用のinvokers-polyfillポリフィルも利用できます。

keithamus/invokers-polyfill - GitHub

どちらのポリフィルもサイズが小さく、ネイティブサポートがない場合にのみ実行されます。

🔗 Turboの確認ダイアログに統合する

さて、スタイリング可能なネイティブダイアログを使いながら、Turboのdata-turbo-confirm属性も使いたい場合はどうすればよいでしょうか?

実は、TurboではまさにそのためのTurbo.config.forms.confirmが提供されています。Mikael Henrikssonがこれに関する良記事を書いており、Chris OliverによるGoRails動画もあります。

参考: mhenrixon | Turbo confirm
参考: Custom Turbo Confirm Modals with Hotwire in Rails | GoRails

まず、レイアウトにダイアログ用のテンプレートを追加しておきます。もちろん、アプリで使われているCSSをこのレイアウトに設定して自由にスタイルを設定できます。

<%# app/views/layouts/application.html.erb %>
<dialog id="turbo-confirm-dialog" closedby="any"
        aria-labelledby="turbo-confirm-title" aria-describedby="turbo-confirm-message">
  <header>
    <hgroup>
      <h3 id="turbo-confirm-title">Confirm</h3>
      <p id="turbo-confirm-message"></p>
    </hgroup>
  </header>

  <footer>
    <button type="button" commandfor="turbo-confirm-dialog" command="close">
      Cancel
    </button>
    <form method="dialog">
      <button type="submit" value="confirm">
        Confirm
      </button>
    </form>
  </footer>
</dialog>

[Confirm]ボタンは、フッターの<form method="dialog">フォーム内にあるtype="submit"です。

  <footer>
    ...
    <form method="dialog">
      <button type="submit" value="confirm">
        Confirm
      </button>
    </form>
  </footer>

このフォームが送信されると、ブラウザはダイアログを閉じて、returnValueプロパティにボタンのvalue属性を設定します。この方法によって、どのボタンが押されたかを検出します。そのためにJavaScriptイベントを調整する必要はありません。

次に、Turbo側を設定します。

const dialog = document.getElementById("turbo-confirm-dialog")
const messageElement = document.getElementById("turbo-confirm-message")
const confirmButton = dialog?.querySelector("button[value='confirm']")

Turbo.config.forms.confirm = (message, element, submitter) => {
  // Fall back to native confirm if dialog isn't in the DOM
  if (!dialog) return Promise.resolve(confirm(message))

  messageElement.textContent = message

  // Allow custom button text via data-turbo-confirm-button
  const buttonText = submitter?.dataset.turboConfirmButton || "Confirm"
  confirmButton.textContent = buttonText

  dialog.showModal()

  return new Promise((resolve) => {
    dialog.addEventListener("close", () => {
      resolve(dialog.returnValue === "confirm")
    }, { once: true })
  })
}

上のJavaScriptが行っているのは、以下の3つだけです。

  1. 表示するメッセージテキストを設定する
  2. ボタン名をカスタマイズする(指定されている場合)
  3. ダイアログを表示する

それ以外の「ボタンをクリックしたら閉じる」「Escキーで閉じる」「ダイアログの外側をクリックしたら閉じる」は、すべてプラットフォーム側で処理されます。

ブラウザネイティブのconfirm()にフォールバックするようになっているので、ダイアログの要素が見つからない場合(レイアウトが異なる場合やエラーページの場合など)でもアプリは引き続き動作します。

Turbo.config.forms.confirmで指定する関数は、trueまたはfalse(それぞれ「進む」と「キャンセル」に対応)のいずれかに解決されるPromiseを返すことが期待されます。
この関数は、「message(確認メッセージ)」「elementdata-turbo-confirm属性を持つ要素)」「submitter(送信を行う要素)」の3つの引数を受け取ります。

ハンドラーではcloseイベントをリッスンし、returnValueをチェックします。ハンドラーを1回記述し、このダイアログをレイアウトに1つ追加しておけば、アプリ内のあらゆるdata-turbo-confirmで利用されます。

以下のようにdata-turbo-confirm-button属性を使うと、ボタンに表示するテキストをトリガーごとにカスタマイズできます。

<%= button_to item_path(item),
              method: :delete,
              data: {
                turbo_confirm: "Are you sure you want to delete this item?",
                turbo_confirm_button: "Delete item"
              } do %>
  Delete
<% end %>

こうすることで、お仕着せの汎用[Confirm]ボタンの代わりに、状況にふさわしい[Delete item]ボタンを確認ダイアログで表示できるようになります。UXも改善され、ダイアログで行う操作もユーザーにとって明確になります。

🔗 補足: 背景のスクロールを止める

モーダルダイアログの表示中は、ページをスクロールできないようにするのが一般的な要件です。

body:has(dialog:modal) {
  overflow: hidden;
}

modal擬似クラスは、showModal()で表示したダイアログにマッチするようになっています。
これに:has()疑似クラス関数を上のように組み合わせれば、モーダルダイアログが表示中の場合にのみセレクタがbodyを対象とするようになり、ダイアログを表示中はスクロールが無効になります。ダイアログを閉じれば再びスクロール可能になります。これらはブラウザによって制御されるので、JavaScriptは使いません。

関連記事

Rails: Turbo Frameの読み込みプログレス表示をCSSだけで実現する(翻訳)

Railsの技: TailwindスタイルのCSSトランジションをStimulusJSで行う(翻訳)

The post Rails: 最新のブラウザ機能で「JavaScriptなし」のスタイル付き確認ダイアログを構築する(翻訳) first appeared on TechRacho.

]]>
0
hachi8833 <![CDATA[CSSだけで星を"半押し"可能な10段階レーティング機能を実装する(翻訳)]]> https://techracho.bpsinc.jp/?p=154507 2025-12-26T01:33:02Z 2025-12-26T08:30:49Z <![CDATA[

概要 元サイトの許諾を得て翻訳・公開いたします。 英語記事: CSS-only Star Rating Component with Half Steps | Fractaled Mind 原文公開日: 2025/06/20 原著者: Stephen Margheim 日本語タイトルは内容に即したものにしました。 CSSだけで星を"半押し"可能な10段階レーティング機能を実装する(翻訳) 実験と調査、そしてアホなAIとのお付き合いを経て、やっとのことで「ラジオボタンとラベルだけを使って、星を"半押し"可能な」10段階レーティングコンポーネントをクリーンかつシンプルに実装できました。50行の美しいCSSです。 それ […]

The post CSSだけで星を"半押し"可能な10段階レーティング機能を実装する(翻訳) first appeared on TechRacho.

]]>
<![CDATA[

概要

元サイトの許諾を得て翻訳・公開いたします。

日本語タイトルは内容に即したものにしました。

CSSだけで星を"半押し"可能な10段階レーティング機能を実装する(翻訳)

実験と調査、そしてアホなAIとのお付き合いを経て、やっとのことで「ラジオボタンとラベルだけを使って、星を"半押し"可能な」10段階レーティングコンポーネントをクリーンかつシンプルに実装できました。50行の美しいCSSです。
それでは順に見ていきましょう。


コードを解説する前に、他の人たちはこの問題にどうやってCSSだけで取り組んできたのかを調べてみたところ、以下の2つが良さげでした。

参考: A star rating widget in CSS

どちらもシンプルなradioボタンとラベルで構成されていて、私の用件を満たすうえで不可欠でしたが、惜しいことにどちらも一長一短でした。
1つ目のほうは星の"半押し"に対応しておらず、2つ目のCodePenの方はFontAwesomeのフォントに依存しています。
私はシンプルなSVG背景画像とラジオボタンで実装したかったので、これらのソリューションを昇華した上で、独自の実装を試してみました。

ユーザーが星で評価をつけている様子

重要な詳細事項がいくつかありますので、1つずつ見ていくことにしましょう。

詳細その1はHTMLに関連します。
ここでは素のCSSだけを使いたいので、利用できるCSS機能に若干の制約が生じます。マウスをホバーした要素の「直後」にある要素を選択するのであれば、~による後続の兄弟セレクタが使えます。
しかし、星によるレーティングコンポーネントでは、現在マウスをホバーしている星が点灯している場合に次はどの星を点灯するかを示すために、マウスをホバーした要素の(次ではなく)「直前」の要素を強調表示する必要があります。

この振る舞いを実現するために、HTML構造で星を表す10個のラジオボタンを、最大の5から最小の0.5まで「逆順」で並べることにしました。

<fieldset class="star-rating">
  <input type="radio" id="rating10" name="rating" value="10" />
  <label for="rating10" title="5 stars" aria-label="5 stars"></label>

  <input type="radio" id="rating9" name="rating" value="9" />
  <label for="rating9" title="4 1/2 stars" aria-label="4 1/2 stars"></label>

  <input type="radio" id="rating8" name="rating" value="8" />
  <label for="rating8" title="4 stars" aria-label="4 stars"></label>

  <input type="radio" id="rating7" name="rating" value="7" />
  <label for="rating7" title="3 1/2 stars" aria-label="3 1/2 stars"></label>

  <input type="radio" id="rating6" name="rating" value="6" />
  <label for="rating6" title="3 stars" aria-label="3 stars"></label>

  <input type="radio" id="rating5" name="rating" value="5" />
  <label for="rating5" title="2 1/2 stars" aria-label="2 1/2 stars"></label>

  <input type="radio" id="rating4" name="rating" value="4" />
  <label for="rating4" title="2 stars" aria-label="2 stars"></label>

  <input type="radio" id="rating3" name="rating" value="3" />
  <label for="rating3" title="1 1/2 stars" aria-label="1 1/2 stars"></label>

  <input type="radio" id="rating2" name="rating" value="2" />
  <label for="rating2" title="1 star" aria-label="1 star"></label>

  <input type="radio" id="rating1" name="rating" value="1" />
  <label for="rating1" title="1/2 star" aria-label="1/2 star"></label>
</fieldset>

これで、現在点灯している星以下の星をすべて点灯するCSSを以下のように楽に書けます。

.star-rating {
  display: inline-flex;
  flex-direction: row-reverse;
  justify-content: flex-end;
}

たとえば、10個のラジオボタンのうちrating9がオンになっているとすると、DOM上ではそれ以下のすべてのラジオボタン(つまりrating8からrating1まで)の星が金色に点灯します。

しかし、星のラジオボタンを最高点から順に最低点まで順に並べると、コンポーネントで星が金色にレンダリングされる順序が逆になってしまいます。ユーザーは、最初は0.5点(星半分)、次は1点(星1つ)、1.5点(星1.5個)、といった具合に星が点灯することを期待するので、星をDOM上で逆順にレンダリングする必要があります。

ありがたいことに、CSSで提供されているflexレイアウトを使えば、flex-directionで要素を簡単に逆順にできます。

.star-rating {
  display: inline-flex;
  flex-direction: row-reverse;
  justify-content: flex-end;
}

.rateコンテナにflex-direction: row-reverseを指定してFlexコンテナにすると、DOMの順序を変更せずに星を逆順に表示できます。

DOM上の順序をCSSセレクタに合わせて最適化し、UIの表示順を用途に合わせて最適化するというこの手法は、CSSの道具箱にぜひ常備しておきたいツールです。


詳細その2は、星のレンダリング方法です。
星の"半押し"をサポートすると、レーティングコンポーネントは著しく複雑になります。実装をシンプルに保つために、FontAwesomeの半分になった星のアイコンを元に、右半分と左半分だけの星のCSVをそれぞれパディングなしで作成しました。

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 264 512">
  <path d="M264 0c-12.2.1-23.3 7-28.6 18L171 150.3 27.4 171.5c-12 1.8-22 10.2-25.7 21.7-3.7 11.5-.7 24.2 7.9 32.7L113.8 329 89.2 474.7c-2 12 3 24.2 12.9 31.3 9.9 7.1 23 8 33.8 2.3L264 439.8V0Z"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 264 512">
  <path d="M0 0c12.2.1 23.3 7 28.6 18L93 150.3l143.6 21.2c12 1.8 22 10.2 25.7 21.7 3.7 11.5.7 24.2-7.9 32.7L150.2 329l24.6 145.7c2 12-3 24.2-12.9 31.3-9.9 7.1-23 8-33.8 2.3L0 439.8V0Z"/>
</svg>

続いて、2つのSVGをCSSのbackground: urlに取り込んで、ラベルごとに適切な星画像を表示するようにします。

/* 完全な星のステップ: 右半分だけの星 */
label:nth-of-type(odd) {
  background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 264 512"><path d="M0 0c12.2.1 23.3 7 28.6 18L93 150.3l143.6 21.2c12 1.8 22 10.2 25.7 21.7 3.7 11.5.7 24.2-7.9 32.7L150.2 329l24.6 145.7c2 12-3 24.2-12.9 31.3-9.9 7.1-23 8-33.8 2.3L0 439.8V0Z"/></svg>') no-repeat;
}
/* 完全な星のステップ: 左半分だけの星 */
label:nth-of-type(even) {
  background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 264 512"><path d="M264 0c-12.2.1-23.3 7-28.6 18L171 150.3 27.4 171.5c-12 1.8-22 10.2-25.7 21.7-3.7 11.5-.7 24.2 7.9 32.7L113.8 329 89.2 474.7c-2 12 3 24.2 12.9 31.3 9.9 7.1 23 8 33.8 2.3L264 439.8V0Z"/></svg>') no-repeat;
}

labelには星を表示するので、代わりにinputを非表示にします。

input {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border-width: 0;
}

続いて、labelにCSSでスタイルを設定して、星の背景SVG画像が適切にレンダリングされるようにします。

label {
  display: block;
  height: 2rem;
  width: 1rem;
}

このとき、widthheightの「半分」にしておくことが肝心です。

input要素は非表示になっているので(なおスクリーンリーダーではDOMから引き続きアクセスできます)、label要素で半分の星画像をレンダリングすれば、コンポーネントの基礎工事が完成します。

5個の星が横一列に並んでいる様子


詳細その3は、マウスで星をホバーしたら星のセグメントを点灯させることです。

残念ながら、この振る舞いは、CSSのbackgroundプロパティにSVGをurlに埋め込んだ形では実現できません。CSSでは、マウスを星にかざしてもSVG背景画像の色のfillを動的に変更できないのです。

ありがたいことに、こちらの記事のテクニックを応用して、backgroundプロパティの代わりにmaskプロパティを使えば、background-colorの背景が透けるようにできます。

そこで、まずCSSのlabelに以下のようにbackground-color: currentColor;を追加します。

label {
  display: block;
  height: 2rem;
  width: 1rem;
  background-color: currentColor;
}
/* 完全な星のステップ: 右半分だけの星 */
label:nth-of-type(odd) {
  mask: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 264 512"><path d="M0 0c12.2.1 23.3 7 28.6 18L93 150.3l143.6 21.2c12 1.8 22 10.2 25.7 21.7 3.7 11.5.7 24.2-7.9 32.7L150.2 329l24.6 145.7c2 12-3 24.2-12.9 31.3-9.9 7.1-23 8-33.8 2.3L0 439.8V0Z"/></svg>') no-repeat;
}
/* 完全な星のステップ: 左半分だけの星 */
label:nth-of-type(even) {
  mask: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 264 512"><path d="M264 0c-12.2.1-23.3 7-28.6 18L171 150.3 27.4 171.5c-12 1.8-22 10.2-25.7 21.7-3.7 11.5-.7 24.2 7.9 32.7L113.8 329 89.2 474.7c-2 12 3 24.2 12.9 31.3 9.9 7.1 23 8 33.8 2.3L264 439.8V0Z"/></svg>') no-repeat;
}

これで、星にマウスをかざしたときにbackground-colorをシンプルに変更すれば、星のセグメントを点灯できるようになります。

/* チェックされている現在の星とそれ以下の星に色をつける */
input:checked ~ label,

/* マウスオーバーしたときにそれ以下の星に色をつける */
label:hover, label:hover ~ label {
  background-color: goldenrod;
}

同様に、checked状態についても星に適切なスタイルが適用されるようにします。

/* 現在の星とそれ以下の星を点灯させる */
input:checked + label:hover, input:checked ~ label:hover,
/* highlight previous selected stars for new rating */
input:checked ~ label:hover ~ label,
/* 選択したそれ以下の星を点灯させる */
label:hover ~ input:checked ~ label {
  background-color: gold;
}

これで、以下のように星がいい感じにインタラクティブになります。

星によるレーティングコンポーネントをユーザーが操作している様子


詳細その4は、星と星の間隔を少し広げることですが、このときにマウス操作が自然でスムーズになるよう調整します。

最初に思いついたのは、以下のように完全な星のステップ要素にmarginを追加する方法でした。

/* 完全な星のステップ: 右半分だけの星 */
label:nth-of-type(odd) {
  mask: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 264 512"><path d="M0 0c12.2.1 23.3 7 28.6 18L93 150.3l143.6 21.2c12 1.8 22 10.2 25.7 21.7 3.7 11.5.7 24.2-7.9 32.7L150.2 329l24.6 145.7c2 12-3 24.2-12.9 31.3-9.9 7.1-23 8-33.8 2.3L0 439.8V0Z"/></svg>') no-repeat;
  margin-inline-end: 0.25em;
}

しかしやってみると、以下のようにマウスが星と星の「間に」さしかかると、左側の星の明かりが全部消えてしまい、ぎくしゃくしてしまいます。

レーティングコンポーネントがぎくしゃくしてしまった様子

これではユーザーエクスペリエンスが台無しです。
ここでやりたいのは、星と星の間の間隔は広げつつ、マウスが星と星の間にさしかかったときにもマウスが「技術的に」星のセグメント上にあるかのようにすることです。
これは、星のサイズにマージンを加えたものと同じサイズを持つ擬似要素を別途追加することで修正できます。

label:nth-of-type(odd) {
  mask: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 264 512"><path d="M0 0c12.2.1 23.3 7 28.6 18L93 150.3l143.6 21.2c12 1.8 22 10.2 25.7 21.7 3.7 11.5.7 24.2-7.9 32.7L150.2 329l24.6 145.7c2 12-3 24.2-12.9 31.3-9.9 7.1-23 8-33.8 2.3L0 439.8V0Z"/></svg>') no-repeat;
  margin-inline-end: 0.25em;
  &::after {
    content: "";
    display: block;
    height: 2rem;
    width: 1.25rem;
  }
}

ここで大事なのは、:after擬似要素のwidthを、星の幅とmargin-inline-endを足したwithと同じにすることです。ここでは手動で値を設定しましたが、CSSプロパティとcalc()関数でwidthを自動算出する方法も使えます。

どちらの場合も、この:after擬似要素を作成することで、星の要素に属する透明なエリアを使って星と星の間のギャップを埋めることが可能です。これにより、マウスを星にかざしたときにがたつかずに反応するようになり、ユーザーエクスペリエンスもスムーズになります。

今度こそ最後の詳細5です。コンポーネントの右側に残っている最後のマージンを削除します。

label:first-of-type {
  margin-inline-end: 0;
}

思い出していただきたいのですが、ここでfirst-of-typeセレクタを使っている理由は、画面上の一番右の星が実はDOM上では「最初の」星だからです。

これで、star-ratingコンポーネントの幅は5つの星の幅と完全に同じになりました。なお、fieldset要素をラッピング要素として使っている場合は、そのborderも取り除いておきましょう。

fieldset {
  border: none;
}

これでとうとう、素のCSSだけで作った10段階評価のレーティングコンポーネントが完成しました。動作は完全で使いやすく、ユーザー操作に適切に反応してくれます。星と星の間のがたつきも解消されたので、ユーザーエクスペリエンスも快適です。

完成したレーティングコンポーネント

コード全体を見たい方は、以下のPlaygroundをご覧ください。本記事を気にっていただけましたら、ぜひXで@fractaledmindをフォローしてください。

関連記事

CSS: 2025年に押さえておきたい最新CSS機能13選(翻訳)

CSS: z-indexの問題はisolationプロパティで解決できる(翻訳)

2025年のファビコンを極める: 必要なファイルはほぼ3つに減った!(翻訳)

The post CSSだけで星を"半押し"可能な10段階レーティング機能を実装する(翻訳) first appeared on TechRacho.

]]>
0
hachi8833 <![CDATA[M1/M3 MacでRuby 2.4〜4.0をrbenvでビルドする最小限のセットアップを全部調べた]]> https://techracho.bpsinc.jp/?p=128506 2025-12-26T02:15:20Z 2025-12-26T02:14:38Z <![CDATA[

2025/12/26: Ruby 4.0-devをRuby 4.0.0に更新(New!) 2025/11/21: Ruby 4.0-devの場合を臨時で追記 2024/12/26: Ruby 3.3とRuby 3.4、およびSequoia環境について追記、日付を更新 2023/12/15: Ruby 3.3以降でbisonが不要になったことについて追記 2023-07-10: 追記 2024/06/13: 追記 2023/04/14: 追記 追記(2024/12/26) 以下の#20760によると、macOS 15 SequoiaではRuby 3.0.6をビルドできなくなったそうです。 参考: Bug #2076 […]

The post M1/M3 MacでRuby 2.4〜4.0をrbenvでビルドする最小限のセットアップを全部調べた first appeared on TechRacho.

]]>
<![CDATA[
  • 2025/12/26: Ruby 4.0-devをRuby 4.0.0に更新(New!
  • 2025/11/21: Ruby 4.0-devの場合を臨時で追記
  • 2024/12/26: Ruby 3.3とRuby 3.4、およびSequoia環境について追記、日付を更新
  • 2023/12/15: Ruby 3.3以降でbisonが不要になったことについて追記
  • 2023-07-10: 追記
  • 2024/06/13: 追記
  • 2023/04/14: 追記

追記(2024/12/26)

以下の#20760によると、macOS 15 SequoiaではRuby 3.0.6をビルドできなくなったそうです。

参考: Bug #20760: Ruby 3.0.6 fails to build on macOS 15.0 24A335 arm64 - Ruby master - Ruby Issue Tracking System

このことから、macOS 15 Sequoia以降の環境では、何らかのパッチを自力で当てるなどして対応しない限り、Ruby 3.0.xまでのバージョンは本記事の手順をもってしてもビルドできなくなったと考える方がよいと思います
また、Docker HubのRubyにも、Ruby 3.1.xより古い公式イメージは置かれていません。


なお、Ruby 3.0.6については、hsbtさんが上のissueで回避方法の例を示しています。

$ curl -sSL https://github.com/ruby/ruby/commit/1dfe75b0beb7171b8154ff0856d5149be0207724.patch | rbenv install --patch 3.0.6

Ruby 3.0.6は既にサポート終了しているので、上の回避方法の動作は保証できません。また今後こうしたパッチがバックポートされることもありません。

🔗 M1 MacでRuby 2.4〜3.4をrbenvでビルドするための要点

要点から先に書きます。

基本的には、以下の公式ドキュメントの記載通りにすればビルドできます。

参考: Building Ruby -- ruby/building_ruby.md at master · ruby/ruby

ビルドで使うOpenSSLについては以下に注意してください。

  • Ruby 3.1以降: OpenSSL 3系とOpenSSL 1.1.1系の両方が使える
  • Ruby 2.4〜3.0: OpenSSL 1.1.1系が必要

ただし、OpenSSL 1.1.1は2023/09/11にEOLとなり、以後のアップデートには有償サポートが必要です↓。特に業務で古いRubyのビルドが必要な場合はご注意ください。

参考: OpenSSL 1.1.1 End of Life - OpenSSL Blog

Ruby 3.0とOpenSSL 1.1.1の関連については、Rubyコミッターのhsbtさんによる記事がおすすめです↓。

参考: Ruby 3.0 がセキュリティメンテナンスフェーズになったのでいくつか補足 - HsbtDiary(2023-03-31)

OpenSSLの最も細かいバージョンは割と変わりやすいので、本記事では基本的にそれぞれ3系と1.1.1系と表記します。

🔗 ビルドでハマっている人のための最小ビルドオプション

公式ドキュメントの記載通りにすればビルドできると書きましたが、環境のセットアップによってはハマることがあります。

インストールでハマる場合、現在のbashの環境変数セットアップで何かをしくじっている可能性があります。私も.profileでの環境設定が適切でなかったためにハマりました😅

忙しい人向けに、そうした既存のセットアップを一時的に上書きすることで確実にビルドできるビルドオプションを手短にまとめました。

🔗 ざっくりとした前提

詳しい前提については後述します

  • M1/M3 MacBook環境(検証できませんでしたが、Intel Macなどでも使えると思います)
  • bash環境であること
  • Homebrew自身のインストールとHomebrew用の環境変数のセットアップ↓が終わっていること
  • gitrbenvruby-buildがインストールされて利用可能な状態であること
  • ビルドに必要なライブラリやツールが「Homebrewでインストールするもの」に示したとおりにHomebrewでインストールされていること
  • コンパイラはCommand Line Toolsを使いました(他のコンパイラでもよいはずです)
# Homebrew用の環境変数セットアップ
eval "$(/opt/homebrew/bin/brew shellenv)"
export LDFLAGS="-L$HOMEBREW_PREFIX/lib"

なお、HOMEBREW_PREFIXevalで自動的に設定されます。

追記

Command Line Toolsが古くなっているとconfigure: error: C compiler cannot create executablesエラーになる可能性があります。その場合は以下を実行してCommand Line Toolsを再インストールしましょう。

sudo rm -rf /Library/Developer/CommandLineTools
sudo xcode-select --install

🔗 追記(2024/12/26): Ruby 3.3以降をビルドする場合

Ruby 3.3.0からはBisonライブラリに代えてLrama gemが使われるようになったため、Bisonのセットアップは不要になりました。

参考: Ruby 3.3.0 リリース

  • シェルで以下を実行する
# あえてbrew shellenvのevalを現在のbashでのみ再実行する
eval "$(/opt/homebrew/bin/brew shellenv)"
# 現在のbashでのみLDFLAGSを上書きする
export LDFLAGS="-L$HOMEBREW_PREFIX/lib"

# 邪魔になりそうな環境変数を現在のbashでのみクリアする
export CPPFLAGS=""
export optflags=""
export OPENSSL_CFLAGS=""
export RUBY_CONFIGURE_OPTS=""
# 続けて以下のいずれかを実行

# Ruby 3.4の場合の例
rbenv install 3.4.1
# Ruby 3.3の場合の例
rbenv install 3.3.6

ビルドが完了したら、シェルを終了して再起動します。

🔗 A: Ruby 2.7〜3.0をビルドする場合

  • シェルで以下を実行する
# あえてbrew shellenvのevalを現在のbashでのみ再実行する
eval "$(/opt/homebrew/bin/brew shellenv)"
# 現在のbashでのみbisonパスを設定する
export PATH="$HOMEBREW_PREFIX/opt/bison/bin:$PATH"
# 現在のbashでのみLDFLAGSを上書きする
export LDFLAGS="-L$HOMEBREW_PREFIX/lib"

# 邪魔になりそうな環境変数を現在のbashでのみクリアする
export CPPFLAGS=""
export optflags=""
export OPENSSL_CFLAGS=""
export RUBY_CONFIGURE_OPTS=""
# 続けて以下のいずれかを実行

# Ruby 3.2の場合の例
rbenv install 3.2.6
# Ruby 3.1の場合の例
rbenv install 3.1.4
# Ruby 3.0の場合の例
rbenv install 3.0.6
# Ruby 2.7の場合の例
rbenv install 2.7.8

ビルドが完了したら、シェルを終了して再起動します。

追記(2024/06/13)

その後、Ruby 3.0.xに限ってCommand Line Toolsでコンパイルするとエラーが発生するようになりました(他のRubyバージョンでは発生しませんでした)。

以下のようにコンパイラをgccに変更することでRuby 3.0.xをビルドできました。

brew install gcc
export CC=gcc
rbenv install 3.0.6

参考: rbenv install 3.0.4 fails on macOS 13.3.1 · rbenv/ruby-build · Discussion #2185

🔗 B: Ruby 2.4〜2.6をビルドする場合

# あえてbrew shellenvのevalを現在のbashでのみ再実行する
eval "$(/opt/homebrew/bin/brew shellenv)"
# 現在のbashでのみbisonパスを設定する
export PATH="$HOMEBREW_PREFIX/opt/bison/bin:$PATH"
# 現在のbashでのみLDFLAGSを上書きする
export LDFLAGS="-L$HOMEBREW_PREFIX/lib"
# Ruby 2.4〜2.6で必要なオプション
export RUBY_CFLAGS="-w"

# 邪魔になりそうな環境変数を現在のbashでのみクリアする
export CPPFLAGS=""
export optflags=""
export OPENSSL_CFLAGS=""
export RUBY_CONFIGURE_OPTS=""

追記(2023-07-10)

その後Ruby 2.4.10のOpenSSLを1.1.1uにアップグレードするためインストールすると、ffi_prep_closureでエラーが発生しました。
RUBY_CFLAGSを以下のように変更することでインストールできました。

export RUBY_CFLAGS="-w -DUSE_FFI_CLOSURE_ALLOC"

参考: error: implicit declaration of function 'ffi_prep_closure' on M1 macOS Big Sur · Issue #869 · ffi/ffi

# 続けて以下のいずれかを実行

# Ruby 2.6の場合の例
rbenv install 2.6.8
# Ruby 2.5の場合の例
rbenv install 2.5.9
# Ruby 2.4の場合の例
rbenv install 2.4.10

ビルドが完了したら、シェルを終了して再起動します。

なお、Ruby 2.4〜2.6の場合の違いはexport RUBY_CFLAGS="-w"を追加していることだけです。

🔗 参考: Rubyのビルド内容を後からチェックする方法

irb -r rbconfigでIRBを起動してから、RbConfig::CONFIGのキーを指定することで、ビルドの詳細な内容を表示できます。この方法は、今回試したすべてのRubyバージョンで使えます。

たとえばRbConfig::CONFIG['configure_args']でビルドに指定したオプションを表示できます。

$ irb -r rbconfig
irb(main):001:0> RbConfig::CONFIG['configure_args']
=> " '--prefix=/Users/hachi8833/.anyenv/envs/rbenv/versions/2.4.10' '--with-openssl-dir=/Users/hachi8833/.anyenv/envs/rbenv/versions/2.4.10/openssl' '--enable-shared' '--with-readline-dir=/opt/homebrew/opt/readline' '--with-libyaml-dir=/opt/homebrew/opt/libyaml' '--with-gmp-dir=/opt/homebrew/opt/gmp' 'CC=clang' 'CFLAGS= -w' 'LDFLAGS=-L/Users/hachi8833/.anyenv/envs/rbenv/versions/2.4.10/lib -L/opt/homebrew/lib' 'CPPFLAGS=-I/Users/hachi8833/.anyenv/envs/rbenv/versions/2.4.10/include  -I /opt/homebrew/include -I/opt/homebrew/opt/libpq/include -I/opt/homebrew/opt/sqlite/include -I/opt/homebrew/opt/binutils/include'"

たとえばCPPFLAGSのオプションを知るにはRbConfig::CONFIG['CPPFLAGS']で表示できます。

$ irb -r rbconfig
irb(main):001:0> RbConfig::CONFIG['CPPFLAGS']
=> "-I/Users/hachi8833/.anyenv/envs/rbenv/versions/3.2.6/include  -I /opt/homebrew/include -I/opt/homebrew/opt/libpq/include -I/opt/homebrew/opt/sqlite/include -I/opt/homebrew/opt/binutils/include -I/opt/homebrew/opt/gmp/include -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -D_DARWIN_UNLIMITED_SELECT -D_REENTRANT  "

たとえばjemallocが有効かどうかを知るにはRbConfig::CONFIG['MAINLIBS']で表示できます。

irb -r rbconfig
irb(main):001:0> RbConfig::CONFIG['MAINLIBS']
=> "-ljemalloc -lgmp -ldl -lobjc -lpthread "

参考: ubuntu - How to check ruby-2.6.3 is using jemalloc ? I installed ruby-2.6.3 as $ rvm install 2.6.3 -C --with-jemalloc - Stack Overflow

また、OpenSSLのバージョンはIRBで以下を実行するとチェックできます。

require 'openssl'
puts OpenSSL::OPENSSL_VERSION

参考: Rubyから使用しているOpenSSLのバージョン確認

🔗 詳細

ここからは読みたい人だけ読めばよいと思います。

以下は最小限のRubyビルドを確かめた結果です。YJITやjemalloc、--disable-install-docなどのオプションは含めていません。

環境変数の具体的な設定方法については省略します。私は環境変数のセットアップを.profileに書いていますが、人によっては.bashrcなどに書いていたりするかもしれません。詳しくは以下の記事をどうぞ。

Bash: .bashrcと.bash_profileの違いを今度こそ理解する

Homebrew以外のパッケージ管理(MacPortsなど)や、rbenv以外のRubyバージョン管理(rvmやadsfなど)については本記事では試していません🙏

🔗 環境

バージョン表示方法もメモします。

マシン
M1 MacBook Pro 2021
追記(2024/12/26)Ruby 3.3.x以降はM3 Macbook Air 2024
macOS
Ventura 13.2.1
追記(2024/12/26)Ruby 3.3.x以降はSequoia 15.2
シェル: bash 3.2.57(1)-release (arm64-apple-darwin22)
bash --version
CommandLine Tools: 14.2.0.0.1.1668646533
14.3.0.0.1.1679647830
pkgutil --pkg-info=com.apple.pkg.CLTools_Executables
Git 2.39.2
git --version
Ruby: 3.2.6 (2023-03-30 revision e51014f9c0) +YJIT [arm64-darwin22](ビルドで使われるカレントのRuby)
ruby --version
Homebrew 4.0.6
brew --version
rbenv 1.2.0-16-gc4395e5
rbenv --version
ruby-build RUBY_BUILD_VERSION="20230330"
Release ruby-build 20230330 · rbenv/ruby-build

rbenvとruby-buildのセットアップについては省略します。インストールについては以下を参考にしてください。

参考: Rails Girls - Japanese

🔗 Homebrewでインストールするもの

追記(2024/12/26): Ruby 3.3.x以降はbisonのインストールは不要です。

以下を実行してからシェルを再起動します。

$ brew install readline libyaml bison gperf zlib libffi

OpenSSLもインストールする場合は以下を実行してからシェルを再起動します。

$ brew install openssl@3 [email protected] readline libyaml bison gperf zlib libffi

以下は参考までにバージョンを示します。

🔗 必須

openssl@3
stable 3.1.0 (bottled) [keg-only]
[email protected]
stable 1.1.1t (bottled) [keg-only]

後述するように、rbenvを使う場合はOpenSSLが自動ダウンロードされるので、HomebrewでインストールしなくてもOKです。
openssl@3[email protected]はHomebrewで両方インストールしておくことも可能ですが、環境変数はどちらか一方だけを指定する必要があります。

libyaml
stable 0.2.5 (bottled)
zlib
stable 1.2.13 (bottled), HEAD [keg-only]

🔗 Gitリポジトリからのビルドで必要なもの

autoconf
stable 2.71 (bottled)
bison(Ruby 3.2までは必要、3.3以降はlramaがあるので不要)
stable 3.8.2 (bottled) [keg-only]
gperf
(なくてもいいらしいけど念のためインストール)
stable 3.1 (bottled)

RubyにlramaがマージされてBison依存がなくなった(RubyKaigi 2023)

🔗 オプション

readline
stable 8.2.1 (bottled) [keg-only]
libffi
stable 3.4.4 (bottled), HEAD [keg-only]
gmp
stable 6.2.1 (bottled), HEAD

YJITを有効にしてビルドするには、rustcをインストールして利用可能にする必要があります。

🔗 参考: rbenvによるOpenSSLの自動ダウンロード

細かい話ですが、rbenvでRubyをビルドする場合、ローカル環境にOpenSSLがなければ自動インストールされます。Ruby 3.1以上なら自動的にOpenSSL 3系を、Ruby 2.4〜3.0では自動的にOpenSSL 1.1.1系をダウンロードします。

通常はこれで十分でしょう。

ビルドを頻繁に行うのであれば、HomebrewでOpenSSLをインストールしておけば、ダウンロードの時間を節約できます。

インストールするならbrew install openssl@3またはbrew install [email protected]のどちらかをインストールし、以下のいずれかをセットアップします。

# OpenSSL@3 の場合のセットアップ
export PATH="$HOMEBREW_PREFIX/opt/openssl@3/bin:$PATH"
export PKG_CONFIG_PATH="$HOMEBREW_PREFIX/opt/openssl@3/lib/pkgconfig"
export RUBY_CONFIGURE_OPTS="$RUBY_CONFIGURE_OPTS --enable-yjit --with-openssl-dir=$HOMEBREW_PREFIX/opt/openssl@3"
# [email protected] の場合のセットアップ
export PATH="$HOMEBREW_PREFIX/opt/[email protected]/bin:$PATH"
export PKG_CONFIG_PATH="$HOMEBREW_PREFIX/opt/[email protected]/lib/pkgconfig"
export RUBY_CONFIGURE_OPTS="$RUBY_CONFIGURE_OPTS --with-openssl-dir=$HOMEBREW_PREFIX/opt/[email protected]"

Homebrewでは両方のバージョンをインストールしても構いませんが、環境変数セットアップには2つのバージョンを混ぜないようご注意ください。

🔗 参考: bison 3について

新しめのRubyでは、bison 3をHomebrewでインストールしたうえで、以下のパスを設定する必要があります。なお、Ruby 2.4〜3.2はすべてbison 3で問題なくビルドできました。

export PATH="$HOMEBREW_PREFIX/opt/bison/bin:$PATH"

参考: RubyをVisual C++でビルドする 2022.04版(+2022.12追記)

追記(2023/12/15)

Ruby 3.3からはbisonがlramaに置き換わるため、bisonのセットアップは不要になります。

参考: Ruby 3.3.0-rc1 リリース

ruby/lrama - GitHub

🔗 ビルドの結果

以下はビルドの記録です。どのRubyバージョンについても最新バージョンのみビルドを確認しました。

🔗 Ruby 3.3以降

$ rbenv install 4.0.0
# 略
$ ruby -v
ruby 4.0.0 (2025-12-25 revision 553f1675f3) +PRISM [arm64-darwin25]
$ rbenv install 3.4.1
# 略
$ ruby -v
ruby 3.4.1 (2024-12-25 revision 48d4efcb85) +PRISM [arm64-darwin24]
$ rbenv install 3.3.6
# 略
$ ruby -v
ruby 3.3.6 (2024-11-05 revision 75015d4c1f)

3.4.x以降のRubyにアップグレードするときはRubyGemsとBundlerもアップグレードすることが多いと思います。以下の記事もどうぞ。

Bundler 4.0.0とRubyGems 4.0.0にアップグレードするときの注意点

🔗 Ruby 3.1〜3.2

$ rbenv install 3.2.6
# 略
$ ruby -v
ruby 3.2.6 (2024-10-30 revision 63aeb018eb)
$ rbenv install 3.1.4
# 略
$ ruby -v
ruby ruby 3.1.4p223 (2023-03-30 revision 957bb7cb81) [arm64-darwin22]
  • OpenSSLのパスを設定しない場合: ビルド成功
    • rbenvがOpenSSL 3.1.xを自動ダウンロードします
  • OpenSSL 1.1.1をhomebrewでインストールしてパスを設定: ビルド成功
  • OpenSSL 3.1をhomebrewでインストールしてパスを設定: ビルド成功

なお、Ruby 3.2からは--disable-install-rdocが無効になったのでご注意ください。
ドキュメントのインストールを無効にするのであれば、RUBY_CONFIGURE_OPTS--disable-install-docを指定します。

# 使ったセットアップ(自動ダウンロードが前提)
# Homebrew -----------------------------------
eval "$(/opt/homebrew/bin/brew shellenv)"
export LDFLAGS="$LDFLAGS -L $HOMEBREW_PREFIX/lib"

# bison 3--------------------------------------
export PATH="$HOMEBREW_PREFIX/opt/bison/bin:$PATH"

Ruby 3.2でRust版YJITもビルドに含める方法について詳しくは、以下の記事をどうぞ。

Ruby 3.2.0-devをM1 Macbook Pro(Ventura)でビルドする

🔗 Ruby 2.7〜3.0

$ rbenv install 3.0.6
# 略
$ ruby -v
ruby ruby 3.0.6p216 (2023-03-30 revision 23a532679b) [arm64-darwin22]
$ rbenv install 2.7.8
# 略
$ ruby -v
ruby 2.7.8p225 (2023-03-30 revision 1f4d455848) [arm64-darwin22]
  • OpenSSLのパスを設定しない場合: ビルド成功
    • rbenvがOpenSSL 1.1.1を自動ダウンロードします
  • OpenSSL 1.1.1をhomebrewでインストールしてパスを設定: ビルド成功

Ruby 3.0から下のバージョンではOpenSSL 3系が使えないので、OpenSSL 1.1.1系が必要です。rbenvの自動ダウンロードもOpenSSL 1.1.1系になります。

参考: Bug #18658: Need openssl 3 support for Ubuntu 22.04 (Ruby 2.7.x and 3.0.x) - Ruby master - Ruby Issue Tracking System

# 使ったセットアップ(自動ダウンロードが前提)
# Homebrew -----------------------------------
eval "$(/opt/homebrew/bin/brew shellenv)"
export LDFLAGS="$LDFLAGS -L $HOMEBREW_PREFIX/lib"

# bison 3--------------------------------------
export PATH="$HOMEBREW_PREFIX/opt/bison/bin:$PATH"

🔗 Ruby 2.4〜2.6

$ rbenv install 2.6.8
# 略
$ ruby -v
ruby 2.6.8p205 (2021-07-07 revision 67951) [arm64-darwin22]
$ rbenv install 2.5.9
# 略
$ ruby -v
ruby 2.5.9p229 (2021-04-05 revision 67939) [-darwin22]
$ rbenv install 2.4.10
# 略
$ ruby -v
ruby 2.4.10p364 (2020-03-31 revision 67879) [-darwin22]
  • OpenSSLのパスを設定しない場合: ビルド成功
    • rbenvがOpenSSL 1.1.1を自動ダウンロードします
  • OpenSSL 1.1.1をhomebrewでインストールしてパスを設定: ビルド成功

2.4〜2.6では、export RUBY_CFLAGS="-w"を追加する必要があります。

# 使ったセットアップ(自動ダウンロードが前提)
# Homebrew -----------------------------------
eval "$(/opt/homebrew/bin/brew shellenv)"
export LDFLAGS="$LDFLAGS -L $HOMEBREW_PREFIX/lib"

# bison 3--------------------------------------
export PATH="$HOMEBREW_PREFIX/opt/bison/bin:$PATH"

# Ruby 2.6.x以下用
export RUBY_CFLAGS="-w"

🔗 おまけ1: Ruby 2.3

Ruby 2.3.xはOpenSSL 1.0.2uまでしか使えないようです。
しかしrbenvにOpenSSL 1.0.2uを自動ダウンロードさせてもエラーになります。いろんなオプションを試しましたが失敗しました。

以下の記事によると、Ruby 2.3のビルドについては、OpenSSL 1.0.2uをソースでインストールしたりM1用のパッチを当てるなどしてRubyそのものはビルドできたものの、C拡張を使うgemが動かなかったりしたそうです。

参考: M1 Mac に古い ruby をインストールしてみたけど業務利用を諦めた話 - エムスリーテックブログ

Ruby 2.3以前はDocker Hubから探して使う方が無難なようです。もちろんRubyをアップグレードするに越したことはないのですが。

参考: ruby - Official Image | Docker Hub

🔗 おまけ2: 不要だったオプション

以下の設定オプションは検索するとよく見かけますが、少なくとも今回試したRuby 3.2〜Ruby 2.4では不要でした。Command Line Tools以外のCコンパイラで必要になるのかもしれないと想像しています。

  • export optflags="-Wno-error=implicit-function-declaration"
  • export OPENSSL_CFLAGS=-Wno-error=implicit-function-declaration
  • RUBY_CONFIGURE_OPTS--enable-shared"を追加

🔗 参考

関連記事

Bash: .bashrcと.bash_profileの違いを今度こそ理解する

Ruby 3.2.0-devをM1 Macbook Pro(Ventura)でビルドする

The post M1/M3 MacでRuby 2.4〜4.0をrbenvでビルドする最小限のセットアップを全部調べた first appeared on TechRacho.

]]>
0