??Warning note: Please take everything on this page with a grain of salt until several experienced people have approved of it.??

h2. Background

An admonition right at the outset. Before using any of the Ruby/Rails specific techniques, please consider using common object-oriented good sense. Don't use a power tool where a screwdriver will suffice.

Rails achieves an admirable combination of power and simplicity. Much of this hides behind what looks like special-purpose declarations added to the language. Among these "declarations" are @scaffold@, @belongs_to@, and @validates_presence_of@. The remarable thing about these is that they are _not_ declarations, they are not handled by a special preprocessor or something. In fact, all of them are ordinary methods! To understand how this can be, let's bring to mind the ingredients that make the magic work.

* In programming languages like C++ and Java there's a distinction between ordinary _methods_ and _class_ _methods_. The latter can be invoked without reference to a specific object. In Ruby this distinction isn't really there behind the scenes, but nevertheless, viewed from an application development perspective, methods can be usefully divided instance-level methods and class-level methods.

* Ruby is a very dynamic language. Among other things, t supports defining new methods at runtime.

* When a Ruby source file is loaded, the statements in the file are executed. Among other things this results in classes and methods being defined. If there are method invocations contained within a class definition, the respective methods are executed in the context of the class being defined.

* Method invocations in Ruby don't necessarily have their arguments surrounded by parantheses.

Now, with this understanding in mind, ponder the following snippet from the Rails API documentation.

<pre>
<code>
class Project < ActiveRecord::Base
    belongs_to :portfolio
end
</code>
</pre>


h2. Extending Rails

With this background, how does one go about extending Rails with home-grown pseudo-declarations? For this there's one more relevant feature of Ruby: Classes are open. Meaning that, unlike in, say, Java and C++, methods and instance variables can be added to a class in multiple places and at various times. Thus, in effect, one can open up Rails classes and add new features to them. So, without much further ado, here's how this is done.

<pre>
<code>
module RailsExtension
  module SensiblyNamedModule # :nodoc:

    def self.append_features(base)
      super
      base.extend(ClassMethods)
    end

    module ClassMethods

      def enhance_class_in_some_way(param1, param2)
        # handle class context dependent stuff
        # ...
        define_method(added_method_name) do
          # handle object context dependent stuff
          # ...
          do_something(param_a, param_b)
        end
      end

    end

    private

    def do_something(param_a, param_b)
      ...
    end

  end
end
</code>
</pre>

So far, these are just some nested modules. The @ClassMethods@ module contains the method @enhance_class_in_some_way@. This method, when executed, adds a new method to the class in whose context it is executed. The private @do_something@ method is a simple method that is exemplarily extracted used by the methods defined in @enhance_class_in_some_way@.

An unusual feature of the @SensiblyNamedModule@ module is its implementation of @append_features@. This is a hook-method that's called by the Ruby runtime when a module is included in a class (or other module). In this case, when @SensiblyNamedModule@ is included in a class, the methods from @ClassMethods@ are added to the including class, thereby adding new class-level methods to that class.

Given all this and assuming the file containing your newly defined module resides in your project's @lib@ directory, you can finally add new class-level, "declaration-style" methods to your own classes like this

<pre>
<code>
require "#{RAILS_ROOT}/lib/sensibly_named_module.rb"

class MyRecord < ActiveRecord::Base
  include RailsExtension::SensiblyNamedModule
  enhance_class_in_some_way arg1, arg2
end
</code>
</pre>

This, still, is somewhat wordy. How about reducing it to the essence and hide away the scaffolding? If you want the extension to be available in all your ActiveRecord-classes, you can do it once and for all

<pre>
<code>
ActiveRecord::Base.class_eval do
  include RailsExtension::SensiblyNamedModule
end
</code>
</pre>

The best place for code such as the above is a file @config/custom_environment.rb@. Then, add the end of @config/environment.rb@ add

<pre>
<code>
require 'custom_environment.rb'
</code>
</pre>

h2. Can't this be done easier still?

Yes, it can and HowToAddCustomValidationKeywords demonstrates it. The drawback to that approach is tighter coupling. When additional features are added to modules and classes directly, you lose the option of _not_ including them.

--MichaelSchuerig

category:Howto
