Andrew
Andrew

Reputation: 1013

Why is this class which inherits from ActiveRecord::Base incapable of class_eval ing scope

Note: I know what I'm doing here is probably stupid but I'm not even necessarily going to use this code I'm just curious at this point.

I'm trying to dynamically add an empty Rails scope to a class in the event a specific method is missing on the class

This documentation in particular seems to suggest this should be possible: https://api.rubyonrails.org/classes/ActiveSupport/Concern.html

module Mod
  extend ActiveSupport::Concern

  class_methods do
    def method_missing(m, *args, &block)
      if m.match? /method/
        klass = class << self; self; end

        klass.class_eval { scope :method, -> {} }
      end
    end
  end
end

klass is correctly set to the class here however trying to eval the creation of the scope fails with undefined method 'scope' for #<Class:0x0000560e35d2eb48> however when I call klass.ancestors I can confirm that klass is inheriting from ActiveRecord::Base and should have scope.

Any ideas why I can't class_eval scope here

Upvotes: 1

Views: 121

Answers (1)

Lam Phan
Lam Phan

Reputation: 3811

Assume that there's a class Person < ActiveRecord::Base that will include your module Mod. In Ruby, inheritant hierarchy look like below (the horizontal superclass chain):

  Class                   Class
    |                       |
  Person -superclass->  ActiveRecord::Base

So that Person.class will not return ActiveRecord::Base but Class:Person (the vertical chain above), that mean your code kclass = class << self; self; end actually return Person.class which is Class:Person - a Class object that has not anything relative to ActiveRecord::Base - so it'll not respond_to? :scope

module Mod
  extend ActiveSupport::Concern

  class_methods do
    # override Person#method_missing
    def method_missing(m, *args, &block)
      # anytime call Person.a_method_that_not_be_defined
      puts self # Person
      kclass = class << self; self; end # <--- Class:Person

So in this case, you should use self.class_eval instead of kclass.class_eval.

Upvotes: 1

Related Questions