ActiveRecord::Baseにscope追加しても上手く動かないからダメだよ、という話

ソフトウェア開発

バグなのか仕様なのかは分からないんですが、仕様ぽい感じもする動きです。

こんなコードを書いても動かない

あくまで一例ですが、テーブル内のdataカラム以外を取得したいなーというときに、以下のようなコードを書いたとします。いろんなテーブルにdataカラムがあるから、いっそActiveRecord::Baseに追加しちゃえという感じのコードです。

# -*- coding: utf-8 -*-
ActiveRecord::Base.class_eval do
  class << self
    def columns_without(except_columns = [])
      self.columns
        .map(&:name)
        .delete_if{|name| except_columns.include? name}
        .map{|name| "`#{self.to_s.tableize}`.`#{name}`"}
    end

    def columns_without_data
      columns_without(%w(data data_yml))
    end

    def select_without_data
      select(columns_without_data)
    end

    scope :without_data, -> { select_without_data }
  end
end

これでhoge.pages.without_dataという感じでスコープを使おうとすると、columns_withoutメソッドのselfがPageではなくActiveRecord::Baseを指してしまっていて、上手く動きません。

どうやらモデルの継承元で定義したスコープのselfは、継承元のクラスを指す動きになるようです。AbstractClassを作成しても、同じくAbstractClassを指す動きになっていました。

というわけで、scopeはそれぞれのモデルで定義をするか、モジュールを作ってそれぞれのモデルに直接mixinする必要がありそうです。