Rob Olson's blog



Rob OlsonRob Olson
Second thoughts on initializing modules
edit Posted by Rob Olson on Monday February 15, 2010 at 03:35PM

This morning Yehuda Katz posted a response to my previous post, Technique for extending a method from a module, showing how a more modular organization of the Person class would allow for a solution that does not require a crazy meta programming hack. The idea is that by extracting the method we want to decorate into an ancestor class, Ruby makes it a lot easier to do what we want.

Previously I was aware that there were other ways I could structure the host class to make the module's job easier but I did not try that because but I was writing the code with the knowledge that I would only be in control of one side of the equation, the module. The host class was going to be written by the end-user of the Rubygem the module was to be packaged in. Since I did not want to try dictate how the end-user structured the host class I ended up adding a lot of complexity to the module. The goal became how to write the module in a such a way that the class would "just work" upon including Teacher without requiring any additional steps to be taken. Asking the user to create an AbstractPerson class that contained their initialize method and then creating a subclass felt like an obtrusive request to make through a README that would ultimate negatively impact the user's experience with the library.

Shortly after I put that blog post up I got this tweet from Josh Susser:

Rob OlsonRob Olson
Technique for extending a method from a module
edit Posted by Rob Olson on Sunday February 14, 2010 at 12:17AM
Update: Read the follow-up post Second thoughts on initializing modules

I was recently presented the problem of appending to the initialize method from a module that was being included. To do this it would need to override the class's initialize method with my own but keep the functionality of the original initialize method.

Whenever I need to do something in Ruby that I know will require some experimentation I like to move outside of my application and reproduce the problem in a simple way. For this problem I created a Person class that mixes in a Teacher module.

module Teacher
  def initialize
    puts "initializing teacher"
  end
end

class Person
  include Teacher

  def initialize
    puts "initializing person"
  end
end

The goal is to get the following output when a Person object is created:

> Person.new
initializing teacher
initializing person

The basic program fails as expected; Teacher.new prints "initializing person" because Person's initialize is trumping Teacher's. Our immediate goal is to replace Person's initialize with Teacher's but in a way that preserves the original initialize method. By using alias_method we can create a copy of the original initialize method that we can call later.

Rob OlsonRob Olson
Boosting with Acts As Solr
edit Posted by Rob Olson on Monday August 10, 2009 at 09:40PM

Probably my favorite feature of the Solr full text search engine and the acts_as_solr plugin is a feature called boosting. Boosting is a great tool that gives you the ability to wield some influence over how the results that are returned are going to be ordered. When boosting is applied properly the quality of the search results appears improve dramatically even though the same results are being returned, just in a different order. There are two different kinds of boosting that you need to be aware of: column boosting and document boosting.

Field Boosting

Field or column boosting allows you to specify that if a query matches on a boosted field, give that more weight than usual. In the app I am working on, I added a field boost to the name attribute because I want results that have the query string in the name to appear before those results that have it somewhere in their description or as a tag. Here is an example of how to do a field boost when using acts_as_solr.

acts_as_solr :fields => [{:name => {:boost => 3.0}}, :description, :tags]

Document Boosting

A document boost should be utilized there is a way of quantifying one result as being better than another result, regardless of the query. For example, there are two entries in my database that both have a tag of "twitter client": Twitterific and Twitterfon. In the iPhone App Store, Twitterfon has a higher popularity rating than Twitterific so I want Twitterfon to appear above Twitterific if someone searches for "twitter client" within the app. To specify document boosting based on the app store popularity field I can pass a Proc object to acts_as_solr (rdoc) and return the member field that holds the popularity rating. A great thing about the Proc object is that I can execute any ruby code inside of it that I want. This is useful if the popularity score is not directly stored in the database and must be calculated on the fly.

acts_as_solr :fields => [:name, :description, :tags],
           :boost => Proc.new { |item| item.popularity_score.to_f }

Closing

If you are using Solr at all it is important to be aware of what boosting can accomplish. When using multiple boosts, finding the right boost values to produce the best search results is a bit of black magic. I have found that after achieving "pretty good" results the law of diminishing returns comes into play and slows down progress. With a single boost it is much easier because there is only one variable in play.

Rob OlsonRob Olson
"Missing host to link to!" Rails 2.3 Upgrade Issue
edit Posted by Rob Olson on Monday August 03, 2009 at 09:52PM

During the process of upgrading a project from Rails 2.2.2 to Rails 2.3.2 several of our tests were breaking with the error:

Missing host to link to! Please provide :host parameter or set default_url_options[:host]

This error was most commonly occurring in model specs where we had mixed in ActionController::UrlWriter in order to get access the named routes (e.g. invitation_path) inside of the model class. I believe this change in a behavior is the result of this patch to Rails but I am not certain. Interestingly the code falls apart in the tests but it still works fine within the browser.

With the assistance of Adam Milligan we were able to find an acceptable way to handle setting the default_url_options in the test environment.

# app/models/invitation.rb
class Invitation < ActiveRecord::Base
  include ActionController::UrlWriter

  ...
end

# spec/models/invitation_spec.rb
describe "Invitation" do
  before(:all) do
    Invitation.default_url_options[:host] = 'localhost'
  end
  after(:all) do
    Invitation.default_url_options[:host] = nil
  end
  ...
end

As I wrap up I want take a moment a properly shame myself for generating urls in the model. There is definitely a good argument that you should not be using named_routes in your models and I am eager to agree. Rails makes it hard to do for a reason and if you find yourself ever explicitly including UrlWriter take a step back and think the problem over. You may find yourself needlessly going down the wrong path and a different approach is in order.

Rob OlsonRob Olson
Take a Looksee at how a Ruby Object got its Methods
edit Posted by Rob Olson on Monday July 27, 2009 at 12:10AM

One property of the Ruby object model and object oriented programming in general is that a subclass of an object automatically inherits all of the methods of its superclass. Classes can further expand the number of methods available by mixing in a Module, or several.

Because of mixins and subclassing even a class that has declared just a few methods can actually have hundreds of methods on it. In Ruby, all classes subclass Object by default which declares a hefty 45 methods, guaranteeing you to have at least that many. Out of the box in 1.8.7, a Ruby String object has 176 instance methods. If you are programming on top of the Rails framework, ActiveSupport adds 98 methods bringing the total to 274!

On numerous occasions I have needed to see what methods are available on an object I am working with I will type the following in irb.

myobject.methods - Object.instance_methods

This prints out a large array of instance methods with the methods inherited from Object removed from the list. This is useful but what if the object I am working with mixed in several modules and I am left with a list of over a hundred methods? It would be great to view which Class or Module each method came from. Well, actually there's a gem for that.™

Looksee

Looksee is a new gem by George Ogata that examines the method lookup path of any object. To use it add require 'looksee/shortcuts' to your ~/.irbrc. This will add a lp ("lookup path") method to your irb environment. When passed an object lp prints out a colored display showing where each of an object's methods lives.

Rob OlsonRob Olson
Standup 07/24/2009: Corrupt Fixture Files
edit Posted by Rob Olson on Friday July 24, 2009 at 12:43PM

Ask for Help

"We are attempting to upgrade one of our projects using Fixture Scenarios to Rails 2.3.2. When we attempt to run our tests we get errors about a corrupt fixture file. Is anyone successfully using Fixture Scenarios with Rails 2.3?"

Interesting Things

It is really easy to declare an additional route just for use in a controller test. All that is needed is to recall ActionController::Routing::Routes.draw at the top of your spec file. One situation in which this can be useful is if you are creating a new controller just for testing purposes.

class DummiesController < ApplicationController
  before_filter :require_profile

  def index
  end
end

ActionController::Routing::Routes.draw do |map|
  map.resources :dummies
end

describe DummiesController do
  ...
end

Rob OlsonRob Olson
Standup 07/23/2009: Timeouts with AWS-S3
edit Posted by Rob Olson on Thursday July 23, 2009 at 09:30AM

Ask for Help

"When attempting to upload files with the aws-s3 gem I am receiving a lot of timeouts. This seems to happen with both small and large files. Has anyone run into this before?"

It was hypothesized that this could be the result of a slow internet connection and saturating the upload stream. Does anyone know of a fix for s3 timeouts?

Rob OlsonRob Olson
Standup 07/22/2009: Temporarily Redefine a Method
edit Posted by Rob Olson on Wednesday July 22, 2009 at 10:15AM

Ask for Help

"Is there a good way to temporarily redefine a method on a controller during a functional test?"

Reopening a controller and overriding a method affects all tests in a suite. Is there a good way to redefine a controller method for a single test?

Rob OlsonRob Olson
Standup 07/21/2009: Rails 2.3.3 is out
edit Posted by Rob Olson on Tuesday July 21, 2009 at 06:17PM

Interesting Things

  • Rails 2.3.3 was released yesterday. It is a minor point release but a notable new feature is a faster decoding backend for JSON.
  • The Evening with Palm webOS event is tonight at 6:30!
  • There is a new mailing list for the Jasmine Javascript testing framework.
  • Braid gotcha be careful when using Braid to checkout a specific branch of a Git repository. If you checkout a repository with Braid, and then later decide that you want to switch to a different branch (i.e. going from master to 2-3-stable with Rails) doing a braid remove vendor/rails is not sufficient! The reason is when you add a external with Braid it also adds a remote branch in your Git repository. If you re-add the external, the old remote will be reused, even if you specify a different branch. To avoid this, remove the remote in addition to removing the external. To view your remotes run git remote and remove a remote with git remove rm some/remote/name.

Weirdness with using serialized with Single Table Inheritance in Rails

If you have a class that uses a serialized categories attribute like this:

class Wibble < ActiveRecord::Base
  serialized :categories
end

Rob OlsonRob Olson
Standup 07/20/2009: render_template bug in RSpec
edit Posted by Rob Olson on Monday July 20, 2009 at 01:54PM

Interesting Things

Prior to RSpec 1.2.7, render_template in rspec-rails had a bug where render_template('new') would pass if 'newer' was rendered (or anything that started with 'new'). Internally render_template was converting the string argument to a regular expression which was allowing 'new' to positively match 'newer' even though it was not an exact match. In RSpec-Rails 1.2.7 this bug has been fixed.

Other articles: