Ruby 101: Make your class behave like a Ruby built-in

I got re-acquianted with this scenario while working on the OpenAmplify gem – a wrapper for the OpenAmplify API. When you give the api a text like a blog comment, it will return a list of common terms, opinion scores, named locations, and other information that can be used for text mining operations.

The OpenAmplify returns key-value pairs in an XML string by default, but it can also be in JSON, CSV, or RDF format. From a Ruby client’s point of view, we want it in Hash. You can choose to use an XML library like Nokogiri but in my opinion, working with a Hash fits nicely with Ruby.

Anyway, back to the problem. I have an instance variable that holds the data. One approach is to give clients access to the instance variable.

  class Response
    attr_reader :data
  
    def initialize
      @data = {}
    end
  end
  
  data = response.data
  topics = data[‘Topics’]

One major issue with this approach is you’re exposing the internals of your class. What if you decided to rename the variable into ‘@results_in_hash_form’? Then, all programs that uses your code will break. Worse, you will be limited from enhancing the behavior of your class like lazy loading of the data. You can wrap the access to your data inside a method but that still presents the problem of exposing the internals of your class. Also, that’s an unnecessary extra line of code🙂

My suggestion is to make ‘Response’ behave like a Hash so we can do these:

  topics = response['Topics']
  response.has_key?('Topics')
  
  # And still have our own methods:
  response.some_method_we_defined

So, how can we do this? The trick is to delegate the calls to the instance variable. One approach is to define the Hash methods you want to support:

  class Response
  
    [‘[]’, ‘has_key?’, ‘fetch’, ‘empty?’, ‘keys’].each do |method_name|
      class_eval <<-EOS
        def #{method}(*args)
           @data.send("#{method_name}", *args)
        end
      EOS
    end
  
  end

The code above is a shortcut to writing every method by hand. If you want to support all Hash methods, that would be a lot of typing.

A better approach is to just take advantage of Ruby’s ‘method_missing’ which is called every time an undefined method is called.

  class Response
  
    def method_missing(name, *args, &block)
      @data.send(name, *args, &block)
    end
  
  end

Of course, how your ‘method_missing’ will look like depends on your requirements. In our simple case, we can simply delegate to @data.

This approach is called a “Dynamic Proxy” from the book Metaprogramming Ruby by Paolo Perrota. If you want to take your Ruby skills to the next level, I highly recommend this book.

One thought on “Ruby 101: Make your class behave like a Ruby built-in

  1. Change the Way You Eat: To make sure you reduce the quantity of food
    you eat, make it a habit to drink water and eat snacks that are healthy all through the day so that your
    rate of intake is not increased at meal time. Because
    the Paleo low carb diet is more satiating per calorie than other diets, you won’t struggle with hunger while shedding pounds quickly and safely.
    It is possible to also substitute an apple with the banana.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s