Monday, July 26, 2010

Writing methods which write methods

http://www.raulparolari.com/Ruby2/define_method - Here is a nice writeup showing how to use Ruby's define_method feature.

Sunday, July 25, 2010

Suggestion for Community Service Hours - Curating a Community Calendar

http://elmcity.cloudapp.net/ - This is an interesting website which allows you to provide calendar events in several different formats. A great community project would be to aggregate school events in a locality.

Sunday, July 11, 2010

Using ImageMagick To Create Horizontally Repeating Background Image

  1. Find an image that you want to slice vertically, which I'll refer to as the base image. This slice is what will be repeated across the background of the web page.
  2. Use the identify home_page.jpg command to find out some details about the base image. The result of this command looks like this: home_page.jpg JPEG 1300x1380 1300x1380+0+0 8-bit DirectClass 736KiB 0.240u 0:00.240
  3. For our purposes, the first set of numbers is sufficient. They hold the width and height.
  4. The next step involves extracting a vertical slice (top to bottom) from the base image. Use this command: convert -extract 10x1380+0+0 home_page.jpg body_background.png. Notice that you can vary the width of the slice using the first number. In this case, I use 10 but some other number is probably more appropriate for you.
  5. If you want to test various widths, you can 'tail' the background image using display -update 1 body_background.png which redisplays the image every second.
  6. Once you've got the vertical slice, you need to integrate it with your web page.
  7. Use CSS similar to this:
    body {
      background-image: url('body_background.png');
      background-repeat: repeat-x;
    }

Thursday, July 01, 2010

Using module_eval to Define Instance Methods in a Ruby Gem to Enable Per-Model Configuration

In the last post, I mentioned that I wrote a small gem to post changes to ActiveRecord models to Twitter. In that version, the configuration was handled by a YAML file. I wanted to evolve the gem so that developers could switch Twitter accounts for each model.

Basically, I wanted to able to the use the following:

class Place < ActiveRecord::Base
  alastrina :twitter => { :username => 'alastrina_gem', :password => 'QQQQQQ' }
end

After a bit of tinkering and searching, I decided to use the following code in my gem.

ALASTRINA_CONFIGURATION_FILE = 'config/alastrina.yml'

module Alastrina
  def self.included(base)
    base.extend(ClassMethods)
  end
  
  module ClassMethods
    def alastrina hash
      module_eval do
        def configuration
          throw "Missing #{ALASTRINA_CONFIGURATION_FILE}" unless File.exists? ALASTRINA_CONFIGURATION_FILE 
          @config ||= YAML::load(File.read(ALASTRINA_CONFIGURATION_FILE))
        end
        if hash[:twitter]
          def send_to_twitter?
            true
          end
          eval"def twitter_username\n\"#{hash[:twitter][:username]}\"\nend\n"
          eval "def twitter_password\n\"#{hash[:twitter][:password]}\"\nend\n"
        else
          def send_to_twitter?
            @twitter_flag ||= !configuration['twitter'].blank?
          end
          def twitter_username
            @twitter_username ||= configuration['twitter']['username'] if send_to_twitter?
          end
          def twitter_password
            @twitter_password ||= configuration['twitter']['password'] if send_to_twitter?
          end
        end
      end
    end
  end

  def after_save
    if send_to_twitter?
      require 'twitter' 
      throw "Missing Twitter userid" if twitter_username.blank? 
      throw "Missing Twitter password" if twitter_password.blank?
      send_via_twitter
    end
  end

private

  def send_via_twitter
    if changes.size > 0
      httpauth = Twitter::HTTPAuth.new(twitter_username, twitter_password)
      client = Twitter::Base.new(httpauth)
      begin
        client.update(changes.to_yaml)
      rescue
        RAILS_DEFAULT_LOGGER.error "alastrina.send_via_twitter; Unable to send change. Message[#{$!}] Change[#{changes.to_yaml}]"
      end
    end
  end
  
end

ActiveRecord::Base.class_eval { include Alastrina }

The key insight to this code is how the hash passed on the alastrina of the Model is passed down into the instance. The code is fairly straightforward once you see what the eval is doing.