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

週刊Railsウォッチ: JRubyが9.4.0.0でRuby 3.1に対応、IRB v1.5.0リリースほか(20221207後編)

こんにちは、hachi8833です。土曜日に第12回中高生国際Rubyプログラミングコンテストの最終審査会を見に三鷹産業プラザまで行ってまいりました。受賞者の皆さんおめでとうございます!🎉 今回はMatz賞が2本も出ました(Matz賞は出ない年もあるそうです)。

参考: 中高生国際Rubyプログラミングコンテスト in Mitaka
参考: 「中高生国際Rubyプログラミングコンテスト2022 in Mitaka」 受賞者が決定いたしました! | 中高生国際Rubyプログラミングコンテスト in Mitaka

週刊Railsウォッチについて

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

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

🔗Ruby

🔗 IRB v1.5.0リリース(Ruby Weeklyより)

なお現時点の最新バージョンはv1.5.1です。

ruby/irb - GitHub

つっつきボイス:「ruby/debugでおなじみのst0012さんのツイートです」「お〜、irbでeditコマンドが使えるとは!」「カレントのコンテキストにあればそのファイルを開くんですね」「さすがに実行中のファイルはロードし直さないと反映されないかな?」

「しかもedit FooでFooのファイルをエディタで開いたり、edit Foo.barみたいに特定のメソッドを指定するとFooのbar定義箇所にカーソルが合った状態で開けたりするんですね」「これは賢い👍」

🔗 オブジェクトシェイプのマージとYJITの最適化

YJIT is now optimized to take advantage of object shapes. [[Feature #18776]]
ruby/NEWS.md at master · ruby/ruby · GitHubより

# #6386より
class Foo
  def initialize
    # Starts with shape id 0
    @a = 1 # transitions to shape id 1
    @b = 1 # transitions to shape id 2
  end
end

class Bar
  def initialize
    # Starts with shape id 0
    @a = 1 # transitions to shape id 1
    @b = 1 # transitions to shape id 2
  end
end

foo = Foo.new # `foo` has shape id 2
bar = Bar.new # `bar` has shape id 2

つっつきボイス:「少し前のニュースですが、CRuby向けのオブジェクトシェイプがマージされていた↓ことを最近知りました」「RubyKaigi 2022で発表された後まもなくだから9月末ぐらいですね」

参考: Implementing Object Shapes in CRuby - RubyKaigi 2022

Rubyオブジェクトの未来をつくる「シェイプ」とは(翻訳)

「オブジェクトシェイプだからRuby内部のVMに関連するということですね: ユーザーコードでは特に何もしなくていい高速化」「RubyVM::Shapeというものが追加されたようなので、これを使ってシェイプを調べたりできそう」

参考: class RubyVM::Shape - RDoc Documentation -- 3.2.0.preview3

「オブジェクトシェイプの件に気づいたのは、もともとM1 MacbookでYJITをコンパイルしようとしてあれこれやっていたときに、YJITがx86_64に加えてarm64(M1チップ)でもオブジェクトシェイプの最適化が進んでいるらしいことに気づいた↓のがきっかけでした↓」

参考: ruby/yjit/src/asm/x86_64 at master · ruby/ruby · GitHub
参考: ruby/yjit/src/asm/arm64 at master · ruby/ruby · GitHub

Ruby 3.2.0-devをM1 Macbook Pro(Ventura)でビルドする

「ついでに、以下はM1 Macbookで3.2.0-devのパフォーマンスをとりあえずbenchmark-driverで3.1.2と2.6.8と雑に比較してみたものですが、結果がよかったものを恣意的にピックアップしたのでベンチマークとは言えないヤツです😆」「そうしたい気持ちわかる😆」

# M1で3.2.0-devのYJITを有効にした場合の比較
$ ruby general_attr_accessor_vs_getter_and_setter.rb
Benchmark general_attr_accessor_vs_getter_and_setter.rb
Warming up --------------------------------------
                slow     6.354M i/s -      6.415M times in 1.009678s (157.38ns/i)
                fast     7.154M i/s -      7.261M times in 1.014869s (139.78ns/i)
Calculating -------------------------------------
                          2.6.8       3.1.2   3.2.0-dev
                slow     7.370M      7.411M     10.032M i/s -     19.062M times in 2.586581s 2.572160s 1.900036s
                fast     8.266M      8.143M      9.638M i/s -     21.462M times in 2.596360s 2.635542s 2.226854s

Comparison:
                             slow
           3.2.0-dev:  10032424.1 i/s
               3.1.2:   7410879.2 i/s - 1.35x  slower
               2.6.8:   7369561.2 i/s - 1.36x  slower

                             fast
           3.2.0-dev:   9638007.3 i/s
               2.6.8:   8266355.6 i/s - 1.17x  slower
               3.1.2:   8143461.6 i/s - 1.18x  slower

benchmark-driver/benchmark-driver - GitHub


なお、後でやってみたところ、M1 Macbook環境で自分がビルドしたRuby 3.2.0-devではRubyVM::Shapeクラスがまだ有効になりませんでした😢。ビルドログにはcompiling ../shape.cがあるのですが。

$ ~/.rubies/ruby-master/bin/ruby -v
ruby 3.2.0dev (2022-12-06T02:52:34Z master 53473f8ea9) +YJIT [arm64-darwin22]
$ ~/.rubies/ruby-master/bin/irb
>> defined?(RubyVM::YJIT)
#=> "constant"
>> defined?(RubyVM::Shape)
#=> nil

参考: class RubyVM (Ruby 3.1 リファレンスマニュアル)

Ruby の 内部情報へのアクセス手段を提供するクラスです。デバッグ用、プロトタイピング用、研究用などのとても限定された用途向けです。一般ユーザーは使うべきではありません。
class RubyVM (Ruby 3.1 リファレンスマニュアル)より

🔗 JRubyが9.4.0.0リリースでRuby 3.1に対応(Ruby Weeklyより)


つっつきボイス:「JRubyがRuby 3.1をキャッチアップしたのはすごい🎉」「以前のJRubyはRuby 2.6対応だったから、かなり大きなアップグレード」「それをやりきったのはマジすごいですね」「今年1月のウォッチにJRubyの人からアップグレード作業中と返信いただいたのを思い出しました↓(ウォッチ20220118)」「JRubyが活発に活動していることがよくわかる👍」

「お、JRubyトップページのバナー表示がまだ"Ruby 2.6 compatible"のままですね」「ホントだ」「まだWebサイトまでは更新が追いついていない感じですね」

後でJRubyの中の人にお知らせいたしました↓。

🔗 git blameでRuboCopの自動変更を非表示にする方法(Ruby Weeklyより)


つっつきボイス:「短い記事です」「gitにこのコンフィグを設定して、git blameで無視して欲しいコミットを.git-blame-ignore-revsファイルに書き込んで配置すれば、git blame時に当該コミットをスキップした上で履歴を見ることができるのか」

# 同記事より
git config blame.ignoreRevsFile .git-blame-ignore-revs

「GitHubのドキュメントを見ると、.git-blame-ignore-revsファイルにこんな感じで書けばいいらしい↓」「gitの機能なんですね」

# docs.github.comより
# .git-blame-ignore-revs
# Removed semi-colons from the entire codebase
a8940f7fbddf7fad9d7d50014d4e8d46baf30592
# Converted all JavaScript to TypeScript
69d029cec8337c616552756310748c4a507bd75a

参考: Viewing a file - GitHub Docs

「CI環境でrubocop -aのようなツールで自動修正を行わせていると、git blameしたときに人間でないユーザーのコミットが最終コミットとして表示されて邪魔になったりするんですが、そういう問題を解消するということですね👍」「たしかにgit blameしても誰の修正かすぐにわからなくて困ることってありますよね」

🔗 module_functionextend selfと同じなのか?


つっつきボイス:「このextend selfっていう書き方見たことなかった↓」「何これ?!」「うはっ」

# 同記事より: extend self
module RubyCademy
  extend self

  def headline
    puts "Learn Ruby and Ruby on Rails!"
  end
end

RubyCademy.headline # => Learn Ruby and Ruby on Rails!
# 同記事より: module_function
module RubyCademy
  def headline
    puts "Learn Ruby and Ruby on Rails!"
  end

  module_function :headline
end

RubyCademy.headline # => Learn Ruby and Ruby on Rails!

「Rubyのモジュールはクラスではないのでheadlineメソッドをそのままでは呼び出せないんですよね: それがextend selfすることでRubyCademy.headlineのように呼び出せるようになる、と」「ここだけ見ればmodule_functionみたいな動作ですね」

参考: class Module (Ruby 3.1 リファレンスマニュアル)
参考: Module#module_function (Ruby 3.1 リファレンスマニュアル)

「おさらいすると、Rubyのモジュールは基本的にincludeして使うことが前提で、そのままだとモジュールのメソッドは呼び出せない」「そうそう」「逆に、includeしないでモジュールのまま使う前提の場合は、module_functionで特別にモジュールのメソッドを直接呼び出せるようにするのが常道ですね(同じモジュールをincludeして普通のモジュールとして使うこともできます)」

「ところで、RubyCademy.headlineという呼び出しの形は一見クラスメソッドっぽいけど、上のRubyCademyはモジュールだからクラスメソッドとは呼ばないんですよね」「そうそう、かといってモジュールメソッドという言葉もないんですよ」「ややこしい」「モジュールの特異メソッドと呼ぶのがいいのかな」

「実はRubyでクラスメソッドと呼ばれているものも、その実体はあくまでクラスの特異メソッドなんですよ」「そういえばそうでした」

参考: 特異メソッド定義 -- クラス/メソッドの定義 (Ruby 3.1 リファレンスマニュアル)

「それにしてもextend selfという書き方はちょっとびっくり」「上のコードを見ると、extend selfする場合はモジュール内のメソッドをまとめて全部アクセス可能にできるんですね」「逆にmodule_functionはメソッドを名指しで個別に指定しないといけないのか」

「個別に指定する方がよさそうですけどね」「でもメソッドの数が多いときはextend selfでまとめてやりたくなるかも」

「記事によると、module_functionの場合は、モジュールをオープンクラス的に再度開いて同名のメソッドを再定義しても、module_functionを再度呼ぶまでは、module_functionの対象は最初に定義したメソッドのままで、再定義したメソッドに自動的に差し変わらないんだそうです↓」「お〜、そうなのか!」

# 同記事より
module ModuleFunction
  def who_am_i
    "ModuleFunction"
  end

  module_function :who_am_i
end

ModuleFunction.who_am_i # => "ModuleFunction"

module ModuleFunction
  def who_am_i
    "overriden ModuleFunction"
  end
end

ModuleFunction.who_am_i # => "ModuleFunction"

module ModuleFunction
  module_function :who_am_i
end

ModuleFunction.who_am_i # => "overriden ModuleFunction"

「逆にextend selfの場合は、同名のメソッドを再定義した時点で自動的に新しいメソッドの方が呼び出されるようになる↓」

# 同記事より
module ModuleFunction
  extend self

  def who_am_i
    "ModuleFunction"
  end
end

ModuleFunction.who_am_i # => "ModuleFunction"

module ModuleFunction
  def who_am_i
    "overridden ModuleFunction"
  end
end

ModuleFunction.who_am_i # => "overridden ModuleFunction"

「記事の最後でextend selfmodule_functionのメリットを比較していますね↓」「"module_functionの方が読みやすい"、おっしゃるとおり」「公開されるメソッドが明示されますからね」

  • extend self

  • module_function

「この挙動の違いを業務コードで使い分けることがあるかしら」「たぶんない😆」「記事の最後に、"module_functionはRuby標準ライブラリでものすごく使われている"と書かれていますね」「いわゆるユーティリティ関数用のモジュールで多用されるのはわかる」「数学関数みたいな、インスタンス化しないで使う関数のモジュールですね」「数学関数をnewすることはまずないでしょうね」

「学びのある記事👍」「Rubyにはまだまだ知らないことがあった」


そういえばずっと前に記事を書いて以来module_functionのことをすっかり忘れてました↓

[Ruby] module_functionでモジュールの特異メソッドを簡潔に書く

🔗 その他Ruby

つっつきボイス:「ツイートのリンクをクリックするとリポジトリを開けます」「お〜、このマップですね↓」「うちの近所にCoderDojoの教室あるかな」(しばしマップをつつく)

参考: 地図から探す (DojoMap) - CoderDojo Japan

🔗DB

🔗 AWS RDSがBlue/Greenデプロイに対応


つっつきボイス:「@shyouheiさんのツイートにもあるように、このコンセプト自体は昔からあるものですね」「なるほど」「これを自力でやると大変なので、マネージドなのはたしかにありがたい: ただ、今あるものに足すというよりは最初から構成しておく必要があるようですけど」

参考: より安全、簡単、迅速な更新のための Amazon RDS ブルー/グリーンデプロイを発表
参考: MySQL を MHA + HAProxy で冗長化してみよう - インフラエンジニアway - Powered by HEARTBEATS

MHA for MySQL: Master High Availability Manager and tools for MySQL
Google Code Archive - Long-term storage for Google Code Project Hosting.より

「こういう仕組みは大きめのデプロイをやるときに欲しくなりますね: productionとstagingを完全同期しておいて、破壊的変更をstagingにリリースした上でproduction <-> stagingの役割を切り替えるというもので、サーバー2つ分の費用がかかる代わりに問題が起きたらすぐ元に戻せるのが強み」

「これなしでエイヤでデプロイしてミスしたら目も当てられないし、数か月前から計画を立てる必要があって作業者や期間のコストもかさむ」「作業のストレスも半端ないですよね」

「こういうサービスが10年前に欲しかった...」「呼び方はいろいろあれど、似たようなことは昔から行われていましたね: まあ10年前にそういうサービスがあってもすぐには信用できなかったでしょうけど」「それわかります」

「実はこのBlue/Greenデプロイという用語を知りませんでした😅」「私が昔作ったスライドがありますのでどぞ↓」「どもです!」

🔗 設計・セキュリティ

🔗 マトリョーシカ人形のようなメソッド設計を避ける


つっつきボイス:「jnchitoさんの記事です」「メソッドチェインを追いかけないと何をやっているのかわかりにくいコード、あるある」「それをマトリョーシカ人形と呼んでるんですね、なるほどわかりみです」

「RuboCopのMethodLengthを回避しようとして、メソッドを細切れにしてチェインする書き方をする人がときどきいるんですよ」「RuboCopがメソッドの行数を増やすなって怒るのもわかるんですけど、あれを守るのはつらい」「分割すること自体はいいんですけど、再利用しないものまで超細かく分割されるとむしろ読みづらい」「ズキッ!耳痛い...」

参考: MethodLength -- Metrics :: RuboCop Docs


後編は以上です。

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

週刊Railsウォッチ: 月刊のHotwireニュースレター、pessimize gemほか(20221206前編)

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

Ruby Weekly


CONTACT

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