Goodspeed IT Rants RSS

Rants from Goodspeed IT

Archive

Feb
21st
Sat
permalink

Ruby: hooks and metaprogramming 2

Here are some real life examples that come from Ruby-Concordion.

(A ruby active specification framework based on annotated HTML.  Try it out with: gem install concordion .)

The “inherited” method, to detect subclasses of ConcordionTestCase:

def self.inherited(subclass)

# Concordion config/housekeeping code omitted.
bind_test_method_to(subclass, config)
end

and the similarly defined “included” method, to detect classes that include the ConcordionTestMethods module:

def self.included(cmod)

# Concordion config/housekeeping code omitted.
bind_test_method_to(cmod, config)
end

Note that we’re keeping these methods DRY.  The method we want to add to both classes is the same: a test method that runs the association concordion spec. (Note: in practice be sure to capture the existing inherited/included() methods with alias_method, and delegate to the original implementation!).

Here’s the metaprogramming code that creates the appropriate test method:

def bind_test_method_to(subclass, config)
subclass.class_eval do
define_method :test_spec do
filename =  snake_cased_test_name(subclass.to_s)
parse_spec(filename,config)
failures  =   run_spec(filename, config)
report_spec(filename,config, failures)
end
end
subclass
end

This code opens the users class (which descends from ConcordionTestCase or includes ConcordionTestMethods) and defines a new method: “test_spec”.  Why is it done this way?

Because the framework can then:

  1. Infer the name of the specification (e.g. a class FooBarTest is bound to the spec “foo_bar.html”).
  2. Look for fixture code in the user’s class without the user doing anything but extending ConcordionTestCase or including ConcordionTestMethod: no plumbing code in user fixture code.
  3. Report errors with the appropriate context.  For example: specification errors in a concordion fixture UserTest come from UserTest.test_spec, not the parent class: ConcordionTestCase.
  4. Provide support for declarative configuration per user fixture.  For example: expected errors for a specific test case can define the method “expected_failure_count” to return the expected number of incorrect results in the spec.

I think it provides a nice example of the trade-offs involved in metaprogramming.  On the one side you have flexibility: the power to create a more friendly API than could be practically created without it.  On the other hand, the code is less direct and thus harder to read/maintain.

Readers interested in further context can view the complete Ruby-Concordion source code online.

The methods quoted come from “lib/concordion_test_methods.rb”.

Feb
19th
Thu
permalink

Ruby: hook methods and metaprogramming

Here are a handful of hook methods I’ve used in my Ruby career:

  • method_added
  • method_missing
  • const_added
  • const_missing
  • inherited
  • included
  • extended

These methods are called when the appropriate event takes place (e.g. method_missing in rails is used to detect method calls like Model.find_by_other(other) and delegate to the appropriate find(:other => other) method.)

Sometime just detecting the class/module or symbol name being used/defined is not enough.  Ruby provides various query methods to provide addition info, such as:

  • methods
  • instance_methods
  • class_methods
  • constants
  • method_defined?

It’s also possible the behaviour we need to define on the fly is not present in the original class. This is unlike Model.find_by_criterion(c) in rails, which already has a Model.find() method, which needs only be invoked with the correct arguments.

If we need to define the method we’re delegating to at the time of delegation, then we need metaprogramming. Ruby gives us several metaprogramming methods to accomplish this:

  • eval
  • instance_eval
  • class_eval
  • define_method
  • define_const

I’ll clarify some of my uses of these methods in practice in the next post.

permalink

CSS to debug alignment problems

If you want to check the alignment of two design elements, you can add a grid line such as one would use in Photoshop or Gimp.

Just add an empty span to the page that contains the elements you want to align:

<span class=”horizontal_grid_line”>&nbsp;</span>

Then in your CSS add the following declaration:

.grid_line {

position:     absolute;

width:         1000px;

margin-top:  35px;

border-top:   2px red solid;

}

Then just make sure all the design elements touch the grid line, and get rid of it.

Notes:

  • “position: absolute;” allows the span to float above everything else and not shift anything on the page (which would defeat the purpose).
  • “width” just needs to be long enough to reach between the elements you care about.
  • “margin” sets the position of the grid line.
  • “border” just needs to be a color that won’t blend in to the background.

To create a vertical line, it’s pretty much the same.  Just switch the following:

  • “width” should become “height”.
  • “margin-top” should become “margin-left”.
  • “border-top” should become “border-left”.
permalink
permalink

Design: It’s all CRAP.

CRAP is a handy mnemonic for (aesthetic) design guidelines: contrast, repitition, alignment and proximity.

Contrast: Use dark text on a light background or vice versa.

“Performance is optimal when contrast levels […] exceed 70 percent.”

[UPD:124]

Repetition: Repeat important thematic elements of the design.

“Repeated exposure to stimuli for which people have neutral feelings will increase the likeability of the stimuli.”

[UPD:70, Exposure-Effect]

Alignment: Don’t place anything haphazardly.

“Elements in a design should be aligned with one or more elements.  This creates […] unity, cohesion and [influences] perceived stability.”

[UPD:22]

Proximity: Place related elements together.

“Elements that are close together are perceived to be more related than elements that are farther apart [… and this proximity] will generally overwhelm competing visual cues (e.g., similarity).”

[UPD:160]

If you keep this CRAP in mind, you’ll always have design guidelines at your disposal ;).

-ben

ben@goodspeed-it.ca

References:

[UPD]: Universal Principles of Design, 2003, Lidwell et al.  (C) Rockport publishing.

Feb
17th
Tue
permalink

RCor renamed to Ruby-Concordion

I just renamed RCor to Ruby-Concordion, and renamed the gem to just concordion.

It’s available through the usual gem sources:

gem install concordion

I haven’t moved anything but releases to the rubyforge page yet.  For now the wiki and source are only available from the  google code page.

Renaming a project with actual client code and vhosts and accounts named after it makes one appreciate refactoring tools…

Feb
16th
Mon
permalink
Be not over-concerned with money, or position, or glory. For some day you will meet a man who cares for none of these things. Then you will know how poor you are.
— Rudyard Kipling
permalink
Feb
15th
Sun
permalink

Ruby: gem which

I just stumbled across this today while doing some work on RCor:

gem which            Find the location of a library file you can require

Ok, so why is that useful?

Lets say you’re working on some ruby code with a line like:

require ‘concordion_test_case’

But ‘concordion_test_case’ isn’t in the project (which runs), so you deduce it must come from a gem.

For example: Rails applications are notorious for bundling all gem dependencies into ‘environment.rb’.  This can obscure which gem is used to fulfill the dependency.

The question is: which one?  The command “gem which” can solve this problem:

$ gem which concordion_test_case

(checking gem rcor-0.9.3 for concordion_test_case)

/usr/lib/ruby/gems/1.8/gems/rcor-0.9.3/lib/concordion_test_case.rb

The output will vary on different installations of ruby/gem, but the important bit to note is it told us which gem (& version) defines the symbol: “rcor-0.9.3”.

Feb
14th
Sat
permalink

Logo Design Process

Here’s the process I went through with some great people to come up with my company logo.

  1. Choose a motif that is visually connected in some way to your companies industry.
  2. Generate various examples within that motif.
  3. Select a color palette.
  4. Refine the result: tweak image size/placement, font size/placement, whitespace, etc.

For Goodspeed IT Consulting, the selected motif was mathematical artwork, because it expressed some of the core values of the company: namely elegance and simplicity.

In particular, the screensaver Electric Sheep was a source of inspiration.  Viewing it led to research of Apophysis, the still-frame generator it uses to render the video.

The basis of both artistics paradigms is the Fractal Flame, and a search in Wikimedia Commons turned up many gorgeous images from which I generated demos for step (2) above.

Here’s one that didn’t make it past step (2):

(This image is based on an original work by Nevit.)

I like the colors, they’re vibrant, but it did’t quite have the right feel.

Another candidate was:

(This image is based on an original work by Jon Zander (Digon3))

This one had a mixed response.  It almost won because people either loved it or hated it.  That has a certain charm: it’s better to be loved AND hated than ignored in the middle.

There were numerous others (some are posted on my blog), but the next one was the overall favorite.

(This image is based on an original work by Nevit)

This was almost everyones favorite “fractal flame” shape.  It had pleasant properties of closure as well as the ‘expected’ fractal self-similarity.

But, I wasn’t happy with the colors, so I moved on to step (3).  I had to select a color pallete.

I like the grey background, but it was hard to completely permeate the fractal with another color.

I also experimented with “color rotation”: mapping the brown/white range in the original fractal to the blue/white range.  The next image is another variation using the same technique, but mapping into the red spectrum this time.

This is not bad, except I don’t really like red.  But this image has some advantages from a printing perspective: this image could be reduced to a single color (with variation in hue).

So I mapped all the reds to blues, and came up with this:

This one speaks to me more than the others.  I went ahead with this color palette.

The last step was to refine to the font (type, size, placement) and image scale.

These iterations are less visually stimulating so I’ll skip to the end result of step (4).

I’m quite happy with this one.

It satisfies numerous design criteria.  It has high contrast.  Alignment is consistently applied, so pieces of the image and the text flow together nicely (thanks mom!).

Hope this helps someone :)

Send any comments or complaints to:

ben@goodspeed-it.ca

Legal:

All of these images are licensed under the GNU Free Documentation License, and where relevant original works and authors were attributed.