こんにちは、hachi8833です。Kaigi on Rails 2022のCFPとTRICK 2022のエントリーは共に7月いっぱいで締め切りです。
/#kaigionrails
CFP募集中!!
\
Kaigi on Railsでお話してみませんか?
初心者・中級者・上級者、あなたにしかできないトークがあるはず✨
締め切りは7月31日(日)です!
たくさんのご応募お待ちしております🙌https://t.co/dXK7vTguy5— Kaigi on Rails (@kaigionrails) June 21, 2022
The weird Ruby programming competition, TRICK 2022, still open for entries! We are looking forward to seeing your "esoteric" Ruby programs!https://t.co/op65u7yE61
Winning entries will be honored at RubyKaigi in September. Deadline is in July!
— Yusuke Endoh (@mametter) July 23, 2022
🔗Rails: 先週の改修(Rails公式ニュースより)
コミット差分: Comparing @{2022-07-15}...main@{2022-07-21} · rails/rails
🔗 ActionDispatch::ServerTiming
が既存のServer-Timing
ヘッダーを上書きしないよう修正
修正: #45607
小さな修正。ミドルウェアでServer-Timing
ヘッダーに何かを追加し、そのミドルウェアの後にActionDispatch::ServerTiming
があると値が上書きされてしまう。このプルリクは、ActionDispatch::ServerTiming
の振る舞いを上書きではなく追加に修正する。
同PRより
つっつきボイス:「ServerTiming
ミドルウェアは昨年追加されたヤツですね(ウォッチ20211011)」「Server-Timing
ヘッダーを複数回設定したときに上書きせずにカンマ区切りでprepend
するようにしたんですね↓」
# actionpack/lib/action_dispatch/middleware/server_timing.rb#L13
def call(env)
events = []
subscriber = ActiveSupport::Notifications.subscribe(/.*/) do |*args|
events << ActiveSupport::Notifications::Event.new(*args)
end
status, headers, body = begin
@app.call(env)
ensure
ActiveSupport::Notifications.unsubscribe(subscriber)
end
header_info = events.group_by(&:name).map do |event_name, events_collection|
"#{event_name};dur=#{events_collection.sum(&:duration)}"
end
+
+ header_info.prepend(headers[SERVER_TIMING_HEADER]) if headers[SERVER_TIMING_HEADER].present?
headers[SERVER_TIMING_HEADER] = header_info.join(", ")
[ status, headers, body ]
end
「ところで、こういうことをやり始めると、これまで何度も話題になったOpenTelemetry(ウォッチ20210601)のような統一的な方法でやりたくなるのではという気がしますね: ちなみにOpenTelemetryはアプリケーションやミドルウェアのトレースログを一括で扱える規格で、あらゆるミドルウェアやアプリケーションから統一的に取得して紐付けて処理できる」
参考: OpenTelemetry
参考: OpenTelemetry とは | Google Cloud
🔗 ネストしたSQL関数を安全なSQL文字として許容する
- PR: Accept nested functions in Dangerous Query Methods by siegfault · Pull Request #44010 · rails/rails
危険なクエリの検出による非推奨メッセージやエラーについて、余分な偽陽性を削減する機会があると思える。#36448と同様に、他の関数を受け取っている関数(
length(trim(title))
など)を許容するのは妥当と思われる。
同PRより
つっつきボイス:「デンジャラスクエリメソッドって強そうな言葉」「クエリが危険かどうかの判定を少し緩めるということみたい」「length(trim(title))
のようなネストしたSQL関数も使っていいと認める↓: たしかにこう書きたくなることもありそう」
# activerecord/test/cases/unsafe_raw_sql_test.rb#183
test "pluck: allows nested functions" do
title_lengths_expected = Post.pluck(Arel.sql("length(trim(title))"))
title_lengths = Post.pluck("length(trim(title))")
assert_equal title_lengths_expected, title_lengths
end
🔗 Active Storageのpreview
やrepresentation
でも定義済みバリアントを使えるようにする
- PR: Allow to use pre-defined variants for previews by richardboehme · Pull Request #45098 · rails/rails
従来の名前付きバリアントは添付ファイルでバリアントメソッドを呼び出すときしか利用できなかった(
variable?
ではないがpreviewable?
であるファイルでは定義済みメソッドを利用できなかった)。
このパッチによって、preview
メソッドやrepresentation
メソッドでもバリエーション名をシンボルとして渡せるようになる。
class User < ActiveRecord::Base
has_one_attached :file do |attachable|
attachable.variant :thumb, resize_to_limit: [100, 100]
end
end
<%= image_tag user.file.representation(:thumb) %>
同PRより
つっつきボイス:「Active Storageの改修」「preview
はActive Storageのプレビューのことなんですね」「今までpreview
やrepresentation
にバリアントを渡せなかったとは知らなかった」
参考: ActiveStorage::Blob::Representable
参考: §2.2.13.6 :variants
オプション -- レイアウトとレンダリング - Railsガイド
🔗 production向けのcredentialファイル生成にのみsecret_key_base
を含めるよう修正
#45543の続き。
tmp/development_secret.txt
にsecret_key_base
が保存されて混乱するのを防ぐ。
同PRより
# railties/CHANGELOG.md#L13より(編集済み)
+* `development`と`test`を除き、
新規生成する環境ごとのcredentialファイル(`config/credentials/production.yml.enc`など)に
利便性のため(`config/credentials.yml.enc`と同様に)`secret_key_base`を含めるようになった。
つっつきボイス:「先週のcredential改修の続きだそうです(ウォッチ20220719)」「production以外ではsecret_key_base
をデフォルトで含めないようにする、なるほど」「ちなみに個人的にはcredentialより環境変数を使いたい方です: credentialが依存するマスターキーは流出の可能性が付きまとうので」
🔗 ActionView::OutputBuffer
を高速化
MRIの文字列結合には、結合先が
String
の場合にのみ利用できる最適化が多数あるが、String
のサブクラスを使うとこれらの最適化が無効になってしまう。
この違いは、YJITとVMの両方でString#<<
のパフォーマンスが著しく向上しているRuby 3.2でさらに重要性を増す。
そういうわけで、このバッファをString
のサブクラスにしないのが理想。さいわいAction Viewのバッファは内部利用に限定されているので、さほど手間をかけずに継承をコンポジションに置き換えられる。
ベンチマーク:
ActionView::OutputBuffer: 147644.2 i/s
optimized buffer: 228001.4 i/s - 1.54x (± 0.00) faster
ソース: https://gist.github.com/casperisfine/7199579a138e268fda71d6a91366af49
注: この50%の高速化はコンテキスト次第で変わる(テンプレートを別のものに変えると大きく変わる)が、常に高速。
同PRより
つっつきボイス:「RubyのString
を継承するサブクラスがString
よりも遅くなるのは昔からよく言われていますね」「なるほど」「String
を継承しているActiveSupport::SafeBuffer
を使うと遅いので、継承を避けるためにコンポジションの形でString
クラスを直接使うようにすることで高速化したんですね↓」
# actionview/lib/action_view/buffers.rb#L21
- class OutputBuffer < ActiveSupport::SafeBuffer # :nodoc:
- def initialize(*)
- super
- encode!
+ class OutputBuffer # :nodoc:
+ def initialize(buffer = "")
+ @buffer = String.new(buffer)
+ @buffer.encode!
+ end
+
+ delegate :length, :blank?, :encoding, :encode!, :force_encoding, to: :@buffer
+
+ def to_s
+ @buffer.html_safe
+ end
+ alias_method :html_safe, :to_s
+
+ def to_str
+ @buffer.dup
+ end
+
+ def html_safe?
+ true
+ end
「この改修は、オブジェクト指向プログラミングで一般によく言われる"継承よりコンポジション"に沿っているとも言えるでしょうね: ここでString.new(buffer)
のようなコンポジションに変えたのは、RubyではString
クラスを継承するよりも速いというRuby特有の事情に依存していると思いますが」「50%も速くなるのはちょっとびっくり」「おそらくですが、String
を直接使うとCで実装されたメソッドが使われると思います: 逆に継承を使うと継承パスをたどったりしないといけない」
参考: 継承よりもコンポジションを選ぶのとデザインパターンの話
🔗Rails
🔗 Hanami v2.0.0.beta1リリース(RubyFlowより)
# 同記事より
⚡ tree .
.
├── Gemfile
├── Gemfile.lock
├── README.md
├── Rakefile
├── app
│ ├── action.rb
│ └── actions
├── config
│ ├── app.rb
│ ├── routes.rb
│ └── settings.rb
├── config.ru
├── lib
│ ├── bookshelf
│ │ └── types.rb
│ └── tasks
└── spec
├── requests
│ └── root_spec.rb
├── spec_helper.rb
└── support
├── requests.rb
└── rspec.rb
9 directories, 14 files
つっつきボイス:「Hanami v2.0.0.beta1が出た」「Hanamiは使ってないので何とも言えないけど、Rails以外にもRubyのフレームワークがあって着実に進化しているのは健全なことだと思いますね」
🔗 マイクロサービスのテスト戦略
つっつきボイス:「Railsに限定しないテスト記事です」「マイクロサービスのテストは比較的設定しやすい方かなという気もしますけどね」
「こういうコントラクトテストは大事↓」「記事では、2つのサービスがインターフェイスを介して結合するとコントラクトが形成されるとありますね」「大雑把には、サービス間のインターフェイスが満たすべき要件をコントラクトとして記述する感じかな」
参考: ソフトウェア テストの手法 - パート 1: モッキング、スタビング、コントラクト テスト
「たしか英国政府のRailsテスト記事にもコントラクトテストの話があった↓」
「記事は統合テストの話に続いて、インプロセスとアウトプロセスのコンポーネントテストのような具体的な話もある↓」「要点を押さえたまっとうな記事👍」
🔗 RailsConf 2022の動画が公開
RailsConf2022の動画がアップロードされ始めたっぽいhttps://t.co/nlo8e6H5Gb
— はしもと (@hasimoto1009) July 19, 2022
つっつきボイス:「2日ぐらい前(編注: 7/21のつっつき時点)に一気に公開されていましたね」「キーノートスピーチ↓も含まれていたし、アップロードは終わっているようですね」「YouTubeの通知がこれで埋まりまし た 」「自動の字幕ももうついているのがありがたい🙏」
🔗 その他Rails
GMOペパボ様、マネーフォワード様、ピクシブ様(アルファベット順)と、Railsアプリ界の錚々たる皆様にご登壇いただき、Railsアプリを長生きさせる秘訣をお伺いします!【iCARE Dev Meetup #34】10年続くRailsアプリ開発のために大事なこと https://t.co/9MC3ww82fy #icare_meetup
— hiroendore (@hiroendore) July 19, 2022
つっつきボイス:「7/26(火)の開催だそうです」「10年続くRailsアプリ開発か」「Railsを長く使っている会社が増えてきたので、こうしたノウハウが蓄積されているのを感じますね」
ブログ書きました。「なんのためにクラスメソッドってあるの?インスタンスメソッドだけでいいんじゃないの??」と思ってる初心者さんはぜひ読んでみてください!
【プログラミング初心者向け】クラスメソッドとインスタンスメソッドはどう使い分けるべき? - give IT a try https://t.co/cEVtPR188I
— Junichi Ito (伊藤淳一) (@jnchito) July 19, 2022
「クラスメソッドとインスタンスメソッドの違いですか」「Rubyの世界ではクラスもオブジェクトだから、その視点で見れば実は両者に違いはないとも言える」「まあそうですけど😆」「もちろん設計として両者をどう使い分けるかは依然として問題ですけどね」
「基本的にはインスタンスメソッドにする、たしかに」「パターン1のようなユーティリティ関数的なものは自分もクラスメソッドでいいかなという気持ち↓」
# 同記事より
# 日付として有効であればtrue、そうでなければfalseを返す
Date.valid_date?(2022, 6, 30) #=> true
Date.valid_date?(2022, 6, 31) #=> false
「パターン2のようなファクトリーメソッド的なものも基本的にはクラスメソッドにするでしょうね↓」「単純なものならクラスメソッドにするけど、テンプレートから生成するようなときはインスタンスメソッドにすることもあるかな」
# 同記事より
# 文字列をパースしてDateオブジェクトを生成する
Date.parse("2022-06-30") #=> #<Date: 2022-06-30 ((2459761j,0s,0n),+0s,2299161j)>
「パターン3は"そのクラスの複数のインスタンスを扱うメソッド"↓」「export_as_xlsx
は説明用の架空のメソッドなんですね」「こういうのって、そもそもそのクラスのクラスメソッドにすべきなのかどうかという観点もあるかも」「それ同じこと思いました」
# 同記事より
# carsのデータをExcelファイルに出力する(架空のメソッド)
Car.export_as_xlsx(cars)
「この種のメソッドをクラスメソッドにすると経験上バグの元になりやすかった覚えがある: exportするクラスメソッドが同時に複数呼ばれるとtmpファイルが競合して壊れるとか」「あるある」「これはパターン1の関数的なメソッドに分類できそうな気もしますね」
「ところで、こういう議論を随分長いことやっていると、初心者がどういうところでつまづくかを思い出せなくなってくると実感します」「後からもう一度初心者の気持ちになるのは難しいですよね」
「最近学生からこんな相談を受けたんですよ: iOSアプリからデータを定期的に受け取って保存したものをビジュアル表示しようと思っていたらサーバーが必要だろうと言われたけど、サーバーで何かしたことがなくてイメージが湧かないのでどうアプローチしたらいいかって」「言われてみるとサーバーサイドを最初から説明するのって大変そうですね」「iOSの書籍ではほとんどサーバーサイドに触れていないし、その学生にしてみればサーバーは枝葉であって本質的ではないんですよ」「たしかに」「とりあえずFirebaseのデータストアあたりを使ってみてはどうかと回答しましたけど」
「そのとき調べていて気づいたんですけど、サーバーでJSONデータを受け取ってDBに保存して、所定のURLにアクセスするとDBから読み出して表示するだけみたいなシンプルなチュートリアルって、意外と今の世の中にないんですよ」「オススメするならRailsチュートリアルが王道だと思うけどボリュームありますもんね」「サーバーサイド未経験でiOSで何かしたい気持ちが高まっている学生に、RailsやLaravelみたいな本格的なものをがっつり学ばせるのはちょっと忍びなかった」「自分たちのときはたいていPerl CGIで掲示板なんかを作ったりした経験がありましたけど、言われてみると今のサーバーサイドでそれに相当する汎用性の高い経験を手軽に得られる感じではないですね」
前編は以上です。
バックナンバー(2022年度第3四半期)
週刊Railsウォッチ: Active Modelのパターンマッチングがいったん取り消し、Ruby技術者認定試験が10月3日から3.xに対応ほか(20220719)
- 20220719 RubyのGCが高速化、RuboCopのストレスを減らす4つの方法、Defensive CSSほか
- 20220711前編 AR::RelationにCTEを利用できるwithメソッドが追加、Propshaftアップグレードガイドほか
- 20220705後編 6月のRubyコア動向、Stack Overflowアンケート結果ほか
- 20220704前編 マイグレーションをStrategyパターンで拡張可能にほか
今週の主なニュースソース
ソースの表記されていない項目は独自ルート(TwitterやはてブやRSSやruby-jp SlackやRedditなど)です。
週刊Railsウォッチについて
TechRachoではRubyやRailsなどの最新情報記事を平日に公開しています。TechRacho記事をいち早くお読みになりたい方はTwitterにて@techrachoのフォローをお願いします。また、タグやカテゴリごとにRSSフィードを購読することもできます(例:週刊Railsウォッチタグ)