Kaigi on Rails2024 GMOインターネットグループRubyクイズガラポン

Q1.2024/10/1現在、以下の中からRubyGemsに載っていないGemを1つ答えてください。

1.tempura(天ぷら)

2.nori(海苔)

3.sushi(寿司)

4.sashimi(刺身)

5.maguro(マグロ)

6.oden(おでん)

7.ramen(ラーメン)

8.udon(うどん)

9.soba(そば)

解説

答:6 .oden(おでん)

rubygems.org を検索することで確認することができます。


他のGemリンク:
tempura: https://rubygems.org/gems/tempura
nori: https://rubygems.org/gems/nori
sushi: https://rubygems.org/gems/sushi
sashimi: https://rubygems.org/gems/sashimi
maguro: https://rubygems.org/gems/maguro
ramen: https://rubygems.org/gems/ramen
udon: https://rubygems.org/gems/udon
soba: https://rubygems.org/gems/soba

Q2.次のうち、正しく動作しないコードはどれでしょうか?
(MySQLを使っているものとします。また、articlesテーブルがあって、VARCHAR型のtitle/contentカラムを持つものとします。)

1.Article.where('(title, content) IN (?)', [['title1', 'text1'], ['title2','text2']])

2.Article.where(title: ['title1', 'title2'])

3.Article.where("title = ? OR content = ?", 'title1', 'text1')

4.Article.where(title: 'title1').where(content: 'text1')

解説

答:Article.where('(title, content) IN (?)', [['title1', 'text1'], ['title2','text2']])

1の記法は、rails7.2.2時点では使用できない記法です。実行するとTypeErrorが発生します。
過去に記法を実現しようとしてissueや複数のPRが作成されたことはありますが、マージされている実績はないようです。
【issue】
https://github.com/rails/rails/issues/35925
【closeされたPR】
https://github.com/rails/rails/pull/36003
【OpenのままのPR】
https://github.com/rails/rails/pull/48393

Q3.

# config/application.rb
module Q
  class Application < Rails::Application
    config.time_zone = "Asia/Tokyo"
    config.active_job.queue_adapter = 
      ActiveJob::QueueAdapters::AsyncAdapter.new
  end
end

# app/model/foo.rb
class Foo < ApplicationRecord
end

# app/jobs/bar_job.rb
class BarJob < ApplicationJob
  queue_as :default
  self.enqueue_after_transaction_commit = :never
  
  def perform(id)
    Foo.find(id)
    Rails.logger.info(":::BarJob:::")
  end
end

上記のコードがある状態で以下のコードを実行した時の挙動を以下の選択肢から選んでください。

※前提

・Rails 7.2.1
・Ruby 3.3.5
・実行OSのタイムゾーン ‘Asia/Tokyo’

Foo.transaction do
  foo = Foo.create!
  BarJob.perform_later(foo.id)
  sleep 2
end

1.例外 RecordNotFound が送出され、トランザクションはロールバックされる

2.例外 RecordNotFound が送出され、トランザクションはコミットされる

3.ログ “:::BarJob:::” が出力され、トランザクションはロールバックされる

4.ログ “:::BarJob:::” が出力され、トランザクションはコミットされる

解説

答:例外 RecordNotFoundが送出され、トランザクションはコミットされる

Rails 7.1まではトランザクションの中で呼ばれたActiveJobのperform_laterは即時エンキューされ実行されます。問題のコードではsleepによってトランザクションのコミットが遅延されています。ジョブのコードはトランザクションがコミットされていない状態でfindメソッドでレコードを検索しようとし、RecordNotFoundを発生させます。こういったレコードがコミットされる前に他のプロセスでそのレコードを参照しようとするのは不具合のよくあるパターンかと思います。
Rails 7.2からはトランザクションの中で呼ばれたActiveJobのperform_laterはトランザクションのコミットが行われるまでエンキューが遅延されるようになります。この挙動の変更により、ジョブの実行はトランザクションがコミットされた後に行われるため、ジョブの中で例外は発生しません。
ただし、この挙動は self.enqueue_after_transaction_commit = :never を指定することで7.1までの挙動に戻すことができます。 今回はこの設定が記載されているので、ジョブはトランザクションがコミットされる前に処理されてしまうのでした。
参考: https://guides.rubyonrails.org/7_2_release_notes.html#prevent-jobs-from-being-scheduled-within-transactions

Q4.以下のコードが用意されています

# なるべく多くの文字列を残しつつ、4byte以下に収める
#
# @params [String] long_string 文字列
# @return [String] 4byte以下でなるべく多くの文字数を残した文字列
def omit_string(long_string)
  truncated_string = long_string.byteslice(0, 4)
  truncated_string = truncated_string.byteslice(0...-1) #{1~4の制御構造} truncated_string.valid_encoding?
  truncated_string
end

omit_string("GMOビューティー") # => "GMO"

問. ビューティー社のエンジニア キッコはとある都合で文字列を4byteに切り詰めようとしています。
なるべく多くの文字を残しながら4byteに収めたいです。
有効なbyte列まで切り詰めるコードを考えつきましたが、どの制御構造を用いれば良いか悩んでいます。
1~4から適切な制御構造を選んで キッコの仕事を手伝ってください(ヒント: そのまま切り詰めると有効なbyte列にならない可能性があります)

1.if

2.unless

3.until

4.while

解説

答:3 . until

以下の3点が試される問題でした。

  • 制御構文を後置で書く場合も、制御構文の処理の中身であるという理解
  • byteslice(0…-1)は先頭から末尾の一個前までのbyte列を返す処理であるという理解
  • 式の評価がtrueの間に処理の中身が実行されるwhileに対して、falseの間に処理の中身を実行するuntilの理解


問題の処理を複数行に置き換えると、

#{1~4の制御構造} truncated_string.valid_encoding?
  truncated_string.byteslice(0...-1)
end

となります。
期待する処理を実現するために必要は制御構文は、truncated_stringが有効な文字列になるまでbytesliceを実行するものが適切であり、式がfalseの間に実行し続ける制御構文であるuntilが正解となります。
ちなみに、同様の処理を実現するためにStringクラスには#scrub メソッドが存在します。

Q5.

# config/application.rb
module Q
  class Application < Rails::Application
    config.time_zone = "Asia/Tokyo"
    config.active_job.queue_adapter = 
      ActiveJob::QueueAdapters::AsyncAdapter.new
  end
end

# app/jobs/baz_job.rb
class BazJob < ApplicationJob
  queue_as :default
  def perform
    Rails.logger.info(":::BazJob:::")
  end
end

以下のコードのうち、Rails 7.2.1 で実行した時、2024-10-26 18:10:00 JSTにジョブが実行され :::BazJob::: が出力されるコードの組み合わせを全て選んでください。

※前提

・Rails 7.2.1
・Ruby 3.3.5
・実行OSのタイムゾーン ‘Asia/Tokyo’

1.BazJob.set(wait_until: '2024-10-26 18:10:00' ).perform_later

2.BazJob.set(wait_until: Time.new(2024,10,26,18,10,00)).perform_later

3.BazJob.set(wait_until: DateTime.new(2024,10,26,18,10,00, "+09:00")).perform_later

4.BazJob.set(wait_until: Time.zone.parse('2024-10-26 18:10:00') ).perform_later

5.BazJob.set(wait_until: 1729933800 ).perform_later

解説

答:2,3,4

2.BazJob.set(wait_until: Time.new(2024,10,26,18,10,00)).perform_later
3.BazJob.set(wait_until: DateTime.new(2024,10,26,18,10,00, "+09:00")).perform_later
4.BazJob.set(wait_until: Time.zone.parse('2024-10-26 18:10:00') ).perform_later

Rails 7.2 の時点のActiveJob wait_until に渡した引数は utc メソッドが実装されていることが期待されています。
参考: https://github.com/rails/rails/blob/fb6c4305939da06efdf2893d99130e7829c53e8b/activejob/lib/active_job/core.rb#L120


aは文字列を渡しているので、エンキュー時に以下のようなNoMethodErrorが発生し、エンキューに失敗します。

undefined method `utc' for an instance of String (NoMethodError)
"scheduled_at" => scheduled_at ? scheduled_at.utc.iso8601(9) : nil,


eも同様です。ただし、7.1以下のバージョンまでは内部でTime.atを呼んで時刻として扱うような実装になっているため、エンキューに成功します。
https://github.com/rails/rails/blob/6f57590388ca38ed2b83bc1207a8be13a9ba2aef/activejob/lib/active_job/core.rb#L182

よって、b,c,d がエンキュー可能な処理となります。

ちなみに、問題の前提でOSのタイムゾーンと config.time_zone が Asia/Tokyo で一致させているので、Time , DateTime, ActiveSupport::TimeWithZone の間で時差は生まれません。

q(dev)> Time.new(2024,10,26,18,10,00).utc
=> 2024-10-26 09:10:00 UTC
q(dev)> DateTime.new(2024,10,26,18,10,00, "+09:00").utc
=> 2024-10-26 09:10:00 UTC
q(dev)> Time.zone.parse('2024-10-26 18:10:00').utc
q(dev)>
=> 2024-10-26 09:10:00 UTC


Time.nowとTime.zone.nowで参照されているタイムゾーン設定が異なるためにジョブがperformされる時間が異なるのを見抜く問題と見せかけて、 wait_untilに渡せる引数のクラスの問題でした。

GMOインターネットグループのSNSをフォローして最新情報をチェック