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.
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
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)Scoping module which resulted in having module called ActiveRecord::Scoping::Named the module mentioned above 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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With