- Ruby / Rails関連
週刊Railsウォッチ(20200601前編)Active Recordに新機能「delegated typing」追加、RuboCopのデフォルト設定アンケートほか
こんにちは、hachi8833です。この記事↓にどよめきました👍🎉😂。
⚓オープニングつっつき: 経産省のnpmモジュールが素晴らしい
- 元記事: 経産省発の npm モジュール!住所や電話番号の正規化、ジオコーディングなどができる IMI コンポーネントツールを試した! - Geolonia developer's blog
- サイト: IMI 情報共有基盤 コンポーネントツール
経産省が住所変換や法人種別名、電話番号の正規化に使えるIMIコンポーネントツールを公開しました。
ソースコードも公開。README にも使い方が丁寧に書かれていました。https://t.co/fPbV00EgZP
素晴らしい動き。こういう... #NewsPicks https://t.co/bew0qGKMFE— Hal Seki (@hal_sk@fosstodon.org)🛡️ (@hal_sk) May 28, 2020
つっつきボイス:「そうそう、経産省がnpmモジュール出してましたね」「個人的にめちゃ感動しました」「やだこれ素晴らしいじゃないですか!」「ようやく登場した感」「住所の正規化が公式の方法でできるのはいいですよね」
「これまでは住所の正規化が民間でまちまちだったから、これが正しい住所だという一致チェックがなかなかできませんでしたし」「そうなんですよ」「これ使えば今滞りまくっている特別給付金の支払い作業もはかどるでしょう、きっと」「ちゃんと使われればですけど」「こういう公式のものがちゃんとあれば使う人は割といるはずですし」
「個人的に1つ注文あります: 住所のふりがな表記とローマ字表記のフィールドもぜひ欲しいです😭」「あ〜たしかに」「ふりがなやローマ字表記がないと読みを正確に翻訳できないんですよ」「住所のふりがなって、まったく同じ地域で同じ漢字なのにふりがなが違うことすらありますよね」「そこが問題なんです」「役所の書類上ではこう読むけど地元の人はこう読む、みたいなこともありそうですよね」
「この手の情報で公式にかろうじて近いものといえば日本郵便のKEN_ALL.csvぐらいしかありませんでしたよね↓」「そうそう、頑張ってKEN_ALL.csv使ってました」「自分も」「郵政省の頃からですからKEN_ALL.csvの時代どんだけ長かったのかと」
「そういえば経産省のnpmには郵便番号のフィールドがありませんね👀」「ほんとだ」「郵便番号データベースってすべての住所番地を網羅してるわけではなくて、『その他』とかで丸められてるところが多数あるんですよ」「そうそう、私書箱とかもありますし、管理が別なんでしょう」「まあ住所を正規化できれば郵便番号は推測できるかな」「住所からの郵便番号逆引きは可能ですね」
「住所のような情報は国が公式に正規化するのが重要👍、民間でばらばらにやるのってマジで意味ありませんし」「まったくです」
豆知識: 原文に含まれる「人名」「地名・住所(ビル名や店名)」「固有名詞(企業名や商品名など)」「動植物の名前」の正確な翻訳は、汎用の機械翻訳が極めて苦手とする作業です。例: honestyは「正直」以外に「ギンセン草」を表すことがあります。
住所といえば、以下の文庫『地名の謎』『番地の謎』は住所や番地についての研究として自分の知る限り最強です。住所改定や平成の大合併など歴史や由来も網羅していて、熱量の高さが素晴らしいです。住所を扱うときに一読をおすすめします。Kindleで読めないのが残念。
- 各記事冒頭には⚓でパーマリンクを置いてあります: 社内やTwitterでの議論などにどうぞ
- 「つっつきボイス」はRailsウォッチ公開前ドラフトを(鍋のように)社内有志でつっついたときの会話の再構成です👄
⚓Rails: 先週の改修(Rails公式ニュースより)
以下のコミットリストのChangelog差分を中心に見繕いました。
⚓新機能: Active Recordにdelegated_type
が追加
DHH自らのプルリクです。説明がかなり長いですが、いずれRailsガイドに載ることを期待します。delegated typingのいい訳語が思いつかないのでそのままにしてあります。
リレーショナルデータベースのテーブルにクラス階層を対応付ける方法はいろいろある。たとえばActive Recordは純粋な抽象クラスを提供してスーパークラスでは属性を永続化しないようにしているし、STI(Single-Table Inheritance)では階層の全レベルの全属性が単一テーブルで表現される。どちらにもそれぞれ使い所はあるが、どちらにも欠点がないわけではない。
純粋な抽象クラスの問題点は、あらゆる具象サブクラスが各テーブルですべての共有属性そのものを永続化しなければならない点(「クラス-テーブル継承」とも呼ばれる)。この場合、階層をまたがるクエリを書くのが難しい。たとえば以下のクラス階層があるとしよう。
Entry < ApplicationRecord
Message < Entry
Comment < Entry
Message
のレコードとComment
のレコードの両方を持つフィードを表示して、楽にページネーションするにはどうすればよいだろうか?これは無理。メッセージの背後にはmessages
テーブルがあり、コメントの背後にはcomments
テーブルがあるが、一貫したOFFSET/LIMITスキームを使って両方のテーブルから一発でデータを取り出すことはできない。STIはページネーションの問題を回避できるが、今度は全サブクラスにあるすべての属性を単一の巨大なテーブルに押し込めざるを得なくなる。クラスがどれほど違っていようとそうなる。
Message
にsubjectがあるがComment
にはない場合、Comment
でもsubjectが使えてしまう!つまりSTIが最適なのは、サブクラス同士やその属性同士の違いが極力少ない場合ということになる。しかしここで第3の方法がある: それが「delegated type」だ。このアプローチでは、「スーパークラス」は独自のテーブルを持つ具象クラスであり、すべての「サブクラス」で共有されるすべてのスーパークラスの属性がそこに保存される。そして各サブクラスも、サブクラスの実装に必要な追加属性用に独自のテーブルを持つ。これはDjangoの「multi-table inheritance」と呼ばれるものに似ているが、実際にはこのアプローチで使うのは継承ではなく、階層を形成して責務を共有するのに「委譲(delegation)」を用いる。
「delegated type」でentry/message/commentを実装した例を見てみよう↓。
# Schema: entries[ id, account_id, creator_id, created_at, updated_at, entryable_type, entryable_id ]
class Entry < ApplicationRecord
belongs_to :account
belongs_to :creator
delegated_type :entryable, types: %w[ Message Comment ]
end
module Entryable
extend ActiveSupport::Concern
included do
has_one :entry, as: :entryable, touch: true
end
end
# Schema: messages[ id, subject ]
class Message < ApplicationRecord
include Entryable
has_rich_text :content
end
# Schema: comments[ id, content ]
class Comment < ApplicationRecord
include Entryable
end
参考: Module#included
(Ruby 2.7.0 リファレンスマニュアル)
ご覧のとおり、
Message
もComment
も孤立していない。2つのクラスの重要なメタデータは「Entry
スーパークラス」にあるが、Entry
は特にクエリ送信能力という点では絶対的に独立可能になっている。これでAccount.entries.order(created_at: :desc).limit(50)
みたいな書き方が楽にやれる。commentsとmessagesをまとめて表示するときに欲しいのは、まさしくこれだ。このentry自身は、その「delegated type」として以下のように簡単にレンダリングできるようになる。
# entries/_entry.html.erb
<%= render "entries/entryables/#{entry.entryable_name}", entry: entry %>
# entries/entryables/_message.html.erb
<div class="message">
Posted on <%= entry.created_at %> by <%= entry.creator.name %>: <%= entry.message.content %>
</div>
# entries/entryables/_comment.html.erb
<div class="comment">
<%= entry.creator.name %> said: <%= entry.comment.content %>
</div>
concernsとコントローラで振る舞いを共有する
この
Entry
スーパークラスは、messagesやcommentsの両方に適用する共有ロジックの完璧な置き場も提供してくれるし、主に共有属性として振る舞う。以下で考えてみよう。
class Entry < ApplicationRecord
include Eventable, Forwardable, Redeliverable
end
上のように書くことで、entriesで
ForwardsController
やForwardsController
のように振る舞うコントローラが手に入る。しかもmessagesとcommentsの両方で共有する機能も提供できる。新規レコードの作成
「delegated typing」を用いてレコードを1件作成するには、次のようにdelegatorとdelegeteeを同時に作成する。
Entry.create! message: Comment.new(content: "Hello!"), creator: Current.user
もっと複雑なコンポジションが必要な場合や、依存バリデーションの実行が必要な場合は、factoryメソッドかクラスを構築して、その複雑なニーズの面倒を見てやるべきだ。これは以下のようにシンプルにやれる。
class Entry < ApplicationRecord
def self.create_with_comment(content, creator: Current.user)
create! message: Comment.new(content: content), creator: creator
end
end
delegationをさらに追加する
「delegated type」は、背後のクラスで何が呼ばれているかという問いに単に答えるだけのものであってはならない。実は、それはほとんどの場合アンチパターンになる。この階層を構築するのは、ポリモーフィズムのメリットを享受するのが目的である。シンプルなコード例を以下に示す。
class Entry < ApplicationRecord
delegated_type :entryable, types: %w[ Message Comment ]
delegates :title, to: :entryable
end
class Message < ApplicationRecord
def title
subject
end
end
class Comment < ApplicationRecord
def title
content.truncate(20)
end
end
これでentriesをいくつでもリストして
Entry#title
を呼び出せるようになり、ポリモーフィズムが答えを提供してくれるようになる。
同コミットより大意
つっつきボイス:「時間が取れたので、この長いプルリクメッセージをガガガッと大急ぎで訳してみました(訳さないと理解が遅くて😅)」「delegated typing?」「説明長い〜」
「STI(Single-Table Inheritance)のつらみを避けるためにDHHが実装したということみたい」「普通のモジュールインクルードとどう違うのかな?👀」
参考: STI -- Active Record の関連付け - Railsガイド
「どれどれ、以下みたいな設計でMessageとCommentの両方にあるものをリストとして引っ張ってくるときは、通常ならEntryに対してクエリを投げるけど、当然ながらMessageやCommentのメソッドは呼び出しづらくなるし、STIだと説明にもあるように全サブクラスの属性が1個のテーブルに入ってしまってテーブルがでかくなると」「DHHも『どちらにも使い所はあるけどそれぞれ欠点がある』って書いてる😆」
# 同PRより
Entry < ApplicationRecord
Message < Entry
Comment < Entry
「それがdelegated typingだとそれぞれが独自テーブルを持つからSTIの形にはならなくなるのか」「DHHは『第3の方法』と呼んでますね」「第3😆」「Djangoのmulti-table inheritanceにも似てるみたい」
参考: Multi-table inheritance -- Models | Django documentation | Django
「うん、サンプルコード↓を見るとEntryもMessageもCommentも単にApplicationRecordを継承してる: なるほどだんだん見えてきた😋」「delegate typingでは、共通のカラムはEntryにあるけど差分のテーブルはMessageやCommentにあって、それらがデータベース上Entryへの参照を持っていると」「EntryはEntryableモジュールを取ってくることでワンクッション置いてるのね☺️」
# 同PRより
# Schema: entries[ id, account_id, creator_id, created_at, updated_at, entryable_type, entryable_id ]
class Entry < ApplicationRecord
belongs_to :account
belongs_to :creator
delegated_type :entryable, types: %w[ Message Comment ]
end
module Entryable
extend ActiveSupport::Concern
included do
has_one :entry, as: :entryable, touch: true
end
end
# Schema: messages[ id, subject ]
class Message < ApplicationRecord
include Entryable
has_rich_text :content
end
# Schema: comments[ id, content ]
class Comment < ApplicationRecord
include Entryable
end
「これもうマージされてます?」「されました😋」「おぉ〜、じゃ次のRailsをバージョンアップしたら入ってくるのかな?😋」「masterブランチにマージされただけだとどのバージョンに入るかは決まりませんけど、でかい機能追加なのでRails 6.1あたりになりそうですね☺️」「これはいい機能👍」
「DHHの説明長いので、ウォッチ公開した後で切り取って別記事にしようと思います: Railsガイドに追加されるまでのつなぎということで」「お〜いいんじゃないでしょうか☺️」
⚓STIよもやま話
「delegated typing、STIキライな人によさそう😆」「この書き方もっと前からできててもよかったかも」「公式にこれが入ったことが大きいですね😂」「このプルリクに『いいね👍』が大量に付いてました」「STIキライな人多いので😆」
「もちろんSTIでも問題なくやれる場合はあります: STIで一番問題になりやすいのは、まさにこのサンプルコードでやっているみたいに、リッチテキストのようなむちゃくちゃ大きいコンテンツが入ってくるカラムをSTIで増やすと行サイズがバカでかくなること😇」「そうですね😅」「ほとんどのカラムが共通カラムで、ごく一部のカラムだけが特定のテーブル用に追加される形(integerカラムをちょっと足す程度)なら、STIでも大したことにはなりませんし🧐」「ふむふむ」「STIで特定のテーブルにしかないカラムが長大になると(text型とか)、データベース上の無駄が多すぎるんですよ😢」
「そういう長大なカラムを使う場合は明らかにこのdelegated typingの方がいいでしょうね❤️: データモデル的にもキレイですし、オブジェクト指向プログラミングでよく言われる『なるべく委譲しよう』的な考え方ですし🧐」「おぉ😍」「サブクラスを増やして継承するより先にデリゲートを検討しましょう、コンポジションしましょうというヤツ☺️」「なるほど!」
参考: Rubyによるデザインパターン5原則 - Qiita
「もちろんどんな場合でもdelegated typingがいいわけではないと思います: STIの方が速くなることも普通にありますので🚀」「設計の選択肢が増えるのはいいこと👍」
⚓スキーマにENGINE=InnoDB
をデフォルトで含めないようになった
スキーマをよりagnostic(=決め打ちしない)にするため、デフォルトのエンジン
ENGINE=InnoDB
をダンプしないようにした。
Changelogより
# Changelogより
# before
create_table "accounts", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci", force: :cascade do |t|
end
# after
create_table "accounts", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
end
つっつきボイス:「@kamipoさんの改修です」「そうそう、昔はMySQLでENGINE=InnoDB ROW_FORMAT=DYNAMIC
しないとutf8mb4でインデックス作れなかった、なつかしい」「今のMySQLではENGINE=InnoDB
は不要だから出さなくなるのもわかる」「そこは時代が変わったということで」
「MyISAMを使い続ける人、今もたまに見かけますよ」「いるんですか?」「ソシャゲ界隈あたりだったかな?、たぶん枯れているものしか使わないポリシーなのかも」「MyISAMはログDBに向いてるって昔教わったことあります」「MyISAMで参照が発生しないカラムサイズにうまく押し込められればいいでしょうね: MyISAMは長年チューニングされてますし」
参考: 漢(オトコ)のコンピュータ道: MyISAMからInnoDBへ切り替えるときの注意点
⚓キャッシュフラグメントキーに関連テンプレートをすべて含めるようになった
まとめ
Railsビューは、アクションに対応するテンプレート(show.html.erbなど)のレンダリング時に(レイアウトやパーシャルなどの)複数のテンプレートにも依存することがある。現在レンダリング中のビューファイルをトラッキングしてキャッシュフラグメントキーで使うテンプレートツリーのダイジェストを生成するために、Action Viewはスタックを使っているが、スタックのトップには
@current_template
変数経由でアクセスされる(1581cabで導入)。
以下のテンプレートで考える:
<!-- home.html.erb -->
<%= render layout: "wrapper" do %>
<%= cache "foo" %>
HOME
<%= end %>
<%= end %>
上の
render
ヘルパーに渡されるブロック内の@current_template
は、(home.html.erb
ではなく)wrapper.html.erb
テンプレートに対応する。そしてキャッシュフラグメントキーで使われるテンプレートツリーダイジェストの生成にはwrapper.html.erb
が使われるので、home.html.erb
が変更されてもキャッシュフラグメントの有効期限が期限切れしなくなってしまう。さらに、2番目のテンプレートでwrapper.html.erb
レイアウトが使われ、キーが同じキャッシュフラグメントを含んでいると、2つのテンプレートのキャッシュフラグメントキーが同一になってしまい、あるビューのキャッシュの中身が別のビューにお漏らししてしまう(#38984)。
このプルリクは、render
ヘルパーにブロックを与えてテンプレートをレイアウトとしてレンダリングした場合にスタックにテンプレートを追加する。これにより、@current_template
が現在レンダリング中のビューファイルに正しく対応するようになり、キャッシュフラグメントダイジェストが一意かつ有効期限も正しく期限切れになる。さらに、CacheHelper
で見つけたvirtual_path
キーワード引数を削除し、そこに関数が置かれることのないようにした。(@current_template
の導入後、virtual pathは単独の変数としてではなく、@current_template.virtual_path
経由でアクセスされるようになっていた)。最後に、@virtual_path
インスタンス変数をコードベースから完全に削除し、そこへの参照をすべて@current_template.virtual_path
に置き換えた。その他の変更点
actionview/test/template/render_test.rb
で使われていたコントローラーキャッシュストアがnil
だったのを:memory_store
が設定されるようにした(このプルリクで修正された「失敗するテスト」を提供するために必要)。その他の情報
このプルリクは2つのコミットに分かれている。最初のコミットは、テストがすべて通ってしまう#38984のバグを最小限の形で修正するのに必要。次のコミットは、余分な
@virtual_path
変数をコードベースから完全に取り去って@current_template.virtual_path
に置き換えるリファクタリング。2つに分けたのはレビューしやすくするためで、#38984の修正で変更された点や無関係なリファクタリングを追いやすくする目的だが、マージの前に1つのコミットにrebaseしてもらえるとうれしい。
同コミットより大意
つっつきボイス:「これも説明長かったので同じくガガッと翻訳してみました」「『キーが同じキャッシュフラグメントを含んでいると2つのテンプレートのキャッシュフラグメントキーが同一になってしまう』ようになってたとは」「自分はこんなにキャッシュフラグメントキーを細かく指定したことがほぼないのでこれ踏んだことなかった」「キャッシュフラグメントキー、昔使ってみたけどややこしいことになりました」
<!-- 同PRより -->
<!-- home.html.erb -->
<%= render layout: "wrapper" do %>
<%= cache "foo" %>
HOME
<%= end %>
<%= end %>
「<%= cache "foo" %>
の部分って普通はActive Recordとかのオブジェクトを指定することが多いので、そういうものなら同一のものを指していても問題ないんですけど、サンプルコードみたいに固定値を指定するとこういうことが起きちゃってたんですね」「キャッシュフラグメントキーなつかしいから後でちょっと試してみようかな」
参考: 1.3 フラグメントキャッシュ -- Rails のキャッシュ機構 - Railsガイド
⚓group
を重複フィールドで集約することが非推奨化
- PR: Deprecate aggregations with group by duplicated fields by kamipo · Pull Request #39358 · rails/rails
クエリのannotate
で付けたメッセージの重複も非推奨化されています。
つっつきボイス:「重複例ではキーが[1, 1]
みたいに配列になってますね↓」
accounts = Account.group(:firm_id)
# グループ化のフィールドが重複する(非推奨化)
accounts.merge(accounts.where.not(credit_limit: nil)).sum(:credit_limit)
# => {
# [1, 1] => 50,
# [2, 2] => 60
# }
# グループ化するフィールドの重複解消には今後`uniq!(:group)`を使うこと
accounts.merge(accounts.where.not(credit_limit: nil)).uniq!(:group).sum(:credit_limit)
# => {
# 1 => 50,
# 2 => 60
# }
「新しく追加されたuniq!(:group)
↓で重複解消して欲しいそうです」「その方がよさそう」「これを追加したから重複を非推奨化したという流れなんでしょうね」
# 同PRより
# Deduplicate multiple values.
def uniq!(name)
if values = @values[name]
values.uniq! if values.is_a?(Array) && !values.empty?
end
self
end
「uniq!(:group)
って書き方、ぱっと思いつけるだろうか?」「思いつけなさそう」「ともあれこういう正しい書き方が提供されたところに意味がありますね👍」
⚓キーワード引数関連の改修多数
つっつきボイス:「@kamipoさんのキーワード引数関連改修です」「みんな大好きキーワード引数」
# activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb#L376
- def column(name, type, **options)
+ def column(name, type, index: nil, **options)
name = name.to_s
type = type.to_sym if type
if @columns_hash[name]
if @columns_hash[name].primary_key?
raise ArgumentError, "you can't redefine the primary key column '#{name}'. To define a custom primary key, pass { id: false } to create_table."
else
raise ArgumentError, "you can't define an already defined column '#{name}'."
end
end
- index_options = options.delete(:index)
- index(name, index_options.is_a?(Hash) ? index_options : {}) if index_options
@columns_hash[name] = new_column_definition(name, type, **options)
+
+ if index
+ index_options = index.is_a?(Hash) ? index : {}
+ index(name, **index_options)
+ end
+
self
end
「お、column()
にindex:
キーワード引数が追加されたのかと思ったら、index:
オプションが**options
から取り出されてキーワード引数になったということか」「なるほど!」「今まではindex:
オプションをoptions = {}
で受けていた」「index:
の指定は必須かなと思ったら、デフォルト値がnil
だからindex:
キーワードは省略できるのね」「options = {}
というオールドスタイルな書き方も**options
に置き換えられた」「options = {}
が置き換わっていくのうれしい」
⚓Rails
⚓「RuboCopのデフォルト設定」に関するアンケート結果
ruby-jp Slackで知りました。最新のRuby Weeklyでもトップに上がっていました。
つっつきボイス:「ruby-jp Slackでも話題になってました🎉」「そうそう、ちょうどさっきkoicさんがLayoutLineLength
のデフォルトを120文字に変えた記事↓を見てマジ感動しました」
⚓1行の最大文字数はいくつがいいか
「line lengthは120文字欲しい!」「これはオレたちの待ち望んでいた改修」「koicさん記事の"いつかこの変更をするのが目標のひとつだった"、わかる」「80文字のつらさは異常」「80はない」「コンソールが80文字x25行だったのって相当昔ですよね」「まあ1行があまりにもでかいのも考えものなんですけど、ビジネスロジックを真面目に書いているとせめて120文字は欲しい」「メソッドチェインのたびに改行するのもあまりやりたくないかも」
⚓文字列リテラルは""
で囲むか''
で囲むか
「クォートのスタイルはどんな結果になったかな?」「シングルクォート派は58%」「Rubyのシングルクォート' '
とダブルクォート" "
は意味が違うので、自分は両方使い分けるでいいと思います」「同感」「悩むぐらいならRuboCopに直させればいいと思います」
参考: 文字列リテラル -- リテラル (Ruby 2.7.0 リファレンスマニュアル)
⚓コレクションリテラルのケツカンマはどのスタイルがいいか
# 同記事より
# ケツカンマなし
{ one: 1,
two: 2 }
# 複数行リテラルの場合のみケツカンマ
{ one: 1,
two: 2, }
{ one: 1, two: 2 }
# 常にケツカンマ
{ one: 1,
two: 2, }
{ one: 1, two: 2, }
「trailing comma付けない人多いんだ」「付けたくない気持ちもわかる」「まあこれもRuboCopにお任せで」
⚓and
やor
は使うか使わないか
「優先順位の低いand
演算子やor
演算子は以前から使わない方がいいって言われてますし」「記事にもありますけど、例外はRailsコントローラのリダイレクト and return
みたいなフローコントロールのとき」「Railsではイディオムになってますね」「そうそう、Railsでこの書き方は認められてますし、自分もあっていいと思いますけど、なくすのはそれはそれでありかもって思います」
参考: 演算子式 (Ruby 2.7.0 リファレンスマニュアル)
参考: 2.2.14 二重レンダリングエラーを避ける -- レイアウトとレンダリング - Railsガイド
⚓!!
は使うか使わないか
「!!
は好きでもキライでもないかな」「ダブルビックリ」
⚓ハッシュリテラル{}
のすぐ内側にスペースを置くか置かないか
「ハッシュリテラルのすぐ内側にはスペース置きたい派({ key: :value }
)」「私もスペースないと窮屈な感じします」「ある方が見やすいけどたまに書き忘れます」
⚓Kernel
メソッドの引数を()
で囲むか囲まないか
「Kernel
メソッドの引数に()
付けるか問題」「自分は付けない」「exit
なら付けるかもだけどputs
には付けないかな」「自分はあんまり意識しませんし、そもそもKernel
メソッドかどうかも気にしてない」
参考: module Kernel
(Ruby 2.7.0 リファレンスマニュアル)
⚓Metrics copは便利か
「Metrics copキライな人が多いから聞いてみたとありますね」「まあうるさいのはたしか」「でもないよりはある方がいい」「うるさかったらRuboCopの設定変えれば済みますよね」「記事の下に書いてあるこのyaml設定↓でMetricsをまとめて殺せるとありますね」「Metricsを全部無効にしたいと思うことってあんまりなさそうですけど」「たしかに」
Metrics:
Enabled: false
参考: Metrics Cops - RuboCop: The Ruby Linter that Serves and Protects
⚓RuboCopを素の設定で使ってどのぐらい幸せになれるか
「素のRuboCop設定でどのぐらい幸せか問題」「これはもう人それぞれでしょう」「単なるhappyが一番多いけど、very happyとまではいかないということ?」「デフォルトでいいと思ってる人が大多数なんでしょうね」「デフォルトだとちょっとつらいかも」「デフォルトのスタイルがあることが大事だと思います: スタイルにデフォルトがないと迷ったときに不便」
「そういえばRailsがジェネレートするRubyコードって素のRuboCopに怒られますよね」「そうそうめっちゃ怒られる、オレが書いたんじゃないのに」「RailsジェネレーターのRubyコードはもっと素のRuboCopに寄せてもいいと思います」
⚓delete_in_batches: Active Recordのバルク削除(Ruby Weeklyより)
# 同リポジトリより
# Slow
Tweet.where(user_id: 1).delete_all
# DELETE FROM tweets WHERE user_id = 1
# Faster
Tweet.where(user_id: 1).in_batches(of: 10000).delete_all
# SELECT tweets.id FROM tweets WHERE user_id = 1 ORDER BY id LIMIT 1000
# DELETE FROM tweets WHERE user_id = 1 AND id IN (1, 2, 3, ...)
# ...
# Fastest
Tweet.where(user_id: 1).delete_in_batches
# DELETE FROM tweets WHERE id IN (SELECT id FROM tweets WHERE user_id = 1 LIMIT 10000)
# DELETE FROM tweets WHERE id IN (SELECT id FROM tweets WHERE user_id = 1 LIMIT 10000)
# ...
つっつきボイス:「ankaneさんのARバルク削除gemだそうです」「こんなのあるんですね」
「fastestつおい」「これはロックを小さくしてDELETEする感じかな」「おぉ?」「slowのTweet.where(user_id: 1).delete_all
だとロックがかかって遅くなるけど、fasterだとDELETE FROM tweets WHERE user_id = 1 AND id IN (1, 2, 3, ...)
の中にトランザクションが挟まれるようになって速くなるんだろうと想像してます🚀: DELETEする対象が大量にある場合に速くするということなんでしょう」「よさそう」「Railsのfind_in_batches
↓と対をなす感じの名前ですね」
⚓TRUNCATE
にご注意
「ところでREADMEに『全部消すならTRUNCATE
が最速だぞ』って書いてあるんですけど」「それは爆速だけど危険」「これをギャグだとわかって読める人が使うべきでしょう」「READMEに書くと本気にする人出そうでコワイ」「TRUNCATE
書いてもコードレビューで却下されるでしょうね」
# 同リポジトリより
ActiveRecord::Base.connection.execute("TRUNCATE tweets")
参考: TRUNCATE と DELETE の違い - オラクル・Oracleをマスターするための基本と仕組み
参考: PostgreSQLでTRUNCATEをROLLBACKした際の内部の挙動を追いかけてみた | Developers.IO
⚓命名の基準
つっつきボイス:「jnchitoさんのQiita記事です」「どこかでちらっと見かけたかも」「そもそも基準ってあるのかと思ったら、Stack Overflowレベルで一応それっぽい回答はあるらしい」「実は前に自分のブログでこのネタ書きました」
「ActionだっけActiveだっけって割と迷いますよね」「迷います」「モデル層に属するコンポーネントはActiveという考え方は割と腑に落ちます😋」「Action Viewはビューに近いからActionになったとは」
「ところでActive StorageとActive Supportを省略すると、どちらもASになっちゃう」「Action MailerとActive ModelもAMになってしまう」「惜しい」「この辺に一意な省略形があるといいんですけどね〜」
⚓Rails製ゲームサイトの応答速度を5倍改善(Ruby Weeklyより)
- 元記事: Catch a batch: Making Mayhem 5 times more responsive — Martian Chronicles, Evil Martians’ team blog
Evil Martians記事です。
つっつきボイス:「Evil Martiansが手掛けているらしいMayhemとかいうゲームコミュニティプラットフォームのレスポンスを改善したということらしいです」「Mayhem見たことない」「自分もゲーム音痴なので詳しくはわかりません」「全然ちゃんと見てないけど、ゲームのロビーマッチングをやってるサービスなのかしら?」「サイトにもlobby nameってありますね」
これに参加したらいいらしいです!
— Noloa (@nlxgg) April 10, 2020
「記事ではGraphQLや、以前ウォッチでちょっとだけ取り上げた同社のAction Policyを使ってるようです(ウォッチ20180420)」「GraphQLになるのわかる」「Action Policyはauthorizationのgemか」「それをGraphQLと使ってるっぽい」
- リポジトリ: palkan/action_policy: Authorization framework for Ruby/Rails applications
- サイト: Action Policy: authorization framework for Ruby/Rails applications
「そしてレスポンスが5倍改善したと↓」「開発方法がちょっと特殊になったので開発者を集めて特訓したそうです🏋🏻♀️」「Evil Martians、ゲーム系もやってるのね」
「Mayhemって『暴動』みたいな意味らしくて、同名のヘビメタバンドもあるのでややこしい」「Mayhemってギリシャ語とかラテン語あたりの神様っぽい雰囲気のスペルかな?⛩」「そういえばどことなくヘブライ語っぽい響きかも?🇮🇱」
後で調べるとmayhemは古フランス語由来のようです。
参考: mayhem | Origin and meaning of mayhem by Online Etymology Dictionary
前編は以上です。
おたより発掘
delegate typingかなり嬉しい!今までcti無理やり自作してたからフレームワークレベルで対応してくれるの良い!しかも委譲の形なのもさらに良い!
週刊Railsウォッチ(20200601前編)Active Recordに新機能「delegated typing」追加、RuboCopのデフォルト設定アンケートほか https://t.co/fmKeQCwf7W
— Hidetaka Okita (@hokita222) June 2, 2020
バックナンバー(2020年度第2四半期)
週刊Railsウォッチ(20200526後編)Rubyでよくやるスレッドバグ、Kubernetesでよくあるミス10、CSS/SVG/Canvasの使い分けほか
- 20200525前編 2020年のRailsマストgem 19個、スライド『Fat Modelの倒し方』、AR mergeのrewhereオプションを変更ほか
- 20200519後編 Rails 5と6のセキュリティ修正、Ruby 3.0のGuildがRactorに名前変更、Node作者によるDeno登場ほか
- 20200518前編 スライド『令和時代のRails運用』、Ruby 3.0のキーワード引数変更リスケ、Action CableのCLIほか
- 20200512後編 RubyのPStoreライブラリ、Lambda StoreのサーバーレスRedisは有能、Amazon Linux 2のライブパッチほか
- 20200511前編 Rails 6.0.3リリース、rails newに–masterオプションが追加、system specとfeature specの違いほか
- 20200428後編 Rubyのバックトレース順序が戻る、KubernetesでRailsをスケール、セキュリティソフト入れますか?ほか
- 20200427前編 Railsで避けたい8つのミス、ridgepole導入の注意点、RDS ProxyのPostgreSQL対応ほか
- 20200421後編 Ruby 2.4サポート終了、Ruby 3の右代入演算子、GitHubコア機能無料化ほか
- 20200420前編 anyway_config gemでRails環境設定、ShopifyのLiquidテンプレートエンジン、書籍『Beyond the Twelve-Factor App』ほか
- 20200414後編: Ruby 3で”endレス”メソッド定義構文が追加、ECMAScript 2020の新機能、紛失防止デバイスほか
- 20200413前編: 最近macOSでRailsが遅い、トランザクションでのreturnやbreakなどが非推奨化、Rails監視ツールリスト2020年度版ほか
- 20200407後編: RubyのTracePointでデバッグ、Rubyとモナド、Gitノウハウ集、リモートワークほか
- 20200406前編: Ruby 2.7.1セキュリティ修正、RailsビューHTMLにテンプレート名を出力、Action Mailboxテスト用フォーム改良ほか
今週の主なニュースソース
ソースの表記されていない項目は独自ルート(TwitterやはてブやRSSやruby-jp SlackやRedditなど)です。