- 開発
週刊Railsウォッチ(20190304-1/2:前編)Rails 6.0.0 beta2リリース、Ruby 2.7の新しい記法、各種自動レビューツール、ULIDとはほか
こんにちは、hachi8833です。Railsウォッチ制作がそろそろ本気で身が持たないので、今回からウォッチを分割公開することにしました。今回は前編となります。引き続きよろしくお願いします🙇。
しかしそう決めた途端、何とつっつきの録画に失敗してしまいました...😇。可能な限り記憶を元に再現します🙏。
- 各記事冒頭には⚓でパーマリンクを置いてあります: 社内やTwitterでの議論などにどうぞ
- 「つっつきボイス」はRailsウォッチ公開前ドラフトを社内有志でつっついたときの会話の再構成です👄
- 毎月第一木曜日に「公開つっつき会」を開催しています: お気軽にご応募ください
⚓週刊Railsウォッチ「公開つっつき会#8」開催のお知らせ
次回は今週の3/7(木)開催です。参加枠は余裕あります。鍋でもつっつくようなお気軽なご参加をお待ちしております🙇。
⚓臨時ニュース: Railsガイドが5.2に対応
#Railsチュートリアル 完走者やRailsエンジニアに活用して頂いている #Railsガイド が5.2対応になりました! 😆🎉✨ #YassLab
5️⃣0️⃣ 以上のファイルを更新し、約 6️⃣0️⃣0️⃣0️⃣ 行を変更しました!
記事 https://t.co/peRoidfcOU
電書 https://t.co/2a1fBdjUoz皆さんの開発の一助になれば嬉しいです 💖 pic.twitter.com/01JEWR46l9
— YassLab 株式会社 🏝 (@YassLab) March 4, 2019
半年以上前から更新翻訳をすすめていましたが、このたびリリースいただきました🙇。次の目標はRails 6ですね。
⚓Rails: 先週の改修(Rails公式ニュースより)
⚓Rails 6.0.0 beta2リリース
つっつきボイス:「出ましたね🎉」「来たか(すっく)」「予定では「February 1: Beta 2.」「March 1: Release Candidate 1」なのでまずまずですね☺️」
⚓beta2のZeitwerk情報
つっつきボイス:「先行して上の要訳記事を出しました↑」「社内でもオートローダーの話からモジュール/クラスを入れ子にしたときの名前空間の話になったりしましたね」
このときrequire_dependency
についてもSlackで話題になったのですが、私がまだまとめきれないのでウォッチ20181022をどうぞ。
参考: 7 require_dependency
-- 定数の自動読み込みと再読み込み - Rails ガイド
その後autovivifiedの話題になりました。
参考: fxn/zeitwerk: Efficient and thread-safe code loader for Ruby -- implicit namespaces
参考: Autovivification - Wikipedia
Perlにおけるautovivificationとは、未定義の値が参照解決(dereference)されるたびに新しいarrayやhashを自動生成すること。
Wikipediaより大意
参考: autovivificationとうまく付き合う - Qiita
以下のように、素のRubyのirbでMyName
を定義してない状態からいきなりMyName::MyModel
クラスを定義すると1行目でいきなりエラーになることもわかりました。
class MyName::MyModel #=> `<main>’: uninitialized constant MyName (NameError)
def hello
'hello world'
end
end
puts MyName::MyModel.new.hello
なお、Railsでもコンソールで定数Foo
が未定義のままFoo::Bar
をclass
で定義するのはさすがに無理でした。
アウターボイス:「忘れがちだけど、クラスの継承関係と名前空間はまったく別物なのよね😎」「あー言われてみれば🤭」
参考: Railsでモデルクラスの名前をネームスペースとして流用してはいけません(warning: toplevel constantが発生します) - Qiita
Rails 6ではautoloadingの仕組みが変わるのね。微妙な仕様の違いで、変にハマったり、特殊なgemが動かなくなったりしそうな予感・・・。(あくまでただの予感です)
Zeitwerk integration in Rails 6 (Beta 2) | Riding Rails https://t.co/E0GeAhAEPS— Junichi Ito (伊藤淳一) (@jnchito) February 26, 2019
⚓generated_relation_methods
をリファクタリング
# activerecord/lib/active_record/relation/delegation.rb#L3
+require "mutex_m"
+
module ActiveRecord
module Delegation # :nodoc:
module DelegateCache # :nodoc:
...
def inherited(child_class)
child_class.initialize_relation_delegate_cache
super
end
+ def generate_relation_method(method)
+ generated_relation_methods.generate_method(method)
+ end
+
...
private
def generated_relation_methods
- @generated_relation_methods ||= Module.new.tap do |mod|
- mod_name = "GeneratedRelationMethods"
- const_set mod_name, mod
- private_constant mod_name
- end
+ @generated_relation_methods ||= GeneratedRelationMethods.new
end
+ end
+
+ class GeneratedRelationMethods < Module # :nodoc:
+ include Mutex_m
+
+ def generate_method(method)
+ synchronize do
+ return if method_defined?(method)
- def generate_relation_method(method)
if /\A[a-zA-Z_]\w*[!?]?\z/.match?(method)
- generated_relation_methods.module_eval <<-RUBY, __FILE__, __LINE__ + 1
+ module_eval <<-RUBY, __FILE__, __LINE__ + 1
def #{method}(*args, &block)
scoping { klass.#{method}(*args, &block) }
end
RUBY
else
generated_relation_methods.define_method(method) do |*args, &block|
define_method(method) do |*args, &block|
scoping { klass.public_send(method, *args, &block) }
end
end
end
end
end
private_constant :GeneratedRelationMethods
つっつきボイス:「コードのかぶりをなくすリファクタリング」「require "mutex_m"
なんてのを使ってますね」「排他制御が必要になるからですね☺️」
参考: module Mutex_m
(Ruby 2.6.0)
「そういえばmutexってmutual exclusionの略だった覚えが: 相互排他というか」「マジで😆: ずうっとミューテックスはミューテックスだと思ってたし」
mutexとくればセマフォもよく登場しますね。遠い昔、職場で日本語ペラペラの米国人エンジニアがクライアント/サーバー通信の文脈で「セマフォ」という言葉を使ったときに思わず「それ何ですか?」と聞き返してしまったのを思い出しました。「まあ信号機みたいな」と説明されましたが、ネットのない時代につき本を買うまで結局よくわからずじまいでした😅。
参考: セマフォ - Wikipedia
語源となった本物のセマフォ↓です。
これまたついでに、昔の単線鉄道では、列車の衝突を回避するために「タブレット」というものを一種のトークンのように使っていたんだそうです。タブレットを所有している列車のみがその区間を通過できるというルールで、受け渡しのときに落っことすこともちょくちょくあったとか。コンピュータがない時代の苦肉の策的な排他制御というか。
参考: タブレットとは - はてなキーワード
参考: トークン - Wikipedia
参考: トークンリング - Wikipedia -- ほぼ消えましたね😇
もっとも鉄道のトークンにもいろんな意味があったりして、イギリスやニューヨークあたりの地下鉄のチケットもそう呼ばれてたと思います。
⚓テンプレートレンダリングの改良
このコミットではテンプレートオブジェクトのビルド時に
locals
を渡し、次にテンプレートオブジェクト側でlocals=
ミューテーターメソッドを削除するようにした。意図としては、decorate
メソッドでlocals
を用いてテンプレートオブジェクトを改変して欲しくないということである。
#35408や#35406と同様。
同PRより大意
# actionview/lib/action_view/template/resolver.rb#L203
private
- def find_templates(name, prefix, partial, details, outside_app_allowed = false)
+ def find_templates(name, prefix, partial, details, outside_app_allowed = false, locals)
path = Path.build(name, prefix, partial)
- query(path, details, details[:formats], outside_app_allowed)
+ query(path, details, details[:formats], outside_app_allowed, locals)
end
つっつきボイス:「テンプレート周りでlocals
を渡すようになったのね」「地の文でlocalsって書いてあると割と何だかわからない😅」
# actionview/test/template/template_test.rb#L
def test_refresh_with_partials124
- @template = new_template("Hello", virtual_path: "test/_foo")
- @template.locals = [:key]
+ @template = new_template("Hello", virtual_path: "test/_foo", locals: [:key])
assert_called_with(@context.lookup_context, :find_template, ["foo", %w(test), true, [:key]], returns: "partial") do
assert_equal "partial", @template.refresh(@context)
end
end
⚓STIのサブクラスでfind_by
をキャッシュしないようにした
> STIのサブクラスでfind_by
をキャッシュするのは、type IN (?,?,?,?)
の部分が動的であるため安全ではない。なお、STIサブクラスが作成または削除されるときにSQLステートメントのキャッシュを無効にすることまでは当面行わない。
同コミットより大意
STIサブクラスのクエリキャッシュの訳、ちょっと文のつながりが意図と違ってて「STIサブクラスでfind_byクエリをキャッシュすることは、type IN (?,?, ...,?)の?の数が動的であり、またSTIサブクラスが定義未定義されるときにキャッシュの無効化を持っていないので、安全ではない」ぐらいの意味です。
— Ryuta Kamizono (@kamipo) March 4, 2019
STIサブクラスのさらにその子クラスの定義されてる数が増えたり減ったりするとbase classを除く全ての親STIクラスの type IN (?,?, ...,?) に影響を与えるため安全ではないということが言いたい感じです。
— Ryuta Kamizono (@kamipo) March 4, 2019
フィードバックありがとうございます!😂。
# activerecord/lib/active_record/core.rb#L184
def find_by(*args) # :nodoc:
- return super if scope_attributes? || reflect_on_all_aggregations.any?
+ return super if scope_attributes? || reflect_on_all_aggregations.any? ||
+ columns_hash.key?(inheritance_column) && !base_class?
hash = args.first
return super if !(Hash === hash) || hash.values.any? { |v|
StatementCache.unsupported_value?(v)
}
# We can't cache Post.find_by(author: david) ...yet
return super unless hash.keys.all? { |k| columns_hash.has_key?(k.to_s) }
keys = hash.keys
statement = cached_find_by_statement(keys) { |params|
wheres = keys.each_with_object({}) { |param, o|
o[param] = params.bind
}
where(wheres).limit(1)
}
begin
statement.execute(hash.values, connection)&.first
rescue TypeError
raise ActiveRecord::StatementInvalid
end
end
⚓Rails
⚓自動レビューツールいろいろ
- リポジトリ: prontolabs/pronto -- Rubyで書かれています。
- リポジトリ: haya14busa/reviewdog -- Go言語で書かれてます
つっつきボイス:「前回のウォッチでVelocityやGitPrimeやSiderの話から自動レビューツールの話になったのと(ウォッチ20190225)、その後チームミーティングでも自動レビューツールが検討され始めたので取り急ぎ集めてみました」「お、ちょうどいいタイミングですね👌🏽」「prontoやreviewdogはどことなく手作り感あって暖かみありそう🥘」「reviewdogのシンボルマークがいらすとやさんにしか見えない件😆」「エッジのざらつき感がそこはかとなくそれっぽいし😆」「Siderよさげだったんで軽く問い合わせてみたんだけどGitLabへの対応予定は今のところないんですって😭」「BPSはGitHubも使ってるけどGitLabがメインですしね」「さてどれにしようかな🤔」
⚓Rails 6でenum値用の負のスコープを持つメソッドが追加(RubyFlowより)
# 同記事より
# アクティブでない全デバイスの取り方
# 従来の書き方
Device.where.not(status: :active)
# 今後はこう書ける
Device.not_active
# 止められていないすべてのデバイス
# 従来の書き方
Device.where.not(status: :disabled)
# 今後はこう書ける
Device.not_disabled
つっつきボイス:「enumのスコープがネガティブというのがよくわからなくて😅」「enumで上のようにwhere.not(status: :active)
と書く代わりにnot_なんちゃら
で書けるということですね☺️」
その後公式情報にも出ました↓。
参考: reselect, negative enum scopes and more! | Riding Rails
⚓この頃流行りのULIDとは
- リポジトリ: ulid/spec
- 元記事: Going deep on UUIDs and ULIDs - Honeybadger Developer Blog
- 元記事: ULID の順序性を確保するには|瀬戸口光宏|note
以下のツイートで知りました。
https://t.co/MipwU1Zb8S
最近はULIDってのも見かけるな。— さく (@sakuro) February 26, 2019
つっつきボイス:「ユニバーサリーユニークレキシコグラフィカリィソータブルアイデンティファイアー」「長っ🐍」「ソートできるUUIDっていいですね😋」「↓こんな感じのUUIDになって、しかも大文字小文字も区別しないし記号も使わないからURLに使っても安心ということみたいです」「いいじゃないの~🥰」「リポジトリは仕様しか載ってませんが、既にいろんな言語で続々実装されてるみたいです」
ulid() // 01ARZ3NDEKTSV4RRFFQ69G5FAV
「タイムスタンプとランダムでビットが分かれてるのね」
01AN4Z07BY 79KA1307SR9X4MV3
|----------| |----------------|
Timestamp Randomness
48bits 80bits
「お〜、I
とかL
とかO
とかU
みたいな見た目に紛らわしい文字を排除してるのはとてもいい👍」「Webサービスのパスワードなんかもシリアルコード(ソシャゲの事前登録とか)なんかもこの点に配慮しておくと問い合わせがぐっと減りますし😋」
以下のようにCrockfordのBase32を用いる。I、L、O、Uのような紛らわしい文字や乱用される可能性のある文字を排除する。
0123456789ABCDEFGHJKMNPQRSTVWXYZ <= この文字だけを使う
参考: Base32 Encoding -- Douglas Crockford
Crockford氏のサイトを見ると、IやLやOがいろいろ紛らわしいのはわかるのですが、UはVと紛らわしいのかなと思いきや、accidental obscenity(はずみで下品な意味になる)という説明が気になりました。おそらくf●ckとかs●ckみたいな語を偶然含むULIDが生成されるということだと思うのですが。
以下の記事でも「そんなの組み合わせ次第でいくらでも下品になるから無理じゃない?」という見解がありますね。
参考: Base36 accidental obscenity - Treating PHP Delusions
参考: Online PHP editor | output for Kc5gN -- U使ってない下品な例
⚓rack-queries: プレビルドされたクエリを高速実行(RubyFlowより)
- リポジトリ: CultureHQ/rack-queries
つっつきボイス:「クエリを事前に作っておいてRackで高速実行するとかそんな感じみたいです」「ルーティングみたいなRailsアプリを経由しないから速そうではある🤔」
使い道についてもいろいろ話が出たのですが思い出せません🙇。
追いかけボイス:「rack-queriesのときに話したのは、アプリケーションメトリクス的なものを吐き出させるときに便利じゃないかな?って話でしたね:
単体SQLで数だけ返すようなURL作ってDatadogとかCloudWatchみたいなのに食わせて眺める的な」
🙇。
⚓その他Rails
- 元記事: Wrapping JSON-based ActiveRecord attributes with classes - DEV Community 👩💻👨💻
- リポジトリ: mikker/passwordless -- パスワードなし認証gem(Ruby Weeklyより)
- リポジトリ: rocketjob/semantic_logger -- セマンティックロガー(Ruby Weeklyより)
AWSのPA-API(Product Advertising API)のAPIテスト用コード、言語別サンプルで例に挙げられている言語がPHP、JAVA、そしてROR ...。 pic.twitter.com/IZdi0DX5F6
— masa寿司 (@masa_iwasaki) February 26, 2019
⚓Ruby
⚓Ruby 2.7で新記法.:
が追加
# 同記事より
# Math.sqrtで数値の平方根を求める
[1, 4, 9].map(&Math.method(:sqrt))
# => [1.0, 2.0, 3.0]
# 新記法だとこうなる
[1, 4, 9].map(&Math.:(:sqrt))
# => [1.0, 2.0, 3.0]
つっつきボイス:「ちょっとわかりにくいですが新記法だそうです」「あ、.:
で短く書けるってことか!😳」「見慣れてないせいかもしれないけどちょいキモい😅」「これが欲しいのわかる気がする: method(:シンボル)
みたいな書き方って、冗長なのもそうだけど、自分たちエンジニアからすると目がついついmethod
の方に行ってしまって割と紛らわしいのよね🤢」「あーなるほど!」「本当にやりたいことにフォーカスするという意味では.:
は面白い😋」
⚓CRubyでRTL(Register Transfer Language)(Ruby Weeklyより)
# 同記事より
getlocal_OP__WC__0 <b index>
getlocal_OP__WC__0 <c index>
opt_plus
setlocal_OP__WC__0 <a index>
つっつきボイス:「RTLってそもそも何だっけのレベルです👶」「RTLはRubyのVM(Virtual Machine)で使われるインストラクションですね」「あ、そういうことか!」「この間も、RubyではVMインストラクションに直結する書き方は速いみたいな話ありましたね」
参考: RubyでRubyVMを実装してRubyVMの気持ちになってみる - Qiita
上のフィードバックをいただきました(ユーザー名にアンダースコア_
があるため埋め込みが効きません)。ありがとうございます。至らない点は今後改善いたします🙇。
⚓その他Ruby
- 元記事: Rubyhack -- ソルトレークシティで開催だそうです(Ruby Weeklyより)
つっつきボイス:「Rubyhackの直後ってそんなに間あかずにRubyKaigi 2019じゃないですか!」「Matzの仕事は旅から旅が多い🛤」「忙しそう...💼」
- サイト: RubyKaigi 2019
最近知ったOpalの使い方を勉強中。ブラウザのcanvasに時計を表示するだけのコードを書いてみた。全部Rubyで書けるのが素晴らしい。 pic.twitter.com/sqTSHl0LSZ
— ICM7216 (@icm7216) February 27, 2019
次の日曜日によちよち.rbさんにお邪魔してお悩み相談会をやりますー。すでに満席ですが、とりあえず申し込んでおけば繰り上げ当選のチャンスがあるかも!?みなさんよろしくお願いします😄
よちよち.rb 第143回 〜 jnchito さんと!Ruby・Rails での『困った』を解消しよう会〜https://t.co/xZytUrOm2E— Junichi Ito (伊藤淳一) (@jnchito) February 25, 2019
前編は以上です。
バックナンバー(2019年度第1四半期)
週刊Railsウォッチ(20190212)EnvoyとIstioに大注目、SQLQLとは、buildkite.comのCI、さよならItanium、PWA vs Androidほか
- 20190204 あってうれしい40のgem、Ruby 2.6.1セキュリティリリース、Hanami v2.0.0.alpha1リリースほか
- 20190128 Rails 6のオートローダーがZeitwerkに置き換わる?Rails 6はRuby 2.5が必須、最近のSQLiteほか
- 20190121 Rails 6.0.0 beta1リリース、Railsは2019年も「あり」か、Jetsでサーバーレス、ES2018の新機能、RSpecの心ほか
- 20190115 Rubyの<=でクラス同士を比較、Rubyの記号の読み方いろいろ、Ruby C API解説サイトほか
- 20190107 Railsのパフォーマンス改善Tips集、Rubyの
&:シンボル
ほか
今週の主なニュースソース
ソースの表記されていない項目は独自ルート(TwitterやRSSなど)です。