While wandering lost through the strange land of Rails code I stumbled upon many lines of code. One particular bit that crossed my path was the /railties/lib/rails/generators/migration.rb code.
Having never seen “ClassMethods” module in use, I decided to investigate. It turns out that this is a design pattern prevalent in Rails. Confused I decided to investigate using code examples. First we need to understand Class and Instance methods. Class methods are methods that belong to the class and are not available to an object instantiated from the class. Instance methods are just the opposite. They are methods that belong to an instantiated object but not to the class. The following code will illustrate this point.
language/include-extend/expected.rb
Which when run gives us the following results
run expected.rb in command line
Having the basics under our belt let us take another step and talk about include and extend. Include and extend are used to include Modules into classes and other modules. Include “includes” instance methods and extends “includes” class methods. It can actually get quite a bit more complicated than this simple explanation. We will come back to this at the end.
Using include and extend we can write the following:
Using-module.rb while an interesting experiment isn’t what you will likely see. You are more likely to see the use of only a ClassMethods sub module along with included class method as shown next.
Clear as mud? Good! I highly recommend playing with this code to get a more intuitive grasp of how it works. We now proceed to how Rails does all of this with hidden magic. It does this with ActiveSupport::Concern. This next code example requires you to have the ActiveSupport gem installed.
ActiveSupport::Concern, among other things, searches for a ClassMethods sub module and extends it. This brings us to where we can understand a bit more of Rails code and the Ruby magick it employs. Finally lets look at one last piece of code that can confuse the use of extend. What happens if we call extend on an object?
The results of running this class may be somewhat suprising.
Terminal window in mysecurity directory
The confuse method defined in Diabolical::ClassMethods mocks us! Extend assigns the methods to the object which is calling it. You do remember that in Ruby classes are also objects? So if you call extend from an object it will assign the methods to the object not the class.
Below are links to sources the first of which I found to be a fantastic resource.