How is method being called outside of Module

Viewed 98

I'm coming from the PHP world and I'm specifically looking at the Act As Votable gem but it may apply to anything in Ruby. I don't understand that you can add acts_as_votable method to your class when it is in a Module.

class Post < ActiveRecord::Base
  acts_as_votable
end

I would expect ActsAsVotable::Extenders::Votable::acts_as_votable. What allows this to be run without calling the modules?

4 Answers

at act_as_votable.rb is where the magic happens. at line 13 you can see ActiveRecord::Base.extend ActsAsVotable::Extenders::Votable. extend method here will inject ActsAsVotable::Extenders::Votable methods into ActiveRecord::Base class methods.

since Post inherits from ActiveRecord::Base which has ActsAsVotable module methods injected as class methods, Post can call acts_as_votable directly.

When you first add acts_as_votable gem it extends ActiveRecord::Base with methods defined in ActsAsVotable::Extenders::Votable module, its pretty common pattern to use modules as mixins mixins.

This initialization is done with this line:

ActiveRecord::Base.extend ActsAsVotable::Extenders::Votable

in lib/acts_as_votable.rb file

in other words methods inside ActsAsVotable::Extenders::Votable will be available as class methods to ActiveRecord::Base along with its inheritance chain.

the acts_as_votable in the post model is the execution of the method defined in the Votable module, which usually called a macro. It's a class method that defines another instance methods in that model. It has the same idea behind the has_many, belongs_to, .... macros.

macros

extend

What allows this to be run without calling the modules?

It works because:

  • Ruby uses self if you don't specify an explicit receiver
  • the class body is evaluated in the context of your class (self is Post)
  • your Post class inherits from ActiveRecord::Base
  • the gem adds acts_as_votable as a class method to ActiveRecord::Base

When you write:

class Post < ActiveRecord::Base
  acts_as_votable
end

it basically means:

class Post < ActiveRecord::Base
  self.acts_as_votable
end

which is actually:

class Post < ActiveRecord::Base
  Post.acts_as_votable
end

which could be rewritten as:

class Post < ActiveRecord::Base
end

Post.acts_as_votable

The above isn't specific to the gem. Within the class body, you can call any of your class' class methods directly. You might know:

class Foo
  attr_accessor :bar
end

That's not some special keyword – attr_accessor is a regular (class) method. The same applies to all the Rails DSL methods like has_many, validates or after_save.

Related