週刊Railsウォッチ(20190318-1/2前編)RailsとRubyでセキュリティ修正リリース、Rails 6でinsert_allやupsert_all追加、Webpacker 4登場ほか

こんにちは、hachi8833です。World Wide Web生誕30周年おめでとうございます。


つっつきボイス:「歴史だ⛩」「そうそう、もう30周年か」「↑このTシャツ素敵すぎ😍」「ふと見渡してみると、今日のつっつきに集まっているのは、お〜うまい具合に20代、30代、40代、50代とWebの歴史を4世代でひととおり網羅してる感😆」「カバレッジは完璧😆」

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

週刊Railsウォッチ「公開つっつき会」第9回開催のお知らせ

次回の公開つっつき会は4/4(木)です。皆さまのご応募をお待ちしております!🙇

WWW生誕30周年よもやま話

今回のつっつきでは秘密のゲストがお見えになりました。

「では早速、ネットに最初に触ったのは?」「本格的に使ったのは大学に入った1997年かなー: あの頃はSun OSもあったし」「お〜なるほど!」「高校のときにWindows 3.1をインストールしましたね: フロッピー30枚ぐらいあったかな😆」「私はマカーだったせいでMS-DOSとWin3.1がすっぽり抜け落ちてて、Windows は95から初めて触りました」「自分の高校にはWindows 95入りのPCあった〜、ディスク20枚ぐらいで😆」

「20代だとフロッピーって見たことあります?なんて😆」「ありますっ😆」「お〜、じゃフォーマットしたことは?」「それは🤣」「🤣」「🤣」「フロッピー、家に普通にありましたし」「ファミコンディスクシステム↓とかじゃなくて?🤣」「店頭でディスク書き換えるヤツ🤣」「4世代カバーしていると面白い〜😆」

ファミリーコンピュータ ディスクシステム - Wikipedia

「50代だとやっぱりパソコン通信が最初みたいな?」「ですです: モデムNiftyServeやってましたし、インターネットは某客先で監禁されてたときにそこの人がモノクロMacでMOSAIC(最初のWebブラウザ)を見せてくれたのが最初でした」「いつ頃?」「思い出した、オウム真理教の大事件の頃でした: あのニュースが大量に流れたせいでそこのサーバーがパンクしたんだった😇」「じゃ1994年か95年頃!」「歴史や…」「30代だけどWindows 95でインターネットに触れたので逆にパソコン通信を知らない😆」「20代の人も知らないよねパソコン通信?」「知らないですー😆」「知ってたらコワい😆」

もう使い道がありませんが、当時の英語圏のパソコン通信((AOLとかCompuServe↓とか)はひっくるめてonline servicesと呼ばれてました👴。当時は海外への電話代が高値だったので、英語圏とチャットするとか当時学生だった自分には到底無理でした。

臨時ニュース3本

1: Railsでセキュリティ修正リリース

3つの修正についてはアップグレードの他に緊急用のパッチも提供されています。詳しくは必ずリンク先を参照してください。

後述しますが、つっつき後にもさらにRuby 2.5.5アップデートが来ててんてこ舞いでした😭。私も一応オレオレRailsアプリのRailsとRubyのアップグレードを完了しました。


つっつきボイス:「お、例のRailsセキュリティ修正」「Rubyもアップデート来てます」「3月半ばでbeta3ってことはスケジュール的に順調な方なのかな?」「4月末のRailsConf 2019がファイナル目標ですね↓」「ならbeta4とか5ぐらいまでいくかも😆」

参考: Timeline for the release of Rails 6.0 | Riding Rails


rubyonrails.orgより

「Railsはだいたいリリースが後ろにずれ込むものなので😆」「もうみんなわかってるし☺️」「リリースを急いでバグ埋め込むよりその方がいいですし☺️」「RC2とかまで進んだところで微妙に機能修正が入ったりなんてこともありえるし😆」「リリース直後のRailsは基本人柱として使うぐらいの勢いで」

追記(2019/03/19)

ペパボさんの以下の記事で、より具体的にセキュリティ問題を追いかけています。

CVE-2019-5418: render file:の脆弱性

render file:を使うとAction Viewで任意のファイルが露出する可能性。

class UserController < ApplicationController 
  def index 
    render file: "#{Rails.root}/some/file" 
  end 
end 

以下のようにすることで回避可能。file:以外のrenderは影響を受けない。

render file: "#{Rails.root}/some/file", formats: [:html] 

つっつきボイス:「render file:ってそもそもあんまり使わないけど😆、ディレクトリトラバーサル的なことができちゃう脆弱性ってことかな?」「かなと思いました」「任意のファイルって時点でたしかにあかん😇けど、CVE見てみるとcontent disclosureと記述されてる」

参考: ディレクトリトラバーサル - Wikipedia

「ところで脆弱性関連でcontent disclosureって用語あったかなー?」「あーどうでしたっけ?」「手元でググった感じではこのAction View以外に出てこないようなので、どことなく造語か何かの匂いがする🤔」「ここではおそらくディレクトリトラバーサル的な意味で使われてるっぽい🤔」

後で調べてみると、content disclosureは「情報の(公式な)開示」のような割と一般的かつポジティブな言葉として使われる方が多いようです。コンテンツお漏らしのようなネガティブなニュアンスだと、今思いつくのはleakとかexposureあたりでしょうか。

参考: disclosure content — コーパスでの用例です

CVE-2019-5419: Action ViewがDOS攻撃を受ける可能性

コントローラで以下のような暗黙のレンダリングが影響を受ける可能性がある。

class UserController < ApplicationController 
  def index 
  end 
end 

つっつきボイス:「説明用のサンプルコード↓がぱっと見ループしそう」「CPUが100%になるということだからDoS攻撃でしょうね: こんなコード書かないけどっ😆」「やってみたらすぐわかるでしょうし😆」

# CVE-2019-5419より
class UserController < ApplicationController 
  def index 
    render "index" 
  end 
end 

「でもこうやって↓書くとvulnerableじゃないのか!へ〜」「respond_toがポイントなのかしら」「どっちにしろこんなの書かないし😆」

# CVE-2019-5419より
class UserController < ApplicationController 
  def index 
    respond_to |format| 
      format.html { render "index" } 
    end 
  end 
end 

vulnerable(脆弱な)はセキュリティ方面で多用されている堅苦しい言葉ですが、元々軍事や諜報で昔から使われてるような趣です。やはりラテン語が起源でした↓。自分の場合、一部の心理学だか文化人類学だかで「いじめられっ子オーラ」的なものをvulnerability(攻撃誘発性)と呼んでいたのを見たのが最初でした。

参考: vulnerable | Origin and meaning of vulnerable by Online Etymology Dictionary

CVE-2019-5420: developmentモードで生成された秘密鍵が推測される可能性

秘密鍵が推測されるとリモートコード実行の可能性。Railsをアップグレードするか、config/environments/development.rbに以下を記述することで回避可能。

config.secret_key_base = SecureRandom.hex(64) 

つっつきボイス:「まdevelopmentモードだし😆」「よほど下手打たない限りは😆」「上でSecureRandomを使っているのは、(Rubyの素の)#randRandomを使うなという、セキュリティ方面でよく言われる話でしょうね」

参考: 2.2 セッションID — Rails セキュリティガイド - Rails ガイド — RailsのセッションIDはSecureRandomで生成されているという解説
参考: What is so “secure” about SecureRandom? – Christian Blais – Medium

Rubyの素の#randRandomによる乱数生成は素朴なので、外部から推測されてしまう可能性があります。お遊び以外では使わないようにしましょう⛔️。

参考: 乱数について CapmNetwork
PDF: デタラメさの効用と、1+1=0の世界

臨時ニュース2: Rubyのセキュリティ修正がリリース

この間のRubyGemsの問題の修正の他にも細かな修正や変更が含まれています。その中にさらっとUNICODE 12対応が含まれていました。

# common.mk#L18
-UNICODE_VERSION = 11.0.0
-UNICODE_EMOJI_VERSION = 11.0
+UNICODE_VERSION = 12.0.0
+UNICODE_EMOJI_VERSION = 12.0
UNICODE_BETA = NO

つっつきボイス:「Ruby 2.6が早くも2.6.2」「早いな〜」「内容はこの間のRubyGems修正を正式に導入した感じですね」「おーUnicode 12も入ってるし😍」「これでRubyで使える絵文字がまた増える😋」

「こういうのはRubyが一番熱心ですよね」「日本人が多い言語だと対応が進みやすい傾向があるかも、なんて😆」

臨時ニュース3: Ruby 2.5.5がリリース

このリリースにはマルチスレッドとマルチプロセス(Process.fork)を利用したアプリケーション(例: puma)でデッドロックが発生することがある不具合の修正が含まれています。

同リリースノートより

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

リリースが近いせいか、以下のようなドキュメントの更新が中心のようです。


つっつきボイス:「小さな修正ですが、painfullyという文言は余計だろうということだそうです」「あ〜なるほど!」

修正前: Setting this up is painfully simple.
修正後: Setting this up is simple.

元をあえて日本語で表すと「嫌になるほどシンプル」みたいな感じでしょうか。

Zeitwerkにカスタムinflectorを追加

# activesupport/lib/active_support/dependencies/zeitwerk_integration.rb#L58
          def setup_autoloaders
-           Rails.autoloaders.each do |autoloader|
-             autoloader.inflector = Inflector
-           end

            Dependencies.autoload_paths.each do |autoload_path|
              # Zeitwerk only accepts existing directories in `push_dir` to
              # prevent misconfigurations.
              next unless File.directory?(autoload_path)
              if autoload_once?(autoload_path)
                Rails.autoloaders.once.push_dir(autoload_path)
              else
                Rails.autoloaders.main.push_dir(autoload_path)
              end
            end
            Rails.autoloaders.each(&:setup)
          end

つっつきボイス:「Zeitwerkの最適化っぽい: 上の部分が要らないので削除したというか」「オートローダーが変わるとやっぱりいろいろ起きるんだな」

SafeBufferのスライスassingmentをサポート

# activesupport/lib/active_support/core_ext/string/output_safety.rb#L202
-   def []=(index, value)
-     super(index, html_escape_interpolated_argument(value))
+   def []=(*args)
+     if args.count == 3
+       super(args[0], args[1], html_escape_interpolated_argument(args[2]))
+     else
+       super(args[0], html_escape_interpolated_argument(args[1]))
+     end
    end

つっつきボイス:「if args.count == 3で引数が3つの場合にSafeBufferが部分文字列を出すように変わったのか、なるほど」「スライスってそういう意味だったんですね!」「文字列のスライスといえば、だいたい部分文字列の切り出しとか分割ですね🧐」

add_pg_decoderのバグを修正

# activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb#L242
      def initialize(connection, logger, connection_parameters, config)
        super(connection, logger, config)
        @connection_parameters = connection_parameters

        # @local_tz is initialized as nil to avoid warnings when connect tries to use it
        @local_tz = nil
-       @default_timezone = nil
-       11:08stamp_decoder = nil
        @max_identifier_length = nil

        configure_connection
        add_pg_encoders
        add_pg_decoders
        @type_map = Type::HashLookupTypeMap.new
        initialize_type_map
        @local_tz = execute("SHOW TIME ZONE", "SCHEMA").first["TimeZone"]
        @use_insert_returning = @config.key?(:insert_returning) ? self.class.type_cast_config_to_boolean(@config[:insert_returning]) : true
      end
...

        def add_pg_decoders
+         @default_timezone = nil
+         @timestamp_decoder = nil
+

古くなった@default_timezonedisconnect!後のreconnect!でエラーになることがあったのを修正。
同コミットより大意


つっつきボイス:「いかにもぽすぐれ関連の修正: やっぱりkamipoさん?」「ですです🎯」「この謎バグよく見つけたな〜って思うし🤓: さすが『kamipoさんはすごい人』とテンプレにまでなってる人」

本当にありました↓。

参考: ハッシュタグ #kamipoさんはすごい人

新機能: insert_many系メソッドが追加

以下の記事で知りました。

# 同PRより
# Insert multiple records, performing an upsert when records have duplicate ISBNs
# ('Eloquent Ruby' will overwrite 'Rework' because its ISBN is duplicate)
Book.upsert_all([
  { title: 'Rework', author: 'David', isbn: '1' },
  { title: 'Eloquent Ruby', author: 'Russ', isbn: '1' }
],
   unique_by: { columns: %w[ isbn ] })

以下が追加されました。

  • insert_all
  • insert_all!
  • upsert_all

つっつきボイス:「おぉ、これは欲しい人いそう❤️」「そういえばこの間activerecord-importでいろいろやってたのを見かけたので、もしかするとそのときに欲しかったんじゃないかなーって😆」

insert_allもあるし」「upsert_allもあると確かにうれしい😋」「upsert_allがRailsのレイヤでサポートされてるとありがたい🙏」

「UPSERTって、UPDATEとINSERTの合わせ技でしたっけ」「なければINSERT、あればUPDATEしてくれるヤツ💪」「知らない世界…🥺」「最初は『へー、そういうのをUPSERTって言うんだ〜』と思ってましたが、あちこちで使われるようになってきたのでそろそろ一般化したと思ってよさそう」

参考: PostgreSQL UPSERT の機能と使い方 - ベストプラクティス| Alibaba Cloud ドキュメントセンター

「そういえばMySQLだとREPLACE文で同じことができる↓」「以前のPostgreSQLだとINSERTとかでWITHを使う↓みたいな感じで、RDBMSによってとにかくやり方がまちまち😅」

参考: MySQLでバルクアップデートを実現するには - Qiita
参考: 7.8. WITH問い合わせ(共通テーブル式) — PostgreSQL 10.5

UPSERT的な処理は業務アプリで必ずといっていいほど使うのでうれしい機能🥰: findして見つかったらそれで更新かけるとか死ぬほど書いたし🤓」

番外: Railsのデフォルトログレベルは4.2からdebug

# guides/source/debugging_rails_applications.md#L141
This is useful when you want to log under development or staging, but you don't want to flood your production log with unnecessary information.

-TIP: The default Rails log level is `info` in production mode and `debug` in development and test mode.
+TIP: The default Rails log level is `debug` in all environments.

つっつきボイス:「これは今日のチームミーティングで軽く話題になったやつですね」「ですです: Railsのデフォルトのproductionログレベルって何だっけというお話でしたね: あの後調べたらy-yagiさんが4.2でRailsガイドを更新してくれてました」「productionのログレベルはinfoだと思ってたらいつの間にかdebugだったという😆」

「実は日本語版Railsガイドでは少し前までこの部分が更新されてなかったんですが、diffベースで差分翻訳する体制になる前の名残だったので追いきれてなかったという😅: それをここ半年で5.2キャッチアップ翻訳をかけたときに全ページを目で照合してやっと更新を拾い上げました↓」


Rails アプリケーションのデバッグ - Rails ガイドより

Rails

Webpacker 4.0がついにリリース(Ruby Weeklyより)

よく見ると、もう4.0.2なんですね。


つっつきボイス:「本家Webpackとのバージョン齟齬がつらかったWebpackerが、やっと4.0になりました😂」「Webpackが4.0になってからWebpackerが4.0になるまでどのぐらい空いたんだろか😆」「今Webpackerのバージョンを上げるといろいろ変わりそうでコワい😨」

「今さらですけど、WebpackerがRails用で、Webpackは汎用みたいな位置付けでよかったんでしたっけ?」「ですね: Webpackerはgemで、WebpackはnpmとかでインストールするJavaScriptのライブラリ」


webpack.js.orgより

調べてみると、Webpack 4.0.0リリースは2018年2月↓なのでほぼ1年かかりましたね。そしていつの間にかWebpackは5.0.0のアルファ版がだいぶ進んでいます。

参考: Release v4.0.0 · webpack/webpack
参考: Release v5.0.0-alpha.11 · webpack/webpack

【保存版】Rails 5 Webpacker公式ドキュメントの歩き方+追加情報

monday.com: タスク管理サービス

RailsとBootstrap 4感出てますね。



monday.comより

参考: 【各社員の業務を見える化】シンプルで使いやすいタスク管理ツール「monday.com」


つっつきボイス:「Chromeのwappalyzer拡張によると、monday.comはどうやらRailsでできてるっぽいのでここに置いてみました: タスクの更新や移動がやりやすくて、BacklogやTrelloよりよさげと言ってたので」「この種のサービスはそれこそ無限にありますから😆」

「タスクやプロジェクトの管理サービスでRailsで書かれているといえば、考えてみたらほら、アレがあるじゃないですか😎」「アレ?」「Basecamp」「あぁ!そういえば😳」「RailsユーザーならDHHのお膝元のコレでしょう🤣」「みんな使えと🤣」「名前しか聞いたことなかった…」「Basecampがどういうサービスなのか今までまるで気にしてなかった😅」


今更のようにサイトをちょっとだけ触ってみると、表示や応答がめちゃ高速ですね。

「改めて説明すると、BasecampはRailsの作者であるDHH↓がいる会社です」「Railsは見ようによっては、DHHがBasecampで欲しい機能をRailsに入れているとも言える😆」「なので、Basecampを見ればRailsにどういう機能があるかがよっくわかる🤣」「🤣」

DHHはレーシングマニアとしても有名です(@dhhracing)。

「Basecamp、日本で使ってる人いるんでしょうか?」「見かけないな〜」「考えてみたらRailsの本家本元なのに、Railsフレームワークと比べてBasecampのサービスって日本だとあまり見かけない感じですね」「英語圏ならきっとプレゼンスそれなりにありそうですけど」「ちょっと試してみたいですね」

featuresを見ながら)「そうそうBasecampのサービスにはチャット機能なんかも統合されてるんですよ: 自分はSlackにずっぽりだからチャットあまり変えたくないけど」「こうしてみると、Railsらしいモノリシックな作りになってるなと思いますね」

execute_sql: RailsコンソールでSQLを直接実行(RubyFlowより)

前にウォッチで紹介したrails_dbと同じ作者です。


つっつきボイス:「pgAdminを立ち上げるのが面倒なときに使うのかな?と思って」「実行だけならActiveRecord::Baseにあるexecuteでできますけどね😎」「それ知らなかった😅」

参考: ActiveRecordを使ってRails consoleからテーブル一覧がほしい - Qiita

「お、でもこのexecute_sqlは結果セットを直接制御できるのか↓: それならわかる😋」「というと?」「ActiveRecord::Baseexecuteで取る場合は結果セットを自分でさらに展開しないといけないんですが、このgemは図でやってるみたいに:singleってやれば1行だけ取るみたいなことができると」「なるほど〜」「ま、自分は要らんけど🤣」「🤣」「🤣」


同リポジトリより

「rails_dbは、MySQLAdmin的なGUIのDB管理ツールをRailsで作ったものですね」

「やや、Windowsノートがまたフリーズ❄️: 今日で何回目だ😇」「でかいモニタにつなぐと起きやすいんでしょうか」「ウォッチをつっつき中に2回というのはなかなかキツイわ〜😭」

RailsのService Objectとactive_interaction gem


つっつきボイス:「TechRacho記事を2本も引用いただいてたので🎉」「古い記事もありますね😆」「もちっと新しいのも一応😆」「記事ではService層というかService Objectとは何ぞやを追求してます」「もう『おまえがService Objectだと思ったものがService Objectだっっっ』でいいんじゃね?🤣」「🤣」「🤣」「ちょうど今日BPSの社内勉強会でもPoEAA本↓の2周目を始めたところで、そこでもいずれこの辺のパターンが出てきます」

エンタープライズアプリケーションアーキテクチャパターンを読む: 1.概要

「さっきの記事でもちょうどService層を『Commandパターン』にするか『Facadeパターン』にするかということをやってますね」「あー、記事の『全部のせServiceクラス』↓ってFacadeのことなのか」「自分はそう理解してます🤓」

同記事より

「そしてService層では主にCommandパターンとFacadeパターンという2種類の実装方法が広まってて、そのあたりで宗教戦争が起きやすい⚔️」

「ところでまとめの最後にlib/に置くみたいなことが書かれてますけど、lib/に置くときってどんなときでしょう?」「Railsがなくても使えるような汎用的なものをlib/に置くべきでしょうね」「なるほど!」「自分の感覚では、たとえばActive Recordにアクセスするようなコードはlib/には置かない感じですね☺️」

[保存版]人間が読んで理解できるデザインパターン解説#3: 振舞い系(翻訳)

また、同記事でactive_interactionというgemが紹介されていました。

「もしかするとこういうgemっていっぱいあるのかなと思いつつ」「これは.runで実行するタイプのようなので、Commandパターンを支援するgemなんでしょうね☺️」「READMEが意外に長い🐍」「フレームワークっぽい雰囲気だから長いのかも」「何となくだけど、どことなくオブジェクト指向の過激派っぽい匂いが😆」「すべてはオブジェクトみたいな😆」

# 同リポジトリより
Square.run!(x: 'two point one')
# ActiveInteraction::InvalidInteractionError: X is not a valid float
Square.run!(x: 2.1)
# => 4.41

「先週のBPS勉強会で言ってた『後から引っぺがすのが大変なgem』っぽくもありますね😆」「これで幸せになれるんでしょうか😅」「😆」「思ったんですが、完全にオブジェクト指向に寄せたいならむしろRubyよりJavaとかを使う方がいいのかも🤔: キレイにカプセル化できるし速いし」「どうなんだろう…記法の問題でもないような気もしますが😆」「Rubyでカジュアルにオープンクラスできるのは結構エグい😆」「😆」

「Rubyの場合クラスをどこから開かれるかわからないので、規模が大きくなってくるとつらくなってくる気がするんですけどどう思います?」「いやーRubyをそこまでズブズブに使ったことがまだなくって😅」「Rubyはその辺ほんと自由ですよ🤣」「🤣」「ヤバい書き方をしようと思えばもういくらでもできるし」

Railsコードのリファクタリング


つっつきボイス:「永和システムマネジメントさんの記事で、例の本を読み込んで解説しているので」「お、Martin Fowlerの新しい版の『Refactoring』だ」「去年発売されてましたね」「日本語訳ってあります?」「出たばっかりだしまだ先でしょうね」

参考: リファクタリング 第2版が素晴らしい - koicの日記

「第2版ではJavaScriptも扱ってるって言ってましたね」「前はJavaとC#でしたっけ」「第2版、買った気がするんだけどまだ読んでない😇」「😆」「ゴツそうな内容ですね」「まあだいたい同じようなこと書いてるだろうし、そんなに読むのは大変じゃないと思いますが」

「実際Martin Fowlerの本ってとても読みやすいのでいいと思いますよ」「そうかも」「コンピューターサイエンスをやってきた人らしい文章」「そういえば勉強会でもさっきのPoEAAには無駄な記述がないって言ってましたね」「読んでるうちに彼の技術英語の言い回しがだんだん身体に入ってきて斜め読みが効くようになってくるし📖」「脳が彼の英語に最適化されるというか🧠」

Railsの素朴な疑問

個人的に、joinsはキャッシュしないから、同じ処理を何度も繰り返すのであればincludesとか、eager_load使えば早くなるのではと思っているけど、そうはならないから違うのか。


つっつきボイス:「社内の日報で見かけたんですが、これだけだと何とも言えない感じでしょうか?」「あーなるほど、そこで言ってる『joinsはキャッシュしない』というのはおそらくRailsレベルでキャッシュしないということなんだろうけど、クエリキャッシュはあると思うので、少なくとも同じクエリを投げればクエリキャッシュは効くんじゃないかなー?🤔」「おー」「eager_loadは、使えば速くなるというようなものではないし: includesは結構速いけど」

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

「RailsのJOINってたしかINNER JOINでよかったっけ?」「そのはずです」「クエリ次第ではJOINも速いことがあるんじゃないかなという気がするんだけど🤔、この場ではこのぐらいが限度かな」「急に振っちゃってすみません🙇」

Ruby

ヤバいRubyコード(Ruby Weeklyより)


つっつきボイス:「Ryan BiggさんはDHHとRailsの設計方針でやりあったりしてた方ですね↓」「邪悪なRubyというか」

Railsの`CurrentAttributes`は有害である(翻訳)

「おー記事は早速+メソッドをオーバーライドしてるぞっ😆」「*も単項演算子の-も片っ端からオーバーライドしてるし」「その辺は元からできる機能ではあるけど」「こういうのを読み込んで既存のRubyの振る舞いを置き換えたりみたいな😆」

# 同記事より
class Integer
  alias_method :old_multiply, :*

  def *(num)
    m = method(:old_multiply).unbind
    m.bind(3).(num / self)
  end
end

「JavaScript風のsortですって↓」「こういうバイナリをひっそり紛れ込ませるとアツい🧨」

[-2, -1, 0, 1, 2].sort()

//=> [-1, -2, 0, 1, 2]
module JSSort
  def self.included(base)
    base.alias_method :old_sort, :sort
  end

  def sort
    self.map(&:to_s).old_sort.map(&:to_i)
  end
end

Array.include(JSSort)

「トリプルイコール===もオーバーライドできるのかー」「へ〜」

class Array
  def ===(_)
    true
  end
end

「これはあの文字列をfreezeさせるショートハンドの-をオーバーライドしてるし↓」「この文字列freezeの-記号とunfreeze用の+って最近のRubyにあるんですけど、ご存じでした?」「いえ〜初めて知りました🥺」「Rubyのfreeze用の-って、もうちょっとマシな記号ってなかったの?って思っちゃいますよね」「.freezeだと長くなるしショートハンドが欲しいのはわかるんだけど😅」「初めて見たときにも『はぁ?』と思ったし😆」「記事で日本語の(漢数字)をメソッド名に使ってるし😆」

>> a = -"string"
=> "string"
>> a.frozen?
=> true

class Array
  def -@
    clear
  end
end

a.clear

参考: 文字列をfreezeさせるいくつかの方法 - Qiita

「これはもうイタズラ用ですね」「記事の冒頭でもproductionでは絶対やるなと書いてますし」「まあ想像のつく範囲のイタズラかなと☺️」「中学2年生におすすめ✏️」「やられたらびっくりするのは間違いない🎈」

その他Ruby


つっつきボイス:「AWS賞ですって」「普通にAWSがスポンサーをやってる賞でしょうね☺️」



つっつきボイス:「高橋会長が出した本で、コードにひとつひとつ解説という意味でのふりがなを振ってるということみたいです」「Pythonでも同じような趣旨でふりがなプログラミングの本が出てたから、その流れでRubyでも本を出したのかも」「そっちは知りませんでした😅」

「ふりがなプログラミング、特に教育方面ではあっていいと思う👍」「ですね」「特に今みたいにプログラミング教育の義務化みたいなことになってくると、こういう本って重要になってくるし」「その前に国語の勉強しようねって言いたいけどっ😆」「😆」「あ〜でも本当にそう😆」「この本でプログラミングを理解できるのって、国語の成績がいい子に限られるんじゃないかって思ったし」「言語能力ってそういうことですよね😆」

「国語の成績はいいんだけどプログラミングがどうもわからないという子どもを助けるという意味では、この本が役に立つかも」「『プログラミングって数学ができないとだめなんでしょ?』みたいな苦手意識のある子どもとかに」「1クラスで数人でもこの本で救われたら価値は絶対あるし: 全員は無理だと思うけど」「たしかに〜」

「話逸れちゃうんですけど、最初本のタイトルを見たときにこのRubyってもしかして『ルビを振る』のルビのこと?って考えちゃいました🤣」「なっはっは🤣」「とっても紛らわしいんですけど、どっちも英語のスペルは同じ”ruby”なんですよ😭」「本の帯にはRailsって書いてるし」「まさかRailsまでカバーしてるとか?😆」

参考: ルビ - Wikipedia

「ツイートの写真の左、よく見るとjnchitoさんの直筆ポップじゃん!」「あ〜全然見落としてた!😆」「マニアックすぎる😆」「さすがジュンク堂池袋本店」

「ジュンク堂池袋本店といえば、Rubyのコアコミッターが割とお互い近いところに住んでるという話が以下の翻訳記事にあるんですが、どうやら池袋につながる沿線らしくって、一説によると彼らが本気出したら池袋本店の技術書コーナーを1日で空っぽにできるとか😆」「😆」「空っぽにするほど買ってみたい〜」

米国から見た日本のRuby事情(翻訳)

Ruby trunkより

Ruby 2.6.0で禁止されたシンボルキーと非シンボルキーの混在を2.6.1で再度許した

# test/-ext-/test_scan_args.rb#L99
  def test_opt_hash
    assert_equal([0, nil, nil], Bug::ScanArgs.opt_hash())
    assert_equal([1, "a", nil], Bug::ScanArgs.opt_hash("a"))
    assert_equal([0, nil, {b: 1}], Bug::ScanArgs.opt_hash(b: 1))
    assert_equal([1, "a", {b: 1}], Bug::ScanArgs.opt_hash("a", b: 1))
    assert_raise(ArgumentError) {Bug::ScanArgs.opt_hash("a", "b")}
-   assert_raise(ArgumentError) {Bug::ScanArgs.opt_hash("a"=>0, b: 1)}
+   assert_equal([1, {"a"=>0}, {b: 1}], Bug::ScanArgs.opt_hash("a"=>0, b: 1))
  end

以下は確かに2.6.2では動くようになっていました。

# #15658より
def foo(opt=nil, **kw)
  p opt, kw
end

foo("str" => 42, :sym => 42)
  #=> 2.5.3: {"str"=>42}, {:sym=>42}
  #=> 2.6.0: non-symbol key in keyword arguments: "str" (ArgumentError)

つっつきボイス:「あ〜これね〜😅」「シンボルキーと非シンボルキーの混在が2.6.0で禁止されて、また戻ってたことに気づいてませんでした💦」「これ変えられちゃったら困るヤツですよね?」「結構困る😇」「実際混在はずっと行われてたし、禁止されたらライブラリがエラーになっちゃうし😢」「とはいえ、Railsでこういう混在した書き方↓されたら後ろから撃ちたくなるけどっ😆」「😆」「😆」

foo("str" => 42, :sym => 42)

そして上の一連の動きは、#14183の「本物のキーワード引数」の議論と関連しているようです。#14183はまだ閉じていませんので、今後どうなるかですね。

k:1のようなハッシュ引数が{}で囲まれていない場合はキーワード引数となって**kwに渡され、{}で囲まれている場合は末尾の引数としてkw = {}に渡される、みたいな流れになっているようです。たしかにこれなら明示的に分けられそうですね。

# #14183より
def foo(**kw); p kw; end
def bar(kw = {}); p kw; end
h = {:k => 1}

# base (non-braced) hash arguments passed as keywords
foo(k: 1)    #=> {:k=>1} in 2.X and 3.0
foo(:k => 1) #=> {:k=>1} in 2.X and 3.0
foo(**h)     #=> {:k=>1} in 2.X and 3.0
bar(k: 1)    #=> {:k=>1} in 2.X, ArgumentError in 3.0
bar(:k => 1) #=> {:k=>1} in 2.X, ArgumentError in 3.0
bar(**h)     #=> {:k=>1} in 2.X, ArgumentError in 3.0

# braced hash arguments are passed as a last argument
foo({ k: 1 })    #=> {:k=>1} in 2.X, ArgumentError in 3.0
foo({ :k => 1 }) #=> {:k=>1} in 2.X, ArgumentError in 3.0
foo(h)           #=> {:k=>1} in 2.X, ArgumentError in 3.0
bar({ k: 1 })    #=> {:k=>1} in 2.X and 3.0
bar({ :k => 1 }) #=> {:k=>1} in 2.X and 3.0
bar(h)           #=> {:k=>1} in 2.X and 3.0

「今回の動きは、どうやらRubyの目下の悩みである『**とキーワード引数をどうする問題』↓と関連してるみたいで、{}で囲むかどうかで挙動を分けるというアイデアが出されて今審議中のようです」「これはとてもわかる: **周りの挙動ってマジでわからなくなるし😭」「キーワード引数が期待と違うパラメータに吸い込まれるとコワいです〜🌪」

参考: キーワード引数の現状と将来構想 - HackMD

Ruby2.5.xのパラメータの制約についてまとめてみた

「この辺のパラメータ渡し問題で一番典型的なのは、Active Supportに昔からあるような古参のメソッドたちで、あれはどうしてそれで動くのか今もってわからない🤪」「form_forあたりとか?」「とかですね、form_with↓になって随分よくなったけど、それでもちょっと心配😅」

[Rails 5.1] ‘form_with’ APIドキュメント完全翻訳

「この問題が難しいのは、どう修正してもbreaking changesが避けられないところでしょうね」「といって放置するわけにもいかず、苦渋の決断を迫られるという」「トロッコ問題的に『n人までなら殺すのもやむなし』みたいな😆」

参考: トロッコ問題 - Wikipedia


今回は以上です。

おたより発掘

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

週刊Railsウォッチ(20190305-2/2後編)PostgreSQL強者から見たMySQL、SEO良記事、分散アルゴリズムChordほか

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

ソースの表記されていない項目は独自ルート(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言語が好きで、Goで書かれたRubyライクなGoby言語のメンテナーでもある。 仕事に関係ないすっとこブログ「あけてくれ」は2000年頃から多少の中断をはさんで継続、現在はnote.muに移転。

hachi8833の書いた記事

BPSアドベントカレンダー

週刊Railsウォッチ