Ruby Method calls declared in class body

Ruby on-RailsRuby

Ruby on-Rails Problem Overview


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?

Ruby on-Rails Solutions


Solution 1 - Ruby on-Rails

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.

Solution 2 - Ruby on-Rails

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

Solution 3 - Ruby on-Rails

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.

Solution 4 - Ruby on-Rails

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

Solution 5 - Ruby on-Rails

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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAreg SarkissianView Question on Stackoverflow
Solution 1 - Ruby on-RailsChuckView Answer on Stackoverflow
Solution 2 - Ruby on-RailsJohn TopleyView Answer on Stackoverflow
Solution 3 - Ruby on-RailsLarry KView Answer on Stackoverflow
Solution 4 - Ruby on-RailsCorban BrookView Answer on Stackoverflow
Solution 5 - Ruby on-RailsCody CaughlanView Answer on Stackoverflow