- Ruby / Rails関連
週刊Railsウォッチ(20190930前編)知られざる7つの便利gem、Duration.buildにstringを渡せなくなる、Webpackerのpacksをマスターほか
こんにちは、hachi8833です。Google Translator Toolkitが12月4日にディスコンになるそうなので忘れないうちにファイルをGTTからダウンロードしておきました。
なんと... 😳💦
— 安川要平/Yohei Yasukawa (@yasulab) September 20, 2019
つっつきボイス:「終わっちゃうんだ😳」「GTTは自分も最近めっきり使ってませんでしたが、突然の終了宣言で😇」「見た感じユーザー数は少なそうではありますけど😆」「ローカライズに関心のある人ぐらいしかいないと思いますので、きっとそうです😆」「マイグレーションパスとかもないのかしら?」「終了までの間にデータを逃がすことはできるみたいですけど、マイグレーションパス的なのはない感じですね: たぶん今後はGoogle内部でしか使わないのかも🤔」「無料とはいえ他に類似の無料ツールがあんまりないから、これに頼ってた人は泣くしかなさそう: 他の商用ツールを使ってくれという感じですかね」「Googleのハシゴ外し伝説がまた1つ増えました😭」
- 各記事冒頭には⚓でパーマリンクを置いてあります: 社内やTwitterでの議論などにどうぞ
- 「つっつきボイス」はRailsウォッチ公開前ドラフトを(鍋のように)社内有志でつっついたときの会話の再構成です👄
- 毎月第一木曜日に「公開つっつき会」を開催しています: お気軽にご応募ください
⚓週刊Railsウォッチ「公開つっつき会」第15回のお知らせ(無料)
第15回目公開つっつき会は、今週10月5日(木)19:30〜にBPS会議スペースにて開催されます。週刊Railsウォッチの記事にいち早く触れられるチャンスです!皆さまのお気軽なご参加をお待ちしております🙇。
⚓Rails: 先週の改修(Rails公式ニュースより)
今回は公式の更新情報があったのでやりやすくて助かりました😋。
⚓キャッシュストアから複数のキーを削除するActiveSupport::Cache::Store#delete_multi
メソッドを追加
キャッシュからキーのarrayを削除する
ActiveSupport::Cache::Store#delete_multi
を追加。
ほとんどの実装ではActiveSupport::Cache::Store#delete
を単にn回読んでいるが、Redisはキーのarray削除をサポートしている。従来のdeleteをn回呼び出していたのを1回の呼び出しにできるので、パフォーマンス向上が期待できる。
なおMemcachedはマルチdeleteをサポートしていない。
同PRより大意
# activesupport/lib/active_support/cache.rb#L480
+ def delete_multi(names, options = nil)
+ options = merged_options(options)
+ names.map! { |key| normalize_key(key, options) }
+
+ instrument :delete_multi, names do
+ delete_multi_entries(names, options)
+ end
+ end
...
+ def delete_multi_entries(entries, options)
+ entries.inject(0) do |sum, key|
+ if delete_entry(key, options)
+ sum + 1
+ else
+ sum
+ end
+ end
+ end
つっつきボイス:「キーのarrayを削除する機能が追加された❤️」「キャッシュエンジンがキーのarrayを一発削除する機能をサポートしているなら、それを使うほうが絶対速いですね👍」「イミュータブルだろうし」「Redisだと効くようになるとPRメッセージにありますね😋」「なるほど!」「大量の削除を大量のAPI呼び出し発行でやるのはしんどいですから😆」
⚓Action Mailerにemail_address_with_name
を追加
現状のガイドでは
mail(to: %("#{@user.name}" <#{@user.email}>))
のように自力でフォーマットするようアドバイスしている。
残念ながらnameがAaron "Tenderlove" Patterson
とか-_-" <3
の場合は破綻する。さらに困るのは、メールは送信完了したと暗黙のうちに思わせてしまうこと。実際にはこれを理解できないメールサーバーに飲み込まれてしまう。
今回の変更で、email_address_with_name
メソッドでmail(to: email_address_with_name(@user.email, @user.name))
のようにフォーマットを扱えるようになる。これは内容を正しくエスケープできるようにする手法のひとつとなる。
同PRより大意
# actionmailer/lib/action_mailer/base.rb#L593
+ def email_address_with_name(address, name)
+ Mail::Address.new.tap do |builder|
+ builder.address = address
+ builder.display_name = name
+ end.to_s
+ end
...
+ def email_address_with_name(address, name)
+ self.class.email_address_with_name(address, name)
+ end
つっつきボイス:「ここで行っているnameのエスケープというのが何が目的なのかわからなくて😅」「あ〜なるほど!: これはですね、メールのto:
ヘッダーとかでは、以下みたいにdisplay nameの後ろに< >
で囲んでメールアドレスを示すなどの仕様がメール関連のRFCに記載されているんですよ😎」「おぉ〜!」「MTAはこういう仕様を頼りにヘッダーから送信先メールアドレスを取り出します☺️」
mail(to: %("#{@user.name}" <#{@user.email}>))
「そして今のRailsではそれをメソッドとして実装できないので、RFCの仕様を知っている人が自力で実装することになっているんですけど、やっぱりこういうふうにメアドと表示名をエスケープしてくれるヘルパーメソッド↓があった方がいいよねということで今回足したんでしょうね☺️」「はぁ〜なるほど!」「表示名に<
や>
や"
があったらエスケープするとかはどのみちやらないといけないので」
mail(to: email_address_with_name(@user.email, @user.name))
参考: 3.4 RFC2822(Internet Message Format)
参考: 3.4 RFC 2822 - Internet Message Format -- 原文
「RFC 2822のこの辺の仕様ですね↓」「そういうことでしたか😳」「いにしえの昔からこの仕様です😎」
「上のname-addrは[display-name] angle-addr
だからdisplay-nameはオプション、そしてdisplay-name = phrase
、phraseはこのRFCを探すと3.2.6でphrase = 1*word / obs-phrase
、そしてobs-phraseは4.1でobs-phrase = word *(word / "." / CFWS)
だからダブルクォートで囲む...という仕様」「こういう感じで仕様を辿るんですね😋」
「というような仕様を満たすためのemail_address_with_name
メソッドがRailsに足されたという話」「このemail_address_with_name
という名前がちょい長い気はしますけど😆、いいんじゃないでしょうか」「😆」
⚓ActiveSupport::Duration.build()
がNumericのみを受け取るように変更
今回の変更は、型が
::Numeric
でない場合にActiveSupport::Duration.build(value)
によってActiveSupport::Duration
インスタンスが作成されることを防ぐ(でないとTypeError
になる)。
この修正によって、#37012のさまざまな振る舞い(ActiveSupport::Duration
が原因不明で失敗する、文字列からビルドしたdurationの比較結果がおかしくなる)が修正される。
同PRより大意
つっつきボイス:「Duration
にbuild
があるとは😳」「しかもstring入れられる!ひゃ〜😅」「数字を入れると秒として認識されると」「従来はsmall_duration_from_int < large_duration_from_string
という比較↓でエラーになる😇」「durationをintから作ったかstringで作ったかで違ってくると」「よく見つけたな〜😳」「知らずに踏みそうなバグ」「どうやってビルドしたかとか普通関知しないですし😆」「to_i
したらむしろそのせいで壊れたりしそう😆」
# Changelogより
# 修正前:
small_duration_from_string = ActiveSupport::Duration.build('9')
large_duration_from_string = ActiveSupport::Duration.build('100000000000000')
small_duration_from_int = ActiveSupport::Duration.build(9)
large_duration_from_string > small_duration_from_string
#=> false
small_duration_from_string == small_duration_from_int
#=> false
small_duration_from_int < large_duration_from_string
#=> ArgumentError (comparison of ActiveSupport::Duration::Scalar
# with ActiveSupport::Duration failed)
large_duration_from_string > small_duration_from_int
#=> ArgumentError (comparison of String with ActiveSupport::Duration failed)
# 修正後
small_duration_from_string = ActiveSupport::Duration.build('9')
#=> TypeError (can't build an ActiveSupport::Duration from a String)
「今回の修正を見ると、Numeric
しかビルドしないことにしたのか↓」「まあ文字列使わなければいいんでしょうけど」「でもこれはDurationを文字列でビルドしてた人にとってはbreaking changesになるから対応が必要になるでしょうね⛔️」「そうか!」「API側で文字列をto_i
してもいいような気もするけど、文字列のto_i
って割と事故りやすいんですよね😅」「たしかに〜😆」「この#37013はもうマージされたんだなあ」
# activesupport/lib/active_support/duration.rb#L183
def build(value)
+ unless value.is_a?(::Numeric)
+ raise TypeError, "can't build an #{self.name} from a #{value.class.name}"
+ end
+
parts = {}
remainder = value.to_f
PARTS.each do |part|
unless part == :seconds
part_in_seconds = PARTS_IN_SECONDS[part]
parts[part] = remainder.div(part_in_seconds)
remainder = (remainder % part_in_seconds).round(9)
end
end
parts[:seconds] = remainder
new(value, parts)
end
「build
にはそもそもstring渡すなという話なんだろうな🤔」「build
がドキュメントでどう扱われているかを参照すべきでしょうね↓」
「以下のサンプルコード↓は載っているけど、yardのドキュメントに書かれてるわけではなかった😅」「つまりstringを渡せるとは書かれていないけど、渡しても動いてた😆」「内部でto_i
してくれてるよねきっと、みたいな気持ちになるのは、わかる気はする☺️」
# APIより
ActiveSupport::Duration.build(31556952).parts # => {:years=>1}
ActiveSupport::Duration.build(2716146).parts # => {:months=>1, :days=>1}
⚓watcherに渡すファイルやディレクトリの扱いを修正
現在のwatcherに渡されるautoloadパスがディレクトリになっている。evented watcherを使うと、これが
Listen
にそのまま渡される可能性がある。しかしautoloadパスにはファイルが含まれているので、ファイルが渡されるとListen
でエラーが発生する。このため、ファイルとディレクトリを正しく仕分ける必要がある。
#37011が修正される。
同PRより大意
# railties/lib/rails/application.rb#L350
def watchable_args #:nodoc:
files, dirs = config.watchable_files.dup, config.watchable_dirs.dup
ActiveSupport::Dependencies.autoload_paths.each do |path|
- dirs[path.to_s] = [:rb]
+ File.file?(path) ? files << path.to_s : dirs[path.to_s] = [:rb]
end
[files, dirs]
end
つっつきボイス:「修正はわずかですけど、watcherっていうのがRailsにあるとは知りませんでした😅」「これだけ見ると、いわゆるファイル変更を監視するwatcherなんじゃないかな?🤔」「Railsのdevelopmentモードでよくやる、ファイルが更新されたらリロードするみたいな?」「gemでそういうのありましたけど、名前が出てこない😅」
guard gemでした↓。
「この修正ではevented watcherと言ってるので変更イベントの通知用でしょうね」「以下の記事に『ファイルの変更を検知しているのはapp/config/environments/development.rb
のfile_watcher
の部分』とあるから、これのことか」「FileUpdateChecker
もあればEventedFileUpdateChecker
もあると😆」「ポーリングもやれるということでしょうね☺️」
参考: dockerでrails5環境構築 - Qiita
参考: Rails アプリケーションを設定する - Rails ガイド
config.file_watcher
:config.reload_classes_only_on_change
がtrueの場合にファイルシステム上のファイル更新検出に使われるクラスを指定します。デフォルトのRailsではActiveSupport::FileUpdateChecker
、およびActiveSupport::EventedFileUpdateChecker
(これはlisten
に依存します)が指定されます。カスタムクラスはこのActiveSupport::FileUpdateChecker
APIに従わなければなりません。
Railsガイドより
⚓ファイル更新通知について
「ファイル更新のトリガー通知って、内部的にはD-Bus使ったり、LinuxだとinotifyとかMacだとFile System Eventsみたいにいろいろあるんですけど、POSIXに仕様がないんですよ」「あ〜」「だからこういう機能は基本的にOS依存になります」「知らなかった〜😅」「OSによってやり方を変えるしかないんですね😅」
参考: D-Bus: DBusWatch
参考: Man page of INOTIFY
参考: macos - Is there a command like "watch" or "inotifywait" on the Mac? - Stack Overflow
「お、fswatch↓はクロスプラットフォームらしい?😳」「リポジトリの冒頭を見ると、macOSではFile System Events、BSD系だとkqueue、Linuxだとinotify、Windowsだとstat()
ベースのバックエンドってあるから、内部で使い分けてますね😆」「へぇ〜!」「だからバックエンドのファイル更新イベントはやっぱりOS依存のAPIを使ってる😆」
⚓レスポンスのVary: Accept
を再設定しないよう修正
#36213で、レスポンスにAcceptヘッダーを使う
Vary: Accept
ヘッダーが追加された。このため、アプリでVary
ヘッダーにAccept
以外の値を設定した場合に問題が起きる。コントローラのアクションでこのヘッダーにどんな値を設定しても、_set_vary_header
メソッドが値を"Accept"
に設定する。
このコミットによって、ヘッダーに"Accept"
を設定する前に、ヘッダーに既に値が設定済みかどうかをチェックするようになる。このヘッダーが空欄の場合はヘッダーを設定する。このヘッダーに既に値がある場合、アプリケーションがこのヘッダーを扱っているという前提に立って_set_vary_header
は何もしない。
同PRより大意
# actionpack/lib/action_controller/metal/rendering.rb#L78
def _set_vary_header
- self.headers["Vary"] = "Accept" if request.should_apply_vary_header?
+ if self.headers["Vary"].blank? && request.should_apply_vary_header?
+ self.headers["Vary"] = "Accept"
+ end
end
つっつきボイス:「Vary: Accept
ヘッダーを二重設定してしまうケースがあったのを修正したと」
「ちょっと前にVary
ヘッダーが追加されたときも話題にしましたね(ウォッチ20190805)」「う、それの続きのようです😅」「たしかキャッシュするかどうかの基準をどのヘッダーにするかをブラウザに通知する的なのがVary
ヘッダーでしたね😆」「HTTPのヘッダーっていろいろありすぎて覚えきれない😅」「見慣れないヘッダーだったので何となく覚えてました☺️」
「Vary
ヘッダーは、クライアント側というかリバースプロキシが使うヤツですね: リバースプロキシがどのヘッダーを見てキャッシュするかどうかを決めるとか」「おぉ〜」「たとえばVary
にUser Agentを指定すればUser Agentごとにキャッシュできるし、それに加えて別のヘッダーも指定すれば、その組み合わせによってキャッシュするしないを指定できると」「な〜るほど!😋」「でもCloudFrontではVary
ヘッダーが効かないらしい、みたいな話を前回してた覚えがあります😎」
⚓Rails
⚓GitHub ActionsでRailsのCIを回す(RubyFlowより)
- 元記事: Building a Rails CI pipeline with GitHub Actions | Boring Rails: Skip the bullshit and ship fast
つっつきボイス:「先週はmizchさんのGitHub Actions記事でしたが(ウォッチ20190925)、今週はこちらのやってみた記事を拾ってみました」「今なら普通に動きますしね☺️」「CircleCIから割とさくっと移行できるみたいですし☺️」
「Boring Railsってなかなかスゴい名前😆」「このサイトはどうやら最新記事以外は単行本に移しているようなので、次の記事が出たら見えなくなるかも😅」「なるほど😆」
⚓Webpackerのpacksをマスターする
# 同記事より
app/javascript
├── admin
├── channels
├── login
└── packs
├── admin.js
├── application.js
└── login.js
つっつきボイス:「1本目は少し前に取り上げた気がしますね😆」「あ、かぶってました(ウォッチ20190902)😅: 2本目がその続き的な記事です」「packファイルを太らせるな、とか書いてますね」
「うん、この記事はmanifest.jsonとかがどういうタイミングで参照されるとか、このファイルがダイジェスト付きになるとかが丁寧に説明されているっぽいのがいいですね👍」「お〜」「大事なのはjs/admin-67dd60bc5c69e9e06cc3.js
みたいなダイジェストを引き回してくれるところ」「webpack-dev-serverを動かせばこの辺の情報が出てくれるのね↓」「後でちょっと読んでおこう😋」「翻訳したいです😋」
# 同記事より
▶ ./bin/webpack-dev-server
ℹ 「wds」: Project is running at http://localhost:3035/
ℹ 「wds」: webpack output is served from /packs/
ℹ 「wds」: Content not from webpack is served from /Users/prathamesh/Projects/scratch/better_hn/public/packs
ℹ 「wds」: 404s will fallback to /index.html
ℹ 「wdm」: Hash: 5387bbdba96d7150c792
Version: webpack 4.39.2
Time: 2753ms
Built at: 09/24/2019 12:23:20 AM
Asset Size Chunks Chunk Names
js/admin-67dd60bc5c69e9e06cc3.js 385 KiB admin [emitted] admin
js/admin-67dd60bc5c69e9e06cc3.js.map 434 KiB admin [emitted] admin
js/application-d351b587b51ad82444e4.js 505 KiB application [emitted] application
js/application-d351b587b51ad82444e4.js.map 569 KiB application [emitted] application
js/login-1c7b2341998332589ec0.js 385 KiB login [emitted] login
js/login-1c7b2341998332589ec0.js.map 434 KiB login [emitted] login
manifest.json 958 bytes [emitted]
記事末尾のまとめ:
- packファイルは最小限にし、必要なコードは他のファイルからインポートすること
- app/javascript/packsにはpackファイル以外は置かないこと
- それ以外についてはapp/javascriptの下は自由にやってよい
- Webpackの出力でbundleのサイズに目を光らせておこう
- packファイルは必要に応じて編成し、それらが提供する機能に依存するpackを管理する
⚓Railsのerrors.added?
とerrors.of_kind?
errors.of_kind? というRails 6の便利メソッドを教えてもらったので本文に追記しました。Thanks to @ttakuru88
どのフィールドにどの検証エラーが追加されたのかを、「表示言語やエラーメッセージに依存しない形で」テストする方法 https://t.co/C52FcknFnV #Qiita
— Junichi Ito (伊藤淳一) (@jnchito) September 24, 2019
つっつきボイス:「ロケールに依存しない形でテストできるそうです」「お?added?
っていうメソッドがあるのか😳」「こういうふうに、実際の生メッセージじゃなくてシンボルでメッセージを指定できるのね↓」「当然この方がいいですよね❤️」
# 同記事より
expect(john.errors.added?(:first_name, :too_short, count: 2)).to be_truthy
# または(predicateマッチャを使う場合)
expect(john.errors).to be_added(:first_name, :too_short, count: 2)
「マルチリンガルのメッセージを変えただけなのにバリデーションロジックのテストが落ちたら、それはテストがI18nから分離できていないということだからおかしいですしね」「そうそう😆」「本来はこういうふうにテストすべき」
「このメソッドを覚えてたら使うだろうけど、そのときに思い出せるかどうか😅」「ついしれっと日本語でテスト書いちゃいそうな気も😆」「自分で何度か使ってみたり、既に誰かが使っててくれたらいいんだけど」「あったら使いますし😋」
jnchitoさんの記事に、Rails 6のerrors.of_kind?
ならcount: 2
を書かなくていいと追記されていました。
⚓Quoraより: Railsとworker killer
- Quora: (9) プロダクションの Rails サーバーの利用メモリがひたすら増加していくような挙動を観測したとき、どう対応するのがよいですか?に対するKenn Ejimaさんの回答 - Quora
つっつきボイス:「サーバーの利用メモリがひたすら増加していくような挙動を観測したら?そりゃもうworker killerでしょう🤣」「🤣」「このQuoraで、Pumaにもworker killer↓があるって初めて知りました」「ありま〜す😆どれにもworker killerありま〜す」「Unicornでworker killer使うという話しか知りませんでした😅」「Passengerにも何リクエスト受けたら自滅するヤツとかありますよ」
- リポジトリ: schneems/puma_worker_killer: Automatically restart Puma cluster workers based on max RAM available
「Quoraのリンク先の記事↓にもあるように、memory bloatだったりリークだったりいろいろ原因は考えられますけど、この辺はもうしょうがないんですよね🤣」「🤣」「Rubyに限りませんけど、動的にオブジェクトを生成するスクリプト言語だと、メモリにきれいに乗らないからエッジをまたぐみたいなことが起きたりしますし、GCしてもうまく寄ってくれないとかありますし」「ふむふむ」「それ以前にGCって重いからそうそうGCしませんし😆」
参考: What causes Ruby memory bloat? – Joyful Bikeshedding -- (ウォッチ20190401でも取り上げました)
「私のオレオレRailsアプリがworker killerなしでやっていけてるのは、単に作りがあまりに素朴だからなのかな?🤔」「そういうちっちゃなアプリならほぼ問題になりませんね☺️」「よかった😂」「問題になるのはメモリフラグメンテーションが大量に発生するようなケースですし、内部のメモリ効率が落ちるだけなので」「そういえば私のRailsアプリはモデル1個しかないし、一般ユーザーはモデルの読み出ししかやってませんし😅」「Active RecordやActive Modelがオブジェクトをぼこぼこ作ったり、スコープ外れて消えたり、ということが起きまくっていると、フラグメンテーションが起きやすくなります☺️」
「というわけでworker killerはみなさん使ってますので😆」「Rubyを使ってRailsのようにひたすら動的にメモリを確保するようなビッグアプリを動かしていれば、この辺は割とどうしようもありませんし☺️」
⚓あまり知られてないけど便利なgem 7つ(Ruby Weeklyより)
つっつきボイス:「gemのリポジトリのリンクが記事にあまりなくて辿りにくいので、下にリストアップしてみます」
- traceroutes
- strong_migrations
- isolator
- test-profとruby-prof
- database_consistency
- attractor
- coverband
1. ⚓traceroutes
「amatsudaさんのgemだ!」「rake traceroute
で使われてないルーティングやアクションを見つけてくれる❤️」「お?記事はこれがRails 6で動かないからプルリクを送ったと書いてる↓」「でもtracerouteのREADMEにはRails 6対応って書いてあるけど?😆」「入れ違いかな?🤔」
- Fix for Rails 6: Zeitwerk class loading by matugm · Pull Request #37 · amatsuda/traceroute -- まだopenです
2. ⚓strong_migrations
「strong_migrations、これは前にもウォッチに出てきたような↓🤔」「だいぶ前だったかな」
「gemのREADMEに、マイグレーションでこういう潜在的に危険な操作↓を行ったときに警告してくれるとある」「これがデンジャラスオペレーションですよ😆」
- カラムの削除
- カラムにデフォルト値を追加する
- 後からデータを入れる
- インデックスをnon-concurrentに追加する
- referenceを追加する
- 外部キーを追加する
- カラムの型を変更する
- カラムをリネームする
- テーブルをリネームする
- forceオプションを付けてテーブルを作成する
change_column_null
でデフォルト値を使う- jsonカラムを追加する
「正しいマイグレーションのやり方もREADMEに書かれているし」「へ〜、ignored_columns
↓って知らなかった!」「先にignored_column
で入れておけば、今動いているRailsワーカーが誤って参照するのを防げるということか〜!」「なかったことにしてくれる😂」「すげ〜こんなのあるんだ😳」「でデプロイしてから最後にsafety_assured
で確定すると」
# 同リポジトリより
# Good
class User < ApplicationRecord
self.ignored_columns = ["some_column"]
end
# 同リポジトリより
class RemoveSomeColumnFromUsers < ActiveRecord::Migration[6.0]
def change
safety_assured { remove_column :users, :some_column }
end
end
参考: ignored_columns
-- ActiveRecord::ModelSchema::ClassMethods
「こうやってデフォルト値を足すときも間違えがち↓」
# 同リポジトリより
# Bad
class AddSomeColumnToUsers < ActiveRecord::Migration[6.0]
def change
add_column :users, :some_column, :text, default: "default_value"
end
end
「strong_migrations gemでこの手の危険をはらむマイグレーション操作を警告してくれるのはありがたいですね😋」「こういうのは気を付けててもやっちゃいますし😅」「わかってても踏む😆」「いいgem!」「おや、既に自分もこのgemに★付けてた😆」
3. ⚓isolator
「isolatorか」「non-atomicなDBトランザクションを検出してくれると」「この例は確かにnon-atomicだわ↓」
# 同リポジトリより
# HTTP calls within transaction
User.transaction do
user = User.new(user_params)
user.save!
# HTTP API call
PaymentsService.charge!(user)
end
#=> raises Isolator::HTTPError
# background job
User.transaction do
user.update!(confirmed_at: Time.now)
UserMailer.successful_confirmation(user).deliver_later
end
#=> raises Isolator::BackgroundJobError
「こういうのを自動検出してくれるならいいですね❤️」「代わりに怒ってくれると😆」「当てにしすぎるわけにもいかないと思うけど、初歩的なミスを拾ってくれるならいいと思います😋」
4. ⚓test-profとruby-prof
- リポジトリ: palkan/test-prof: Ruby Tests Profiling Toolbox
- リポジトリ: ruby-prof/ruby-prof: A ruby profiler. See https://ruby-prof.github.io for more information.
「どのテストが遅いかをこの2つで検出すると」「test-profは記事ありました↓」
5. ⚓database_consistency
「bundle exec database_consistency
でデータベースの一貫性をチェックしてくれるそうです」「ああ、こういうgemを作りたくなることってよくありますね: このgemは、Railsのバリデーターをすり抜けたデータが入ってないかどうかをチェックしてくれる」「おぉ〜😍」
「こういう感じのコードってよく自作しますよ、不安だから😆」「やりますね〜😆」「each
で回してvalid?
メソッドをひたすら呼ぶバッチとか😆」「まさにそういうのをやってくれると」
「生SQLでデータをいじる人が出始めると、だいたい整合性がおかしくなるんですよ😭」「バリデーションをスキップする悪しきupdate系メソッドが使われちゃってたとかね😇」
「このgemは多分単純なことしかしてないし、消費税対応とかで生SQLでデータをいじった後に使いたい感じですね」「ただバリデーションに誰かがcallbackを書いていたりすると副作用でこいつがデータを破壊して回りそうではある🤣」「🤣」
6. ⚓attractor
- リポジトリ: julianrubisch/attractor
「attractor gemはコードからこういうメトリックスレポート↑を出してくれるようですね」「complexityとかあるし、静的に解析してくれるツールのひとつらしき」「グラフの斜めの線より上にあるコードはリファクタリング候補、とか☺️」「こういうツールはたくさんありますね☺️」
7. ⚓coverband
- リポジトリ: danmayer/coverband: Ruby production code coverage collection and reporting (line of code usage)
「coverbandは知ってる!」「production環境で使われてないコードを検出できる」「使ってないビューとかも検出できると↓」「ただし実行されてないというだけで判断してるので、年1でしか動かさないバッチも使われてないとみなされちゃうかも😆」「でも機械的にそうしたコードを検出できるのはいいですね😋」「coverbandはいい👍」
「こういうので、Ruby 2.5以降あたりを使ってもっといい感じにやれるのが確かありましたね」「名前思い出せないけどそういえばありました😅」「たしかRubyKaigiでもこれ↓とか、後もうひとつつぐらいこのテーマで発表されてたと思います☺️」
「この記事のgemのチョイスはいいと思います!❤️」「忘れがちだけど、入れておくとコードの健康が保てる系のgemということで☺️」
⚓Form Objectでモデルを複数使う(Ruby Weeklyより)
つっつきボイス:「Form Objectを使うときって、こうやってモデルが複数出てくるときが多いですよね😆」「自分はForm Objectはキライじゃない派」「自分もForm Objectは好き😍」「つまり2人とも好きなんですね😆」
⚓Form Objectよもやま話
「Form Objectをどう位置づけるかですよね: 世の中にはService Objectでやりたい人たちもいて、もちろんそれもわかるんですけど、Form Objectなら1リクエストを受けるフォームとみなせるじゃないですか」「そうそうっ😋」
「もしかするとForm Objectという言葉が好きになれないとかあったりするのかも🤔」「その気持もわかりますね」
「Form Objectといえば、kazzさんも好きな、Active Modelだけ継承した素のクラスでやる方法を、最近誰かのスライドで『Application Object』だったか『Application Model』って呼んでたのを思い出しました: 一般的な呼び方かどうかよくわからないので、そのスライド限定の呼び方かもしれませんけど」「正しい呼び方は知らないけど😆、そういえばそんな話もしましたね」
「それで言うと、Form Objectと呼んだときは、あくまでユーザーのリクエストとかAPIへのPOSTリクエストを扱うもの、というニュアンスが出ますよね: 逆にApplication Objectと呼ぶと、内部APIからもアクセスされるもの、というニュアンスになる」「そうそうっ😋」
「その意味で、外部のインターフェイスに強依存するならForm Objectと呼ぶ方がしっくりくる」「うんうん」「逆にたとえばバッチの中で所定のハッシュを作って、内部からもそのAPIを呼ぶんだったら、Application Objectの方がしっくりきますね〜」「それはフォームでもあるんですけどね🤣」「そうそう🤣: でもForm Objectを内部APIとして叩くのはちょっと違和感があるので、そういうものだったらApplication Objectと呼ぶ方がより汎用的で使い回しが効く感じはありますね☺️」
「どちらに重きを置くかで変わってくるんですね」「Form Objectの方が限定的です☺️」「ビュー寄りですし☺️」
その後社内でRailsdm 2018 Day4のこのスライド↓を教えてもらいました。ありがとうございます🙇
⚓その他Rails
つっつきボイス:「今からActive Recordを全部追おうとすると大変だろうな〜😅」
後で買っちゃいました😋。
【宣伝】GitLab CIのincludeとlimit制限がうまくハマった事例 / GitLab CIでTerraformを動かす - くりにっき https://t.co/3UvplTRvYg
— sue445 (@sue445) September 21, 2019
つっつきボイス:「GitLabでTerraformも動かせるんですね」「まあ何でも動かせますし😆」「『ベースになるジョブをincludeで別リポジトリから読み込む』とか、GitLab CIハック感ありますね〜😋」「BPS社内でも参考になりそう!」「もうやってる人いるかも😆」
WEB+DB PRESS111のRails6特集、「特にテンプレートエンジンを メンテナンスしている人は注意してください。」の注意喚起がめっちゃピンポイント
— igaiga (@igaiga555) September 25, 2019
フォロワーさんが4000人超えた〜。みなさまいつも贔屓にしていただきありがとうございますー!今はRailsの教科書のRails6.0改訂作業中です。年内には出せるといいなぁ。ちなみに先に買っていただいても新版もDL可能にする予定なので、先に買ってもらっても大丈夫ですよ!!!https://t.co/wAMLWDekJh
— igaiga (@igaiga555) September 25, 2019
つっつきボイス:「igaigaさんの『Railsの教科書』、これはいい本ですよぉ〜❤️」「おぉ〜」「書いてあるとおりにやればできる本😋」「carrierwave使ってますね」
「そういえば、この間Railsチュートリアル↓をRails 6版に更新翻訳したんですけど、旧チュートリアルで使われてたcarrierwaveがRails 6のActive Storageに切り替わってました」「そりゃそうでしょうね😆」「Rails 6でrails new
するのに今どきcarrierwave使うとしたら理由が必要☺️」
前編は以上です。
バックナンバー(2019年度第3四半期)
週刊Railsウォッチ(20190925-2/2後編)AWS Lambdaの秘密鍵保存法、Rubyコミット歴史の動画、Rubyコードの最適化と式展開ほか
- 20190924-1/2前編 Railsのconcernsを考える、Netflixのヘキサゴナルアーキテクチャ、Railsのアロケーション削減ほか
- 20190918-2/2後編 RubyPrize 2019候補者発表、GoogleがTypeScript 3.5に熱烈フィードバック、日本語形態素分析kagomeほか
- 20190917-1/2前編 Sidekiq 6.0がリリース、銀座Rails#13と「出張!Railsウォッチ」、るびま0060号、ロックイン回避の落とし穴ほか
- 20190910-2/2後編 buildersconと「20年後のソフトウェアテスト」、はてなブックマークがScalaに移行、「詳解PostgreSQL」、Go 1.13ほか
- 20190909-1/2前編 Rails 6のキャッシュバージョニング、Rubyのキーワード引数周りが変わる、Faker 2がリリースほか
- 20190902 Ruby 2.6.4セキュリティ修正リリース、スライド「All About Ruby in 2019」、Shrine gem 3.0に入る新機能ほか
- 20190826 6-0-stableの更新を見てみる、『Morning Cup of Coding』ニュースレター、Rails TutorialがRails 6対応に動き出すほか
- 20190821-2/2後編 11のgemにバックドア、ruby-jp Slackがとてもアツい、Fullstaq Rubyでチューンアップ、HTTPサービス監視chaoほか
- 20190819-1/2前編 祝: Rails 6がついにリリース、RailsガイドもRails 6に対応、Arelはpublicだったかほか
- 20190806-2/2後編 RSpec CopのLeakyConstantDeclaration、serveoでゼロコンフィグ公開、RuboCopのPerformance/RegexpMatch改修ほか
- 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など)です。