- Ruby / Rails関連
週刊Railsウォッチ: Rails 7をRuby 3.1で動かす、クックパッドのRuby 3.1解説記事、Rails 6->7更新ほか(20220112)
-
シェア
-
ツイート
-
7更新ほか(20220112)">ブックマーク
-
LINE
あけましておめでとうございます。新年最初の週刊Railsウォッチです。
🔗Rails: 先週の改修(Rails公式ニュースより)
今回はRails 7のリリース後の更新情報から見繕いました。
なお年末に2021年度の更新まとめ情報も公開されています。ひととおりウォッチで扱ったはずです。
🔗 3-tierコンフィグでのrails dbconsole
の挙動を修正
3-tier(階層)構成のアプリケーションにはデータベースの"primary"エントリがない可能性がある。Railsのdbconsoleを起動するときは、primaryエントリがあることを前提とせずに、最初のデータベースを読み込むべき。
この修正でメッセージがより明確になる。環境にデータベースがないためにデータベースが提供されていない場合は「primaryが存在しない」ではなく「データベースが存在しない」というメッセージを返すべき。
この変更では、エラーメッセージ以外のアプリケーションの挙動は変更されないはずだが、primaryエントリがないアプリではうまくいかないので、マルチプルDBをサポートするバージョンにもバックポートする必要がある。
同PRより
つっつきボイス:「マルチプルデータベース用か」「これはRailsのdbconsoleのメッセージを修正したんですね」
参考: 3階層システム(三階層システム)とは - IT用語辞典 e-Words
「Railsのdbconsole(bin/rails dbconsole
)ってほとんど使ったことないかも」「そもそも存在を知りませんでした」「database.ymlのコンフィグを使ってpsqlやmysqlなどのDBクライアントを呼び出すだけのコマンドですが、DBクライアントや必要なヘッダファイルなども入っていないと使えません」「たしかに自分の環境でやってみたら動かなかった😢」
参考: 1.5 bin/rails dbconsole
-- Rails のコマンドラインツール - Railsガイド
🔗 change_table
で認識できないオプションの例外を発生するようにした
このプルリクは、
change_table
ブロック内のテーブルでyieldするTable
が、if_exists
やif_not_exists
キーワード引数を持つメソッドを受け取ったときに例外を発生させる。
これにより、bulk: true
オプションを指定してchange_table
ブロックを呼び出すと黙ってオプションが無視され、このオプションなしで呼び出すとそのとおりになるという予期しない振る舞いを防げる。
同PR冒頭より
つっつきボイス:「change_table
ブロックにサポートされていないオプションを渡すと例外を発生するようになったんですね」「以前は通っちゃったのか」
「ちなみに自分はマイグレーションでif_exists
やif_not_exists
を指定するのは怖いのであまりやりません: 以下みたいにif_exists
でインデックスがあればインデックスを消すとか」「書きたくない気持ちわかります」
# 同PRより
change_table(:table) do |t|
t.column :new_column, if_not_exists: true
t.remove_index :old_column, if_exists: true
end
「Railsのマイグレーションは、途中で失敗したときにマイグレーションが中途半端に残ってしまう可能性があるんですよね」「そうそう、そうなると手動で残りを反映することになって残念な気持ちになる」「Railsのマイグレーションを使いたがらない人がいるのもちょっとわかります」
参考: Rails migration から ridgepole に移行した - DEV Community 👩💻👨💻
🔗 HostAuthorization
ミドルウェアでIP+ポート番号を利用できるようになった
ホストがIPアドレス+ポート番号形式の場合、
IPAddr
オブジェクトの比較でエラーが発生してミドルウェアがリクエストを拒否していたので、IPAddr
オブジェクトを比較するときにホストからホスト名を抽出するようにした。
#43864のコメント(コメント)が修正されるはず。
同PRより
つっつきボイス:「ActionDispatch::HostAuthorization
といえば少し前にセキュリティ修正されたヤツですね↓」
- Rails API:
ActionDispatch::HostAuthorization
「127.0.0.1:3000
みたいにIPアドレスとポート番号を指定できなかったのが修正されたのね」「へ〜、こんなのがあったとは」「これは動かないと困るヤツ」「そういえばRails 7リリース間近でこれがマージされたのを見た覚えがあります」
# actionpack/lib/action_dispatch/middleware/host_authorization.rb#L18
class HostAuthorization
ALLOWED_HOSTS_IN_DEVELOPMENT = [".localhost", IPAddr.new("0.0.0.0/0"), IPAddr.new("::/0")]
- PORT_REGEX = /(?::\d+)?/.freeze
+ PORT_REGEX = /(?::\d+)/ # :nodoc:
+ IPV4_HOSTNAME = /(?<host>\d+\.\d+\.\d+\.\d+)#{PORT_REGEX}?/ # :nodoc:
+ IPV6_HOSTNAME = /(?<host>[a-f0-9]*:[a-f0-9.:]+)/i # :nodoc:
+ IPV6_HOSTNAME_WITH_PORT = /\[#{IPV6_HOSTNAME}\]#{PORT_REGEX}/i # :nodoc:
+ VALID_IP_HOSTNAME = Regexp.union( # :nodoc:
+ /\A#{IPV4_HOSTNAME}\z/,
+ /\A#{IPV6_HOSTNAME}\z/,
+ /\A#{IPV6_HOSTNAME_WITH_PORT}\z/,
+ )
「IPAddr
クラスをちゃんと使うようになった↓」
# actionpack/lib/action_dispatch/middleware/host_authorization.rb#L39
def allows?(host)
@hosts.any? do |allowed|
- allowed === host
- rescue
- # IPAddr#=== raises an error if you give it a hostname instead of
- # IP. Treat similar errors as blocked access.
- false
+ if allowed.is_a?(IPAddr)
+ begin
+ allowed === extract_hostname(host)
+ rescue
+ # IPAddr#=== raises an error if you give it a hostname instead of
+ # IP. Treat similar errors as blocked access.
+ false
+ end
+ else
+ allowed === host
end
end
end
🔗Rails
🔗「Rails 7.0 + Ruby 3.1でゼロからアプリを作ってみたときにハマったところあれこれ」
年末に書いたQiita記事が先週LGTMが多かった投稿の17位に入っていました。Rails 7.0の予習用にどうぞ。
Rails 7.0 + Ruby 3.1でゼロからアプリを作ってみたときにハマったところあれこれ https://t.co/wjuuhY7Ea4
— Junichi Ito (伊藤淳一) (@jnchito) January 5, 2022
つっつきボイス:「jnchitoさんのこの記事とても参考になりました❤️: TechRacho記事も引用してくださってます🙏」「rails new
ってなかなか1回で終わらなくて、オプションを足して何回かやり直したりしますよね」「するする: そういえば昨年業務でrails new
する機会は1回しかなかった😢」「これはもうしょうがないですね」
「以下の記事にも書きましたが、thor gemが出していたwarningも修正されました↓」「そういえばRails 7.0.0でRuby 3.1がまだ動かないんだったか(編集部注: つっつきの翌日にRails 7.0.1がリリースされてRuby 3.1で動くようになりました↓)」
「jnchitoさんの記事で今頃知ったんですが、Rails 7ではアセットをビルドするためにbin/rails
ではなくbin/dev
でdevelopmentサーバーを起動するんですね」「お、なるほど」「ちなみに私はrails new
するときに例のpropshaft↓を有効にしたんですが、その場合はbin/dev
は生成されませんでした: propshaftはビルド不要なので従来のbin/rails
でdevelopmentサーバーを起動するみたいです」
「Rails 7ではpropshaftのときもbin/dev
で起動するように統一してくれたら、起動コマンドを使い分けなくて済むのでよさそうですけど」「それもそうですね」「bin/dev
はforemanを起動するらしいので、もしかするとpropshaftでは余分なforemanをインストールしないようにしているのかもしれませんが」
「あ、自分はRails 6を7にアップグレードしたんですが(後述)、bin/dev
はありませんね」「binstubを生成するみたいにbin/dev
も生成できそうですけどね🤔」「とりあえずrails assets:precompile
は引き続き使えるみたい」
後で調べてみましたがbin/dev
を単独で生成する方法は見つけられませんでした😢。
「Rails 7のTurboはややこしそう」「このstatus: :unprocessable_entity
↓は、Rails 7でscaffoldすると追加されていたので気づきました」「なるほど、Turboがこれを解釈するので必要になるのか」
# 同記事より: コントローラ
def create
@project = current_user.projects.new(project_params)
respond_to do |format|
if @project.save
format.html { redirect_to project_url(@project), notice: "Project was successfully created." }
format.json { render :show, status: :created, location: @project }
else
# status: :unprocessable_entity が必須!!
format.html { render :new, status: :unprocessable_entity }
format.json { render json: @project.errors, status: :unprocessable_entity }
end
end
end
「以下のような変更↓はrails-ujsからTurboに変わることで必要になるんだと思いますが、こういうアップグレード作業は地味に大変そう」「ビューの修正が増えそうですね」「確認ダイアログの出し方もrails-ujsのときと変わるのか」「rails-ujsは昔jQuery版があったほど歴史が長くて自分もそれに慣れているので、新しい書き方に慣れないといけなくなるかな」「この感じだと一括置換が効くとも限らなさそうですね...」「こういう部分をヘルパーメソッドに切り出すことも多いので、それにも配慮しないといけなさそう」
<!-- 同記事より: ERB -->
<!-- 従来の書き方(rails-ujsを使っている場合) -->
<%= link_to 'Delete', [task.project, task], method: :delete %>
<!-- Turboを使っている場合 -->
<%= link_to 'Delete', [task.project, task], data: { turbo_method: :delete } %>
<!-- 同記事より: ERB -->
<!-- link_toの場合 -->
<%= link_to 'Delete', [task.project, task], data: { turbo_method: :delete, turbo_confirm: 'Are you sure?' } %>
<!-- button_toの場合 -->
<%= button_to "Delete", [task.project, task], method: :delete, form: { data: { turbo_confirm: "Are you sure?" } } %>
「destroyアクションでTurboを使うにはこのstatus: :see_other
も必要なのね↓」「これはscaffoldでも生成されなかったので、jnchitoさんの記事を読んでなかったら見落とすところでした」
# 同記事より: コントローラ
def destroy
@task.destroy
respond_to do |format|
# status: :see_other が必須!!
format.html { redirect_to @project, notice: "Task was successfully destroyed.", status: :see_other }
format.json { head :no_content }
end
end
「今さらですがTurboって何をするんでしたっけ?」「Rails 7からはHotwireがデフォルトになって、それと一緒に入るTurboがデフォルトのフロントエンド向けライブラリになります」「なるほど、rails-ujsの代わりになる感じですか」「Turboはrails-ujsも置き換えますが、それ以外にも機能があります」「Turbolinksはどうなるんですか?」「Turboに変わったのでRails 7にはデフォルトでは入りません」「ちなみにTurbolinksはもう開発も止まっています」
「Turboは独立してインストールできるのかな?」「そういえばRails 6.1.1にTurboをインストールする記事↓を見かけたのでできそうです」「HotwireはTurboが必須のようですね」
参考: Rails 6.1.1にTurbo Driveをインストールして困ったこと
「なぜTurboでインターフェイスを変更したんでしょうね?」「おそらくですが、rails-ujsと共存できるようにするためじゃないかな: たしか共存はできるはずですし」「それなら最初は共存させておいて、それから少しずつ書き換えて移行することもできそう」「名前空間が衝突するとそれができなくなるからでしょうね」
「jnchitoさんの記事でもrails-ujsが非推奨になったとある」「いずれTurboに移行しないといけなくなるのか」「その代わりTurboならAjax的なことやturbo_frame_tag
のような強力な機能も使えるようになりますけどね↓」「rails-ujsを昔読んでみたときは比較的シンプルだった覚えがあるんですが、Turboの方が大きいから読むのに気合が要りそう」
<!-- 同記事より: app/views/tasks/_completed.html.erb -->
<%= turbo_frame_tag task do %>
<%= form_with model: task, url: [:toggle, task.project, task] do |f| %>
<label class="<%= task.completed? ? 'completed' : '' %>">
<%= f.check_box :completed, onchange: "this.form.requestSubmit()" %>
<%= task.name %>
</label>
<% end %>
<% end %>
「deviseがまだTurboに対応していないとあるけど、これはいずれ対応すると思います」「deviseのような認証部分のコードはなるべく改変したくないのでぜひ対応して欲しい」「そうそう」「いずれにしろRailsは7.0.1が早々に出そうな予感がします」
つっつきの翌日に本当にリリースされました↓。
🔗 RailsでRuby 3.1を使う
- gist: Ruby 3.1 on Rails
なお、Rails 7.0.1がリリースされたことで同Gistは更新されました。
つっつきボイス:「yahondaさんのこのGistも有用な情報がいろいろあって、以下の記事を出すときに助かりました↓」「Rail 7.0.0でRuby 3.1を使うには7-0-stable
ブランチを指定しないといけないという情報も載っていますね(編集部注: Rails 7.0.1では不要になりました)」
「そうそう、Ruby 3.1のClass#descendants
がリリース直前にいったん外されたことで↓、それに依存していたRails 7.0.0がRuby 3.1で動かなくなった」「RubyのリリースとRailsのリリースが接近していたことによるハプニングでしたね」
参考: Feature #14394: Class.descendants - Ruby master - Ruby Issue Tracking System
🔗 RailsデザインパターンKPT
つっつきボイス:「こうやって社内向けの設計方針を外部向けにも記事として出すのはいいですね👍」「記事では、Service ObjectとDecoratorとForm ObjectとValue Objectを維持する選択をしたそうです」「ちなみに個人的にはDecoratorは好きではなくてForm Objectは割と好き: ただForm Objectをさらに抽象化したDomain Objectの方が柔軟性が高まるのもわかります」
参考: Ruby on Rails Tutorial => Domain Objects (No More Fat Models)
「同記事ではRepositoryパターンは今後なくすそうです」「単に不要になったので削除するのかなるほど」「Repositoryパターンはあってもいいけど、オーバーキル気味な傾向はあるかな: 自分たちはActive Recordにすっかり慣れているので、独自のRepositoryを使うのは不便そうだなという気はする」
「自分は逆にRepositoryパターン大好きで、外部APIを叩くときによく使ってます」「Active Recordみたいにチェインできるオブジェクトかと思ったら実はRepositoryパターンで、チェインしたらめちゃくちゃ重くなったなんてことにもなりそうですけどね」「まあたしかに」「もう自分たちはActive Record脳ですから」
🔗 Rails 6をRails 7にアップグレード
Rails6 -> 7をやったのでブログにまとめたでござる。 / 個人プロジェクトをRails6からRails7にアップグレードした https://t.co/7WhIi9Ep30 @srockstyleより
— すろっくさん (@srockstyle) January 2, 2022
つっつきボイス:「ぼくの記事だ〜❤️」「早速のアップグレードお疲れさまです」「でもさっきTurboの話を聞いていて、自分のアプリにTurboを入れてなかったことに気が付きました😅」
「記事ではbulletとannotate gemがRails 7で動かなかったgemがありますね: 私も2つほど動かないgemがありました」「annotate gemは好みが別れますが、自分は割と好き」「私も」「このgemを入れておくと、他の人がマイグレーションファイルを書くときにちゃんとコメントも書くようになってくれるんですよ」
「記事にもあるけど、Rails APIサーバーならスムーズにRails 7に移行できそうかな」「逆にビューはHotwireとTurbo周りで手間がかかりそうです」「rails-ujsをTurboに書き換えるとなるとそうなるでしょうね」
🔗Ruby
🔗 クックパッドの「プロと読み解く Ruby 3.1 NEWS」
はてなブログに投稿しました #はてなブログ
プロと読み解く Ruby 3.1 NEWS - クックパッド開発者ブログhttps://t.co/T46x03papt— Cookpad Tech Life (@cookpad_tech) December 25, 2021
つっつきボイス:「Ruby 3.1リリースの日に公開されたクックパッドさんの記事です」「お〜後で読もうっと」「Rubyコミッターが経緯や使い方を日本語でみっちり解説してくれるのはありがたい🙏」「よく見ると記事すごく長いですね」「休みの日にでもゆっくり読んでみるといいと思います👍」
そういえばRuby 2.6は今年2022年03月31日にEOLを迎えます↓のでお忘れなく。
🔗 YJITを調べてみた
- gist: yjit についてメモ書き
つっつきボイス:「お、うちの記事が引用されていますね↓」「はてなブックマークは検索対象を引用している記事を見つけられるんですが、その機能で浮かび上がってきた記事です」「YJITをステップバイステップで追いかけていますね: 面白そう👍」
🔗 Faraday 2.0の変更
Looks like Faraday 2.0 will break a lot of apps. https://t.co/bnw7ExHkot
I would advise them to include net/http as a default adapter in the core gem. Don't force the user to choose. Omakase.
— Mike Perham (@getajobmike) January 5, 2022
つっつきボイス:「ruby/debugメンテナーのst0012さんに教えてもらったツイートです」「お、Faradayがつい最近メジャーアップデートされたのか」「しかもnet_http以外のアダプタをすべて外したそうです」「へ〜」「アダプタが多くてサポートがつらくなったみたいなことが書かれてました」「アダプタが多いとそうなるでしょうね」
「どうせひとつに絞るならnet_httpよりもhttpclientにして欲しかった↓」「httpclientはよく話題にしているgemですね(ウォッチ20211108)」「httpclientはよくできてます」
今回は以上です。
今週の主なニュースソース
ソースの表記されていない項目は独自ルート(TwitterやはてブやRSSやruby-jp SlackやRedditなど)です。
週刊Railsウォッチについて
TechRachoではRubyやRailsなどの最新情報記事を平日に公開しています。TechRacho記事をいち早くお読みになりたい方はTwitterにて@techrachoのフォローをお願いします。また、タグやカテゴリごとにRSSフィードを購読することもできます(例:週刊Railsウォッチタグ)