こんにちは、hachi8833です。休日はトイレの壁紙を剥がして珪藻土を塗り塗りする作業で終わりました。
参考: 【これさえ読めば大丈夫】はじめての漆喰・珪藻土 塗り壁DIY完全ガイド
つっつきボイス:「自宅の壁?」「冬になるたびにトイレの壁にめちゃくちゃ結露してカビの温床になってました」「構造上結露しちゃう家とかありますよね」「団地ともお的な昭和な団地なので冬場はコンクリから外の冷気がもろに伝わってきます」
- 各記事冒頭にはでパーマリンクを置いてあります: 社内やTwitterでの議論などにどうぞ
- 「つっつきボイス」はRailsウォッチ公開前ドラフトを(鍋のように)社内有志でつっついたときの会話の再構成です
- 毎月第一木曜日に「公開つっつき会」を開催しています: お気軽にご応募ください
臨時ニュース: Rails 6がついにリリース
つっつき会の翌日の金曜日に再び6.0.0マイルストーンを見てみるとissueがゼロになっていたので、もしかするとウォッチ公開前にリリースされたりして、と思っていたら週末にリリース情報が出ました。
Rails 6.0: Action Mailbox, Action Text, Multiple DBs, Parallel Testing, Webpacker by default, and Zeitwerk! What a monumental final release. Massive upgrades, intense conceptual compression. Our Best Rails Yet! https://t.co/DQEzRKSChr
— DHH (@dhh) August 16, 2019
そういえば例の画面も変わってましたね。
肌の色以外に車椅子の人も増えたりしてるけど、かわりに犬がクビにされててちょっと悲しいw
— Hiroshi Shimoju (@shimoju_) August 17, 2019
早くも@jnchitoさんががっつりRails 6記事を公開しています。他にもPublic Keyなどで続々速報が出ています。
ブログ書きました。この週末に書いたQiita記事の紹介です。あと、妻のパン屋のWebサイトをRails 6にアップグレードしている最中に発生した、いくつかのトラブルについても情報を載せています。
Rails 6リリース記念!?Qiitaにいろいろ記事を書きました – give IT a try https://t.co/4bCzw0TdVO— Junichi Ito (伊藤淳一) (@jnchito) August 19, 2019
私もRails 6 rc2をちょっといじり始めていたのですが、RailsガイドをRails 6に対応させる更新翻訳(次の記事参照)がせいいっぱいでした。
参考までに、Evil Martiansの少し前のRails 6記事を再掲します。
RailsガイドがRails 6に対応
#Railsガイド が Rails 6.0 に対応!!
リリースノート、アップグレードガイド、Zeitwerk、Mailbox、並列テスト、Action Text、Multiple DB などなど、更新が多すぎて140文字に収まりません…!!
主な更新点は下記にまとめたので詳しくはコチラから ;)
https://t.co/IieBqZjT64 pic.twitter.com/bgynxumPfY
— YassLab 株式会社 (@YassLab) August 19, 2019
YassLabの安川さんのお力添えで、RailsガイドをひとまずRails 6に対応いたしました。ありがとうございます!
上の記事で紹介されているように、今回はZeitwerkのガイドなども追加されています。見落としや誤りなどありましたら、Railsガイドまでフィードバックをお願いします。
眼力頼りの差分翻訳を今後何とかしたいです。
というわけで、ここから下はRails 6リリースより前のつっつきを元にしています。ご了承ください。
Rails: 先週の改修(Rails公式ニュースより)
今回は主に最近の6-0-stableブランチから見繕いました。ドキュメントの修正が増えています。
なお6.0.0マイルストーンはつっつき時点で4件ありました。
つっつきボイス:「ある程度予想はしてたけど、やっぱりリリースまでには時間かかってますね」「マイルストーンに残ってるissueはデグレっぽい感じですし」
aggregate_alias
の余分な”_”を削除
これは6.0.0マイルストーンにあったissueのようです。
# activerecord/lib/active_record/relation/calculations.rb#L308
def execute_grouped_calculation(operation, column_name, distinct) #:nodoc:
group_fields = group_values
if group_fields.size == 1 && group_fields.first.respond_to?(:to_sym)
association = klass._reflect_on_association(group_fields.first)
associated = association && association.belongs_to? # only count belongs_to associations
group_fields = Array(association.foreign_key) if associated
end
group_fields = arel_columns(group_fields)
group_aliases = group_fields.map { |field|
field = connection.visitor.compile(field) if Arel.arel_node?(field)
column_alias_for(field.to_s.downcase)
}
group_columns = group_aliases.zip(group_fields)
- aggregate_alias = column_alias_for("#{operation}_#{column_name.to_s.downcase}")
+ aggregate_alias = column_alias_for("#{operation} #{column_name.to_s.downcase}")
# activerecord/lib/active_record/relation/calculations.rb#L377
def column_alias_for(field)
- return field if field.match?(/\A\w{,#{connection.table_alias_length}}\z/)
-
column_alias = +field
column_alias.gsub!(/\*/, "all")
column_alias.gsub!(/\W+/, " ")
column_alias.strip!
column_alias.gsub!(/ +/, "_")
connection.table_alias_for(column_alias)
end
つっつきボイス:「コードに一箇所余分な_
があったのを修正したそうです」「以前の修正でデグレってたのが解決された」
Zeitwerk関連コミット
- commit: documents how to troubleshoot autoloading in zeitwerk mode · rails/rails@da5e6b8
- commit: defines Rails.autoloaders.log! · rails/rails@0e6f7bf
# railties/lib/rails/autoloaders.rb#L39
+ def log!
+ each(&:log!)
+ end
つっつきボイス:「Zeitwerkデバッグ用の#log!
を追加して、Railsガイドでこれを使ったトラブルシューティング方法が追記されてました」「Rails 6のZeitwerkでハマる人もいそうなので、こういうサポートはありがたいですね」
「ところで上にポツンとあるeach
って何でしょう?」「更新箇所のすぐ上にeach
が定義されている↓からそれではないかと」「あ、これでしたか」「あまり見かけない書き方ですけどね、main
とonce
は単独で使えそうな感じでそれをまとめて呼び出すショートハンド的なメソッドかなと」
# railties/lib/rails/autoloaders.rb#10
def main
if zeitwerk_enabled?
@main ||= Zeitwerk::Loader.new.tap do |loader|
loader.tag = "rails.main"
loader.inflector = ActiveSupport::Dependencies::ZeitwerkIntegration::Inflector
end
end
end
def once
if zeitwerk_enabled?
@once ||= Zeitwerk::Loader.new.tap do |loader|
loader.tag = "rails.once"
loader.inflector = ActiveSupport::Dependencies::ZeitwerkIntegration::Inflector
end
end
end
def each
if zeitwerk_enabled?
yield main
yield once
end
end
def logger=(logger)
each { |loader| loader.logger = logger }
end
ActionDispatch::Responseのcontent_type
の内部でmedia_type
を使うよう変更
- PR: Use media_type instead of content_type internally by eugeneius · Pull Request #36854 · rails/rails
#36490でdeprecateされた
content_type
呼び出しが、アップグレードしたアプリでトリガーされていた。同PRのコメントで報告されていた。
media_type
を使えば、あらゆるケースでdeprecationを回避できる。
同PRより大意
# actionpack/lib/action_controller/metal/renderers.rb#L156
add :json do |json, options|
json = json.to_json(options) unless json.kind_of?(String)
if options[:callback].present?
if media_type.nil? || media_type == Mime[:json]
self.content_type = Mime[:js]
end
"/**/#{options[:callback]}(#{json})"
else
- self.content_type ||= Mime[:json]
+ self.content_type = Mime[:json] if media_type.nil?
json
end
end
add :js do |js, options|
- self.content_type ||= Mime[:js]
+ self.content_type = Mime[:js] if media_type.nil?
js.respond_to?(:to_js) ? js.to_js(options) : js
end
add :xml do |xml, options|
- self.content_type ||= Mime[:xml]
+ self.content_type = Mime[:xml] if media_type.nil?
xml.respond_to?(:to_xml) ? xml.to_xml(options) : xml
end
つっつきボイス:「@y-yagiさんの立てたPRがこのPRに反映されたそうです」「media_type
なんてのがあったとは: MDNを見るとContent-Type
の項目の中にmedia-type
ってあるな↓」「ディレクティブですか」
「これを見る限りでは、Content-Type
の中でmedia-type=なんちゃらMIME type
みたいにMIME typeを書けるらしいけどどういうふうに使うのかな…?」「むむ」「media_type
があればそっちを使って、なければContent-Type
を使うとかなという気もするけど…」「詳しい人の情報求む」
その後もう少し追ってみました。
#36034で
ActionDispatch::Response#content_type
の戻り値を変えたが、#36034でアップグレードの邪魔になっているらしき。
そこでActionDispatch::Response#content_type
の振る舞いを5.2に戻しつつ、古い振る舞いをdeprecatedにした。さらに、振る舞いをconfigで制御できるようにした。
#36490より大意
# #36490より
# actionpack/lib/action_dispatch/http/response.rb#L89
+ cattr_accessor :return_only_media_type_on_content_type, default: false
...
def content_type
- super.presence
+ if self.class.return_only_media_type_on_content_type
+ ActiveSupport::Deprecation.warn(
+ "Rails 6.1 will return Content-Type header without modification." \
+ " If you want just the MIME type, please use `#media_type` instead."
+ )
+
+ content_type = super
+ content_type ? content_type.split(/;\s*charset=/)[0].presence : content_type
+ else
+ super.presence
+ end
end
さらに#36034のコメントを引用します。
#36034の変更がGitHub上で最新の6-0-stable更新に入ったが、breaking changeが発生していろいろfailすることがわかった。
content_type
への変更はたぶん正当だと思うものの、同時に5.2でもアプリを動かしている自分たちにとっては切り替え困難。#36034の変更の意図としては、5.2と6.0ではcontent_type
の戻り値が異なっていて、少なからぬ影響が生じる。この変更をひとまず戻してアプリで従来のcontent_type
値を取れるアップグレードパスを整備するのがよさそう?
さらに遡ると、#35709の「content_type
のcharset
以外のパラメータがRailsで無視される」というissueが始まりだったようです。それを修正するために、#36034でActionDispatch::Response#content_type
がContent-Typeヘッダーをすべて返すようにしたところ、既存の5.2アプリでMIME typeの取得などで不具合が生じることがあったので、段階的に移行することになった、冒頭の#36854はそれに伴う変更…という理解で合ってるかしら。
prevent_writes
のスレッド安全性を修正
#36830で追加したテストやコードで示すように、
prevent_writes
はスレッドセーフではなかった。あるスレッドが読み出し、他のスレッドが書き込んでからもう一度読み出すと、最初の書き込みができなくなる。
この変更は、コネクションハンドラのインスタンス変数を削除してThread.current[:prevent_writes]
のゲッター/セッターに変え、書き込みが許されるかどうかを設定するようにした。
同PRより大意
# activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb#L1006
- attr_reader :prevent_writes
def initialize
# These caches are keyed by spec.name (ConnectionSpecification#name).
@owner_to_pool = ConnectionHandler.create_owner_to_pool
- @prevent_writes = false
# Backup finalizer: if the forked child never needed a pool, the above
# early discard has not occurred
ObjectSpace.define_finalizer self, ConnectionHandler.unowned_pool_finalizer(@owner_to_pool)
end
+ def prevent_writes # :nodoc:
+ Thread.current[:prevent_writes]
+ end
+
+ def prevent_writes=(prevent_writes) # :nodoc:
+ Thread.current[:prevent_writes] = prevent_writes
+ end
+
def while_preventing_writes(enabled = true)
- original, @prevent_writes = @prevent_writes, enabled
+ original, self.prevent_writes = self.prevent_writes, enabled
yield
ensure
- @prevent_writes = original
+ self.prevent_writes = original
end
つっつきボイス:「マルチプルDBでのスレッド関連の問題を修正したみたいですね」
パーシャルレンダリングAPIの古い記述を削除
# actionview/lib/action_view/renderer/partial_renderer.rb#L106
# <%= render(partial: "ad", collection: @advertisements) || "There's no ad to be displayed" %>
#
- # NOTE: Due to backwards compatibility concerns, the collection can't be one of hashes. Normally you'd also
- # just keep domain objects, like Active Records, in there.
- #
# == \Rendering shared partials
#36897によるとActionView::PartialRendererに以下のような記述がある:
後方互換性の懸念上、このコレクションにはハッシュを使えない。通常はActive Recordと同様、そこにドメインオブジェクトも保持する。
上の記述が追加されたのは2005年のことで、どこが懸念点だったかという情報は見当たらないものの、#36897で報告されているとおり、実際はできるらしい↓。自分もRails 6.0 rc2で試したところ、ハッシュを渡せた。
同PRより大意
= render :partial => "info_row", :collection => my_collection, :as => :d
# my_collectionはハッシュの配列
Rails
Arelはかつてpublicだったか?
つっつきボイス:「『先週の改修』でissueを追っていたら、このissueのスレがArelの話題でかなり伸びていて気になったので、手元で雑に訳してみました」
参考: File: README — Documentation for rails/arel (master)
参考: ActiveRecordを支える技術 - Arelとは何者なのか? (全5回) その1 - TIM Labs
以下は#36761の大雑把なまとめです。
- (元々このissueは、threddedというフォーラムエンジンのあるクエリがRC2で期待どおり動かなくなったという報告で始まった)
- (やりとりの中でArelに言及されていて、Arelがかつてpublicだったことがあったかどうかという議論になった)
- publicあった説: 3.2のRailsガイドに
arel_table
を使ったコード例がある、publicではなかったとしても、publicだったと思ってた人はかなり多い - publicなかった説: Arel gemはかつて外部gemだったがArelのAPIはpublicになったことはないはずだし、APIにはArelは出てこない
- publicあった説: APIdockには今もpublicと表示されている
- publicなかった説: APIdockは公式ではないし、Arelはやはりprivateであり予告なしに変更される可能性がある
- publicあった説: 3.2のRailsガイドに
- Arelを使っている人は実際多いようなので、もしかするとArelをpublic APIとして再検討するタイミングなのかもしれない?
- 「
ActiveRecord::Relation
でできないことはArelでやるべし(stringでやるのは禁止)」というプロジェクトに携わったこともある - stringでやる方法だとコンポジションもパラメータ化もできず、DB互換性もなくなるのでよくない
- Arelはリリースのたびに改善されている(5.1はともかく)
- (「Arelをpublicにすべき」説が後半で飛び交う)
- 以下↓はArelでないとこんなに簡潔に書けない
- 「
scope :started, -> {
where(arel_table[:valid_from].lteq(Date.current))
}
最終的にこの#36761はcloseされています。なお本来の問題は別途@kamipoさんが#36805で修正しました。
「(抄訳を見ながら)お〜なるほどなるほど、たしかにArelを生で使う人って以前は普通に見かけましたね」「やはり〜」「RailsガイドにもArelを使った例があったとは(今はありませんが)」「あったあった、自分もRails 3の頃だったか、英語版Railsガイド見てやりましたもん」「」「ArelがprivateなAPIだったら、DB触りたいというだけでprivateなAPIをわざわざ使いたくないですし、使った覚えがあるということはガイドに書いてあったはずですし」「APIdock見に行くと今でも『public』って出ちゃってますし」「ありゃ〜」
「そういうこともあったりしたので『Arel、publicだったんでしょ?』という意見が出たのかもですね」「ま気持ちはわかる」「ArelってActive Recordに取り込まれる前は別gemだったんですね」「元々汎用的なORMビルダーみたいな位置づけでしたし」
- リポジトリ: rails/arel: A Relational Algebra
- 大元リポジトリ: nkallen/arel: A Relational Algebra — 10年前から変わってないんですね
「まあ公式としてはArelをpublic APIとみなしたことはないという見解になるでしょうね: ArelをArelとしてナマで使う人をどうこうするつもりもないでしょうけど」「その分いつAPIが変わっても仕方がないですね」「じっくり読んだらなかなか面白そうなissueではある」「issueを全文翻訳しようかとも思いましたが取りあえずやめときます」
StravaはRailsを使っている
StravaはRubyをメインに使っている(彼らのオフィスに訪問して聞いた)。#AnyPickshttps://t.co/7pdqYyPbyZ
— Yukihiro Matsumoto (@yukihiro_matz) August 14, 2019
- サイト: Strava | アスリートのためのソーシャルネットワークでランニングやサイクリングを記録
- 元記事: Upgrading Strava to Rails 5.0 - strava-engineering - Medium
つっつきボイス:「Matzのツイートで見かけたので拾ってみました: フィットネスSNS的なサービスのようです」「この辺は国とか地域で流行り方が違ったりしますね」「同社のブログでRailsを5.0に上げたという記事があったのでRails使ってるようです」「2018年末で5.0、GitHubもその頃にRailsアップグレードを進めてましたね: どちらもユーザー数めちゃ多そうだから大変そう」
Rails 6のマルチDB応用編(Hacklinesより)
production:
primary:
adapter: postgresql
animals:
migrations_paths: db/animal_migrate
adapter: postgresql
url: <%= ENV["HEROKU_POSTGRESQL_OLIVE_URL"] %>
animals_replica:
adapter: postgresql
url: <%= ENV["HEROKU_POSTGRESQL_PURPLE_URL"] %>
replica: true
- replicaから読む
- replica読み取りとDB書き込みを自動切り替え
- DB読み取りと書き込みを手動で切り替える
つっつきボイス:「Rails 6でやってくるマルチプルDBの機能紹介という感じかな」「タイトルにadvancedとあるけど基本機能っぽい」
config.active_record.database_selector = 2.seconds
「お、この2秒という数値↑、なるほど感」「というと?」「masterというかprimaryに書き込んだ後でそれがreplicaに複製されるまでにはタイムラグがあるので、タイムラグの上限を保証する的な設定なんでしょうね、2秒経過すればreplicaからも最新のデータを取れると」「database_selector
、新しいガイドにあったかな…?」
ありました↓。
参考: rails/active_record_multiple_databases.md at 98a57aa5f610bc66af31af409c72173cdeeb3c9e · rails/rails
### active_record_multiple_databases.mdより
config.active_record.database_selector = { delay: 2.seconds }
config.active_record.database_resolver = ActiveRecord::Middleware::DatabaseSelector::Resolver
config.active_record.database_resolver_context = ActiveRecord::Middleware::DatabaseSelector::Resolver::Session
「この辺の数値って意識しておかないとマズいときがあるんですよ: レプリケーションが詰まったりするとデータの参照に失敗してIDがかぶっちゃうとかありますし」「あ〜」「まあIDをデータベースで生成させていれば問題ないんですけど、別のものを使ってIDを生成しているとIDかぶりで落ちるとか普通に起きますので」「なるほど!」「RailsのマルチプルDBがその辺もちゃんと面倒見てくれるとしたら賢い!記事の方も、マルチプルDBについて取りあえずどのぐらい大丈夫なのかとかを知るのによさそうですね」
Rails 6のAction Textのファイルアップロードを分解調査する
DHHがこの記事をリツイートしてました。
Deep dive into Rails by @_st0012 following the path of a file upload through Trix → ActionText → ActiveStorage and back. https://t.co/KoDDOdBbs6
— Javan Makhmali (@javan) August 9, 2019
つっつきボイス:「これは例のst0012さんが書いた記事で、既に翻訳済みなので近々公開しますけど、オチはAction Textのファイルアップロード機能でN+1クエリ問題を見つけたというものでした(#36177)」「まあAction Textも機能でかそうだからそういうのありそうですし」「data-trix-attachment
とかにTrixエディタの名残りがありますね〜」
「Action Textの情報がなかなか見当たらなかったのでst0012さんは仕方なく自分で調べたそうです」「このあたりは内部実装だから、今はこうでも今後どう変わるかわかりませんけど」「Linuxカーネルの解説書なんかもすぐ古くなりますしっ」
「記事によると、Action Textでは以前もつっつきで話題に出た例のglobalidを使ってるようです(ウォッチ)」「DHHなら使いそう」「しかもSignedGlobalID
使ってる」「不正アクセス防止用」「まだ保存されてないデータでグローバルなIDを使いたい場合はやっぱりglobalid欲しいでしょうね」
参考: rails/globalid: Identify app models with a URI
combustion: Railsエンジンを楽にテスト(Ruby Weeklyより)
つっつきボイス:「combustionって内燃機関の『燃焼』だから、Railsエンジンにひっかけた命名っぽいですね」「これは何がうれしい点かな?」「RailsエンジンをテストするためにRailsアプリを丸ごと作成しなくてもいいようにするとかそういう感じみたいです」「ははぁなるほど、Railsのエンジンをデフォルト設定で作ると確かエンジンテスト用のRailsアプリが作られるという覚えが」「Railsエンジンのリポジトリを作ったときに、中にまるっとRailsアプリが入ってるのはちょい冗長な気はしますね」
「ところでRailsエンジンって、fullとmountableとかあってそれそれでちょっと違ってたような」「え、エンジンに種類があったんですか」「Railsガイドにありますね↓」
参考: Rails エンジン入門 - Rails ガイド
参考: Getting Started with Engines — Ruby on Rails Guides
「--mountable
だとマウンタブルエンジンで、--full
だとフルプラグインということか」「あ、Railsガイドの訳がちょい間違ってた修正します(その後修正済み)」「原文だとfullにさらにオプションを追加したものがmountableということになりますけど↓、まあfullよりmountableの方が機能が多いって普通思わないですし」「直感に反してる」「ある種のワナ?」「原文も単語がちょっと足りないような(負け惜しみ)」
The –mountable option will add to the –full option:
guides.rubyonrails.orgより
「mountableの方が独立性が高くて単独のミニRailsアプリに近そう」「でもfullでも丸ごとRailsが入るのは同じといえば同じかな」「fullオプションだと名前空間化まではやらないらしい」「fullオプションって使いみちあるんだろか?」
参考: Gem、Railtieプラグイン、Engine(full/mountable)の違いとそれぞれの基礎情報 - Qiita
その他Rails
#roppongirb 昨日の資料を公開しました / Rails6にいつ上げるか? https://t.co/lrOLf03m2D
— 神速 (@sinsoku_listy) August 1, 2019
つっつきボイス:「ポイントは『RC1でアップグレードするのがいいよ』ということでした」「RC1で機能がほぼ固まるし、RC2の修正ってそんなに多くは発生しないでしょうし」
前編は以上です。
おたより発掘
うむ。Rails 6へのアップグレードのモチベが高まってきたぞ
https://t.co/T5qhrgfs07— Masafumi Koba (@ybiquitous) August 19, 2019
Arelってpublic APIじゃなかったのかー。lteqとか、良い書き方はどう書くのがいいんだろう?place_holder? / “週刊Railsウォッチ(20190819-1/2前編)祝: Rails 6がついにリリース、日本語RailsガイドもRails 6に対応、Arelはpublicだったかほか” https://t.co/w9HoGI1ZkH
— igaiga (@igaiga555) August 20, 2019
バックナンバー(2019年度第3四半期)
- 20190805-1/2前編 Rails 6のActive Recordは速くなった、Windows WSL2+VSCodeでのRails開発、Martin Fowler記事ほか
- 20190730-2/2後編 Docker 19.03の新機能に注目、ngrokはスゴい、redis-namespaceほか
- 20190729-1/2前編 Rails 6のリリースは近そう?、Evil MartiansのRails+Docker記事、Railsパフォーマンス測定ほか
- 20190723-2/2後編 Rails 6 rc2がリリース、「MySQLパフォーマンスチューニングTips」が超便利、Aurora Serverlessほか
- 20190722-1/2前編 Rails 6エラー画面の改良点、Dateを四捨五入できるtime_calc、Rackミドルウェアのデザインパターンほか
- 20190717-2/2後編 NFSのよさとは、Linuxカーネル5.2リリース、Puppeteerでメモリリーク検出ほか
- 20190709-2/2後編 strong_password v0.0.7がハイジャックされていた、TerraformとCloudFormation、CSSの設計ミスリストほか
- 20190708-1/2前編 ActiveRecord::FixtureSetがめちゃ強くなってた、MacだとRubyが遅い理由、Puma 4登場ほか
- 20190701 RMagickのメモリ使用量が劇的に改善、インスタンス変数の定義順で速度が変わる?、GitLab CIランナーをローカルで回すほか
今週の主なニュースソース
ソースの表記されていない項目は独自ルート(TwitterやはてブやRSSなど)です。