views:

281

answers:

5

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?

+3  A: 

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.

Cody Caughlan
A gem also works as well.
Ryan Bigg
+4  A: 

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

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.

]

Larry K
+10  A: 

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.

Chuck
+4  A: 

Yehuda Katz has a nice explanation of this on his blog. See point 4: Class Bodies Aren't Special.

John Topley
+1  A: 

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
Corban Brook