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

週刊Railsウォッチ: 2021年度Rubyアソシエーション開発助成、Rails REST APIレベルで楽観的ロックほか(20211102後編)

こんにちは、hachi8833です。大江戸Ruby会議09 出前Edition、いよいよ今夜ですね。

週刊Railsウォッチについて

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

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

🔗Ruby

🔗 2021年度Rubyアソシエーション開発助成の結果発表

2021年度Rubyアソシエーション開発助成では、「picoruby-compiler: An alternative mruby-compiler(Monstarlab)」「MRIのWebAssembly対応よるポータブルなRubyプログラムの実現
(齋藤優太)」「“debug.gem”の利用体験・開発効率の改善(小野直人)」「Ruby formatter(Kevin Newton)」が採択されました。おめでとうございます 🎉


つっつきボイス:「今回採択された中に、MRIをスタンドアロンなWebAssembly(Wasm)バイナリにコンパイル可能にするプロジェクトがありました」「おぉ、以前もどなたかがデモしていたのを見たことがありましたが、そのままだと使いにくかったみたいですね: MRIをWasmワンバイナリで配布できるようになったらスゴい」「mrubyはサイズが小さいので以前からWasmで動かせますけどね↓」

mrubyをWebAssemblyで動かす(翻訳)

参考: Rubyアソシエーション: Ruby処理系の概要

MRI (Matz' Ruby Implementation)
C言語で実装されたRubyの公式処理系。すべてのプラットフォームで動作可能。Rubyアソシエーションの理事長である まつもとゆきひろ により開発され始めたもので、最も広く使われている。
http://www.ruby-lang.org/
ruby.or.jpより

🔗 Rubyのメソッド呼び出しでレシーバの有無を判定


つっつきボイス:「この間のKaigi on Rails 2021冒頭のkamipoさんQ&A企画で、kamipoさんが『Active Recordのscopingを修正するために、"レシーバを付けて呼び出したか付けずに呼び出したかを判定する機能"がRubyに欲しいと思っていた』とお話ししていた↓のを自分も見て、その後で上のアンサー記事が出ました」

参考: Rails API scoping -- ActiveRecord::Relation

「お〜、privateメソッドの仕組みをうまく使うことでwhereのレシーバありとなしを判定するとはテクニカル」

# 同記事より抜粋
class User
  def self.where
    "レシーバなし"
  end

  def self.where_with_receiver
    "レシーバあり"
  end

  class <<self
    private :where

    # private メソッドでレシーバありで呼び出された場合に method_missing が呼ばれる
    def method_missing(name, *args)
      if name === :where
        where_with_receiver(*args)
      else
        super
      end
    end
  end
end

参考: Module#private (Ruby 3.0.0 リファレンスマニュアル)

🔗 Rubyのハックよもやま

「こういう高度な技を実際に使うとなると、他の人が見たときにコードの意図がわからなくなる可能性はあるかも」「あ〜、それもそうか」「ActiveSupport::Concernでラップしてそれを使うなどすればよいかもしれませんが」

参考: Rails API ActiveSupport::Concern

「一時期流行ったCSSハックなどもそうですけど、ハック系の技ほどコンテキスト依存が強くなる、つまりハイコンテキストになってしまいがち」「ふむふむ」「Rubyの嬉しい点は『こう書いたら動くかな?』と思って書いたら本当に動くことだと自分は思っているので、あまりハイコンテキストな方向に進まない方が個人的には嬉しいかも」「たしかにこういう状況ではこう書かないと動かないみたいなのはRubyっぽさが下がりそうですね」

参考: CSSハックについて | GRAYCODE HTML&CSS

「kamipoさんの話を聞いたときは、Rubyでもできないことがあるのかと思っちゃいました」「安全その他の理由で言語には何らかの制約があるものなので、できないことはあるでしょうね」

🔗 ブロック内にreturnを書くとどうなるか(Ruby Weeklyより)


つっつきボイス:「ブロック内でreturnを書いたときの話ですね: 思わぬ挙動が起きるヤツ」「ここでハマる記事をよく見かけますね」

# 同記事より
def some_method(&block)
  block.call
  completed = true # won't be called if the block returns early
ensure
  if completed
    puts "ok"
  else
    puts "returned early"
  end
end

some_method { return } # => "returned early"
some_method { next } # => "ok"

参考: Ruby のブロック内で return すると… - Secret Garden(Instrumental)

「ところがブロック内のreturnって、とても書きたくなるときがあるんですよ」「おぉ?」「たとえばRSpecのexpectブロックに書く条件が複雑になってくるとif文が入ってきたりするんですが、Rubyのブロックが返すのは最後に評価された値なので、それを変えたくてreturnを書いてしまうと、そのブロックが終了するのではなく、そのブロックがあるメソッドそのものが終了する」「そういえばブロックの中に他にも制御文を書けたりしますね」「書けてしまうが故にうっかり書いてしまいがち」

参考: 制御構造 (Ruby 3.0.0 リファレンスマニュアル)

「個人的にはブロックにreturnを書いたらwarningを出してくれてもいいのにという気持ちになることもあります」「わかります」

🔗 その他Ruby


つっつきボイス:「チェリー本第2版の発売日が決まったんですね」「急遽ruby/debugの解説を加えたそうです」「発売後ひと月しないうちにRuby 3.1が出るのか...」「Ruby 3.1はツールチェインは増えるけど、書き方はあまり変わらないから影響は小さいでしょうね」

ruby/debug - GitHub

以下はつっつき後に見つけたツイートです。後ひと月で販売ですね。

🔗Rails

🔗 RailsのREST APIの楽観的ロック(Ruby Weeklyより)


つっつきボイス:「よくあるデータベースの楽観的/悲観的ロックの話かなと思ったら、よく見るとRailsのREST APIレベルでの楽観的/悲観的ロックの話のようですね」「あ、RESTレベルのロックでしたか😅: DBカテゴリに置いてましたがRailsカテゴリに変えておきます」

参考: 楽観ロック/悲観ロック(optimistic locking/pessimistic locking)とは - IT用語辞典 e-Words

「記事によると、ActiveRecord::Locking::Optimisticというまさに楽観的ロックのための機能があるらしい」「こんな機能もあったんですね」「そういえばlock_versionカラムがActive Reordで使えるのを思い出した: これを使って行ロックをかけられる」「お〜、lock_versionカラムを追加すればActive Recordの機能だけで使えるんですね」

参考: Rails API ActiveRecord::Locking::Optimistic

Active Recordは、lock_versionフィールドが存在する場合に楽観的ロックをサポートする。レコードが更新されるたびにlock_versionカラムがインクリメントされる。2回インスタンス化されたレコードは、最後に保存された方が更新されるとStaleObjectErrorをraiseする。
api.rubyonrails.orgより

# api.rubyonrails.orgより
p1 = Person.find(1)
p2 = Person.find(1)

p1.first_name = "Michael"
p1.save

p2.first_name = "should fail"
p2.save # Raises an ActiveRecord::StaleObjectError

「元記事では、最終的にHTTPのETagヘッダーをlock_versionと連携させて楽観的ロックを実現しているんですね↓: ensure_if_match_header_providedメソッドでIf-Matchヘッダーの存在を確認して、なければHTTP 428を返す、へ〜!」「HTTP 428は、Precondition Required(前提条件が必要)なんですね」「元記事の1つ前のコードは、If-Matchヘッダーがなくても呼べてしまってHTTP 500(内部エラー)になるので、こう変えたのか」

# 同記事より: 最終的なコード
class RentalsController
  before_action :ensure_if_match_header_provided, only: [:update]
  after_action :assign_etag, only: [:show, :update]

  rescue_from ActiveRecord::StaleObjectError do
    head 412
  end

  def show
    @rental = Rental.find(params[:id])
    respond_with @rental
  end

  def update
    @rental = Rental.find(params[:id])
    @rental.update(rental_params)
    respond_with @rental
  end

  private

  def ensure_if_match_header_provided
     request.headers["If-Match"].present? or head 428 and return
  end

  def assign_etag
    response.headers["ETag"] = @rental.lock_version
  end

  def rental_params
    params
      .require(:rental)
      .permit(:some, :permitted, :attributes)
      .merge(lock_version: lock_version_from_if_match_header)
  end

  def lock_version_from_if_match_header
    request.headers["If-Match"].to_i
  end
end

参考: ETag - HTTP | MDN

参考: 428 Precondition Required - HTTP | MDN

参考: 500 Internal Server Error - HTTP | MDN

「REST APIレベルで楽観的ロックをかけるこの書き方はたしかに正当なRESTful、面白い!」

参考: RESTful API(REST API)とは - IT用語辞典 e-Words

🔗 ETagとRESTful

「まだETagがわかっていないんですが、ETagは使わないこともできるんでしょうか?」「ETagはHTTPキャッシュ機構のひとつで、サーバーがETagを付けてレスポンスを返したらクライアントもETagを付けることになっていた気がしますが、ETagはもともとキャッシュのためのものだから、クライアントもETagに対応する義務はあるんだろうか?うん、この記事のような方法でETagを使うことは果たして正当なのかどうかを考えるのは議論として面白いですね」

参考: HTTP キャッシュ - HTTP | MDN

「MDNを見ると、If-None-Matchをヘッダーに含めることが"できます"と書かれてる↓」「英語版も同じ部分がcanになってますね」「つまりクライアントはETagを無視することも可能ということか」

ETag レスポンスヘッダーは strong validator として使用できる、ユーザーエージェントにとって不透明な値です。ブラウザーなどの HTTP ユーザーエージェントは、この文字列が何を表すかがわからず、またこの値が何になるかを予測することもできません。ETag ヘッダーがリソースのレスポンスの一部に含まれていたら、クライアントは以降のリクエストでキャッシュ済みリソースの確認を行うために If-None-Match をヘッダーに含めることができます
developer.mozilla.orgより

「今のMDNの説明はIf-None-Matchについてのものだから、元記事で使っているIf-Matchがどうなのかも見てみるか」

参考: If-Match - HTTP | MDN

「元記事の記述を見ると、この実装はクライアント側がETagのIf-Matchを実装していることを求めていることになりますね↓」「ふむふむ」「もちろんこれがRESTfulであることは変わりませんが」

If-Matchヘッダーを使えば、APIサーバーはリソース変更の有無を非常に手軽に確認できます。チェックサムやバージョン番号など、その他ETagとして選んだどれでも比較すればよいのです。
同記事より

「RESTfulはなるべくHTTPの機能でやろうという考え方なので、この記事のやり方はRESTfulの解釈のひとつとしてありだと思います👍」「こういうのはRails Wayでもあるんでしょうか?」「RESTfulですが、Rails Wayとは言いにくいかな」「なるほど」「クライアント側での処理を増やしてはいますが、RESTfulという視点ではキレイに決まっているので、Rails WayよりはRESTfulという思想に忠実な実装だと思います」「思想ですかなるほど」

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

🔗 Cloudflareの機能


つっつきボイス:「CloudflareのArgo Tunnelクライアントを使うとngrokみたいなことができる」「ngrokって、ローカルで起動したサーバーをネット上でいきなり公開できる便利ツールですね↓」「クライアント名はcloudflaredで、基本的にはCloudflare側にトンネリングするツールなのか」

cloudflare/cloudflared - GitHub

参考: ngrokが便利すぎる - Qiita

「Cloudflare Pagesは初めて知った」「Cloudflareには他にもツールチェイン的な機能がいろいろ増えている感じですね」

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


後編は以上です。

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

週刊Railsウォッチ: Rails 7アセットパイプライン解説記事、ロジックをapp/operatorsで整理ほか(20211101前編)

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

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

Ruby Weekly


CONTACT

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