Reputation: 833
I have just started learning ruby on rails and I have encountered code like below:
class Post < ActiveRecord::Base
validates_presence_of :title
belongs_to :user
end
There are two method calls inside the class body. I have had a hard time finding any ruby documentation that describes how method calls from within a body of a class (but outside of any method) work. All the books I have, only describe how to define class and instance methods and how to call them from within other methods.
The questions I have are: How and when are these methods called? How are they defined? Are they mixins defined in some active record module?
Upvotes: 77
Views: 10777
Reputation: 49104
Re: How and when are these methods called?
They're called when the class is loaded. You can put a breakpoint in one of the methods and see that it is called as part of the startup of your rails project.
How are they defined?
They're class methods. Since this is ruby, they could be defined in a number of ways.
Are they mixins defined in some active record module?
In this case, validates_presence_of
is defined in vendor/rails/activerecord/lib/active_record/validations.rb
and belongs_to
is defined in
vendor/rails/activerecord/lib/active_record/associations.rb
. ActiveRecord
is a big system, includes many mixins, modules, etc.
Note, to see where the methods are defined, I use http://www.gotapi.com/rubyrails for each method, see the "Show Source" link at the bottom of the definition.
Upvotes: 14
Reputation: 21378
These are class methods or 'singleton' methods. One you should be familiar with is attr_accessor. We can implement something like it in a test class.
class Klass
def self.add_getter_and_setter(symbol)
module_eval "def #{symbol}; @#{symbol}; end"
module_eval "def #{symbol}=(val); @#{symbol} = val; end"
end
end
class Person < Klass
add_getter_and_setter :name
add_getter_and_setter :phone
end
person = Person.new
person.name = 'John Smith'
person.phone = '555-2344'
person # returns <Person:0x28744 @name="John Smith", @phone="555-2344">
In the above example we created the class method with 'def self.add_getter_and_setter' but this is not the only way.
class Klass
class << self # opens the singleton class
def add_getter_and_setter(symbol) # note we dont specify self as it is already within the context of the singleton class
..
end
end
end
Using extend. Module#extend is a method that extends a class with class methods likewise the method Module#include includes a class with instance methods.
class Klass
extend(Module.new do
def add_getter_and_setter(symbol)
..
end
end)
end
If Klass has already been defined we can reopen it to add class methods
class Klass
end
def Klass.add_getter_and_setter(symbol)
..
end
# or
class << Klass
def add_getter_and_setter(symbol)
..
end
end
Well those are a few ways I know how to do this so if you see different syntax just realize its all doing the same thing.
Note: in rails a common class method we all use is 'find'. It is run directly off the Model class.
person = Person.find(1) # finds a person with id:1
Upvotes: 13
Reputation: 115342
Yehuda Katz has a nice explanation of this on his blog. See point 4: Class Bodies Aren't Special.
Upvotes: 20
Reputation: 237040
The body of a class definition is an execution context for code just like any other. Code there is executed within the context of the class (meaning self
is the class object, which is an instance of Class). You can have locals and instance variables (which will belong to the class object itself rather than instances of the class) and you can call any method that class object responds to. The code is run once the class definition block is finished.
In this case, ActiveRecord::Base defines the class methods validates_presence_of
and belongs_to
.
Upvotes: 39
Reputation: 32748
What you are seeing are class level methods for an ActiveRecord object. To write your own methods that perform like that you would write them as a plugin and then include them into ActiveRecord by re-opening the class definition. The Ruby on Rails guide to creating a plugin:
http://guides.rubyonrails.org/plugins.html
Covers how one would write such a plugin / class-level methods. Its a good document on how to wrap your head around what those kinds of methods mean and how they interact with instances.
Upvotes: 3