Friday, November 07, 2008

ConstantizeAttribute to support Renum on Rails

The most obvious shortcoming of Renum is that there hasn't been any clean way to use enumerated values as ActiveRecord attribute values. I've finally fixed that.

There were a couple of Rails features that seemed like they might be helpful but turned out not to be.

Rails' built-in serialize macro class method uses YAML to store items, which is fine for arrays and hashes but hideous for anything else (if you ever look at your database directly). Though YAML serialization worked on Renum-created enumerated values, deserializing a value created a new instance. That instance might turn out to work fine for your needs, but it wouldn't actually be the instance it ought to be (i.e., the one the constant points to), so it might surprise and disappoint you in subtle ways. The other huge downside to serializing the instance (and its instance variables) is that a change in the encapsulated contents of the enumerated value could break things.

Rails' built-in composed_of macro class method could have been made to work, but it wouldn't have been pretty. The default handling would have required Renum to redefine new in generated enumeration classes to do a lookup instead of allocating and initializing an instance. The latest releases of Rails provide :constructor and :converter options that would have allowed me to avoid messing with new, but it still would've been ugly, not to mention requiring a very recent version of Rails.

What I finally realized is that the beauty of constants is that I didn't need to write a lookup mechanism: Ruby already does that. All I needed to do was store the name of the constant when writing the attribute and constantize it when reading to get the proper value. I also realized that approach is in no way tied to Renum: It would also allow classes and modules to be attribute values, which could be helpful if, for example, you have a module to mix in or a service class to call based on some reference data.

So I wrote a tiny little Rails plugin called ConstantizeAttribute that does this for you. This example pretty much says it all:

# ... your migration ...
create_table :robots do |t|
  t.column :behavior_module, :string
end

# ... your model ...
class Robot < ActiveRecord::Base
  constantize_attribute :behavior_module
end

# ... some classes or modules you want to store as attribute values ...
module RobotBehaviors
  module Handy
    def self.encounter
      "Is there anything I can help you with?"
    end
  end

  module Evil
    def self.encounter
      "I will destroy all humans."
    end
  end
end

# ... so now,
robby = Robot.create :behavior_module => RobotBehaviors::Evil

# Now "RobotBehaviors::Evil" is in the behavior_module column.

robby.behavior_module.encounter # => "I will destroy all humans."

robby.update_attribute :behavior_module, RobotBehaviors::Handy

# Now "RobotBehaviors::Handy" is in the behavior_module column.

robby = Robot.find :first

robby.behavior_module.encounter # => "Is there anything I can help you with?"

Install ConstantizeAttribute with

script/plugin install git://github.com/duelinmarkers/constantize_attribute.git

if your Rails is recent enough to install from git or grab a copy of the repo manually and drop the plugin in place. (There's no install script to worry about.)

The cool thing about this is that it works with old version of Renum without any changes, and it all happens in only about 15 lines of code.

Saturday, October 18, 2008

Module#to_proc h0tness

I don't need this right now, so I'm not putting it into any of the codebases I touch. But if I ever do need it, I haven't thought of a reason yet that I shouldn't pull it in.

describe "Module#to_proc" do
  it "provides a proc that mixes the module into the yielded object" do
    
    # Say you've got some stuff,
    
    stuff = ["jimmy", "jane", Object.new]
    
    # and you want to have all that stuff extend a certain Module.
    
    module Fooable
      def foo() "This #{self.class} is fooing"; end
    end
    
    # stuff.each {|thing| thing.extend Fooable } is just so many
    # characters to type!
    
    # With a little hackery in Module, you can do this.
    
    stuff.each &Fooable
    
    # Now all that stuff will have your module mixed in,
    
    stuff.each {|thing| thing.should be_a_kind_of(Fooable) }
    
    # and have interesting new behavior.
    
    stuff.collect {|o| o.foo }.should == [
      "This String is fooing",
      "This String is fooing",
      "This Object is fooing"]
  end
end

Here's all it takes to make it pass, plus some simple memoization so it's cheaper to call.

class Module
  def to_proc
    @to_proc ||= Proc.new {|o| o.extend self }
  end
end

Friday, September 12, 2008

Keep Backtraces Honest: Set Forwardable::debug

If you use Forwardable at all, you may have noticed that when you misspell or forget to implement something, the backtraces can be a little baffling. Take this example.

require 'forwardable'

class Foo
  extend Forwardable
  def_delegator :a, :bar # a is not defined
  def_delegator :b, :baz # b is defined but returns nil
  def b; end
end

f = Foo.new

When you call f.bar, you'll get an unsurprising NameError: undefined local variable or method 'a' for #<Foo:0x1044fec>. The backtrace will point at the line where you called bar, which is a little weird, but since that line doesn't have any mention of 'a' on it, you'll probably know to go looking for bar and discover the missing (or misspelled) delegate accessor.

When you call f.baz, you'll get an unsurprising NoMethodError: undefined method 'baz' for nil:NilClass. But, again, the backtrace will point at the line where you called baz, and here you're much more likely to go chasing down the wrong problem. If you really created your own instance of Foo just before that line, you're probably not going to worry that Foo.new returned nil. But what if you'd gotten that instance of Foo from some other call? The backtrace suggests that other call returned nil.

It's a dirty lie!

You have a perfectly good instance of Foo. The real problem is that its delegate accessor returned nil.

So why does the backtrace lie?

The first time I ran into this, I assumed Forwardable was implemented using some weird native magic that kept it from appearing in the backtrace. That seemed odd, since it doesn't do anything you couldn't do from normal Ruby code, but I didn't have time to dig into it.

When I finally did, I was surprised to find that it's normal Ruby code, but the method it writes (via module_eval) is (as interpolated for this example):

def baz(*args, &block)
  begin
    b.__send__(:baz, *args, &block)
  rescue Exception
    $@.delete_if{|s| /^\\(__FORWARDABLE__\\):/ =~ s} unless Forwardable::debug
    Kernel::raise
  end
end

As you'll have guessed, "(__FORWARDABLE__)" is passed as the file name to module_eval, so Forwardable's default behavior is to delete itself from backtraces, potentially making them misleading and wasting a lot of debugging time.

I don't know why it does that, but thankfully the authors realized you may not want that and made the hiding conditional on Forwardable::debug being false.

I highly recommend that any application using Forwardable has some bootstrapping code set that flag.

Forwardable.debug = true

Cleaner Utility Modules with Module#module_function or extend self

[Disclaimer: GLoc is a nice and generally well written library, and I'd encourage anyone who needs to translate strings to consider it. Also strongly consider i18n, particularly if you're using Rails 2.x.]

My pair and I were looking at the code for GLoc this week. When a class includes the GLoc module, it makes its methods available on instances of the class (as one would expect with include) as well as on the class itself (which would normally only happen with a call to extend). Those same methods are also available as module methods of GLoc itself. So all of the following will work.

GLoc.l(:hi)

class Greeter
  include GLoc
  
  def greet
    l(:hi)
  end
  
  def self.greet
    l(:hi)
  end
end

What caught our eye was how the library goes about making itself so available. Minus all the functionality, here's what it does.

module GLoc
  module InstanceMethods
    # ...
    # useful methods defined here
    # ...
  end
  
  include ::GLoc::InstanceMethods
  
  module ClassMethods
    include ::GLoc::InstanceMethods
  end
  
  def self.included(target)
    super
    class << target
      include ::GLoc::ClassMethods
    end
  end
  
  class << self
    include ::GLoc::InstanceMethods
  end
end

Patrick's comment was "I don't know off-hand what the cleanest way is to do that, but I know that ain't it."

So what's the cleanest way?

First let's clear away the noise of the nested modules. The convention of nesting a ClassMethods module to extend on including classes makes sense when there are distinct behaviors to add at the instance and class levels, but when you want the same methods in both places, it's unnecessary noise.

Here's what we get when we just define the behavior in the top-level module.

module GLoc
  # ...
  # useful methods defined here
  # ...
  
  def self.included(target)
    super
    target.extend self
  end
  
  class << self
    include ::GLoc::InstanceMethods
  end
end

That's much easier on the eye. (Note that calls extend on target rather than having its singleton class include the module. That's important because otherwise the included hook method goes into an (indirect) infinite recursion and you end up with a SystemStackError: stack level too deep.)

Next up, we want a nicer way to expose the methods on the module itself (so you can call GLoc.l(...)).

There's a little-used method on Module for exactly that: Module#module_function. It's a visibility modifier like public, private, and protected and supports the same two usage patterns: you can pass it the names of previously defined methods as symbols or you can use it like a pseudo-keyword that affects subsequently defined methods. It's a weird visibility modifier though: it makes the methods it's applied to private (in the Ruby sense, so they're callable only with an implicit self as receiver) but it also creates public copies of the methods as singleton methods of the module. That gives us two of the three usages that GLoc wants to provide, leaving only the "pretend the including class also called extend" feature to implement in the included hook.

Using module_function gets us down to this.

module GLoc
  module_function
  
  # ...
  # useful methods defined here
  # ...
  
  def self.included(target)
    super
    target.extend self
  end
end

Very nice.

Unfortunately that wouldn't actually work for GLoc.

You can't tell from these snippets, because I've hidden the useful methods, but GLoc's useful methods rely on some lower-level methods that the author didn't want to expose as module functions. Private methods (or any other methods that don't have module_function applied to them) aren't copied into the module's singleton class like the module functions, so they're not there to be called: NoMethodError. Luckily the same sort of flexible API can be created without any copying of methods from one method table to another by having the module extend itself.

module GLoc
  extend self
  
  # ...
  # useful methods defined here
  # ...
  
  def self.included(target)
    super
    target.extend self
  end
end

Using extend self is a bit mind-bending. After all it creates this strange situation.

GLoc.is_a? GLoc    # => true

But keep in mind that it's not for use in modules that actually model anything, just for modules that are bundles of functions. In other words, it's for the kinds of objects where you'd never ask what it is_a?, so the surprising line above shouldn't worry you.

GLoc is what triggered this write-up, but the most instantly grok-able example of a utility module is Ruby's Math. A class might include Math so it has convenient access to sin, cos, sqrt, or whatever. While it's far from intuitive that an instance of that class is_a?(Math), we know why Math is sitting in the class's ancestors list: it's a side-effect of how mix-ins work. (For other kinds of modules, it's quite intuitive that mixing in alters the inheritance chain: it's not surprising, for example, that [1, 2, 3].is_a?(Enumerable).)

So in the pros column module_function is clear and declarative. In the cons column it falls down if you want to compose module functions out of lower-level functions and don't want to expose those the same way. It also scores at best a below average on the principle of least surprise test (at least by my intuition) because for each method you def, methods are added to two different method tables, and modifying the module later will only change one of those. If those cons trouble you (either purely on principle or because of actual requirements), you can use extend self. The only real con there is that it's a slightly weird looking idiom.

I'd like to see either module_function or extend self used on every utility module to communicate about what sort of module it is. What do you think?

Sunday, August 31, 2008

Learn these keyboard shortcuts ...

... for better TextMate project window management.

First, open Keyboard.prefPane and map Control-Command-Z (^⌘Z) to "Zoom". (Detailed instructions can be found here, and the example he uses is this exact shortcut, in case you weren't convinced this is the right thing to do.)

Now open up a project in TextMate so that you've got the project drawer showing.

Ready? Here's your new keyboard calisthenic routine.

  • Control-Option-Command-D (^⌥⌘D or "mash D") to Hide Project Drawer
  • Control-Command-Z (^⌘Z) to Zoom the editor window to fill the screen
  • Control-Command-Z (^⌘Z) again to Zoom the editor window back to a size that can accommodate the project drawer
  • Control-Option-Command-D (^⌥⌘D or "mash D") again to Show Project Drawer
  • Option-Command-Backtick (⌥⌘`) to move focus to the drawer
  • Option-Down (⌥↓) to jump to the bottom of the drawer
  • Option-up (⌥↑) to jump to the top of the drawer
  • Option-Command-Backtick (⌥⌘`) again to move focus back to the editor
  • Again! Faster!

UPDATE: Note that the second Control-Command-Z (^⌘Z), while excellent exercise, isn't really necessary. TextMate will automatically zoom out the window if you ask for the project drawer and there isn't any room for it. Sweet!

Friday, August 15, 2008

A Ruby Metaprogramming Introduction Updated

Update: The Rspec conversion is done, and I added a section demonstrating macro methods. Read the latest here.


Early last year I published A Ruby Metaprogramming Introduction on the Practical Ruby blog. Earlier this year while commuting back and forth between Grand Central and White Plains—not a place you should strive to commute to, though MetroNorth is far better than the subway for giving you room to work on a laptop—I started making refinements and converting it to use Rspec instead of custom Test::Unit extensions.

I put it aside for a while but am now resolved to continue work on it. I've just put that work onto Github, so anyone with an interest can take a look, browse history, and even submit suggested patches.

The project is located here:
http://github.com/duelinmarkers/metaprogramming_introduction/tree

If you're annoyed at the half-Rspec state of things, you can read the originally published version in this tree. (It's way prettier as rendered on Github than I could probably manage to get it on any blog.)

Tuesday, June 03, 2008

Contributing to Rails

Yea for me! I actually contributed something that should be running in lots of Rails apps after the next release (even if no one's using the feature but my previous project).

After a month or so of working with git, and watching the ease with which git and github allow open source projects to take contributions from the field, I'm really excited. I can't imagine my little patch would have made it into Rails so quickly (or at all) if not for the incredible ease with which any developer can bring the patch into their own repo, test it out on whatever branch they like, and commit it wherever they like.

If you haven't already, it's a great time to start contributing!

(If you don't have any features to add or bugs to fix, check out some other people's patches and give them a +1 or -1 based on the instructions on Rails' Lighthouse. Maybe you can start with this one.)

Monday, May 19, 2008

Declarative Class Method Prettiness for ActionHelper

I don't know why it didn't occur to me sooner, but the most Rails-consistent way for ActionHelper to support a controller declaring action-specific helper modules is pretty obviously extending the existing ActionController::Helpers#helper method to accept an options hash supporting :only and :except, the same way the filter class methods do.

It may not be the coolest way to do it, but it's definitely the least surprising.

So ActionHelper now supports :only and will likely support :except sometime soon. (Though except seems much less likely to be useful.)

Wednesday, May 14, 2008

In defense of helpers

...in which I defend helpers[1] as good OO, if you use them just so; point out an aspect of the convention that stands in the way of that style; and provide an alpha plugin that tries to change that.

By Rails convention helper modules are where view logic belongs. During request processing, a controller will automatically look for a module in the helpers directory with a name matching the controller. If found, the module will be mixed in to the controller's response's template object, an instance of ActionView::Base (and self in the rendered erb template). A controller can also specify additional helper modules to mix in using the helper class method.

The typical approach I've seen is to define helper methods that take model objects or their attributes as arguments (where the model was typically put into an instance variable by the controller). So the template does something like

<span class='contributors'>
  <%= contributors_list @project.contributors %>
</span>

to use a helper like this

module ProjectsHelper
  
  TOO_MANY = 10
  
  def contributors_list contributors
    if contributors.size < TOO_MANY
      contributors.to_sentence
    else
      contributors[0...(TOO_MANY - 1)].join(', ') + ', and more'
    end
  end
end

I think it's because of this functional style of helper method that I've seen a fair amount of bias against helpers. OO developers like encapsulation, and helper modules generally encapsulate logic but not the information needed to apply that logic.

For example, the first Rails project I was on didn't use any application helpers. The team had created a parallel construct called presenters. The "final" state of the presenter stuff evolved over months of development, but by the end, a page-specific presenter object was always made available in the @presenter instance variable (thanks to some frameworkey extensions in our ApplicationController based on a naming convention), and eventually a method_missing was monkey-patched into ActionView::Base to automatically delegate everything to @presenter so our templates weren't cluttered.

By the time the method_missing went in, we'd come back around to something very close to Rails' built in helpers, and I had a little bit of an aha moment. The helper is the page (because it's mixed in), I thought, why would I pass it my instance variables?

The approach of taking in arguments for things that could have been pulled from instance variables is consistent with a general rule in Ruby that modules ought not to mess with instance variables if they can avoid it. This rule makes good sense in general-purpose modules (like Enumerable or Comparable) because by design these modules are meant to be mixed in to all sorts of objects, and they don't want to put weird constraints on their hosts. (Imagine if the documentation for Enumerable told you "in addition to providing an #each method, the object should take care to avoid using instance variables called @_cursor, @_enumerators, @...." No one would like that.)

Helper modules aren't like that though. They're designed for a specific page or set of pages (i.e., a specific view concern) in your application. The only reason they're modules rather than classes is that you might have multiple view concerns on the same page.

So I thought it might be interesting to let the helper know more and the template know less about Ruby by moving knowledge of the controller-exposed instance variables into the helper. It worked and felt good.

For a while.

Then I realized that the helper wasn't a page definition: it was a few of them. Since all the actions on a controller get the controller's helpers mixed in, a typical controller would have listing pages, detail pages, and edit pages all with the same helper modules.

Blast!

So I wanted helpers to be selected per action rather than controller-wide. But they weren't. So after talking about it for a while, last Friday morning I finally rolled a Rails plugin to make it the way I wanted: it's called ActionHelper and you can find it on GitHub.

For the moment, it does the naming convention thing that you'd probably expect: when processing UsersController's show action, the module UsersShowHelper will be mixed in to the template if it exists. It also allows actions to declare what helper modules they want by calling action_helper inside the action. (You'd expect a class method, and I agree there should be one, but I haven't figured out a pretty API yet, so for now it's not there.) See the README for an actual example.

If you have thoughts on a pretty declarative class method API for this (whether it's annotation-style or more Railsey), call it out in the comments. Better yet, fork the repository on GitHub and send me a pull request once you've got something going. (ActionHelper has been my "get comfy with git" mini-project.)

Thanks for reading.


1^ Note that I'm talking about the helpers in your application, not the ones Rails provides in ActionView::Helpers. Those are general-purpose modules.

Friday, January 25, 2008

Renum goes one-oh with a nice new syntax for an important new feature

Last week while watching Project Runway, I realized there was a cool way to provide a feature Renum was sorely lacking.

The basic enum supported by Renum like the below is nice, but not very interesting.

enum :Status, %w[ NOT_STARTED IN_PROGRESS COMPLETE ]

You get the collection of values and the sorting, which might be worthwhile for you, but there's not much to recommend it over strings or symbols. You could also put behavior in the values like this.

enum :Color, [ :RED, :GREEN, :BLUE ] do
  def abbr
    name[0..0]
  end
end

But with just the name to work with, the behavior couldn't be very interesting unless you resorted to defining singleton methods on the values, which is fine but not pretty. (It sort of defeats the purpose of Renum: to provide a terse and readable API for creating object constants.)

What would really make enum behavior interesting is if you could associate some extra data with each value. But since we want the instances created automagically, we can't just let you provide a custom constructor.

Back in the post where I first tried to create a good API for this, I experimented with a const_missing hack to create the values. Unfortunately I couldn't get const_missing to be called on the enum class that was being created: it would be called on whatever module the code was in the scope of—Object at the top level—which would have meant redefining const_missing on the receiver of the enum method (either the module we were in or the "main" object), which I wasn't willing to do.

What it took me 6 months to realize is that (thanks to the differences between constant lookup and method sending) almost the same syntax I couldn't support with const_missing can be supported with method_missing! Because Renum already has the newly created enumerated value class class_eval the associated block, it would receive the method_missing calls, so I wouldn't have to mess with the methods on any pre-existing object. Method calls would also allow providing additional arguments that could be passed along to the values so that you can define more intersting behavior.

That's probably more detail than anyone wanted. The upshot of all that is that Renum 1.0 allows you to do this:

enum :Size do
  Smaller("Really really tiny", 0.6)
  Medium("Sort of in the middle", 1)
  Larger("Quite big", 1.5)

  attr_reader :description

  def init description, factor
    @description, @factor = description, factor
  end
  
  def adjust original
    original * @factor
  end
end

For more details, see the spec.