- Ruby / Rails関連
週刊Railsウォッチ(20200511前編)Rails 6.0.3リリース、rails newに--masterオプションが追加、system specとfeature specの違いほか
こんにちは、hachi8833です。『みんなで筋肉体操』を今頃知って、軌道に乗せようと試行錯誤中です。
- 元記事: 【コロナ自粛】谷本道哉監修「おうちで筋肉近大体操」第一弾!裏切らない筋肉を手に入れろ! | Kindai Picks
- 元記事: 筋肉は裏切らない! NHK『みんなで筋肉体操』の筋トレ指導者・谷本道哉に直撃インタビュー | Kindai Picks
つっつきボイス:「筋肉体操一時期流行ってましたね💪」「当時まるで気づいてなくて、『筋肉は裏切らない』の出どころもやっとわかりました😅」「おうちでやる版が出たのね☺️」「今や空前の筋トレブーム」「みんな家にいますし😆」
真面目な話、これから20年くらいは筋肉と免疫力が差別化要因じゃないかと思うんで、若い人は体鍛えておくと良いかと。AIの発展と世界的な高齢化で求められるのはIT武装した賢い肉体労働者。会社も地位もすぐ無くなるし、仕事自体も消滅する時代。「筋肉は裏切らない」は当面成立する。
— masa寿司 (@masa_iwasaki) July 10, 2017
「ジムによってターゲットの客層がそれぞれ違うんですけど、ゴールドジムはガチの筋トレ勢向けらしい」「ボディビルダーとか」「一般向けとかスタジオレッスン中心ではないと😳」「行ったことありますけどガチ勢多かった😅」(以下延々)
- 各記事冒頭には⚓でパーマリンクを置いてあります: 社内やTwitterでの議論などにどうぞ
- 「つっつきボイス」はRailsウォッチ公開前ドラフトを(鍋のように)社内有志でつっついたときの会話の再構成です👄
- 毎月第一木曜日に「公開つっつき会」を開催しています: お気軽にご応募ください
今回のつっつき会は、試験的にZoomで外部ゲストをお迎えして小規模に開催しました。ご参加ありがとうございました!🙇
⚓Rails: 先週の改修(Rails公式ニュースより)
4/27の更新情報を中心に見繕いました。
GW中は@kamipoさんによる修正がかなり増えていました。
つっつきボイス:「Railsの公式更新情報ってウィークリーでしたっけ?」「自分はいつもチェックしてますけど平気で2か月とかインターバルあります😆」「ですよね😆、そんなしょっちゅう出てた気がしない」「公式が毎週やってたらRailsウォッチでそんな頑張らなくていいでしょうし😆」「😆」
「@kamipoさん、めっちゃアクティブにやってる」「すげ〜、毎日コミット!」「今の時期ライブが開催されていないからかも😆」「こんなにアクティブになれる秘訣が知りたいです」「あとお知らせにもありますけど、Railsのメーリングリストが以下のDiscourseに統合されたそうです↓」
⚓rails new
に--master
オプションが追加
- PR: Allow `rails new . --master` to point to master branch by Schwad · Pull Request #38631 · rails/rails
つっつきボイス:「--master
とは?」「どうやら--dev
とか--edge
というオプションは前からあったみたいです」「知らな〜いこれ🤣」「何に使うんだろ🤣」「rails new
するときに最新版を取ってきたりできるということ?」「だと思います」
「たぶん業務では直接使わないでしょうね😆」「gemを作ってる人がこれをCIに仕込んで常にedgeでテストできるようにするとか?」「普通にgitでブランチ指定すればよさそうですけど😆」「同じこと思いました😆」「これがどうしても欲しい理由はよくわからん😆」「手っ取り早くmasterでnewしたいとか?」「グローバルgemを汚さずにedgeを取って来たいとか?」「それならrbenvとかでやればよかったりして😆」「rbenvならグローバルgemは汚れないけどCIだとうまく動かなかったりするんですよね😅」「ああたしかに」「この機能がrails new
コマンドに入っていれば、不自由な環境でもrails
コマンドさえ動けばやれるから、そういうのが欲しい人もいるのかなと」「まあ--dev
と--edge
が前からあるなら--master
足してもいいかも☺️」
背景
現在の
rails new
ジェネレータでサポートされているオプションは--dev
と--edge
。
--dev
はローカルのRailsセットアップを指し、--edge
はRailsの最新stableブランチを指す。
しかしRailsコミュニティでedgeと言えば最新のmasterだと考えることが多く、Shopifyの以下の素敵な記事で最新のmasterを指す方法が書かれているほど。
Living on the Edge of Rails – Shopify Engineering
本来ならedgeがmasterを指す変更をおすすめするところだが、利用が多すぎて詰まった。変更
現在のedgeの機能を変えるよりは、欲しいものを足そうかと思う。
このPRは--master
フラグを追加する。
これはまさにedge
に期待される動作を行うもので、Railsの最新stableブランチではなくmasterブランチを指す。しかし何でまた?
ほら、開発者が水平シャーディングのようなRailsに最近コミットされた新機能を見たら、とりあえず新しいRailsアプリで試してみたくなるじゃないですか❤️。
このコマンドがあればコンフィグなしでやれるし、普段OSSをサポートする余裕のない自分らみたいな人でもテストコードをコミットしたりもできるので🙏。
同PRより大意
⚓リファクタリング3つ
- PR: Refactor invert predicate by eileencodes · Pull Request #38636 · rails/rails
- PR: Refactor fetch_attribute by eileencodes · Pull Request #38719 · rails/rails
つっつきボイス:「上の2つはほぼ同じ感じのリファクタリングだそうです」「不必要なwhen
を減らしてシンプルにした感じ😋」
# activerecord/lib/active_record/relation/where_clause.rb#L136
def invert_predicate(node)
case node
when NilClass
raise ArgumentError, "Invalid argument for .where.not(), got nil."
- when Arel::Nodes::In
- Arel::Nodes::NotIn.new(node.left, node.right)
- when Arel::Nodes::IsNotDistinctFrom
- Arel::Nodes::IsDistinctFrom.new(node.left, node.right)
- when Arel::Nodes::IsDistinctFrom
- Arel::Nodes::IsNotDistinctFrom.new(node.left, node.right)
- when Arel::Nodes::Equality
- Arel::Nodes::NotEqual.new(node.left, node.right)
when String
Arel::Nodes::Not.new(Arel::Nodes::SqlLiteral.new(node))
else
- Arel::Nodes::Not.new(node)
+ node.invert
end
end
「明示的に呼び出すように変えてる↓」
# actionview/lib/action_view/renderer/partial_renderer.rb#L279
def render_partial_template(view, locals, template, layout, block)
- instrument(:partial, identifier: template.identifier) do |payload|
+ ActiveSupport::Notifications.instrument(
+ "render_partial.action_view",
+ identifier: template.identifier
+ ) do |payload|
content = template.render(view, locals) do |*name|
view._layout_for(*name, &block)
end
content = layout.render(view, locals) { content } if layout
payload[:cache_hit] = view.view_renderer.cache_hits[template.virtual_path]
build_rendered_template(content, template)
end
end
「こういうinstrumentationの呼び出しって元々シングルトンというか、Railsのどこからでも呼び出せるものなんですけど、以下のinstrument(name, **options)
みたいなメソッドがレンダラーのクラスに中途半端に存在していると呼び出しパスが複数できちゃうから、上みたいにグローバルな呼び出しに統一しようよということだと思います」「ふ〜む」
# actionview/lib/action_view/renderer/abstract_renderer.rb#L173
- def instrument(name, **options) # :doc:
- ActiveSupport::Notifications.instrument("render_#{name}.action_view", options) do |payload|
- yield payload
- end
- end
-
「自分もたまにこういうメソッドって書いちゃうんですよね: ActiveSupport::Notifications.instrument
みたいな長ったらしい名前空間を何度も書くのが面倒なときとか😆」「わかります〜」「でも実際このメソッドって無意味ですし、こういうのを残しとくと#instrument
の引数が変わったときに挙動が変わったりして害を生じたりすることもあるのであんまりよくないよってことだと思います🧐」「なるほど!」「今なら長い名前空間の入力はIDEに補完させれば済みますし☺️」
「公式情報には『あえてDRYでない方向にリファクタリングした』という感じで書かれてました」「ここではメソッドに別名を付けているだけなので、たぶんですけどDRYでないようにしたというよりは、以前のショートハンドメソッドがよくなかったよねということかなと思います🧐」「なるほど😅」「メソッド内で別のことをしているならともかく、他に何もしてないので☺️」
⚓PostgreSQLアダプタのresult_as_array
をmap_types!
に置き換えた
# activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb#L13
def query(sql, name = nil) #:nodoc:
materialize_transactions
log(sql, name) do
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
- result_as_array @connection.async_exec(sql)
+ @connection.async_exec(sql).map_types!(@type_map_for_results).values
end
end
end
つっつきボイス:「map_types!
って何だろうと思ったらPGライブラリのメソッドだそうです↓」「なるほど、便利メソッドが元からあるならそれを使いましょうということね☺️」「自分らがPGライブラリを直接触ることはまずなさそう☺️」
⚓find_by_sql
の不必要なループを回避
つっつきボイス:「これも@kamipoさんによる修正ですね」「やらなくていい処理をunless
で囲ったのね☺️」「こういう更新はちょくちょく入ってる」
# activerecord/lib/active_record/querying.rb#L45
def find_by_sql(sql, binds = [], preparable: nil, &block)
result_set = connection.select_all(sanitize_sql(sql), "#{name} Load", binds, preparable: preparable)
- column_types = result_set.column_types.dup
- attribute_types.each_key { |k| column_types.delete k }
+ column_types = result_set.column_types
+
+ unless column_types.empty?
+ column_types = column_types.reject { |k, _| attribute_types.key?(k) }
+ end
+
message_bus = ActiveSupport::Notifications.instrumenter
payload = {
record_count: result_set.length,
class_name: name
}
message_bus.instrument("instantiation.active_record", payload) do
if result_set.includes_column?(inheritance_column)
result_set.map { |record| instantiate(record, column_types, &block) }
else
# Instantiate a homogeneous set
result_set.map { |record| instantiate_instance_of(self, record, column_types, &block) }
end
end
end
⚓アソシエーションを複数回autosaveできるよう修正
- PR: Allow associations to be autosaved multiple times by eugeneius · Pull Request #39124 · rails/rails
# activerecord/lib/active_record/autosave_association.rb#L367
def before_save_collection_association
- unless defined?(@new_record_before_save)
- @new_record_before_save = new_record?
- end
+ @new_record_before_save ||= new_record?
end
つっつきボイス:「これはまた面倒くさそうな部分の修正😆」「たしかにdefined?
だと1回しかチェックできないし」 「undef
しないと戻せない😆」
#38166以来アソシエーションは1回しかautosaveされないようになっていた(レコードをsaveした後は
@new_record_before_save
が常にfalseになる)。ここではレコードが永続化に移行するのが1回だけであると仮定しているが、複数回起きるケースが2つある。1つはレコードをsaveするトランザクションがロールバックする場合、もう1つは永続化したレコードが後に複製される場合。
私たちのアプリを6.0.2.2から6.0.3.rc1に進めたときにリグレッションが発生したので、バックポートを希望する。
同PRより大意
⚓番外: ドキュメントの改善
- リポジトリ: Removed unnecessary words from Getting Started docs by sarcas · Pull Request #37946 · rails/rails
# guides/source/getting_started.md#L612
-we don't specify what the response should be. We just added the `create` action
+we don't specify what the response should be. We added the `create` action
つっつきボイス:「最後のトリビアは、justとかsimpleのような大して意味のない語を削除したそうです」「フォーマルに少し寄せたというか」「軽くポリコレ感😆」「高校生の書くエッセイとかを先生が注意したりするヤツ😆」「言い回しに突っかかる人いますし😆」
「でもjustって便利なんですよ英作文のときとか😆」「言葉に詰まったときにとりあえず書いちゃうヤツ😆」「日本人だと学校英語の影響で始めのうちはonlyを使いがちなんですけど、onlyは文のどこに置くかで限定の対象がデリケートに変わってくるので文が込み入ってくると割と面倒くさくて🤣」「へぇ〜😳」「わかる🤣」「特に会話だとjustって便利😂」
参考: 「Only」の位置で、文の意味は変わりますか? - eigopedia
日本語で言うと「ちゃんと」とか「しっかりと」みたいなあってもなくてもいい語が雰囲気で投入されるのと少し似ているかもと思いました。自分が英文にするときには「ちゃんと」や「しっかりと」みたいな語はだいたい削除しちゃいます😉。
『The Elements of Style』↓という一世紀前からある定番の本は薄くてすぐ読めるのでおすすめです。
参考: 百年前の英文指南書『The Elements of Style』に学ぶ、効果的な文章の書き方11の法則 | ライフハッカー[日本版]
⚓Rails
⚓Rails 6.0.3がリリース
セキュリティリリースでないことがバージョン番号だけでわかるようになって助かります。
つっつきボイス:「そうそう、ちょっと前からRailsのバージョニングが変更されたんですよね😋(ウォッチ20200302)」「たしかにわかりやすくてありがたいです🙏」「セキュリティフィックスのみのリリースには4桁目が付くようになった: 仮に6.0.3にセキュリティリリースが出るときは6.0.3.1みたいになる」「ちょうど今朝rails new
したんですけど見たら6.0.3になってる〜😋」「逆に、たとえば6.0.3.1の次に6.0.4が出たら、それはセキュリティ以外の修正や変更ということですね☺️」
- changelog: Release 6.0.3 · rails/rails
「6.0.3の変更内容は知ってるものも知らないものもありますね」「マルチプルデータベース周りはまだごりごり変わってるっぽい😆」「もうちょい様子見な感じですね😅」
⚓Active Supportのto_sentence
「to_sentence
↓ってナニコレ知らない〜😆」「"A, B, and C"みたいな英語っぽい形式に変換するのか😆」「あ〜いかにも英語圏のためのメソッド😆」「to_sentence
にはORみたいの他の接続詞も渡せるんですって😆」
- API:
Array#to_sentence
参考: Railsで配列の要素から"A, B, and C"形式の文字列を作る時はArray#to_sentenceが便利 - WEB SALAD
# web-salad.hateblo.jpより
%w(one).to_sentence
# => "one"
%w(one two).to_sentence
# => "one and two"
%w(one two three).to_sentence
# => "one, two, and three"
「なんで英語だとand入れるんですかね?」「英語的には列挙するときに最後にandとかorを入れないと許してもらえないところあります😆」「英語圏ではA, B, and Cみたいに書くのが普通だと思いますし😆」「ですです」「日本語なら"AとBとCとD"みたいに全部"と"でjoinすれば済むんですけどね😆」
「ところでこのメソッドってどこで使うんでしょう?」「メッセージじゃないですか?」「あ、そうか😳」
「そういえば工業英語だったか特許英語だったかな、andの前のカンマ,
を置くか置かないかで意味が変わるらしいというのを知って英語って面倒くさいな〜って思った覚えがあります😆」「マジで?😆」「自分らはandの前にカンマを置かないと習ったような気がしますけど🤔」
後で本棚から探しだしてみると、"英語ではandの前にカンマを置く(列挙)書き方と、置かない(結合が強まることが多い)書き方の両方があるが、ネストした列挙で誤解を招かないためには、原則として列挙を表すandの前にカンマを置くよう統一するのが望ましい"という趣旨でした。自分なら箇条書きで書きたいところです😆。
- あいまいな例
- A, B, C and D and E
- 「A」「B」「CとD」「E」なのか「A」「B」「C」「DとE」なのか区別できない
- よい例
- A, B, C, D, and E
- 「A」「B」「C」「D」「E」で一意に定まる
- A, B, C and D, and E
- 「A」「B」「CとD」「E」で一意に定まる
- A, B, C, and D and E
- 「A」「B」「C」「DとE」で一意に定まる
『続・技術翻訳のテクニック』(富井篤) p145-146より大意
なお自分は日本語なら列挙を「A」「B」「CとD」「E」のように「」の連続だけで表す方法が好きです❤️。ついでながら、bread and butterは全体でひとつの英熟語です。
⚓system specとfeature specの微妙な違い(Ruby Weeklyより)
つっつきボイス:「お気持ち系の記事かな😆」「『オレはこう思う』みたいな😆」
「feature specって一時期流行ったんですよ」「そういえば😳」「turnipとかcucumberとか最近あんまり聞かなくなったと思いません?」「見かけたの結構前だったような😆」「あれですよ、BDD(ビヘイビア駆動開発)が流行ったとき😆」「それですね😆」「feature specもてはやされてましたけど、今思えば夢の世界だったんだろうか🌠」
- リポジトリ: cucumber/cucumber-rails: Rails Generators for Cucumber with special support for Capybara and DatabaseCleaner
- リポジトリ: jnicklas/turnip: Gherkin extension for RSpec
「一応cucumber-railsとかはメンテはされてるかな👀」「あれ、テスト落ちてる?😆」「ホンマや😆」「cucumber本体の方はちゃんとメンテされてるしテストも通ってる」
「cucumberみたいなツールは、たとえば政府調達系みたいな案件ならむしろマッチするかなという気もしますね🧐」「つまり要件が固いもの?」「そう、めちゃくちゃ要件の固い案件」「ふむふむ」「一次請けの会社がcucumberで要件定義を書いて下請けに発注するみたいな😆」「なるほど〜」
記事見出しより:
- Railsにある2つのレベルのテスト
- 高レベルかつ粒度が大きい: feature spec
- 低レベルかつ粒度が小さい: model spec
- feature specからsystem specへ
- 背景
- system specはsystem testを包含する
- 構文上の違い
- feature specの例
- system specの例
- まとめ
⚓Docker内でRailsのシステムテストを動かす(Ruby Weeklyより)
# 同記事より
services:
app:
build: .
command: bundle exec rails server -p 3000 -b '0.0.0.0'
# ... more config ...
ports:
- "3000:3000"
- "43447:43447"
# ... more config ...
environment:
- SELENIUM_REMOTE_HOST=selenium
つっつきボイス:「ハウツー記事ということで」「Dockerの中のテストでSeleniumをぶん回すときの話みたい」
記事冒頭より:
- Dockerの中でシステムテストを気持ちよく回すために
- RSpecをシステムテストに使う方法
- モダンなブラウザ内でヘッドレスモードのテストを回す方法
- ビルドやデバッグを効率よく行うために、ヘッドレスでないブラウザでテストを回せるか
⚓Railsでライブラリを使わずにReact componentをレンダリングする
// 同記事より
export const mountComponent = (component, componentName) => {
const nodes = Array.from(
doc.getElementsByClassName(`react-${componentName}`)
);
nodes.forEach((node) => {
const data = node.getAttribute("data");
const props = data && data.length > 2 ? JSON.parse(data) : {};
const el = React.createElement(component, { ...props }, []);
node.innerHTML = ''
return ReactDOM.render(el, node);
});
};
// 同記事より
document.addEventListener("turbolinks:load", () => {
mountComponent(MessageDisplay, "MessageDisplay");
mountComponent(AnotherComponent, "AnotherComponent");
mountComponent(AThirdOne, "AThirdOne");
}
つっつきボイス:「ライブラリってどのライブラリ?😆」「何ともファジーな😆」「サードパーティのライブラリを使わずにReact componentをコンポーネント単位でレンダリングしたりテストしたりしたいということかな」「コンポーネントのテストをRailsで書きたいかというと、データが絡むときとかなら書きたいな〜😋」
見出しより:
- しくみについて
- Railsビューの中で特殊なelementをレンダリングする
- JSを書く
mountComponent
を呼ぶタイミング- まとめ
⚓Rails+StimulusReflex+CableReadyでチャット機能を作る
つっつきボイス:「お、Stimulusか」「こうなるとRailsは単なるWebSocketサーバー同然だったりして😆」
「CableReadyって最近ちらほらと見かけますけど、Action Cableを使いやすくするとか?」「単にWebSocketを使いやすくするヤツかなと思ったら、Action Cableと接続するライブラリなのか〜😳」「なるほど!」「Action Cableに生でつながれるよりはいいのかな😋」「またフロントエンドの人に嫌がられそう😆」「😆」
<!-- 同記事より -->
<!DOCTYPE html>
<html>
<head>
<title>StimulusReflexCableReadyDemo</title>
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/semantic-ui@2.4.2/dist/semantic.min.css">
<%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
</head>
<body>
<div class="ui centered grid">
<%= yield %>
</div>
</body>
</html>
Ruby Weeklyで上の記事と同じ趣旨の以下の動画が紹介されていました。10分で作れると言ってます。
⚓その他Rails
- 元記事: From Secrets to Credentials: How to Update a pre-5.2 Rails App
- 元記事: How to debug webpack on Rails - rossta.net
- 元記事: Aaron Patterson's keynote was fantastic - DEV Community 👩💻👨💻(Hacklinesより)
- RailsConf 2020動画リスト: RailsConf 2020 CE - YouTube
つっつきボイス:「3本目はAaron Pattersonのキーノート↓がよかったという感想文😆」「まだ見てませんがめっちゃ楽しいって書いてますね」「RailsConf 2020か、後で見てみよっと😋」
動画冒頭ににオーディエンスの笑い声が入ってますが、本物かな?と思ったらイントロは事前に録画してたようです。
「そういえばAaronさんはつい最近GitHubから離れたんでしたっけ」「今度はShopifyだそうです」
Finished my second week at @Shopify. I'm having a really great time but it's going to take me a while to learn all of the emoji 😅
— Aaron Patterson (@tenderlove) May 8, 2020
前編は以上です。
バックナンバー(2020年度第2四半期)
週刊Railsウォッチ(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など)です。