週刊Railsウォッチ(20190603-1/2前編)Ruby 2.7.0-preview1リリース、RailsConf 2019を追う、pluckとincludesの組み合わせに注意、deep_transform_keys追加ほか

こんにちは、hachi8833です。

  • 各記事冒頭には⚓でパーマリンクを置いてあります: 社内やTwitterでの議論などにどうぞ
  • 「つっつきボイス」はRailsウォッチ公開前ドラフトを(鍋のように)社内有志でつっついたときの会話の再構成です👄
  • 毎月第一木曜日に「公開つっつき会」を開催しています: お気軽にご応募ください

今回は私たちにとって珍しいゲストがいらっしゃいました。


つっつきボイス:「どうもお久しぶりです🙇」「タイミングが合ったので今回初めて参加します: TechRachoいつも読んでます☺️」「お〜嬉しいお言葉!😂」

お知らせ: 第11回公開つっつき会(無料)

第11回目を迎えた公開つっつき会は6月6日(木)19:30〜にBPS会議スペースにて開催されます。皆さまのお気軽なご参加をお待ちしております🙇。

臨時ニュース: Ruby 2.7.0-preview1(Ruby公式ニュースより)

つっつきの後でリリースされていました。次回の公開つっつきでもう少し見ていこうと思います。まずは記念写真でインクリメンタルシンタックスハイライトしてみました📷。iTerm2に設定してあるハイライトと混じってしまいましたが😇。

Rails: 先週の改修(Rails公式ニュースより)

ドキュメント: includespluckに与える影響

@st0012さんのコミットです。

Note: pluckは、relationオブジェクトにincludesの値が含まれると、以下のようにeager loadingがそのクエリで不要な場合であってもeager loadingをトリガすることも知っておくべき。
同PRより

# associationを再利用のため保存
assoc = Company.includes(:account)
assoc.pluck(:id)
# SELECT "companies"."id" FROM "companies" LEFT OUTER JOIN "accounts" ON "accounts"."id" = "companies"."account_id"

この挙動を避ける方法のひとつとして、次のようにそのincludesをunscopeする方法がある。
同PRより

assoc.unscope(:includes).pluck(:id)

つっつきボイス:「今日社内SlackのRails板に貼って盛り上がった、Raiilsガイドの更新です」「そうそう、includesを使ったときのpluckの挙動😇」

「コミットメッセージの状況で単にpluckを使うとLEFT OUTER JOINしてしまう: すなわちpluck(:id)の結果に含まれるidの値が重複する可能性があるということだそうです」「はは〜なるほど😅」「問題はincludesと組み合わさった場合なんですよね」

参考: pluck — ActiveRecord::Calculations

「これはバグではなくて『こういう仕様である』みたいなものでしょうか?」「仕様ということですけど、これはORMの限界なのかも🤣」「ちょ🤣」「見ようによってはincludesがよしなにやってくれすぎるというか🤣」

参考: includes — ActiveRecord::QueryMethods

「自分は元々pluckがuniqueとは思ってないし😆: Slackにも書いたように↑pluckメソッドの挙動を考えればunique保証されないので」「まあCompany.pluck(:id)みたいな形になっていればidpluckなんだから重複しませんけど😆、たしかにuniqueとは限らない」「uniqueにしたければ明示的にpluck.uniqueするとかDISTINCT付けるとかしないと: まあ自分がORMを真っ先にSQL寄りに考える癖が付いているのでそう思うんでしょうけど☺️」

なお、後で@st0012さんとチャットで話したところ「includesの値を含むAR relationオブジェクトは使い回さないことをおすすめする」そうです。

Rails: JOINすべきかどうか、それが問題だ — #includesの振舞いを理解する(翻訳)

追記(2019/06/04)

@kamipoさんからです🙇。

pluckのdatetimeの高速化

pluckといえば、@kamipoさんが以下でpluckのdatetimeを高速化しました」「今までは必要がないときにもdatetimeの小数以下を丸めていたのをやめたのか」「速度向上はわずかだけど」「速くなるのはいいこと😋」

# activemodel/lib/active_model/type/helpers/time_value.rb#L24
-       def apply_seconds_precision(value)
-         return value unless precision && value.respond_to?(:usec)
+         number_of_insignificant_digits = 6 - precision
+         return value unless precision && value.respond_to?(:nsec)
+
+         number_of_insignificant_digits = 9 - precision
          round_power = 10**number_of_insignificant_digits
-         value.change(usec: value.usec - value.usec % round_power)
+         rounded_off_nsec = value.nsec % round_power
+
+         if rounded_off_nsec > 0
+           value.change(nsec: value.nsec - rounded_off_nsec)
+         else
+           value
+         end
+       end

「Ruby側で精度調整してるんですね😳」「お〜、DBからORMを通してRubyのタイムスタンプに変換すればどっちみち小数6桁目以下は使えないからここで切り落としている?と想像してみました🤔」「DBのタイムスタンプの方が分解能が高いんでしょうか?」「ことがある、ということでしょうね☺️」「普段そこまで考えてませんけど😆」


「ふと@kamipoさんのこのツイート↓が関係あるかな?と思ったけど別のコミットの話っぽいですね☺️」

アダプタのキャストにフォールバックするようにした

# activerecord/lib/active_record/type_caster/connection.rb#L11
-     def type_cast_for_database(attribute_name, value)
+     def type_cast_for_database(attr_name, value)
        return value if value.is_a?(Arel::Nodes::BindParam)
-       column = column_for(attribute_name)
-       connection.type_cast_from_column(column, value)
+       type = type_for_attribute(attr_name)
+       type.serialize(value)
      end

つっつきボイス:「これも@kamipoさん」「やっぱりスゴい人」「前に似たようなものを見たような気がするけどどうだったかな〜」「情報が足りなかったっぽい」

「まあActiveRecordのコードっていきなり見てもマジわからないし😆」「データベースアダプタ本来の挙動を知っておかないと読み方わかんないです〜😇」「MySQLとかPostgreSQLのアダプタは基本的にコネクションを管理してつなぐだけのはず、と思いたい🤣」「Active Record自体にもMySQL専用のコードとかPostgreSQL専用のコードとかが結構な量ありますし😆: それぞれに通せるクエリがまるで違ったりしますので」「でしょうね〜😅」

explainをMySQLとSQLiteのDatabaseStatementsに移動

# activerecord/lib/active_record/connection_adapters/mysql/database_statements.rb#L29
+       def explain(arel, binds = [])
+         sql     = "EXPLAIN #{to_sql(arel, binds)}"
+         start   = Concurrent.monotonic_time
+         result  = exec_query(sql, "EXPLAIN", binds)
+         elapsed = Concurrent.monotonic_time - start
+
+         MySQL::ExplainPrettyPrinter.new.pp(result, elapsed)

つっつきボイス:「abstract_mysql_adapter.rbからmysql/database_statements.rbとかに移動してる」「リファクタリングですね」「最後の1つだそうです」「Railsみたいに巨大なフレームワークだと、行数爆発してきたときにこうやって小まめにリファクタリングしてくれるのはうれしいですね😍」

deep_transform_keysを追加

# actionpack/lib/action_controller/metal/strong_parameters.rb#L699
+   def deep_transform_keys(&block)
+     new_instance_with_inherited_permitted_status(
+       @parameters.deep_transform_keys(&block)
+     )
+   end
+
+   ...
+   def deep_transform_keys!(&block)
+     @parameters.deep_transform_keys!(&block)
+     self
+   end

つっつきボイス:「strong parametersでdeep_transform_keysが使えるようになった🎉」「キーと値を入れ替える?」「いえ、これはキーの変換ですね」「お、指定した方法でキーを一括変換できるヤツですか😋」「しかもdeepだから再帰的にぶん回せると」「最近このあたりをようやっと使いこなせるようになってきましたよ〜💪」

参考: Hash#transform_keys — Rails API

「キャメルケースをスネークケースに変えたりできそうですね😋」「そうそう〜、ありがち😆」「文字列キーとシンボルキーが混じっているのをどっちかに揃えたいときとかもっ」「Railsにそんな機能があったとは😅」「今回deepでもできるようになったから、これでもう一網打尽🔫」「HashWithIndifferentAccessの下がHashWithIndifferentAccessとは限らないし😆」

参考: ActiveSupport::HashWithIndifferentAccess

scopeオプションに値がある場合にpartが落ちないようにした

# actionpack/lib/action_dispatch/journey/formatter.rb#L66
        def extract_parameterized_parts(route, options, recall, parameterize = nil)
          parameterized_parts = recall.merge(options)

          keys_to_keep = route.parts.reverse_each.drop_while { |part|
-           !options.key?(part) || (options[part] || recall[part]).nil?
+           !(options.key?(part) || route.scope_options.key?(part)) || (options[part] || recall[part]).nil?
          } | route.required_parts

          parameterized_parts.delete_if do |bad_key, _|
            !keys_to_keep.include?(bad_key)
          end
          if parameterize
            parameterized_parts.each do |k, v|
              parameterized_parts[k] = parameterize.call(k, v)
            end
          end
          parameterized_parts.keep_if { |_, v| v }
          parameterized_parts
        end

ルーティングがオプションのscope内で定義されていた場合、ルーティングにパラメータがないとパスヘルパーを用いるときにscopeが落ちてしまっていた。このコミットは、ルーティングがパラメータを取るかどうかにかかわらずscopeを維持する。
同PRより


つっつきボイス:「ルーティングのスコープですね」「どこかでパラメータが落ちてたのか」「自分はルーティングでnamespaceは使うけど、scopeってあまり使わないし😆」「どちらかを使えばオレの思うルーティングにできるかな?と思うと、実はどっちでもできなかったりすることありますけど🤣」「ルーティングのこの辺って毎回ググってます😅: でTechRachoが出てきたりします😆」

参考: Rails のルーティング - Rails ガイド

「ルーティングはちょっと油断するとすぐ沼るから😆、悩むぐらいだったらベタにいっぱい書く方がマシ😆」「そうそうっ!ベタでいいと思うのっ😆」「resourcesぐらいは使いたいですけど☺️」「頑張ってDRYに書こうとするとむしろ読みにくくなったりしますし😢」

「その辺が極まってるのがDeviseスコープ🤣」「そうっ🤣」「あれは書いていて毎回すっごく不安になりますし😇」「rails routesしながら書かないと怖すぎるし😆」「Deviseは沼」「Deviseは沼」

[Rails] Devise Wiki日本語もくじ1「ワークフローのカスタマイズ」(概要・用途付き)

Active Storageのdelegate_missing_to:allow_nilオプションを追加

  • ファイルが添付されていない場合に単一の添付ファイルのメソッド呼び出しでnilを返すようになった。
    従来は、たとえばUserモデルのuser.avatar.filenameは、アバターがない場合にModule::DelegationErrorになっていた。
class User < ApplicationRecord
  has_one_attached :avatar
end

上がnilを返すようになった。
changelogより

# activestorage/lib/active_storage/attached/one.rb#L3
module ActiveStorage
  # Representation of a single attachment to a model.
  class Attached::One < Attached
-   delegate_missing_to :attachment
+   delegate_missing_to :attachment, allow_nil: true
    ...

つっつきボイス:「ActiveStorageのdelegate_missing_toの修正か」「他のには入ってるみたいですね」「デフォルトは:allow_nil: trueだったんだろうけど、今までは:allow_nil: falseにできなかったということか」「ややこしい😆」「見方を変えれば存在保証ができなかったんですね☺️」

delegate_missing_toって何でしたっけ」「おやこんなところにTechRachoの記事が↓😆」「自分で訳しておいて忘れてました😅」「便利なブログですね〜☺️」

[Rails 5.1] 新機能: delegate_missing_toメソッド(翻訳)

Rails


同サイトより

RailsConf 2019の1日目の出し物から、有用そうなものを見繕ってみました。

見てみると、セッションの他にワークショップの数もかなり多いことに気づきました。右の2つのスロットは基本的にワークショップのようです。しかも「コマンドライン入門」のような敷居の低いワークショップもあったりと、強者だけのカンファレンスではなさそうに思えました。

探すときには、以下のカテゴリを活用するのがポイントのようです。


railsconf.comより


つっつきボイス:「しばらくRailsウォッチでRailsConf 2019でよさげなセッションを追ってみようと思いました: その第1弾です」「7スロットかける3日間ですからそのままだと多すぎて大変☺️」「RailsConfもう終わってるのか〜😇」

「そういえばこのRailsConf 2019でRails 6を発表するという話があったような…?」「予定通り遅れてます😆」「過去に予定通りリリースされたことってどのぐらいあったかしら😆」「あったかもしれないけど記憶にないし🤪」

「GitHubで使っているRailsの歴史を晒す」、「2年半かかった移行」

スライドだけさっと見ても楽しめるセッションです。


つっつきボイス:「GitHubの中の人の発表で、GitHub自身のRailsアップグレードの話です」「今のGitHubのRailsは最新のはずですね」「スライド↓では2009年にRails 2.3をforkしてるんですけど『大失敗だった😇』だそうです」「でしょうね〜☺️」

「Rails 2ベースからのfork、そりゃたしかにキツイわ〜」「本家からのcherry-pickでしのいでたんでしょうね」「Rails 3.1ってかなり変更点多かったはずだし」「アセットパイプラインがない時代からある時代への移行は苦行😭」「あと誰が使ってたか知らないけどあの頃はRJSなんてのもありましたし🤣」「つ、使ってた覚えが😆」「RJS、個人的には悪くなかったんですけど☺️」

参考: アセットパイプライン - Rails ガイド
参考: RJSなら数行のRubyコードでAjaxアプリを作成できる (1/4):Ruby on RailsのRJSでかんたんAjax開発(後編) - @IT

「Rails 4のためにGemfile.lockが2本立てに↓😆」「イテテテ、痛みが伝わってくる〜😭」「GitHubこれでやってたってスゴいですね」「まあ大規模な移行になると動かしてみないとわからないことがよくあるので、こういうふうにするのは仕方ない😅」

「大規模移行のベストプラクティスのひとつに、一気に移行しないで、最初は一部のページだけを新しいサーバーで扱って、様子を見ながらだんだんその割合を増やしていく、というのがありますね」「あぁ、リバースプロキシとか使って振り分けるヤツですね☺️」「ベストプラクティスは他にもあるようですけど☺️」「たまーにGitHubがおかしいときってこういうのをやってる最中だったのかも🤔」「そういうときって、こちらのブラウザ側でcookieをフラッシュすると切り替えが正常に戻ったりしますよね」「あるある〜」

「出た〜↓😱」「これがあるのはもう仕方ない」「ちゃんとやろうとするとこうなりますし☺️」「そして2018年にようやっとforkが解消されて最新になった!」「GitHubぐらい大規模になるとホント大変」

「Railsをforkするとセキュリティパッチを当てられなくなるとかつらそう」「つかforkして後悔したことが山のように😇」「gemのサポートやプライベートRails APIもつらかったそうです」「プライベートRails API、しれっとリネームされたりしますよね😆」

「GitHubは今はもう完全に最新のRailsなんでしょうか?」「GitHubだけに、独自の最適化は今もやってそうですけど🤔」「独自のパッチ当ててまた苦しむぐらいなら本家Railsにプルリク投げる方を選ぶかも😆」

「いや〜想像するだけでゾッとします🥶」「体力ないととても無理😢」「まあ移行期間中に担当者が辞めないことが一番大事🤣」「たしかに🤣」「大規模な移行ってそのリスクも考えるとなるべく短期間に収めたいですし☺️」「ちょっとずつでもバージョン上げていかないと」「ぼやぼやしてるとやってる間にまたバージョン上がったりしますし」「実際、移行中はどうしてもメンテナンスコストが最大になるので、短期決戦に持ち込むか、でなければさっきのようにリバースプロキシでURLを少しずつ新しいサーバーに振り分けるとかしないと」

参考: 以下の「The 30-Month Migration」も上とコンセプトが近そうです。

「こちらも大変そう…」「30か月だから2年半😇」「考えてみれば、Twitterなんかもまさにこういうことをやってきましたよね」「RailsからScalaベースになったり」「素のMySQLからCassandraになったり」

「そのキャッシュのコスト、いくらだと思う?」

参考: Tender Lovemaking | tenderlovemaking.com


つっつきボイス:「tenderloveことAaron Pattersonさんのキーノートで、前振りが割と長いんですが、Railsテンプレートの最適化の話でした」「ぱっと見、ERBをRubyに変換して高速化とかもやってるみたい」「そういえば今Rails 5のERBってどのgemで処理してるんでしたっけ?」「たしか前より軽いgemになってた気が🤔」

後で調べると、#30945でerubisからJeremy Evansさんのerubiに移行していました(ウォッチ)。

「ところでビューレンダリングのファイル名のprediction(予測)って、いつも不安になる😅」「prediction?」「以下のスライド↓で言うと左の上と下で実際に呼ばれるファイルが異なる」「それそれっ😭」「context dependentってそのことでしたか!」「まあちゃんと/書けばいいんですけど😆」「プロジェクトでパーシャルを明示的にしてフルパスで書くこと、みたいな運用にしたことありますヨ😤」

「そうそう、スライドをチェックしててこの一言が目に止まりました↓: テンプレートの実行よりキャッシュキーの算出の方が重かったと」「は〜なるほど」「スライドでもこの少し前で、キャッシュを変えてベンチマークを取ったらそうなってましたね」

「これはOSのファイルキャッシュも関係しているでしょうね: テンプレートがメモリに乗ればOSのファイルキャッシュにも乗るし」「ありそう!」「キャッシュを別ネットワークのRedisとかに乗せるより、ローカルOSのファイルキャッシュの方が速そうだし」

「プリコンパイルできるコードとできないコード↓」「なるほど、遅延評価が残るコードは当然できないし☺️」

「この辺は、可能なERBをプリコンパイルして高速化みたいなディープな話をしてるっぽい」「スライドではその辺を代わりにやってくれるっぽいactionview_precompilerというgem(Rails 6向け)↓を紹介していました↓」「それ事故ったりしないのかな?😆」「可能なものはプリコンパイルしてくれるっぽい」「組み合わせ爆発したらスゴいことになりそう😆」「最初のプリコンパイルが重かったらどうしましょう😆」

「オチは『コンテキストに依存させるな』とこれ↓😆」「まあここまで言うとちょっと違う気もするかな〜: 『キャッシュの方が遠いこともある』ぐらいに考えとこう」「見出しの収まりの良さでこうしたのかもですね」「しかしキャッシュキーの生成だけならそっちの方が速そうなんだけどな〜🤔: キーを生成したキャッシュがネットワーク越しにもなっていればそっちの方が遅いのはわかるけど、スライドをざっと見た限りだとよくわかんない😆」

Rails 6マルチDBとぽすぐれの落とし穴/パターン/パフォーマンス

抜き書き:

  • マルチDBになれば読み書きを分離できる
  • マルチDBはARクラスでどう認識されるか
  • 落とし穴: readレプリカでタイムラグが生じることがあった
    • ぽすぐれのパーティショニングで切り抜けた(詳しくはまた今度)
  • 落とし穴: シャーディングしたらDBでループが頻発
  • コネクションプールの考察
  • どのキャッシュが効いてるのか
  • ところでHerokuでPostgreSQL 11使えるようになったのでよろしく

つっつきボイス:「Herokuスポンサードのセッションで、Rails 6のマルチDBやってみた話でした」「マルチDB自体はRailsのプロジェクトやってればどこかで普通に出会いますけどね☺️: switch_pointとか」「最後はHerokuの宣伝で締めてます😆」

キャッシュは重要


つっつきボイス:「Kingと言ってるのは『重要』ぐらいの意味かなと」「スライド多い〜」「こういうキャッシュの話よさそう😋: 速度が遅くなったときの対応方法は『簡単にある程度対応できる』『大変だけど改善度合いが大きい』みたいに複数の戦略を知っておかないとつらくなりますね」

「後は時間押してるので軽く流しま〜す」

Railsのスケーラブルな監視

Action Cableのしくみ

その他Rails




jetbrains.comより

つっつきボイス:「RubyMineのIDEトレーニングプラグインだそうです」「ほほ~、ふぃーちゃーとれーなー😋」「RubyMineって使ったことないんですけど、便利ですか?」「そりゃもう便利ですよ〜❤️」「ないと生きていけないレベル」「ワイも」「そんなに!?自分はAtomとかでちょろっとRailsやってるぐらい😅」「Railsをメインで書いてるのでなければそれでいいと思いま〜す☺️」

参考: IDE Features Trainer - Plugins | JetBrains

Ruby

IRBのインクリメンタルシンタックスハイライト


つっつきボイス:「IRBのインクリメンタルシンタックスハイライト、話題になってますね」「コンソールがぶっ壊れているときにインクリメンタルシンタックスハイライトが動くとスゴいことになりそうでコワい😆」「$PS1がおかしくなったときとか😆」「端末リセットしてもだめだったらCtrl-C押しまくってセッション切断するしかなくなったり」「デフォルトはオンとオフのどっちになるんでしょう?🤔」

後で2.7.0-preview-1で試したところ、デフォルトでオンでした。

「k0kubunさんといえば、Hamlをむちゃくちゃ速くしたりとかいろいろエクストリームにやってる方ですよね😆」「あーHamlもでしたっけ」「Hamlもやってたやってた」「Hamlit…は別の人でしたか😅」「カッとなってJITやったり😆」「いろいろ凄すぎる💪」

追記: Hamlitもk0kubunさんでした。訂正いたします。
* リポジトリ: k0kubun/hamlit: High Performance Haml Implementation

参考: Hamlを3倍速くした - k0kubun’s blog
* リポジトリ: haml/haml: HTML Abstraction Markup Language - A Markup Haiku
* サイト: Haml


haml.infoより

ハッシュテーブルを学べるスライド

ちょうどRubyのハッシュテーブルがどうなってるか気になっていたのですが、そこにも言及していました❤️。

追記(2019/06/04)

お知らせありがとうございます!Rubyは2.4からopen addressingだったんですね😅。

参考: Ruby 2.4.0 リリース


つっつきボイス:「この間BPS社内Slackに貼っていただいたヤツです」「これはいいスライド👍」「学びすごくありました😋」「ハッシュテーブルってあるから初心者向けの割とやさしい話かなと思いきや、シノニム処理とかリハッシュみたいな特濃の話: これはいいよ〜😍」「もろ内部実装の話😆」「しかも説明がめちゃくちゃ丁寧😋」「CuckooとかHopscotchなどのアルゴリズムをこれで初めて知りました↓🙇」

Hopscotch {名} : 〔子どもの〕けんけん遊び◆英国や米国の小学校で一般的な、主として女の子の遊び。日本のけんけん(または、けんけんぱ)と同様に片足や足を広げてマスの中を跳ぶ

Mongoid: Ruby向けのMongoDB ODマッパー


mongodb.comより


つっつきボイス:「Mongoidなつかし〜😂」「Mongoid、いつの間にかMongoDBのプロダクトに入ってるんですね」「公式になったみたい」「すっごく昔(2016年ぐらい?😆)に使いましたよ」「NoSQLが流行った頃ですか?」「ですです」「でデータ壊れたりしてつらくなったりして🤣」「図星: 一周回ってみて結局RDBどんだけ最高かと🤣」

httplog: HTTPリクエストのログを取る(Ruby Weeklyより)

# 同リポジトリより
[httplog] Connecting: localhost:80
[httplog] Sending: GET http://localhost:9292/index.html
[httplog] Status: 200
[httplog] Benchmark: 0.00057 seconds
[httplog] Response:
<html>
  <head>
    <title>Test Page</title>
  </head>
  <body>
    <h1>This is the test page.</h1>
  </body>
</html>

つっつきボイス:「これは…あ〜なるほどなるほど!」「モンキーパッチするヤツ?」「いえ、これはリクエストログを全部残しておけるヤツ」「お〜、それは便利そう!😋」「たとえばRailsサーバーから出ていくリクエストを記録するとか?」「そんな感じです、たぶんプロキシとして動くんじゃないかと思う☺️」「あくまでgemなんですね」

「どうやらこの辺のおなじみgem↓をラップするっぽい」「Faradayはまだ一部のみサポート、と」「curlを直接使うツールだとラップできないかも(Faradayはどうなのか知りませんが😆)」


同リポジトリより

「Faraday昔使ってましたけど、皆さんも使ってます?」「すべてがRESTfulなUIならFaradayはいいですね😍: その代りRESTfulでないと途端に使いづらくなりますけど😇」「そうかも〜自分のプロジェクトはFaradayで十分だったのか😆」「相手によりけり😆」

その他Ruby

つっつきボイス:「この記事↓でRuboCop作者が勧めてたので」「Railsのspring的なツール?」「そんな感じです」「Vimみたいなエディタで保存するときにRuboCopを毎回起動すると遅いからこれでやる、とかなんでしょうね☺️」「READMEでもnetcat使ってやってたりしてるし」「daemon化すれば素でRuboCop実行するよりは速いだろうけど、元々RuboCopがそんな速くないし😆」「ルールが小さければやれるかも」「RuboCop作者も遅いのは認めてますね☺️」

RuboCop作者がRubyコードフォーマッタを比較してみた: 中編(翻訳)

参考: Netcat - Wikipedia



Ruby trunkより

提案: frozenしたオブジェクトをエラーで表示して欲しい

# 同issueより
class A; end
A.freeze
def A.a; end
# can't modify frozen Class: A    ←こういうのを出したい
'a'.freeze << 'b'
# can't modify frozen String: "a" ←こういうのを出したい

つっつきボイス:「frozenされている文字列をいじってエラーになったときに、どの文字列でエラーが出たのか出して欲しいということだそうです」「あは確かに😆」「欲しいヤツだ〜」「文字列が複数あったらなおさらですね」「パスワード入ってたらどうしよう🤣」「パスワードfreezeしないっしょ普通🤣」「freezeがデフォルトになりつつあるし、はずみでログにぽろっと出たりしたらと思うと😆」

パターンマッチングの後置のifの挙動 — 却下

# 通常の後置のif
=> puts 1 if (puts 2; true)
2
1

# 質問
class A
  def deconstruct
    puts 'deconstruct called'
    [1]
  end
end

p case A.new
in A[1] if (puts 'if check'; true)
  'yes'
else
  'no'
end

# 以下が出力される(-- 仕様どおり)
# deconstruct called
# if check
# "yes"

「パターンマッチングの後置のifの挙動についての質問ですが、仕様どおりということでした」「一瞬考えちゃう😆」「後から追加する機能はチェックするところが多くて大変😅」

通常の後置のifの挙動については以下で説明されています。

「そうそう、Rubyの後置のifは条件が偽の場合も代入されますね」「記事ではMatzに直接質問して回答いただいてます」「代入は構文解析の時点で行われるから、結果にかかわらずundefined variableとかにはならない☺️」

「この挙動はRuby試験で頻出しそう😆」「マジっすか!」


前半は以上です。

バックナンバー(2019年度第2四半期)

週刊Railsウォッチ(20190521-2/2後編)サーバーレスクラウドのベンチマーク比較サイト、VueJSパフォーマンス向上、GraalVM 19.0ほか

今週の主なニュースソース

ソースの表記されていない項目は独自ルート(TwitterやはてブやRSSなど)です。

Ruby 公式ニュース

Rails公式ニュース

Ruby Weekly

RubyFlow

160928_1638_XvIP4h

デザインも頼めるシステム開発会社をお探しならBPS株式会社までどうぞ 開発エンジニア積極採用中です! Ruby on Rails の開発なら実績豊富なBPS

この記事の著者

hachi8833

Twitter: @hachi8833、GitHub: @hachi8833 コボラー、ITコンサル、ローカライズ業界、Rails開発を経てTechRachoの編集・記事作成を担当。 これまでにRuby on Rails チュートリアル第2版の監修および半分程度を翻訳、Railsガイドの初期翻訳ではほぼすべてを翻訳。その後も折に触れて更新翻訳中。 かと思うと、正規表現の粋を尽くした日本語エラーチェックサービス enno.jpを運営。 実は最近Go言語が好き。 仕事に関係ないすっとこブログ「あけてくれ」は2000年頃から多少の中断をはさんで継続、現在はnote.muに移転。

hachi8833の書いた記事

週刊Railsウォッチ

インフラ

ActiveSupport探訪シリーズ

BPSアドベントカレンダー