Howto: Write a plug-in

In my previous post, I listed 6 things that you should try in Rails. I also promised some example code to get you started. Since I have already covered installing and upgrading rails, the next cab off the rank is writing a plug-in.

Plug-ins are fantastic – they allow you to abstract away common code into nice little bundles that you can re-use on other projects. Rails even has a built-in system for downloading other peoples plug-ins straight from their SVN repository. With the change over to Rails 2.0, some used-to-be-core functionality has been moved into plug-ins, to clean up the core tree and to allow other developers to release new versions of the plug-ins outside of the regular Rails release cycles.

In this (very brief) tutorial, we will create a plug-in called acts_as_blabbermouth that will print out random quotes . Obviously this plug-in is of little use in the real world, but it should act as a nice demonstration of how plug-ins work.

It’s really easy to generate the boilerplate code thanks to the generate script. To start your plug-in, run the following command in the root of your Rails app:


script/generate plugin acts_as_blabbermouth

You should see an output similar to this:


create  vendor/plugins/acts_as_blabbermouth/lib
create  vendor/plugins/acts_as_blabbermouth/tasks
create  vendor/plugins/acts_as_blabbermouth/test
create  vendor/plugins/acts_as_blabbermouth/README
create  vendor/plugins/acts_as_blabbermouth/MIT-LICENSE
create  vendor/plugins/acts_as_blabbermouth/Rakefile
create  vendor/plugins/acts_as_blabbermouth/init.rb
create  vendor/plugins/acts_as_blabbermouth/install.rb
create  vendor/plugins/acts_as_blabbermouth/uninstall.rb
create  vendor/plugins/acts_as_blabbermouth/lib/acts_as_blabbermouth.rb
create  vendor/plugins/acts_as_blabbermouth/tasks/acts_as_blabbermouth_tasks.rake
create  vendor/plugins/acts_as_blabbermouth/test/acts_as_blabbermouth_test.rb

As you can see, all of the boilerplate code has been created in the vendor/plugins/acts_as_blabbermouth directory.

The README and MIT-LICENSE are just generic text files that you should fill out – generally, the README file is the first place a new user will look for instructions on the plug-in.

The lib directory will hold the guts of your plugiin. The tasks directory is a place where you can store any rake tasks that your plug-in might need. The tests directory is, well, pretty self explanatory – just as you can test your Rails app, you can test your plug-in too. The install.rb and uninstall.rb files are called when installing and uninstalling your plug-in – you can place code here to initialize your environment and to cleanup after yourself. Finally, init.rb is the file that Rails actually calls to load your plug-in.

The main file we have to worry about is /lib/acts_as_blabbermouth.rb – this is where our main code will go.

Firstly, we need to include the acts_as_blabbermouth.rb file into the environment. This is done by adding the following line to the init.rb file:


require File.dirname(__FILE__) + '/lib/acts_as_blabbermouth'

Next, we add the following code to lib/acts_as_blabbermouth.rb


# ActsAsBlabbermouth
module ActiveRecord #:nodoc:
  module Acts #:nodoc:
    module Blabbermouth #:nodoc:
      def self.included(base)
        base.extend(ClassMethods)
      end

      module ClassMethods
        def acts_as_blabbermouth
          include ActiveRecord::Acts::Blabbermouth::InstanceMethods
          extend ActiveRecord::Acts::Blabbermouth::SingletonMethods
        end
      end

      module SingletonMethods
        def quote_me
          quotes = [
            "When you come to a fork in the road, take it. -Yogi Berra",
            "Every child is an artist. The problem is how to remain an artist once he grows up. -Pablo Picasso",
            "What we anticipate seldom occurs; what we least expected generally happens. -Benjamin Disraeli",
            "Drive-in banks were established so most of the cars today could see their real owners. -E. Joseph Cossman",
            "The greatest pleasure in life is doing what people say you cannot do. -Walter Bagehot"
          ]

          quotes[rand(quotes.size)]
        end
      end

      module InstanceMethods
        def quote_me
          self.class.quote_me
        end
      end
    end
  end
end

ActiveRecord::Base.send(:include, ActiveRecord::Acts::Blabbermouth)


Now, create a model file called quote.rb by running


script/generate model quote


and add the following line after the class declaration and before the end declaration


acts_as_blabbermouth

Fire up the script/console and type:

Quote.quote_me

Voila! If all went to plan, you should see one of the random quotes. Congratulations! You just wrote a plug-in!

So how did we do it?

  1. We drilled down in to the ActiveRecord::Acts module and mixed in a module called Blabbermouth. This acts like a namespace in other languages, so we can create our own set of classes and methods without stomping on other peoples plug-ins.
  2. We override the included class method, which gets called when the plug-in gets mixed in to another module. Here, we include the ClassMethods module, which exposes the acts_as_blabbermouth method to the model class
  3. We define the acts_as_blabbermouth method. All this method does is include the InstanceMethods and SingletonMethods modules. The InstanceMethods module contains all of the methods available on an instantiated object and SingletonMethods contains all the methods available to the un-instantiated class.
  4. We create a SingletonMethod called quote_me, which returns the random quote. This can be called by calling Quote.quote_me. We also create a method called quote_me in the InstanceMethods module, which calls the SingletonMethod – this way both the class and the object can call the quote_me method.
  5. Finally, we call ActiveRecord::Base.send(:include, ActiveRecord::Acts::Blabbermouth) which tells the ActiveRecord::Base module to include the code we have written.

For those of you playing at home, I’ve attached the plug-in source in a tarball, so you can get a better idea of how it all fits together. So off you go, go and create a plug-in yourself!

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • seema

    really a knowledeful blog

  • Jeff

    How do I test this with local rails app? In other words, how do I use script/plugin install with a local plugin instead of a url?

    Thanks

  • madpilot

    Hi Jeff,

    Using script/plugin install doesn’t really make much sense on a local filesystem. Just copying the plugin into the vendors/plugin directory will be sufficient.

    Of course, if your plugin is under source control, you can easily point script/plugin at your own SVN repository.

  • Chirantan

    Great work… Was helpful! Thanks
    Chirantan

  • Ramesh

    Really worthfull ! Thanks a lot :)

  • Victor Costan

    This is wrong information. Installing a plugin will cause install.rb to run in a Rails environment.

    Why would you want to install a plugin from your local filesystem? So you can test install.rb.

    The quickest ways for me to get this done were

    serve the plugin from a web server
    make a git branch and push changes there

    Hope this helps.