結構詰まったので記録しておく。
今作っているアプリケーションの構造的に、あるモデル(Parent)がたくさん従属要素をもっていて、そのたびにhas_manyを書く必要があった。そこで、下のactive_record_extenstion.rbを作ってlib/以下においた。
ActiveRecord::Base.class_eval do
def self.scoped_to_tenant
belongs_to :parent
association_name = self.to_s.downcase.pluralize
Parent.has_many association_name.to_sym, class_name: self.to_s
end
end
そんで、子モデルの方に、こんな感じで書いた。
require Rails.root.join("lib/active_record_extension.rb"
class Child < ActiveRecord::Base
scoped_to_parent
end
こう書いとけば、子の方で親の分もまとめて定義できる。そう思って、子供の一覧を表示するコントローラーを下のように書いた。(current_parentは別に定義)
class ParentsController < ApplicationController
def index
@children = current_parent.children.all
end
end
そしたら、一覧ページを訪れた時に
undefined:children <class: parent>
てなようなエラーが出た。
ここからが本題。
エラーが出たわけで、pryを使ってデバッグしてみたり、色々原因を探ってみた。いつもこのエラーが出るわけじゃなくて、何回か更新したら出ない時もあった。試行錯誤していくと、コントローラー入ったところでChildを一回呼び出したらエラーが出なくなることに気がついた。
ここで、あれ、もしかしてChild modelが読み込まれてないんじゃ...?とピンときて、「rails model 読み込み」とかでググってみたら、model一覧を
Activerecord::Base.subclasses
で取得できると知った。デバッグ中に実行してみたら、案の定Parentしか表示されない。
なんでや!って思い、さらにググったら、タイトルのことが判明した。
この設定はconfig/environments/development.rbでしてあった。
# In the development environment your application's code is reloaded on
# every request. This slows down response time but is perfect for development
# since you don't have to restart the web server when you make code changes.
config.cache_classes = false
# Do not eager load code on boot.
config.eager_load = false
この2つの設定、production.rbではこうなってる。
# Code is not reloaded between requests.
config.cache_classes = true
# Eager load code on boot. This eager loads most of Rails and
# your application in memory, allowing both threaded web servers
# and those relying on copy on write to perform better.
# Rake tasks automatically ignore this option for performance.
config.eager_load = true
本番環境では起動時にクラスをすべて読み込んで、キャッシュも使うけど、もし開発環境でそうしてしまうとファイルを更新するたびにサーバーを再起動させなくてはならないのでやらない。とのこと。
だから開発環境で一覧ページを訪問した時は、childモデルが読み込まれてなくて、parentにもhas_manyが適用されていなかったというわけ。結局parentにhas_manyを追加しましたとさ。