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

週刊Railsウォッチ(20210202後編)Ruby 3 irbのmeasureコマンド、テストを関数型言語のマインドセットで考えるほか

こんにちは、hachi8833です。

  • 各記事冒頭には⚓でパーマリンクを置いてあります: 社内やTwitterでの議論などにどうぞ
  • 「つっつきボイス」はRailsウォッチ公開前ドラフトを(鍋のように)社内有志でつっついたときの会話の再構成です👄
  • お気づきの点がありましたら@hachi8833までメンションをいただければ確認・対応いたします🙇

TechRachoではRubyやRailsの最新情報などの記事を平日に公開しています。TechRacho記事をいち早くお読みになりたい方はTwitterにて@techrachoのフォローをお願いします。また、タグやカテゴリごとにRSSフィードを購読することもできます(例:週刊Railsウォッチタグ)

Ruby

Ruby 3.0 irbのmeasureコマンド(Ruby Weeklyより)


つっつきボイス:「Ruby 3.0の新機能紹介記事です」「へ〜、irbでmeasureコマンドを実行すると行ごとに時間を測ってくれるのか」「ストップウォッチ的な機能ですね⏱」「たしかに」

# 同記事より
irb(main)> measure
TIME is added
=> nil

irb > sleep 1
processing time: 1.000649s
=> 1

irb > 1
processing time: 0.000025s
=> 1

irb > measure :off
=> nil

参考: library irb (Ruby 3.0.0 リファレンスマニュアル)

「Ruby 3.0の新機能がてんこもりで、この機能に気づかなかった↓」

参考: Ruby 3.0.0 リリース


ruby-lang.orgより

「お、記事によるとmeasureコマンドにstackprof↓を指定するともっと細かく調べられるのか」「irbでこういう機能が使えるようになると、ちょっとした測定が手軽にやれて便利👍」「この辺の機能を使いこなせると強くなった気持ちになれそうですね」

tmm1/stackprof - GitHub

「Ruby 3.0の機能なので新しい環境でしか使えませんけどね」「Ruby 3.0の新機能、がっつり調べないといけないな〜」「パターンマッチングとかもですね」


後で記事のスニペット↓を動かしてstackprofをやってみました。スニペットではさりげなくRuby 3.0のendレスメソッド定義も使っていますね。

# 同記事より
def snippet()= 10_000.times { Date.parse(Date.today.to_s).year != 2020 }

参考: 改めて整理する Ruby 3.0 に実験的に入る予定のエンドレスメソッド定義構文と右代入演算子について - Secret Garden(Instrumental)

以下は素のRuby 3.0.0でも実行できるよう、gem install stackprof activesupportを実行し、irbでrequire 'active_support/core_ext/date'を実行しました。

3.0.0$ ruby -v
ruby 3.0.0p0 (2020-12-25 revision 95aff21468) [x86_64-darwin19]
3.0.0$ gem install stackprof activesupport
Fetching stackprof-0.2.16.gem
Building native extensions. This could take a while...
Successfully installed stackprof-0.2.16
Building YARD (yri) index for stackprof-0.2.16...
Done installing documentation for stackprof after 0 seconds
Fetching activesupport-6.1.1.gem
Successfully installed activesupport-6.1.1
Building YARD (yri) index for activesupport-6.1.1...
Done installing documentation for activesupport after 1 seconds
2 gems installed
3.0.0$ irb
irb(main):001:0> require 'active_support/core_ext/date'
=> true
irb(main):002:0> def snippet()= 10_000.times { Date.parse(Date.today.to_s).year != 2020 }
=> :snippet
irb(main):003:0> measure :stackprof
STACKPROF is added.
=> nil
irb(main):004:0> snippet
==================================
  Mode: cpu(1000)
  Samples: 34 (0.00% miss rate)
  GC: 2 (5.88%)
==================================
     TOTAL    (pct)     SAMPLES    (pct)     FRAME
        17  (50.0%)          17  (50.0%)     Regexp#match
         6  (17.6%)           6  (17.6%)     MatchData#begin
         3   (8.8%)           3   (8.8%)     Date.today
         3   (8.8%)           3   (8.8%)     String#gsub!
         2   (5.9%)           2   (5.9%)     (sweeping)
         1   (2.9%)           1   (2.9%)     Integer#div
        29  (85.3%)           1   (2.9%)     Date.parse
         1   (2.9%)           1   (2.9%)     String#[]=
        32  (94.1%)           0   (0.0%)     IRB::Context#evaluate
        32  (94.1%)           0   (0.0%)     IRB.init_config
        32  (94.1%)           0   (0.0%)     StackProf.run
        32  (94.1%)           0   (0.0%)     IRB::Irb#signal_status
        32  (94.1%)           0   (0.0%)     RubyLex#each_top_level_statement
        32  (94.1%)           0   (0.0%)     Kernel#loop
        32  (94.1%)           0   (0.0%)     Kernel#catch
        32  (94.1%)           0   (0.0%)     IRB::Irb#run
        32  (94.1%)           0   (0.0%)     IRB.start
        32  (94.1%)           0   (0.0%)     <top (required)>
        32  (94.1%)           0   (0.0%)     Kernel#load
        32  (94.1%)           0   (0.0%)     <main>
        32  (94.1%)           0   (0.0%)     <main>
        32  (94.1%)           0   (0.0%)     IRB::WorkSpace#evaluate
        32  (94.1%)           0   (0.0%)     Kernel#eval
         2   (5.9%)           0   (0.0%)     (garbage collection)
        32  (94.1%)           0   (0.0%)     <main>
        32  (94.1%)           0   (0.0%)     Object#snippet
        32  (94.1%)           0   (0.0%)     Integer#times
        32  (94.1%)           0   (0.0%)     IRB::Irb#eval_input
=> 10000
irb(main):005:0>

HexaPDFを最適化した話(Ruby Weeklyより)


つっつきボイス:「こちらの記事は、PDF生成に使うHexaPDF gemでTrueTypeフォントを使うと測定結果↓が妙に遅いので、ランタイムプロファイリングとメモリのプロファイリングを実行し、原因を突き止めて修正するまでをやってますね」「これは泥臭そうな作業...」

Time Memory File size
hexapdf 1x 557ms 34.160KiB 452.598
hexapdf 5x 1.891ms 45.244KiB 2.258.904
hexapdf 10x 3.754ms 57.364KiB 4.517.825
hexapdf 1x ttf 634ms 33.044KiB 549.522
hexapdf 5x ttf 2.335ms 48.908KiB 2.687.124
hexapdf 10x ttf 4.693ms 63.568KiB 5.360.947

同記事より

「修正後のベンチマークはこれか↓」

Time Memory File size
hexapdf 1x 572ms 34.680KiB 452.598
hexapdf 5x 1.840ms 45.352KiB 2.258.904
hexapdf 10x 3.504ms 57.464KiB 4.517.827
hexapdf 1x ttf 542ms 33.540KiB 546.390
hexapdf 5x ttf 2.099ms 43.600KiB 2.670.953
hexapdf 10x ttf 4.016ms 63.584KiB 5.328.382

同記事より

「Rubyコードが遅い原因を突き止めて解決するまでの泥臭くてつらい作業をまとめた、いい記事だと思います👍」「こういう作業をやれる人がチームにいるかどうかで、チームの限界性能が変わってきますよね」「たしかに」

「この記事ではHexaPDFの文字glyphを処理する周辺の効率化を行っているみたい」「つくづく、プログラマーはいろんなことを知らないといけないんだなと改めて思いますね」「PDFの仕様を以前読んだことありますけど、あれはホントわからない」「ホントに😆」

参考: TrueType - Wikipedia
参考: PDFテクノロジーセンター | Adobe PDFテクノロジーセンター -- PDF仕様などが置かれています

関数型言語のマインドセットでオブジェクトをテストする(Ruby Weeklyより)


つっつきボイス:「thoughtbotさんの記事を久しぶりにご紹介します」「この記事はタイトルからして、テストの視点をどこに置くかという話のようですね」「意外と短いかも」

「テストに関する記事を書こうとすると、『どういう視点でテストすべきか』『どういう実装方法でテストを実装するか』など考慮すべきことが増えてきて、いわゆる『風呂敷を閉じられない』状態になりがちなのですが、この記事は1つの視点に絞って短くまとめているのが良さそうですね👍」

「お、この記事でfunctionalって言ってるのはどうやら関数型言語のことみたい」「あの難しそうなヤツですか」「ということは、記事で言っているside effect(副作用)も関数型言語における副作用を指すようです」「あ、そういうことか」「このあたりの話は、関数型言語をわかっている人ならもうわかってるのかもしれませんね」

参考: 関数型言語 - Wikipedia
参考: 副作用 (プログラム) - Wikipedia

「それにしても関数型言語が出てくるとは」「mindsetとあるのは関数型言語のマインドセットなのか」「そういう視点でテストを書くということでしょうね: テストの粒度とかテストを書くときの視点って、一般的に説明しようとすると抽象度が上がりがちで、わかったような気がするけど結局わからなかったということになりがちなんですが、『関数型言語の副作用のようなものだ』という視点を持ち込むことで、ある種のテストの書き方を理解しやすくしようという意図をこの記事に感じました」「なるほど」「もちろんテストの書き方の視点はひとつではなく、これ以外にもいろいろあります」

「ところで、副作用というとちょっとネガティブなイメージありますよね」「あるある」

参考: 副作用 - Wikipedia

🔗 その他Ruby


つっつきボイス:「RubyのWebサーバーPumaのメンテナをやっているNate Berkopecさんのツイートが気になったので拾ってみました」

puma/puma - GitHub

「スレッドがこのツイートから始まっているのでそれ以前の文脈はわかりませんが、『あなたはActive Supportのpresent?blank?を普段から使いすぎているか』というような話かな?」「自分はそう思いました: present?blank?を乱用するってどういうことなのかが気になりました」

Railsでnil? blank? empty? present?を使いこなそう

「Active Supportのpresent?blank?は、どういう使い方をするかにもよるんですが、かの有名な『PHPの==による比較』↓に少し通じるところがあるんですよ」「あ、PHPですか」「そういえばJavaScriptの=====も意味が違ってたかも」「JavaScriptの==も厳密でない比較ですね」

参考: 条件式の「==」と「===」の意味と違いの3つのポイント | PLUGMIZE(プラグマイズ)
参考: JavaScript 忘れがちな === と == の違い - Qiita

「よく考えてみると、present?blank?って、存在するのは何なのか、空白なのは何なのかが実はファジーじゃないですか: 調べる対象は文字列の空文字("")かもしれないし、数値のゼロ(0)かもしれない」「あ〜!」「nilかもしれないですよね」

present?blank?は元のオブジェクトが何であってもチェックできるのでとても便利なんですが、空文字かどうかを調べるならString#empty?がありますし、数値がゼロかどうかを調べるならNumeric#zero?がありますし、nilかどうかを調べるにはnil?で調べるべきでしょうね」「う、心当たりが😅」

「上のツイートは、『本来そうやってチェックすべき箇所をblank?とかで雑にチェックしてる人はまさかいないよね?』ぐらいの意味合いなんだろうなと自分は受け止めました」「なるほど、ちょっと腑に落ちてきました」

「ちょうどNateさんのツイートの続きにこんなのもあった↓、まさにtruthy/falselyしか気にしてない人、つまりtrueっぽければそれでいい、falseっぽければそれで済ませるという人には、present?blank?をそうやって雑に使って欲しくないということなんでしょうね」「Nateさんの気持ちがちょっと見えてきたかも」

「とりあえずのノリでpresent?blank?を使ってくれるなよと」「わかってて使うならいいけど、わかってないうちに使ってくれるなよと」「overuseって言ってるのはそういうことなんでしょうね」

「でも自分は作り捨てのコードなら割と使っちゃいますけどね😆」「😆」「でもpresent?blank?もActive Supportのメソッドなので、たまに素のRubyで使おうとしたら『あ、Active Support入れないと使えないんだった』と思い出したりということもありましたね」

クラウド/コンテナ/インフラ/Serverless

sudoに脆弱性が見つかる


つっつきボイス:「BPSの社内Slackに貼っていただいた情報です」「上の脆弱性情報記事、今日見てみたら情報が更新されてる: ありがたい!」

「sudoの脆弱性はLinux OSの"ほぼ"すべてが対象なのがキツい」「Dockerコンテナのようにコンテナ化されたLinuxは、コンテナにsudoが入ってなければ影響はありません」

参考: sudo - Wikipedia

「それにしても久しぶりに影響の大きい脆弱性ですね」「だいぶ前にbashで起きた脆弱性以来かも↓」「ありましたね、bashの脆弱性」

参考: 更新:bash の脆弱性対策について(CVE-2014-6271 等):IPA 独立行政法人 情報処理推進機構 -- 2014年の情報です

「まだ詳しく見ていないんですが、今回のsudo脆弱性はsudoのコンフィグを変えて対処できる感じではなさそうなんですよ: と思ったら上の脆弱性情報記事にちょうど書いてあった↓」「バイナリをリネームですって」「脆弱なsudo実行バイナリがユーザーから実行可能な場所にある限り起きちゃうんですね...」

CentOS6を使用されている場合には、
sudoを使えないようにする(バイナリをリネームする等)
が、現時点での緩和策では一番確実だと思われます。
同記事より

「おそらく今回の脆弱性は、sudoをアップデートせずに回避するのが難しそうです: 特定の機能に潜む脆弱性ならコンフィグでその機能を無効にすることで基本的に回避できますが、上の脆弱性情報記事を見る限りではsudoをユーザー権限で実行できさえすれば攻撃できてしまうように見えますね」「う〜む」「これはエグい」

「それにしては比較的世の中が静かなよう見えますが、今後どこかのタイミングで急に騒がしくなるかもしれませんね」「情報出てから24時間ぐらいしか経ってないからかなと思ったら、数日経ってるんですね」「sudoの脆弱性は今後も見守っておきたいです」

JavaScript

mongoose: Node.js向けMongoDBクライアント&ライブラリ

Automattic/mongoose - GitHub


つっつきボイス:「こういうのがあると知人から教わりました」「Node.jsでMongoDBを扱う、こういうのは前からありそうかな」「バージョン5とあるぐらいだから前からあるんでしょうね」「よく見ると★も異様に多い」

参考: The most popular database for modern apps | MongoDB


以下はつっつき後に見つけたツイートです。

言語/ツール/OS/CPU

言語は継続


つっつきボイス:「各種プログラミング言語のソースをコレクションですか」「さすが言語を愛する人」「私が手伝っているGoby言語↓も恥ずかしながら開店休業に近い状態です😅」

Goby: Rubyライクな言語(2)Goby言語の全貌を一発で理解できる解説スライドを公開しました!


後編は以上です。

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

週刊Railsウォッチ(20210201前編)Webpackerのガイドがマージ、RailsはRuby 3でどのぐらい速くなったかほか

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

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

Ruby Weekly


CONTACT

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