Tech Racho エンジニアの「?」を「!」に。
  • Ruby / Rails関連

週刊Railsウォッチ(20190527-1/2前編)RuboCopが3分割へ、Railsリクエストのライフサイクル、Opal 1.0、Railsベンチマーク、Rubyパターンマッチングの現状ほか

こんにちは、hachi8833です。Macbook Pro 2019をポチりそうになったのを危うく踏みとどまりました。

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

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

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

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

今回は公式の更新情報から見繕いました。

システムテストにTrixエディタ入力ヘルパーを追加

# 同PRより
# <trix-editor id="message_content" ...></trix-editor>
fill_in_rich_text_area "message_content", with: "Hello <em>world!</em>"
# <trix-editor placeholder="Your message here" ...></trix-editor>
fill_in_rich_text_area "Your message here", with: "Hello <em>world!</em>"
# <trix-editor aria-label="Message content" ...></trix-editor>
fill_in_rich_text_area "Message content", with: "Hello <em>world!</em>"
# <input id="trix_input_1" name="message[content]" type="hidden">
# <trix-editor input="trix_input_1"></trix-editor>
fill_in_rich_text_area "message[content]", with: "Hello <em>world!</em>"

つっつきボイス:「fill_in_rich_text_areaが入った」「with:に任意のHTMLでテキストを渡せるようになったんですって」「Action TextというかTrixはまだ使ったことないけど、チャンスがあれば使うかなぐらいの気持ち☺️」

ActiveRecord#respond_to?の文字列アロケーションをやめて1.5倍高速化

# activerecord/lib/active_record/attribute_methods.rb#L261
    def respond_to?(name, include_private = false)
      return false unless super

-     case name
-     when :to_partial_path
-       name = "to_partial_path"
-     when :to_model
-       name = "to_model"
-     else
-       name = name.to_s
-     end
-
-     if defined?(@attributes) && self.class.column_names.include?(name)
-       return has_attribute?(name)
+     if defined?(@attributes)
+       if name = self.class.symbol_column_to_string(name.to_sym)
+         return has_attribute?(name)
+       end
      end

      true
    end
# activerecord/lib/active_record/model_schema.rb#L391
+     def symbol_column_to_string(name_symbol) # :nodoc:
+       @symbol_column_to_string_name_hash ||= column_names.index_by(&:to_sym)
+       @symbol_column_to_string_name_hash[name_symbol]
+     end

つっつきボイス:「高速化なので@kamipoさんかと思ったら@schneemsさんだった☺️」「上のcase文を汎用化して不要にしたということか」「elseの後のname = name.to_sとかはsymbol_column_to_stringの方でやってるのと同じだし」「リファクタリングですね」

「ところで、この辺の書き方↓Rubocopに怒られそうではある👮」「あは、代入のシングルイコール=を条件で使ってるし😆」

+      if name = self.class.symbol_column_to_string(name.to_sym)

「ダブルイコール==のつもりだったのを間違えたのかと一瞬思っちゃうし😆」「とはいえ、このイディオムは高速化のためには有効だったりしますね☺️」「まあこのぐらいだったら許容範囲と思うし」「ビジネスロジックでは使って欲しくないけど、ライブラリコードの最適化ならしゃーない」

S3に5GBより大きいファイルをアップロードできるようになった

# activestorage/lib/active_storage/service/s3_service.rb#L8
module ActiveStorage
  class Service::S3Service < Service
-   attr_reader :client, :bucket, :upload_options
+   attr_reader :client, :bucket
+   attr_reader :multipart_upload_threshold, :upload_options

    def initialize(bucket:, upload: {}, **options)
      @client = Aws::S3::Resource.new(**options)
      @bucket = @client.bucket(bucket)

 +    @multipart_upload_threshold = upload.fetch(:multipart_threshold, 100.megabytes)
      @upload_options = upload
    end

    def upload(key, io, checksum: nil, content_type: nil, **)
      instrument :upload, key: key, checksum: checksum do
-       object_for(key).put(upload_options.merge(body: io, content_md5: checksum, content_type: content_type))
-     rescue Aws::S3::Errors::BadDigest
-       raise ActiveStorage::IntegrityError
+       if io.size < multipart_upload_threshold
+         upload_with_single_part key, io, checksum: checksum, content_type: content_type
+       else
+         upload_with_multipart key, io, content_type: content_type
+       end
      end
    end

つっつきボイス:「ほほー、ついにマルチパートアップロードに対応!」「マルチパートがポイントなんですね」

「AWSのS3には元々マルチパートアップロードモードというのがあるんですが、何もしないとシングルモードになります」「シングルモードは文字通り1つのHTTP PUTで投げるんですけど、その場合の最大サイズが5GBです」「なるほど!」「それ以上のサイズでアップロードしたいときは複数のHTTP PUTで投げるマルチパートアップロードを使うことになるんですけど、この改修はそれに対応したということですね🧐」「コミットにもマルチパートって書いてありますね」

参考: マルチパートアップロードの概要 - Amazon Simple Storage Service

「100MB以上の場合は自動的にマルチパートモードになるんですね」「5GBとあるのはあくまでシングルモードの場合の上限ですし、それ以下の場合でもマルチパートモードの方が帯域を有効に使えますし☺️」「つか今まで対応してなかったんかい😆」「😆」

「マルチパートアップロードはかなり大事: 実際、S3にでかいファイルをどっかん置くとか割とやりますし(ホームディレクトリをtarで固めて投げたりとか)、マルチパートアップロードができなかったらかなりつらいし、しかもアップロードでさんざん待たされた後で終わった頃にやっと失敗してたことがわかったり😇」「あるある〜🤣」

「だからこの辺の制約は知っておくべき☺️: AWS SDKとかAWS CLIのコマンドでやる場合は自動的にマルチパートアップロードになってくれるからいいんですけど、S3 APIに直接HTTP PUTするようなコードを自分で書くときなんかは注意が必要⚠」「たしかに〜」

HashWithIndifferentAccess#initializeのパフォーマンスを改善

# activesupport/lib/active_support/hash_with_indifferent_access.rb#L52
    def initialize(constructor = {})
      if constructor.respond_to?(:to_hash)
        super()
        update(constructor)

-       hash = constructor.to_hash
+       hash = constructor.is_a?(Hash) ? constructor : constructor.to_hash
        self.default = hash.default if hash.default
        self.default_proc = hash.default_proc if hash.default_proc
      else
        super(constructor)
      end
    end

つっつきボイス:「HashWithIndifferentAccessの更新は久々に見た気がする」「is_a?(Hash)してる?」「あ〜to_hashがめちゃ重くなることがあるから、しなくていい場合はto_hashをやめたのか」「わかりみ〜😋」「中身の構造にもよるけどto_hashが重くなることあるし」

「プルリクに貼られているGist↓でもでかいハッシュ作って試してる👀」「HWIAにどでかいものを渡すことがどれぐらいの頻度であるのかにもよりそうですが😆、速くなるのはいいですね」

mailbox_forをpublicに

# actionmailbox/lib/action_mailbox/router.rb#L23
    def route(inbound_email)
-     if mailbox = match_to_mailbox(inbound_email)
+     if mailbox = mailbox_for(inbound_email)
        mailbox.receive(inbound_email)
      else
        inbound_email.bounced!
        raise RoutingError
      end
    end

+   def mailbox_for(inbound_email)
+     routes.detect { |route| route.match?(inbound_email) }.try(:mailbox_class)
+   end

    private
      attr_reader :routes

+     def match_to_mailbox(inbound_email)
+       routes.detect { |route| route.match?(inbound_email) }.try(:mailbox_class)
+     end
  end
end

つっつきボイス:「いかにもAction Mailboxですね」「メソッド名も変更されてる」「メールのルーティングもroutesで扱うというのは言われてみればごもっとも☺️」

ApplicationMailboxの設定済みルーティングシステムへのエントリポイントとして現在公開されているのはrouteへの呼び出ししかなく、受信メールを完全にprocessする処理が大量に走ってしまう。テストなどで、そうした処理を行わずに、メールがどのメールボックスにルーティングされるかをチェックする手段があるとよさそう。
これにインラインドキュメントをもっと追記して、どういうメソッドをpublicにしてサポートするか、どういうメソッドではそれをやらないかという基準を示しておくとありがたみが増すと思われる。「その価値あり」と皆さんに賛成してもらえるなら、このPRか別のPRで自分がドキュメントを追加してもよい。
同PRより大意

Rails

RailsをAWS->GCPに移行


つっつきボイス:「この間BPSの社内Slackに貼っていただいたヤツです」「これかなり面白かった😋タイトルは釣り記事っぽかったけど読めば読むほど『こ、この会社...大変だったんだな』って苦労が偲ばれる😆」「何しろ元のRailsアプリを作った人もいなくなっててチームもほぼ解散状態で誰にも質問できないところからのスタート😇」「そこまで!😇」「はにゃーん😇」「CTOやRailsエンジニアが全員退職...😇」「『よくしらんRailsアプリ』ってそういう意味でしたか!」「『全社的な退職のビッグウェーブ』って🤣」「もはや災害レベル🤣」


同記事より

「それにしてもよくここまで頑張ったと思うし」「マジで偉業!」「こういう究極の案件を1人で回して切り抜けるとめちゃめちゃ知見がたまります☺️」

Railsリクエストのライフサイクル(Ruby Weeklyより)


つっつきボイス:「これは次のRailsConf 2019のセッションのスライドと記事だそうです」「なるほどなるほど、DNS lookupから始まってRackミドルウェアを通ったりしてRailsのリクエストをひととおり辿ると: これはとてもためになる👍」「採用面接で志望者にこれをひととおり説明してもらったりしますね😆」「この間のRails Traceにも通じそう」

「四六時中Railsのコードを追いかけている人ならこのスライドの内容は自明ですけど、Railsをしばらく触っていなかった人とか、Rails一応触っているけど中で何が起こっているか今ひとつわかってない人にはうってつけの教材😋」「エンジニア全員とまではいかなくても、Railsをメインで触っている人ならこのスライドで説明されていることぐらいは知ってて欲しいです☺️」「同じ内容が元記事でも読めますし」「Railsチュートリアルをやったら次に学んでおきたい感じですね😃」

RailsConf 2019のセッション


同サイトより


つっつきボイス:「日本ではGW中にミネアポリスで開催されたRailsConf 2019のタイムテーブルです」「スロット7つで3日間!」「これをまともに追うのは大変すぎ😅」「6月の公開つっつきのときに、もう少し絞り込んでおこうと思います」

Railsのシンプルなベンチマーク(Ruby Weeklyより)

TechRachoでパフォーマンス関連の記事を何度か翻訳させてもらっているNoah Gibbsさんの記事です🙇。

Ruby 2.5.0はどれだけ高速化したか(翻訳)


つっつきボイス:「特にこういうPumaとかコンカレンシーとかのベンチマークって、やっぱり自分で書くしかないんですよね: ベンチマークソフトに丸投げしてストンと結果が出るなんてことはまずないので☺️」「先週のウォッチの話にも通じますね」

「Pumaやコンカレンシーのベンチマークをやるということは、つまりスレッド周りとかGILとかをチェックすることになるし、そういう下のレイヤの中身を理解した上でコードを書かないといけないから、しんど〜😭」

参考: グローバルインタプリタロック - Wikipedia


同記事より

「こんなにみっちり取ってる↑😳」「ああ、こういうのとても大事: 結構前にUnicornとかPumaはどういうパラメータが適切なのかというのが話題になりましたけど、こういうのはCPUの種類やカーネルのバージョンなどによっても変わってくることを知っておくべき」「うんうん」「シビアに考えるのであれば、ベンチマークはあくまで特定のスペックのサーバーでの結果であるということですね🧐」

Rails: Puma/Unicorn/Passengerの効率を最大化する設定(翻訳)

「ところで、こんな感じのベンチマークって取ったりします?」「バージョンごとのベンチマークまではやったことないです〜」「インスタンスタイプを変えて最適なものがどれかを試したりは?」「それもやってないかな〜😅」「自分はスループットチェックはたまにやってますけど、結局自分で取るしかないし、ダルいので自動化したりします😆」

「記事ではRSBというベンチマークgemを新たに書き下ろしたそうです↓」「改めて思うんですけど、ちゃんとしたベンチマークを書くのって、OSを正しく理解しておかないといけなくてとても難しい😭」

「こういう必要なパラメータ↓渡して一発でベンチマーク回せるようにしてるのって、すげっ!」「複数バージョンのRubyで一気に回せるようになってるし💪」「ウォームアップ時間もちゃんと指定できるようになってるし❤️」「これらのパラメータはしっかり押さえてあるということですね」「ウォームアップはめちゃめちゃ重要」「ウォームアップしないベンチマークはマジで意味ないし」「ウォームアップに実際にかかった時間もチェックしておきたいし」「たしかに〜」「もっと言えば再起動から始めるとかありますし」

RSB_NUM_RUNS=10 RSB_RUBIES="2.6.0 2.4.5 2.0.0-p0" RSB_DURATION=180 RSB_WARMUP=20 RSB_FRAMEWORKS=rack RSB_APP_SERVER=puma RSB_PROCESSES=1 RSB_THREADS=1 ./runners/rvm_rubies.rb
同リポジトリより

「しかしよくこれだけ網羅してますね」「そこがベンチマークを自作したくない理由🤣: パラメータ入れ忘れそう」「🤣」「🤣」

「Noah Gibbsさんはコンピュータサイエンスをきちんと押さえている人なので、彼のベンチマークコードはめちゃめちゃ勉強になります❤️」「何度か翻訳させてもらいましたけどホント濃厚でした😅」「そこはどうしても濃厚にならざるを得ませんね〜: 下のレイヤを正しく理解してないと、まったく違う結果を出してしまうことすらあるので😇」

route_translator: Railsのルーティングを多言語化(Ruby Weeklyより)

# 同リポジトリより
        Prefix Verb   URI Pattern                     Controller#Action
    admin_cars GET    /admin/cars(.:format)           admin/cars#index
               POST   /admin/cars(.:format)           admin/cars#create
 new_admin_car GET    /admin/cars/new(.:format)       admin/cars#new
edit_admin_car GET    /admin/cars/:id/edit(.:format)  admin/cars#edit
     admin_car GET    /admin/cars/:id(.:format)       admin/cars#show
               PATCH  /admin/cars/:id(.:format)       admin/cars#update
               PUT    /admin/cars/:id(.:format)       admin/cars#update
               DELETE /admin/cars/:id(.:format)       admin/cars#destroy
       cars_fr GET    /fr/voitures(.:format)          cars#index {:locale=>"fr"}
       cars_es GET    /es/coches(.:format)            cars#index {:locale=>"es"}
       cars_en GET    /cars(.:format)                 cars#index {:locale=>"en"}
               POST   /fr/voitures(.:format)          cars#create {:locale=>"fr"}
               POST   /es/coches(.:format)            cars#create {:locale=>"es"}
               POST   /cars(.:format)                 cars#create {:locale=>"en"}
    new_car_fr GET    /fr/voitures/nouveau(.:format)  cars#new {:locale=>"fr"}
    new_car_es GET    /es/coches/nuevo(.:format)      cars#new {:locale=>"es"}
    new_car_en GET    /cars/new(.:format)             cars#new {:locale=>"en"}
   edit_car_fr GET    /fr/voitures/:id/edit(.:format) cars#edit {:locale=>"fr"}
   edit_car_es GET    /es/coches/:id/edit(.:format)   cars#edit {:locale=>"es"}
   edit_car_en GET    /cars/:id/edit(.:format)        cars#edit {:locale=>"en"}
        car_fr GET    /fr/voitures/:id(.:format)      cars#show {:locale=>"fr"}
        car_es GET    /es/coches/:id(.:format)        cars#show {:locale=>"es"}
        car_en GET    /cars/:id(.:format)             cars#show {:locale=>"en"}
               PATCH  /fr/voitures/:id(.:format)      cars#update {:locale=>"fr"}
               PATCH  /es/coches/:id(.:format)        cars#update {:locale=>"es"}
               PATCH  /cars/:id(.:format)             cars#update {:locale=>"en"}
               PUT    /fr/voitures/:id(.:format)      cars#update {:locale=>"fr"}
               PUT    /es/coches/:id(.:format)        cars#update {:locale=>"es"}
               PUT    /cars/:id(.:format)             cars#update {:locale=>"en"}
               DELETE /fr/voitures/:id(.:format)      cars#destroy {:locale=>"fr"}
               DELETE /es/coches/:id(.:format)        cars#destroy {:locale=>"es"}
               DELETE /cars/:id(.:format)             cars#destroy {:locale=>"en"}
    pricing_fr GET    /fr/prix(.:format)              home#pricing {:locale=>"fr"}
    pricing_es GET    /es/precios(.:format)           home#pricing {:locale=>"es"}
    pricing_en GET    /pricing(.:format)              home#pricing {:locale=>"en"}

つっつきボイス:「↑こんな感じでルーティングを多言語化するそうです」「ははぁ、URLのSlugを多言語化するということか!」「Slugのローカライズって必要なんでしょうか?」「SEO的には欲しいヤツですね☺️: CMSの代わりにRailsを使うとこういう感じになりそう」「Rails標準のi18nではここまでできないんでしょうか?」「Slugはi18nとは別の機能なので😆」

参考: Slug (スラグ) - MDN Web Docs 用語集: ウェブ関連用語の定義 | MDN

「enが英語のcarでデフォルトか」「ということはvoituresがフランス語、cochesがスペイン語でそれぞれ自動車という意味なんでしょうね」「ヴォアチュ?」「ヨーロッパではこういう機能欲しいでしょうね」「ここまでやるか〜?とは思いますけど😆」

「ということはSlugに日本語も置けると🤣」「やめて〜🤣」

「そもそもこういうことするとめちゃルーティングが増えるし😇」「やりたい気持ちはワカル☺️」

logidze: データベース変更ロガー(Ruby Weeklyより)

現時点ではPostgreSQL 9.5以降のみをサポートするそうです。


同リポジトリより


つっつきボイス:「Evil Martiansの人のgemで、logidzeという名前はグルジア(ジョージア)語由来だそうです」「じゃ読み方わからなくて当然😆」「Evil Martiansにはロシア系メンバーがいるからなのかな🤔」「なるほど、いわゆるaudit(監査)系のgem」

参考: 第1回 データベース監査の現場 [監査とは]|知って得する! Oracle知識|Oracleソリューションサービス|日立ソリューションズ

「paper_trailより速いそうです」「paper_trail、あれは重いよ〜😆」「やっぱり😆」「paper_trailは内部展開までやってるからそりゃ重い😆」

参考: paper-trail-gem/paper_trail: Track changes to your rails models

「logidzeはこうやってバージョンをトラックしたりundoしたりとかもやれるのか↓」「ほほー」「保存先はデータベースでしょうか?」「rake db:migrateやってるからほぼ確実にそうでしょうね☺️」「軽いpaper_trailとして使えそう」「auditツールは信頼性もチェックしときたいですけど☺️」

post = Post.create!(title: "first post") # v1
post.update!(title: "new title")         # v2
post.undo!(append: true)                 # v3 (with same attributes as v1)

high_voltage: Railsで静的ページを扱う(Ruby Weeklyより)


thoughtbot.comより

# 同リポジトリより
# app/controllers/pages_controller.rb
class PagesController < ApplicationController
  include HighVoltage::StaticPage

  before_filter :authenticate
  layout :layout_for_page

  private

  def layout_for_page
    case params[:id]
    when 'home'
      'home'
    else
      'application'
    end
  end
end

つっつきボイス:「『高電圧注意』とか書いてあるけど別に危険な気はしませんね😆」「いわゆる静的ページを普通のRailsビューのように使えるという感じでしょうか?」「それっぽい」「Railsのキャッシュをいい感じに効かせてくれるようだ」

「静的ページを普通にpublic/の下に置くとできないことってあるんでしょうか?」「ビューのレイアウト機能なんかはpublic/ではできないでしょうね: ヘッダーを静的ページでも共通化してレンダリングしたい、というのはよくあるし」「レイアウトは共通化したいけど中身は固定ページ、みたいなときなんでしょうね」「『ご挨拶』ページとか😆」

「わざわざgem入れる?とは思いますが😆、キャッシュのこととかを考えずに静的ページを楽に扱えるのはよさそうだし、Thoughtbotなら一応ちゃんとしてそう😋」

その他Rails

Ruby

ruby-regexp_trie: 文字列リストを正規表現に圧縮するgem

# 同リポジトリより
#!/usr/bin/env ruby
require 'regexp_trie'

# Regexp.union()のように使う
p RegexpTrie.union(%w(foobar fooxar foozap fooza)) # /foo(?:bar|xar|zap?)/
p RegexpTrie.union(%w(foobar fooxar foozap fooza), option: Regexp::IGNORECASE) # /foo(?:bar|xar|zap?)/i

# オブジェクト指向インターフェイスっぽくもやれる
rt = RegexpTrie.new
%w(foobar fooxar foozap fooza).each do |word|
  rt.add(word)
end
p rt.to_regexp # /foo(?:bar|xar|zap?)/

PerlのRegexp::List(小飼弾作)を元にしているそうです。Perl版はえらく短いコードです。


つっつきボイス:「ruby-regexp_trieは近々記事を出そうと思ってます: Trie(トライ木)っていうデータ構造をこれで知りました」「Trieは割と最近のヤツだったかな」「自然言語処理方面で辞書を作るのによく使われているみたいで、単なる二分木と得意不得意がちょっと違うようです」

参考: トライ木 - Wikipedia
参考: 簡潔データ構造 LOUDS の解説(全12回、練習問題付き) - アスペ日記
参考: 情報系修士にもわかるダブル配列 - アスペ日記

「PerlだけにやはりCPANに置いてある☺️」「ところで小飼弾氏ってだいぶ前からコードも書く論客というイメージがあったんですが、やはりPerlの人だからなのか、つっつきでは名前出たことがなかったなと思って」「コードも書くけど今はどちらかというビジネスとか経営寄りな印象かな〜🤔」「元ライブドアのCTO」

参考: CPAN - Wikipedia
参考: 404 Blog Not Found -- 小飼弾氏のブログ

Opalが1.0になって高速化(Ruby Weeklyより)


同サイトより

  • プロトタイプチェーン探索がネイティブに
  • Module.prependの追加
  • c_lexerによる高速化
  • エラーメッセージやsource mapの改善
  • コンパイル時に見つからないrequireを実行時にのみエラーにできるようになった(MRIと同様)
  • Ruby 2.4や2.5の機能が多数使えるようになった
  • MRIと同じアルゴリズムの疑似乱数ジェネレータ、packunpack、Node.jsでFileDirクラスへのアクセスほか

コミットリスト: Comparing v0.11.4...v1.0.0 · opal/opal


つっつきボイス:「ついにOpalが1.0に!」「0.x系だと警戒して使わない人がいるからだったりして😆」「OpalってLambda系のサーバーレスアプリを作るときなんかに全部Rubyで書けるのがよさそうなんですよね〜🥰」

zxcvbn-ruby: パスワード強度チェッカー(Ruby Weeklyより)

# 同リポジトリより
>> pp Zxcvbn.test('@lfred2004', ['alfred'])
#<Zxcvbn::Score:0x00007f7f590610c8
 @calc_time=0.0055760000250302255,
 @crack_time=0.012,
 @crack_time_display="instant",
 @entropy=7.895,
 @feedback=
  #<Zxcvbn::Feedback:0x00007f7f59060150
   @suggestions=
    ["Add another word or two. Uncommon words are better.",
     "Predictable substitutions like '@' instead of 'a' don't help very much"],
   @warning=nil>,
 @match_sequence=
  [#<Zxcvbn::Match matched_word="alfred", token="@lfred", i=0, j=5, rank=1, pattern="dictionary", dictionary_name="user_inputs", l33t=true, sub={"@"=>"a"}, sub_display="@ -> a", base_entropy=0.0, uppercase_entropy=0.0, l33t_entropy=1, entropy=1.0>,
   #<Zxcvbn::Match i=6, j=9, token="2004", pattern="year", entropy=6.894817763307944>],
 @password="@lfred2004",
 @score=0>
=> #<Zxcvbn::Score:0x00007f7f59060150>

Dropboxがやっているzxcvbn.jsをRubyに移植したそうです。ダメパスワードリストもあります。


つっつきボイス:「zxcvbn発音できない😅」「Dropboxがこういうのやってるのか〜」「そのRuby版ですね☺️」

参考: Dropbox開発のzxcvbn(パスワード強度メーター)のブログ記事を全訳してみた - Qiita

USENIXで以下のプレゼンが2017年に行われていたようです。

The USENIX Association

usenix.orgより

RuboCopを3つのgemに分割(Hacklinesより)


つっつきボイス:「RuboCopが以下の3つのgemに分けられるそうです」「ほほぉ〜」

「たしかにRuboCop Railsは分かれていて欲しいかも: 今のRuboCopは色々入れすぎてるから、gemとしてRails用だけ選択できたらうれしいし❤️」「たしかに〜」「RuboCop Railsって単体で使えるかしら?」「まあRuboCop本体にdependするでしょうね☺️」「RSpecみたく😆」

「RuboCopをRailsでもっとデフォルトで使いたいからgemを分けて欲しいというのは気持ちちょっとワカル☺️」「分けないとメンテが大変そうですし😅」「RuboCopは割とプルリクをガンガン受け付ける敷居の低さがあるんですけど、その分ちょっとやりすぎ感はありますし😆」「RuboCopのプルリクの数すごいですよ」

後で気づきましたが、従来のrubocop-railsがRuboCopチームに移管されるというアナウンスが昨年あったんですね。

その他Ruby

つっつきボイス:「PySnooperにヒントを得ためっちゃexperimentalなデバッガーだそうです」「おー、この情報↓ってMac固有のものだったりするのかな?」「4番目の数値がRubyのメモリ使用量っぽいから、これが急激に増えていたら何かおかしいと」「GCすれば普通減りますけどね😆」

Var......... a=["Bored", "Curious"]
Var......... b=["cat", "frog"]
18:38:15.188 line   test/support/test.rb:54        13770752           a << "Insane"
18:38:15.188 line   test/support/test.rb:55        13807616           shuffle(b)
Var......... arr=["cat", "frog"]
18:38:15.188 call   test/support/test.rb:45        13807616       def shuffle(arr)
18:38:15.189 line   test/support/test.rb:46        13807616         for n in 0...arr.size
Var......... n=0
18:38:15.189 line   test/support/test.rb:47        13811712           targ = n + rand(arr.size - n)
Var......... targ=0
18:38:15.189 line   test/support/test.rb:48        13811712           arr[n], arr[targ] = arr[targ], arr[n] if n != targ
Var......... n=1
18:38:15.189 line   test/support/test.rb:47        13811712           targ = n + rand(arr.size - n)
Var......... targ=1
18:38:15.189 line   test/support/test.rb:48        13811712           arr[n], arr[targ] = arr[targ], arr[n] if n != targ
18:38:15.189 return test/support/test.rb:50        13811712       end

短いtips記事です。

# 同記事より
land = 'Mordor'

verse = <<-'TEXT'
One Ring to rule them all,
One Ring to find them,
One Ring to bring them all,
and in the darkness bind them,
In the Land of #{land} where the Shadows lie.
TEXT

つっつきボイス:「ヒアドキュメントで式展開#{}を無効にしたい場合に、'TEXT'みたいにキーワードを一重引用符で囲むと展開されなくなるんだそうです」「へぇ〜😆」「これはマジ知る人ぞ知る😆」「シンタックスシュガーなんですけど、これで効くのがちょっと不思議」

「...このシンタックス、Bashだとメジャーなんですよね😆」「えぇぇ?!」「じゃそこから持ってきた文法なのかな」「そんな気がします: この文法キライなんですけど🤣」「Bashなんかだと使える記号にいろいろ制限があってそうなったんだろうけど、何もそれを踏襲しなくても😆」「Bashに強い人はいいんでしょうけど😆」

↓たぶんこれだと思います。

参考: sh での変数とワイルドカードの落とし穴|てくめも@ecoop.net

# 同記事より
foo="SELECT * FROM table"
echo "$foo"
#=> SELECT * FROM table

Ruby trunkより

パターンマッチングの現状の仕様


つっつきボイス:「この間出した記事↓をまとめていて、パターンマッチングのうれしさがやっと少し見えてきたので、現時点のまとまった仕様と思われるものをこのissueから抜き書きしました」

キーワードで振り返るRubyKaigi 2019@博多(#4)最適化、パターンマッチング、mruby、ブランチメンテナンスほか

case obj
in pat [if|unless cond]
  ...
in pat [if|unless cond]
  ...
else
  ...
end

pat: var                                            # 変数渡しのパターン(任意の値にマッチ可能、その値の変数名にバインドされる)
   | literal                                        # 値渡しのパターン(pattern === objectのようにオブジェクトとマッチする)
   | Constant                                       # 同上
   | ^var                                           # 同上(Elixirのピン演算子^と同等)
   | pat | pat | ...                                # alternateパターン(いずれかのpatとマッチする)
   | pat => var                                     # asパターン(patにマッチする場合、その値の変数にバインドされる)
   | Constant(pat, ..., *var, pat, ...)             # arrayパターン(後述)
   | Constant[pat, ..., *var, pat, ...]             # 同上
   | [pat, ..., *var, pat, ...]                     # 同上(`BasicObject(pat, ...)`と同じ)(トップレベルでのみ丸かっこを省略可)
   | Constant(id:, id: pat, "id": pat, ..., **var)  # hashパターン(後述)
   | Constant[id:, id: pat, "id": pat, ..., **var]  # 同上
   | {id:, id: pat, "id": pat, ..., **var}          # 同上(`BasicObject(id:, ...)`と同じ)(トップレベルでのみ波かっこを省略可)

arrayパターンは以下のいずれかの場合にマッチする:
* Constant === objectがtrueを返す
* Arrayを返す#deconstructメソッドがオブジェクトにある
* ネストしたパターンをobject.deconstructに適用した結果がtrueになる

hashパターンは以下のいずれかの場合にマッチする:
* Constant === objectがtrueを返す
* Hashを返す#deconstructメソッドがオブジェクトにある
* ネストしたパターンをobject.deconstruct_keys(keys)に適用した結果がtrueになる

参考: Pattern matching - New feature in Ruby 2.7 - Speaker Deck

参考: パターンマッチング · Elixir School -- ピン演算子^の解説


前半は以上です。

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

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

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

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

Rails公式ニュース

Ruby Weekly

RubyFlow

160928_1638_XvIP4h

Hacklines

Hacklines


CONTACT

TechRachoでは、パートナーシップをご検討いただける方からの
ご連絡をお待ちしております。ぜひお気軽にご意見・ご相談ください。