Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How rails(ActiveRecord) defines the class method on models and how to remove one of them if needed

I am trying to figure this out on my journey to rails and ruby's deeper understanding.

What i know is that we can check if a method is defined in a ruby class by calling: method_defined? on the object. As all classes are also objects of class Class we can do same for class methods. For example if class Foo defines bar class method, this is what happens:

Foo.method_defined? :bar #-> true

But when applying same on models which are inherited from ActiveRecord::Base(directly or indirectly). this results in:

User.method_defined? :all #-> false
User.method_defined? :count #-> false

I can see the all method defined here, i am struggling to match the dots and make sense of whats going on. And how these methods work on models when they are not implemented as class methods neither is there any funky business of method_missing is going on(as it seems).

While on it, if i can get same explanation for instance methods which rails adds for model objects, like name method in User.first.name(assuming user table has name column). Would be a plus.

Lastly, some word on how to remove one of these methods if we ever need to.

EXTRA: If i can also get to know how to reset the User class to have the method defined again after removing, like if i remove count method with the suggestion from comments: User.instance_eval { undef :count } i also want to be able to redefine this back. Or kind of reset the User class. load 'app/models/user.rb' does not do the job here.

Update: I figured out how to reset the class after undefining a method in the eigenclass of model User before doing load 'app/models/user.rb' i had to explicitly do Object.send(:remove_const, :User) so ruby removes the class entirely than do the load thing.
Still struggling to digest all this and especially how the rails implementation works.

like image 491
Zia Ul Rehman Mughal Avatar asked Dec 09 '25 16:12

Zia Ul Rehman Mughal


1 Answers

No magic here

module X
  def test
  end
end

class A
  extend X
end

class B
  include X
end
A.method_defined? :test #false  
B.method_defined? :test #true 

so it's not defined because it's a class method and class methods are defined in the singleton class. method_defined? check if the method is defined in the class or its ancestors only.

B.ancestors #[B, X, Object, Kernel, BasicObject]
A.ancestors #[A, Object, Kernel, BasicObject]

so simply because it's a class method
UPDATE: Adding more trace to How all is defined

  • the method all is defined as mentioned in https://github.com/rails/rails/blob/b9ca94caea2ca6a6cc09abaffaad67b447134079/activerecord/lib/active_record/scoping/named.rb this module extends ActiveSupport::Concern which mean if you included this module the methods in ClassMethods will be added as class methods to the includer (more about this https://api.rubyonrails.org/classes/ActiveSupport/Concern.html)
  • in the active record entry point here https://github.com/rails/rails/blob/b9ca94caea2ca6a6cc09abaffaad67b447134079/activerecord/lib/active_record.rb#L151 the module Named is autoloaded inside Scoping module which resulted in having module called ActiveRecord::Scoping::Named the module mentioned above
  • here in the base class, the Scoping module is included https://github.com/rails/rails/blob/b9ca94caea2ca6a6cc09abaffaad67b447134079/activerecord/lib/active_record/base.rb#L298 which define all as class method

so it's similar to the simple code above but using some of ActiveSupport magic like autoloading , egarloading and concerns.

like image 144
M.Elkady Avatar answered Dec 12 '25 11:12

M.Elkady



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!