質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.04%

Railsのクラス変数のキャッシュ保持について

解決済

回答 1

投稿

  • 評価
  • クリップ 0
  • VIEW 95

score 91

Railsのクラス変数のキャッシュ保持の仕様について

概要

現在Rails6系で作成中のアプリにおいて、仕様がかなり被っている二つのモデルを扱っています。
CRUDアクションを各コントローラーに別々に書くのはDRYでは無いので、コントローラー、モデルの双方ついて抽象クラスを作ってそちらにCRUDアクションを定義しています。

当然、抽象クラス内からどちらのモデルにクエリを出すのかを判別する必要があるので、クラス変数として該当のモデルをセットして呼び出す仕様にしているのですが、リクエスト毎にクラス変数が更新され無い状況です。

具体的には、後述のコード中において、
self.modelの中身が毎回変わってくれず、Dog::CategoriesControllerにアクセスしているのにcat_categoriesテーブルにクエリが行ってしまうという状況です。

おそらくクラスの初期化が毎回行われずにキャッシュされたものをRailsが使っているという事なのかと思いますが、
恥ずかしながらこのあたりの仕様について理解しておらず、対応策について困っています。

※一応ですがRailsはAPIモードで開発しており、viewについては別で開発しております。

質問

リクエスト毎にクラス変数がセットされる様にするためにはどうすれば良いか?
もしくは、その方法があったとしてもその方法を取る事はパフォーマンス等の観点から問題があれば、どの様に対応するのがベストか?
(後者については抽象的な質問になってしまっており恐縮ですが、、、)

コード

Controller

  • ApplicationController
class ApplicationController < ActionController::API
  def initialize
    super
    self.model = self.class.get_model_class
  end

  def self.call_default_functions
    self.set_class_name_prefix
    self.set_class_name_suffix
  end

  def self.set_class_name_prefix
    @@class_name_prefix = self.name.split('::')[0]
  end

  def self.set_class_name_suffix
    @@class_name_suffix = self.name.gsub(/Controller/, '').split('::')[-1]
  end

  def self.get_model_class
    "#{self.class_name_prefix}::#{self.class_name_suffix.singularize}".safe_constantize
  end
end
  • 抽象Controller
class Abstract::CategoriesController < ApplicationController
  def index
    categories = self.model.find_index({query: category_params})
    render json: {result: true, body: self.model.to_json(cards)}
  end
end
  • 具体Controller
class Dog::CategoriesController < Abstract::CategoriesController
  self.call_default_functions

  private
   def category_params
     params.require(:category).permit(
      :name,
     )
     end
   end
end


※似た様な形でCat::CategoriesControllerがあると考えていただけると幸いです。

Model

  • 抽象Model
class Abstract::Category < ApplicationRecord
  self.abstract_class = true
    def self.define_common_associations
      #class_name_prefixなどはコントローラーと同様にapplication_recordで定義しています。
      has_many :foods, class_name: "#{self.class_name_prefix}::Food", foreign_key: "#{self.table_name_prefix}_food_id"
    end
end
  • 具体モデル
class Dog::Category < Abstract::Category
  self.define_common_associations
  self.find_index(params)
    return self.all
  end
end

お手数ですが、何卒よろしくお願いいたします。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • maisumakun

    2021/04/01 14:40

    concernsではなく親クラスを立てるという選択肢をとった理由はなにかありますか?

    キャンセル

  • benza

    2021/04/01 14:51 編集

    私も経験が浅く、恥ずかしながら自身を持って言える理由はありませんが、以下の通りです。

    ・モジュールについては拡張の仕様について自分が詳しく無いという消極的な理由
    ・受けつけるパラメータが少々違うレベルの実装で、is_aの関係等基本的な考え方から言っても親クラスとして実装する方が自然だと考えられた
    ・親クラスとして実装する方が強制力が高く、前項と合わせてその方が良いと考えました。(アプリの全体像を説明する必要があるので、これについては少し言葉足らずになってしまいますが、、、)
    ・concernsについては、より横断的なモジュールのみにしておいた方が綺麗だと感じた(例えば複数のモデルで使われる画像アップロード用のモジュール等)

    キャンセル

  • benza

    2021/04/01 14:55

    自然だと考えられた、綺麗だと感じたの理由までちゃんと書かないと回答になりませんね、、申し訳ありませんが時間の都合上この程度でお許しいただけると幸いです、、、

    キャンセル

回答 1

checkベストアンサー

0

リクエスト毎にクラス変数が更新され無い状況です。

はい、本番サーバではクラスはサーバ起動時(あるいは処理スレッドの作成時)に1度ロードされるだけですので、クラス変数はリクエストを越えて共有されてしまいます。このような、リクエストごとのデータに使えるものではありません。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2021/04/01 14:48

    この場合、クラスインスタンス変数(@class_name_prefix、@class_name_suffix)あるいはclass_attributeを使えばいいかと思います。

    キャンセル

  • 2021/04/01 14:53

    早々のご回答ありがとうございます!
    自身で調べてみて、消化してからまた返答させて頂きます。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.04%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る