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

週刊Railsウォッチ: Ruby 3.2.0 RC1がリリース、YARVアドベント記事、ChatGPTほか(20221214後編)

こんにちは、hachi8833です。

週刊Railsウォッチについて

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

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

🔗Ruby

🔗 Ruby 3.2.0 RC1がリリース

つっつきボイス:「Ruby 3.2.0 RC1リリース🎉」「Rubyの最終リリースは毎年クリスマスと決まっているからもうすぐなのか」「リリースノート↓のchangelogも更新されていますね👍」

参考: Ruby 3.2.0 RC 1 リリース

後で早速インストールしました。

$ ruby -v
ruby 3.2.0rc1 (2022-12-08) +YJIT [arm64-darwin22]

🔗 Ruby ExplorerをWASIで作った

つっつきボイス:「このruby_explorerサイトにRubyのコードを書くとRuby VMのインストラクションを逆アセンブルしてしてくれるんですね」「ISeqと表示されているからたしかにVMインストラクション」「サーバーサイドでRubyを動かしたりせず、完全にクライアントサイドでWasm版Rubyを実行することで実現されているということか」「今までだとこういうコードを生成するにはサーバーサイドで行うしかなかったけど、WASIを導入したことでブラウザで生成できるようになりますね👍」「サーバー料金も節約できるし」

# 同サイトより
== disasm: #<ISeq:<compiled>@<compiled>:1 (1,0)-(3,3)> (catch: false)
0000 definemethod                           :foo, foo                 (   1)[Li]
0003 putobject                              :foo
0005 leave

== disasm: #<ISeq:foo@<compiled>:1 (1,0)-(3,3)> (catch: false)
local table (size: 1, argc: 1 [opts: 0, rest: -1, post: 0, block: -1, kw: -1@-1, kwrest: -1])
[ 1] bar@0<Arg>
0000 getlocal_WC_0                          bar@0                     (   2)[LiCa]
0002 getlocal_WC_0                          bar@0
0004 opt_mult                               <calldata!mid:*, argc:1, ARGS_SIMPLE>[CcCr]
0006 leave                                                            (   3)[Re]

参考: WASI
参考: WebAssembly - Wikipedia
参考: 逆アセンブラ - Wikipedia

🔗 RubyのDockerイメージとjemalloc


つっつきボイス:「これはDocker Hub公式のRubyイメージにjemallocライブラリを含めるかどうかというissueですね」「2018年からオープンのままか」「ところでjemallocは未だにRubyではデフォルトで使われませんね」「Linuxシステムにjemallocがデフォルトで入っていないし、jemallocがないとRubyが動かなくなる事態は避けたいだろうからデフォルトにしないんでしょうね」「たしかに」「jemallocを入れられる環境なら入れてもよいと思います」

参考: jemalloc.net
参考: Ruby × jemallocのすすめ - メドピア開発者ブログ

Ruby: mallocでマルチスレッドプログラムのメモリが倍増する理由(翻訳)

🔗 YARVアドベント記事

つっつきボイス:「YARVといえばCRuby(MRI)のVM実装ですね」「そのYARVのアドベント記事をKevin Newtoさん一人で書いているのすごい」「kevin Newtonさんといえば今年のRubyKaigiでSyntax Treeの話をしていた方ですね」「VMメモリハックの話とかいろいろ面白そう」「Ruby Under a Microscope(邦題: Rubyのしくみ)に通じる内容っぽい」「時間のあるときに読んでみたいシリーズ記事👍」

参考: YARV - Wikipedia
参考: Syntax Tree - RubyKaigi 2022

🔗 # frozen_string_literal: trueをつけるかどうか

つっつきボイス:「何も考えずに# frozen_string_literal: trueを書いていいのか問題」「基本的には、このマジックコメントをつけても動くコードを書くのがいいかなと思う」「同一文字列リテラルがあった場合にobject_idが異なる想定がないと動かないようなコードを書くことは普通はないですよね」

「このマジックコメントでパフォーマンスが改善するかどうかは、コード内にまったく同じ文字列リテラルを何度も評価するかどうかにもよるでしょうね: たとえば以下のようなコードは同じ文字列を何度も評価するから当然つけた方が改善する↓」

# 同記事より
# frozen_string_literal: true
require 'benchmark'
n = 10**8
Benchmark.bm(0) do |r|
  r.report{ n.times{ 'abcdefghijklmnopqrstuvwxyz' } }
end

「逆にこういうコードはそのままだと遅い↓ので、本当に必要ならば破壊的変更を行うことになるでしょうね」「普段は破壊的変更を使わずにマジックコメントで封じておいて、ここぞというときにだけ使う感じですね」「文字列の破壊的変更って、意図せずにやってしまうことの方が多いんですよ」「jnchitoさんの記事で"文字列リテラルが存在するファイルにだけ、マジックコメントを書く"というスタイルにしているのもわかる」「何も考えずにマジックコメントを書くよりよさそう」

# 同記事より
require 'benchmark'
n = 200_000
s = ''
t = ''
Benchmark.bm(2) do |r|
  r.report('+='){ n.times{ s += 'a' } }
  r.report('<<'){ n.times{ t << 'a' } }
end

p s == t

「ところで、昔のRubyは# coding: utf-8というマジックコメントを書かないとコードに日本語を書いた瞬間に動かなくなったりしましたよね」「Ruby 2.0から書かなくてよくなったんですよね」「マジックコメントは書かなくていい方が嬉しい」

Rubyの内部文字コードはUTF-8ではない...だと...?!


Pythonもエンコーディングを指定するマジックコメントが必要だと思っていましたが、後で調べるとPython 3ではデフォルトエンコーディングがUTF-8になっていて、UTF-8を指定するためのマジックコメントは書くべきではないとのことでした。

参考: 【python】# coding: utf-8はもうやめる - 静かなる名辞

🔗 後置のif

なお、後置のifは英語だと「if modifier」と書かれることがよくあります。自然言語の文法における、後ろから修飾する修飾語句(modifier)のことですね。

参考: 修飾語句の使い方 - Wordvice


つっつきボイス:「横に長いコードの末尾に後置のifがあるとたしかに読みにくい」

参考: Style/IfUnlessModifier :: RuboCop Docs

「行数がすごく多いブロックのendの後ろに後置のifが書かれていることもありますね」「ところが状況によってはその方が読みやすいと感じられることもあって、一概に言い切れないところもあるんですよ」「そうそう、endの後にifを書けるのは割と便利」

「他に変数への代入文の右辺をif文にする書き方もあって↓、すべてが式であるというRubyならではの記法ですが、これも便利なんですよ」「そうそう、大好き」「この書き方はインデントは深くなるけど、むしろ積極的に使ってもいいぐらいの気持ち」「インデントをどこに揃えるかだけは悩みますね」「自分だったらメソッドに切り出すかも」「複雑にしないのが大事」

hoge = if condition
         'hoge'
       else
         'piyo'
       end

「そういえばifのブロックで変数を定義するとRuboCopに怒られるんじゃなかったかな」

参考: Style/ConditionalAssignment :: RuboCop Docs

# https://docs.rubocop.org/rubocop/cops_style.html#styleconditionalassignmentより抜粋
# bad
if foo
  bar = 1
else
  bar = 2
end

# good
bar = if foo
        1
      else
        2
      end

「このandを使う書き方は好き↓」「Rubyでandorの優先順位が低いからできる技ですね」「Railsのコントローラでredirect_to <リダイレクト先> and returnと書くのは定番で、ifを使うよりも読みやすいと思います」「and returnを書き忘れるとDoubleRenderErrorになるヤツですね」

# 同記事より
# 金なしか?それなら処理中止
no_money? and raise TooPoorError

参考: Rails API redirect_to -- ActionController::Redirecting

参考: 演算子式 (Ruby 3.1 リファレンスマニュアル)

# https://docs.ruby-lang.org/ja/latest/doc/spec=2foperator.htmlより
高い   ::
       []
       +(単項)  !  ~
       **
       -(単項)
       *  /  %
       +  -
       << >>
       &
       |  ^
       > >=  < <=
       <=> ==  === !=  =~  !~
       &&
       ||
       ..  ...
       ?:(条件演算子)
       =(+=, -= ... )
       not
低い   and or

🔗 その他Ruby

つっつきボイス:「RubyKaigi 2022でキーノートスピーチをつとめたkateinoigakukunさんへのインタビュー記事です」「深掘りRubyですか」「文字起こしとってもありがたい🙏」


「早くもRubyKaigi 2023のページができているとは」「次回は長野県松本市ですね」「ゴールデンウィーク明けか」「コロナ禍でキャンセルされましたが、今度こそ松本で」

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

🔗JavaScript

🔗 Stimulus v3.2.0リリース(Rails公式ニュースより)


つっつきボイス:「Stimulusも着々とリリースされていますね」「今回の変更は少なめ」「後で自分のRailsアプリでもアップグレードします」

私のRails 7アプリではimportmap-railsを使っているので、config/importmap.rbで直接Stimulusのバージョンを更新しました。

# config/importmap.rb
  pin "application", preload: true
  pin "@hotwired/turbo-rails", to: "turbo.min.js", preload: true
- pin "@hotwired/stimulus", to: "https://ga.jspm.io/npm:@hotwired/stimulus@3.1.1/dist/stimulus.js"
+ pin "@hotwired/stimulus", to: "https://ga.jspm.io/npm:@hotwired/stimulus@3.2.0/dist/stimulus.js"
  pin "@hotwired/stimulus-loading", to: "stimulus-loading.js", preload: true
  pin_all_from "app/javascript/controllers", under: "controllers"
  pin "tailwindcss-stimulus-components", to: "https://ga.jspm.io/npm:tailwindcss-stimulus-components@3.0.4/dist/tailwindcss-stimulus-components.modern.js"

Rails 7: importmap-rails gem README(翻訳)

🔗言語/ツール/OS/CPU

🔗 Open AIのChatGPT


つっつきボイス:「ChatGPT大バズリしてますね」「今日のWebチーム内発表でも早速ChatGPTでRailsアプリを作ってみたメンバーがいましたね」「GPT-3という自然言語処理モデルがそれほど強力だったということなんでしょうね: 上の記事もよさそう👍」

参考: 【論文解説】OpenAI 「GPT-3」を理解する | 楽しみながら理解するAI・機械学習入門

「ChatGPTは趣味で使う分にはとても楽しい」「AIお絵かきの呪文の楽しさにも通じるかも」「強力だけど、当てにしすぎないようにはしたい」「口調がとても自然でもっともらしいけど、ときどきもっともらしい間違いが含まれることもあるので、結果は自分で吟味する必要がありますね」「少なくとも結果のコピペは避けたい」「顧客情報や個人情報を流し込んだりしないよう注意も必要ですね」


つっつき後にBPS社内でも注意喚起がありました。以下はDeepLの場合ですが、ChatGPTに限らずネット上のオープンなサービスを使う場合は情報の扱いに注意が必要ですね。

参考: 自分の契約書の英語版を作成するのにDeepLを使ったら、企業の情報を漏洩させてる人が多数いるのではと疑う結果になった - Togetter


「ChatGPT、早くもいろんな使われ方されていますね」

以下の記事はつっつき後に知りました。使い方についてはこのあたりを見るとよさそうです。

参考: ChatGPT使い方総まとめ - Qiita


「私もChatGPTに正規表現を生成させてみたり、正規表現にReDoS脆弱性があるかどうかを調べさせたりしました↓: ただ、この<[^>]++>が脆弱なのは有名なので、それを知ってて回答した可能性があるかもしれませんが」

  • Q: <[^>]++>という正規表現にReDoS脆弱性があるかどうかを教えて下さい
  • A: <[^>]++>という正規表現は、ReDoS(Regular Expression Denial of Service)脆弱性があります。
    ReDoS脆弱性とは、特に悪意のある入力を与えることで、正規表現を処理する処理が遅延すること、つまりサービスが遮断されることを指します。
    <[^>]++>という正規表現は、HTMLタグを検索するために使われることが多いですが、この正規表現はHTMLタグが深く嵌っている場合に、処理が遅延する可能性があります。 例えば、次のような文字列を処理する場合、処理が遅延する可能性があります。
    ChatGTPがReDoSを実演

参考: その正規表現の書き方で大丈夫? ReDoS 攻撃の怖さと対策方法 | yamory Blog


つっつき後に、念のため自分で作ったReDoSありとなしの正規表現もChatGPTにチェックしてもらいました。正しく判定しただけでなく、正規表現の内容にまで踏み込んでいて驚きました。複雑な正規表現を人間の言葉にしてもらうだけでも便利そうです。


後編は以上です。

バックナンバー(2022年度第4四半期)

週刊Railsウォッチ: 『RubyとRailsの何が強いのか』、書籍『Ruby on Railsステップアップ』ほか(20221213前編)

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

Rails公式ニュース


CONTACT

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