I'm trying to understand a little more of the mysterious ways of rails and I have the following question.
When I am in a class e.g. a model I can call methods directly without putting the method calls inside a method on the model.
For example:
Post < ApplicationRecord
has_many :comments
end
In this case has_many is a method call but its just sitting in my Post class without being inside a method. If I try to do the same thing in a blank ruby project it doesn't work.
for example
class ParentClass
def say_hello
p "Hello from Parent Class"
end
end
class ChildClass < ParentClass
say_hello
end
child = ChildClass.new
# => undefined local variable or method `say_hello' for ChildClass:Class (NameError)
So how does Rails do it? I see it all the time I even see some gems that add these method calls, e.g. acts_as_votable
Can anyone explain whats going on?
There are two important concepts going on here - the first is that in Ruby unlike in more classical languages like Java the class body is simply a block of executable code:
class Foo
puts "I can do whatever I want here!!!"
end
class simply declares the constant (or reopens the class if it already exists) and executes the block in the context of the class being created (or reopened). This class is an instance of the Class class which we call the singleton class or eigenclass which can be a bewildering concept, but it helps if you think about that in Ruby everything is an object and classes are just objects that are blueprints for creating instances and thus can have methods and attributes of their own.
The second concept is implicit self. In Ruby method calls always have a recipient - and if you don't specify the recipient its assumed to be self. self in the class declation block is the class itself:
class Bar
puts name # Bar
# is equivalent to
puts self.name
end
Thus you can fix your example by simply defining the method as a class method instead of an instance method:
class ParentClass
def self.say_hello
p "Hello from Parent Class"
end
say_hello # Hello from Parent Class
end
has_manyis merely a class method inherited down from ActiveRecord::Base that modifies the class itself by adding methods.
class Thing < ApplicationRecord
puts method(:has_many).source_location # .../activerecord-6.0.2.1/lib/active_record/associations.rb 1370
end
Ruby has many examples of this type of meta-programming like for example the built in #attr_accessor method and they are commonly referred to as macro methods. These methods are quite simple once you get your head wrapped around the meta-programming concept:
class Foo
def self.define_magic_method(name)
define_method(name) do
"We made this method with magic!"
end
end
def self.define_magic_class_method(name)
define_singleton_method(name) do
"We made this method with magic!"
end
end
define_magic_method(:bar)
define_magic_class_method(:bar)
end
irb(main):048:0> Foo.new.bar
=> "We made this method with magic!"
irb(main):048:0> Foo.baz
=> "We made this method with magic!"
Its not really magic - its simply a class method that modifies the class itself.
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