Larrikinism

It is the Confession, not the Priest that gives us absolution

Archive | RSS
October 4, 2010 at 6:13am
Home

Stuff I learnt writing Rubycious

Day before yesterday, I released my del.icio.us API wrapper gem rubycious. There are a couple of things I learnt while writing it that I’d like to share here. To give you a heads up on what’s inside:

  1. Meta-Programming
  2. class_eval
  3. self.included
  4. method_missing
  5. Documentation with YARD
  6. Publishing a gem

If that’s piqued your curiousity click on through.

Meta-Programming

Before I go any further, I must warn you that some of the choices I’d made, design-wise weren’t exactly the smartest and I know that. But given those choices, I was faced with certain problems that had to be solved and I’m glad that it led me to learn about some cool Ruby meta-programming.

class_eval

This is a class method which any object of type Class has(by that I mean, of course, any ruby Class (Ain’t ruby grand!)). It takes a block and dynamically puts the contents of that block inside your class. Now this is only useful if you have to dynamically add stuff to your class. One oft-used use case of this facility is in conjunction with self.included. Trivially though the following should illustrate what class_eval can do

          class A
           
def do_something
              something
           
end
         
end
         
          A
.class_eval do
           
def something
              puts
"Hello World!"
           
end
         
end

Now there are a couple of other methods in the eval family, namely, instance_eval(1)(2), module_eval(just an alias for class_eval) and just eval(1). They’re equally cool and useful and to pretty much the same thing so I’ll leave it to you to discover more about them.

self.included

This is basically a call_back defined for any module. When it gets included in a class this method is run. The reason I’m calling it self.included is usually its usage goes something like this:

          module B
           
def self.included(base)
              puts
base.class
           
end
         
end

However the really interesting use case, like I mentioned earlier comes up when I put it together with class_eval like I did in Rubycious here. The module ClientHelper was meant to be included in any class that was a client and I didn’t want to redefine some of the httparty declarations in each of these classes.

method_missing

This one is pretty basic and chances are you probably know about this one. If not you really should. Basically method_missing is the method called when a method called on an object is not found. It takes a symbol which contains the name of the missing method and and array of the arcuments passed to that method. The great thing about method missing is that it can be used to generate an arbitrary number of methods. See more here

Documentation with YARD

YARD has emerged as the standard for documenting ruby projects of late, taking over from rdoc, and after using it to document rubycious, I can see why. YARD uses a tagging based approach to documentation. It uses directives like @param, @return, @see, @author etc. to structure your documentation and give nicely readable docs in a variety of formats. Read more about why you should use YARD here. Here is an example of code documented with YARD from rubycious:

                # Use for searching all URLs
               
# NOTE: Use Sparingly. Call the update function to see if you need to fetch this at all.
               
# @todo Look into caching these results
               
# check the last update time before performing
               
# @param [Hash] options for searching for the URL (all optional)
               
# @option options [String] :tag |optional| "ruby"
               
# @option options [String] :start |optional| Integer: Start returning posts this
               
# many results into the set
               
# @option options [String] :results |optional| Integer: Return these many results
               
# @option options [String] :fromdt |optional| Format: Time#iso8601: On this date or later
               
# @option options [String] :todt |optional| Format: Time#iso8601: On this date or earlier
               
# @option options [String] :meta |optional| yes/no: Include change detection signatures
               
# on each item in a 'meta' attribute. Clients wishing to maintain a
               
# synchronized local store of bookmarks should retain the
               
# value of this attribute - its value will change when any
               
# significant field of the bookmark changes.
               
# @return [Array<:post>]
               
# @see Time#iso8601
               
def find(options)
                  response
= handle_errors { self.class.get('/all', :query => options)}
                  response
["posts"]["post"].collect{|i| Rubycious::Post.new(i)}
               
end

See more here

Publishing a Gem

I used echoe for packaging and releasing the gem. Basically echoe creates a bunch of rake tasks for you that take you through the process of creating a gem. So, first you need to define a couple of defaults in your Rakefile:

          require 'rubygems'
         
require 'rake'
         
require 'echoe'
         
         
Echoe.new('rubycious', '0.1.1') do |p|
            p
.description = "Ruby wrapper to the del.icio.us API"
            p
.url = "http://github.com/rjsvlajean/rubycious"
            p
.author = "Ratan Sebastian"
            p
.email = "rjsvaljean@gmail.com"
            p
.ignore_pattern = ["tmp/*", "script/*"]
            p
.development_dependencies = ["httparty"]
         
end
         
         
Dir["/tmp/tasks/*.rake"].sort.each { |ext| load ext } e

Next assuming that all your code is in your lib directory and you have a file having the same name as your gem in there, run:

          $ rake manifest
          $ rake build_gemspec
          $ gem build rubycious
-0.1.1.gemspec
          $ gem push rubycious
-0.1.1.gem

And that’s about it for now. I plan on doing a rewrite with a bit of a better design soon but for now Rubycious is pretty stable for those using basic auth. I’ll add oauth support soon in a couple of days and I’ll document the procedure here.