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

週刊Railsウォッチ(20210209後編)Rubyでミニ言語処理系を作る、Kernel#getsの意外な機能、CSSのcontent-visibilityほか

こんにちは、hachi8833です。

週刊Railsウォッチについて

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

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

🔗Ruby

🔗 Rubyでミニ言語処理系を作る

sonota88/vm2gol-v1 - GitHub


つっつきボイス:「少し前の記事です」「お〜、Rubyで言語処理系を作ってライフゲームまで動かしたんですね」「VMも自分で作ったのか」

参考: 言語処理系とは

「へ〜、この記事では便利なサポート系の外部ツールを使わずに、Rubyと最小限のgemだけで言語処理を書いてるんですね」「これはいい勉強になりそう」「こういうのは作ってて楽しいですよね」

「小さな言語処理系を一度自分で作ってみるのは大事」「そうそう」「難しすぎることをしてないのもポイントだと思います: リポジトリもシンプルですし、外部ツール群的なものも作っていませんね」「なるほど」

「外部ツールを作り始めると処理系全体がどんどん大きくなってしまいますけど、この記事はVMも純粋なVMにとどめていますし、コンパイラを作らずにコード生成機を直接書いてそこからアセンブリコードを出力しているようですね」「ふ〜む」「グラフィック部分も手作りしているとは面白い!」「外部ツールを作らずに、自分が掌握できる最小限の構成で処理系を作るというのがいいと思いました👍」


「ところで、大学のコンピュータサイエンスの授業で使われていたような古典的な教科書に、よくこういう感じのミニ処理系の作り方が載ってましたね: LISPの教科書あたりでそういうのを見た覚えがちょっとあります」「そうそう、スタックマシンを作って動かすような言語処理系はよくこういう構成になりますよね」「オレオレチューリングマシン的なヤツですね🦾」

参考: LISP - Wikipedia
参考: スタックマシン - Wikipedia
参考: チューリングマシン - Wikipedia

「自分で言語処理系を作るときは、とにかく最後まで作り切れるかどうかが最大の分かれ目」「そうそう、そこなんですよ」「小さくても言語系を作るのは根性要りますから」「でもそれを乗り越えて作り切れば、得るものは大きい」

「自分なら、ひとりでやるより人数集めて勉強会形式でやるかも」「自分はむしろひとりでやりたいかな」「お、こういうものこそ大学のゼミみたいな場の方がはかどりそうですけど?」「そういう面ももちろんあると思いますけど、こういうものを作るなら他の人の進捗を待たずに自分のペースでやりたい方なので」「それもわかります😆」

🔗 method_missingの実用的な使い方3種(Ruby Weeklyより)


つっつきボイス:「Rubyのmethod_missingは、エラーハンドリング、メソッドのdelegation、DSL(Domain Specific Language: ドメイン固有言語)やライブラリで使える、まさにそのとおりですね」

参考: BasicObject#method_missing (Ruby 3.0.0 リファレンスマニュアル)
参考: ドメイン固有言語 - Wikipedia

🔗 method_missingよもやま話

「ところで、最近はRubyでDSLを作るいい教材ってあるのかな?以前はRubyでDSLを作るいい学習サイトがあったんですが、今は閉鎖してしまったんですよ」「ありゃ、残念」「そういう教材では必ずといっていいほどmethod_missingを使うテクニックが紹介されていますね」「『パーフェクトRuby』のような大きな書籍ならカバーされていると思いますが、RubyのDSLでのmethod_missingの使い方を手軽に学べる本もあるといいですよね」

「他の言語をやっていた人がRubyをやってみて驚くことのひとつが、このmethod_missingだろうなと思います」「たしかに!」「Rubyではこんなことができるのか!という一種の感動がありますね」


学習サイトの代わりに、method_missingとDSLについて書かれている英語記事をいくつか見繕いました。

参考: Writing a Domain-Specific Language in Ruby
参考: Metaprogramming: Writing in Ruby with... Ruby | Toptal

🔗 Rubyのgets


つっつきボイス:「意外に短い記事ですね」「Rubyの生のgetsに驚きの挙動ってあったかな?」

# 同記事より
#!/usr/bin/env ruby

puts "What is your name?"
your_name = gets.chomp
puts "Hi, #{your_name}!"

「上のコードを./gets.rb 123のように引数を渡して実行するとエラーになる、これは当然そうなりますよね」「ですよね、引数で渡したものをgetsで取れるというのは聞いたことないかも」「他の言語だとできたりするのかな?」

なお、以下は手元のRuby 3.0.0で上のコードを実行した結果です。

./gets.rb:4:in `gets': No such file or directory @ rb_sysopen - 123 (Errno::ENOENT)
    from ./gets.rb:4:in `gets'
    from ./gets.rb:4:in `<main>'

「Rubyの英語ドキュメント↓を見てみると...え?Kernel#getsにはARGV(Rubyスクリプト実行時に渡す引数の配列)を取る機能があるって書いてある!」「マジですか!?」「これは知らなかった」

参考: gets -- Module: Kernel (Ruby 3.0.0)

Returns (and assigns to $_) the next line from the list of files in ARGV (or $*), or from standard input if no files are present on the command line. Returns nil at end of file.
ruby-doc.orgより

# ruby-doc.orgより
ARGV << "testfile"
print while gets

「日本語のドキュメントにも書かれてる↓」「ARGFはARGVをファイルとみなしたオブジェクトとある: ということはgetsにはファイル名を引数で渡せるのか!」「上でエラーになった123は、123という名前のファイルがないからエラーになったということなんですね」「やっと話が見えてきたかも」

参考: Kernel.#gets (Ruby 3.0.0 リファレンスマニュアル)

ARGFから一行読み込んで、それを返します。行の区切りは引数 rs で指定した文字列になります。
rs に nil を指定すると行区切りなしとみなしてファイルの内容をすべて読み込みます。ARGVに複数のファイル名が存在する場合は1度に1ファイルずつ読み込みます。空文字列 "" を指定すると連続する改行を行の区切りとみなします (パラグラフモード)。
読み込んだ文字列は組み込み変数 $_ にもセットされます。
docs.ruby-lang.orgより

「いや〜今までは記事にもあるようにずっと$stdin.getsでARGVを取ってましたけど↓、Kernel#getsでARGVを取れるなんて思いもよらなかった」「たしかに驚きですね」「getsメソッドのこの挙動にはちょっとびっくりしました」

# 同記事より
your_name = $stdin.gets.chomp

参考: argc,argvは何の略 | C言語のTipsとサンプル | C入門 基本情報対策講座のcClip

🔗 Rubyのプリントデバッグを便利にするライブラリ


つっつきボイス:「Matzがリツイートしているのを見かけたので取り上げてみました」「ricecreamというプリントデバッグに便利なgemを作った記事のようですね」

nodai2hITC/ricecream - GitHub

icは引数をそのまま戻り値として返してくれるから、式展開の中で使ったり以下のようにreturnに書いたりできるのが使いやすそう」「他にもいくつか機能があるようですね」

# 同記事より
return "result = #{ic foo.bar}"

「この機能がRuby本体に入ったら便利かも」「元記事にもありますけど、上と同じことはppでもできますね」「ちなみにpprequireしないと使えないんですよ(追記参照)」「Rubyのコンフィグでこういうデバッグ出力をオンオフできると便利かもしれませんね」「こういうツールは比較的作りやすいと思うので、自分で作ってみるのもいいと思います」

参考: library pp (Ruby 3.0.0 リファレンスマニュアル)
参考: class PP - Documentation for Ruby 3.0.0

「ところでデバッグプリントを消し忘れることってありませんか?」「それはコミット前に消さないといけませんよね」「おっしゃるとおり😅」

追記(2020/02/10)

ppについて以下のご指摘をいただきました🙇。ありがとうございます!


「元記事を見ると、もともとPythonにicecreamという同じようなプリントデバッグツールがあるらしい↓」「Ruby版のicecreamだからricecreamと名付けたのかもしれませんね」「アイスクリームのアイコンがかわいい!」「sorbet↓のアイコンをちょっと思い出しました」「単にアイスクリームとシャーベットというモチーフが似ているだけだと思いますけどね」

gruns/icecream - GitHub
sorbet/sorbet - GitHub

🔗DB

🔗 Googleのsqlcommenter(Publickeyより)


つっつきボイス:「sqlcommenterはまだ使ったことがありませんが、Railsだと類似のgemが以前からいろいろありますね: ただ、他のフレームワークだとそういうツールがあるとも限らないので、そういうところでは便利なのかもしれないと思いました」

「sqlcommenterは、対応しているデータベースが比較的多いとか、OpenCensusのようなツールと組み合わせて使えたりするらしい」「いわゆるAPM(Application Performance Management)ツールでもそれに近いことができますね」「なるほど」「使いたい人が使うということでよいと思います」

参考: OpenCensus(OpenTelemetry)とは | フューチャー技術ブログ
参考: 【ツール8選】APMツールとは?基本解説やおすすめツールをご紹介! | QEEE

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

🔗 sudo脆弱性がmacOSにも影響


つっつきボイス:「sudo脆弱性は先週も間話題にしましたね(ウォッチ20210202)」「現時点ではAppleから情報はまだ出てないようです」

「Linuxのsudo脆弱性と似てるというだけで多少違いがありそうな感じ」「日本語記事にはそう出ていますね↓」

参考: sudoコマンドの脆弱性、「macOS」にも影響 - ZDNet Japan

「Linuxと違ってmacOSのバイナリはIntel版でもMach-Oバイナリですし、メモリマップなども違っているので、まったく同じ攻撃方法が使えるとはあまり思えませんが、sudoの脆弱性という視点から見て類似のものがMac版でも見つかった可能性ならありそう」「ふむふむ」「Macのsudoの脆弱性はソースコードレベルではたぶんLinuxのsudo脆弱性と違うだろうという気はしていますが、英語記事を眺めた感じでは、heap overflowにつながる可能性が書かれていたり、どうやらAPIレベルでは同じような結果になるらしい」

参考: Mach-O - Wikipedia
参考: Heap Overflow: Vulnerability and Heap Internals Explained - Infosec Resources

「これはAppleの対応を待つしかないでしょうね」「Windowsを使ってる人は大丈夫なんですね、いいな〜」

🔗CSS/HTML/フロントエンド/テスト/デザイン

🔗 CSSのcontent-visibility

参考: content-visibility - CSS: カスケーディングスタイルシート | MDN

参考: CSS Containment - CSS: カスケーディングスタイルシート | MDN


つっつきボイス:「英語記事からの翻訳みたいなので、元記事を開いてみよう↓」

参考: content-visibility: the new CSS property that boosts your rendering performance


web.dev/content-visibilityより

「元記事にcodepenのリンクがありますね↓」「これでやってみましょうか」(一同でしばらく動かしてみる)

See the Pen
Content-visibility Demo: Base (Content Visibility on Grids)
by Una Kravets (@una)
on CodePen.

「Rerunボタンを押すといいのかな?」「動かしてみたけどまだピンとこない...」

「これかな?ちょっとわかりにくいですけど、codepenの表示を下にスクロールすると、途中でページが長くなったのが今スクロールバーに反映されたのが一瞬見えました」「え、今のがそうなんですか?」「Zoom越しだとうまく見えないかも...」「ではもう一度」「あ、ちょっと見えたかも」「下にスクロールすると何度かページの長さが変わっているように見えますね」

「CSSの末尾でcontent-visibility: auto;が指定されているから、この部分に効いてるんでしょうね」「あ、ここですか」

<!- https://codepen.io/una/pen/rNxEWLo より-->

.grid-3,
.grid-2,
.p-group-flex {
  content-visibility: auto;
}

「通常のWebページを開いてそのままにしていれば、JavaScriptでlazy loadingなどを行わない限り、ページが長くてもレンダリングはすべて終わっているものなので、普通ならスクロールしたときにページの縦の長さは変わらないはずですよね」「はい、そうですよね」「今codepenでやったように、レンダリングが終わっているはずのページを下にスクロールすると縦の長さが伸びたということは、ブラウザに表示されているページの中でそれまでビューポート↓に入っていなかった部分、つまりそれまでレンダリングされていなかった部分がレンダリングされたということなんでしょうね」「なるほど!」「ちょっと雰囲気がつかめてきたかも」「元記事を読まずに今試してみた限りでは、たぶんそうなんだろうと推測しました」「弊社のbabaさんのようなCSSに詳しい人に聞いてみたい」

参考: ビューポートの概念 - CSS: カスケーディングスタイルシート | MDN

「どうやらcontent-visibility: auto;は、ビューポートにさしかかるまでレンダリングを遅延するプロパティのようですね: これがlazy renderingのようなものだとすると、lazy loadingとは別物と考えるのがよさそうな気がしました」「なるほど、ちょっと腹落ちしました」「無条件に使うよりも、調べてから使う方がよさそう」「もう少しわかりやすい見せ方があるといいんですけどね」

auto
この要素は、レイアウトの封じ込め、スタイルの封じ込め、およびペイントの封じ込めをオンにします。要素がユーザーに関連していない場合は、その内容もスキップします。非表示とは異なり、スキップされたコンテンツは、ページ内検索、タブオーダーナビゲーションなどのユーザーエージェント機能に対して通常どおり利用可能である必要があり、通常どおりフォーカス可能で選択可能である必要があります。
MDN: content-visiblityより


後編は以上です。

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

週刊Railsウォッチ(20210208前編)Rails次期リリースがバージョン7に決定、thoughtbotのアプリケーションセキュリティガイドほか

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

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

Ruby Weekly

Publickey

publickey_banner_captured


CONTACT

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