Tech Racho エンジニアの「?」を「!」に。
  • 開発

[Rails 5]モジュールやクラスレベルの変数をスレッドベースで作成する機能(翻訳)

こんにちは、hachi8833です。BigBinaryシリーズの本日2本目をお送りします。

本機能のRails 5.1での修正点については「[Rails 5.1]thread_mattr_accessorの変数はサブクラスと共有されないようになった(翻訳)」をご覧ください。

概要

モジュールやクラスレベルの変数をスレッドベースで作成する機能(翻訳)

Railsでは以前から、cattr_readercattr_writer、cattr_accessor`という一連のメソッドを使ってクラスレベルの変数やモジュールレベルの変数を作成する機能が提供されていました。

Rails 5ではこれをさらに押し進め、クラスレベルの変数やモジュールレベルの変数をスレッド固有の形で作成できるようになりました。
以下の例をご覧ください。

module CurrentScope
  thread_mattr_accessor :user_permissions
end

class ApplicationController < ActionController::Base

  before_action :set_permissions

  def set_permissions
    user = User.find(params[:user_id])
    CurrentScope.user_permissions = user.permissions
  end

end

このCurrentScope.user_permissionsは、実行中のスレッドが完了するまでアクセスできるので、このコード以降で変数にアクセスできます。

これを利用して、たとえばコントローラで明示的にcurrent_userを渡さなくてもすべてのモデルで変数にアクセスできるようになります。

class BookingsController < ApplicationController
  def create
    Booking.create(booking_params)
  end
end

class Booking < ApplicationRecord
  validate :check_permissions

  private

  def check_permissions
    unless CurrentScope.user_permissions.include?(:create_booking)
      self.errors.add(:base, "Not permitted to allow creation of booking")
    end
  end
end

内部で使われているThread.current#[]=メソッドによって、指定したすべての変数のスコープが現在実行中のスレッドに対して設定されます。

作成したクラスやモジュールの変数では名前空間が有効になるため、衝突の心配なしにCurrentScope.user_permissionsRequestScope.user_permissionsでアクセスできます。

従来PerThreadRegistryでグローバル変数を管理していた場合は、Rails 5からthread_mattr_*thread_cattr_*に置き換えられます。

グローバル変数は一般に悪影響をもたらすため、今後はこのような改良APIを利用しましょう。

訳注: PerThreadRegistryは現在非推奨になっています。

関連記事


CONTACT

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