Close and Go BackBack to Viget

Tools of the Trade

Announcing spect, a helping library for Test::Unit

Clinton R. Nixon
Clinton R. Nixon, Senior Developer, May 22, 2008 3

We've tried out a lot of test frameworks for Ruby here at the Lab. I have been a big fan of test/spec in the past, and like shoulda's nested contexts. test/spec's wrapped assertions - like user.should.not.be.nil instead of assert_not_nil user - are a particular feature I dig.

I wanted to use these with shoulda, and started to extend shoulda to have its own set of wrapped assertions, but when I looked in the test/spec code, I saw this beauty:

module Test::Spec
  class Should
    ...
    def add_assertion
      $TEST_SPEC_TESTCASE && $TEST_SPEC_TESTCASE.__send__(:add_assertion)
    end
    ...
  end

  class TestCase
  ...
    module InstanceMethods
      def setup                 # :nodoc:
        $TEST_SPEC_TESTCASE = self
        super
        call_methods_including_parents(:setups)
      end
    ...
    end
  ...
  end
end

I don't want to put down a piece of code I've used a lot and enjoyed, and I think this is a practical way for test/spec to handle increasing the assertion count for its wrapped assertions, since they all start with a method on Object. I, however, couldn't bring myself to use a global variable to handle this task. I thought for a while, and after being inspired by JDave, I decided to start with a method on Test::Unit::TestCase.

Announcing spect

I released my test library today as a Ruby gem called spect. It's very simple to use. Once you require spect, you can use the method expect in Test::Unit::TestCase or any derived classes, like a shoulda should block or a test/spec specify block. expect sets up expectations like the following:

expect(user).responds_to admin?
expect(user).is.an.admin
expect(user.name).to.not.match /mr_jenkins/
expect(user).to.be.equal User.find(:first)
expect(user).is.not.not.not.not user
expect(ZeroDivisionError).raised_by do
  user.id / 0
end

Most of the Test::Unit assertions are wrapped, with the following mappings:

  • assert_equal is equal
  • assert_in_delta is close_to
  • assert_match is match
  • assert_nil is nil
  • assert_respond_to is respond_to
  • assert_same is same
  • assert_raise is raised_by
  • assert_throws is thrown_by

In addition, all predicates on an object can be tested for truth by calling them per normal (admin?) or by dropping the question mark (admin.)

There are several methods used solely for grammatical purposes. to, an, and a are no-ops and just return self. is and be are no-ops unless they are passed an argument, in which case they assert equality between the original object and this argument. not reverses assertions, but also can be passed an argument to assert inequality.

I am sure there are a lot of improvements to make. I thought of one writing this blog post (equal_to should be an alias for equal.) I invite you to check out the code at our GitHub repository and submit patches.

From TextMate to ExpressionEngine

Patrick Reagan
Patrick Reagan, Development Director, May 01, 2008 10

Between writing code, documentation, and blog posts, I spend a lot of my time working in TextMate. It's an efficient text editor, but it becomes a bit of a headache when it's time to work on a new blog post. The real problem is my process:

  1. Open TextMate and start collecting my thoughts for a post in some cohesive fashion (optional)
  2. Finalize most of the content and basic formatting
  3. Copy the content into ExpressionEngine, tweak the formatting, add hyperlinks
  4. Preview the post and publish to the site

The reason for all this masochism is my dislike for editing content in a web-based form, WYSIWYG or otherwise. I love my editor enough that I'm willing to jump through some hoops in order to use it for the bulk of my editing work. But, there's a better way to do all this.

Continue reading "From TextMate to ExpressionEngine"

I Have a Pull Request on GitHub, Now What?

Patrick Reagan
Patrick Reagan, Development Director, April 24, 2008 0

I'm a n00b when it comes to Git, so I solicited Matt's help to get the changes in my fork up to our main GitHub account. As awesome as GitHub is, there's still no "automatically-pull-and-merge" functionality when someone wants to send you their changes (and for good reason). Here's how I made it happen:

The Scenario

I wanted to work on my constant_cache gem, so I forked the project into my own GitHub account. I made the changes I wanted, ensured that all specs were passing, committed, and then pushed the changes up to my repository. Now it was time to sync up those changes with the main repository.

Continue reading "I Have a Pull Request on GitHub, Now What?"

Maintaining Lookup Data in Your Rails Application

Patrick Reagan
Patrick Reagan, Development Director, April 22, 2008 0

Early last year, Dave Thomas gave a great talk on Metaprogramming in Ruby which he reprised last week at April's NovaRUG meeting. One of the quick examples he gave during the talk inspired me to work on an idea that began as a submission to Advanced Rails Recipes, turned into a Rails plugin, and which I just re-released as a gem. Basically, it allows you to cache the lookup data for your Rails models when your application loads.

Installation

Since we're hosting our code on RubyForge, a simple gem install will fetch the latest version:

sudo gem install constant_cache

Usage

Assuming that your application has a Status model with data that looks like this:

mysql> select * from statuses;
+----+----------+
| id | name     |
+----+----------+
|  1 | Pending  | 
|  2 | Active   | 
|  3 | Disabled | 
+----+----------+
3 rows in set (0.00 sec)

All that's needed is to include the gem in your model class and instruct it to cache its data:

require 'constant_cache'
class Status
  caches_constants
end

Status data is now available as constants on the class:

$ ./script/console 
Loading development environment (Rails 2.0.2)
>> Status.find(:all).map {|s| [s.id, s.name] }
=> [[1, "Pending"], [2, "Active"], [3, "Disabled"]]
>> Status::PENDING
=> #Status id: 1, name: "Pending"
>> Status::ACTIVE
=> #Status id: 2, name: "Active"

Since the constants point to ActiveRecord instances, the real value comes when you want to use them as part of an association:

user = User.create!(:username => 'preagan', :status => Status::PENDING)

The gem relies on a name column in your database, but can be configured to use something else as needed:

class Status
  caches_constants :key => :slug
end

Other configuration options are available, see the README file for more information.

Future Additions

Right now, the gem only handles caching data that exists in the database when the application is loaded, it isn't able to handle cases when the data changes (e.g. a record is deleted, or a name is changed). I plan to handle these cases in a future release. Feedback and patches are always welcome

Passenger: Let It Ride?

Mark Cornick
Mark Cornick, Web Developer, April 21, 2008 4

Rails application deployment has gotten a pretty bad rep. Legitimate Rails developers and blog trolls alike have bemoaned the lack of something equivalent to mod_php, which would make Rails applications "just work" when uploaded to a web server, as we got used to doing in the PHP days.

So Passenger made a big splash when it was released this month. It promises to make deployment "a breeze" and says "No Ruby on Rails-specific server configuration [will be] required!" Furthermore, the developers' own benchmarks show it being a little faster than Mongrel.

After a decade-plus in IT, I'm skeptical of hype and twice as skeptical of benchmarks. But even though I've mastered the sometimes-confusing realm of Apache and Mongrel, I'm curious to see just how easy it is to use, and just how fast.

Continue reading "Passenger: Let It Ride?"

A Development Community for Viget Labs and Beyond

Every team member here at Viget Labs strives to be an innovator. We members of the development team are no different - that's why we're constantly engaging in community discussions and exploring the unknown that is the next generation of open-source web applications.

Viget Is Hiring!

Viget has job openings for Ruby Developers, Interns, and Front-End Developers. Learn More »

Recent Comments

Hi,that,s very nice to see and i got a lot of knowledge from this blog which help me in my future because i am new to this kind of stuff.thanks