InquiryLabs

Politics, Programming and Possibilities

Archive for April, 2006

ColumnComments Rails Plugin

Note: Please see the updated ColumnComments v.1.2 post for the latest with bug fixes.

ColumnComments

Here’s a small plugin that may be useful for documenting your database. Based on Dave Thomas’s AnnotateModels plugin, this plugin goes one step further and allows you to store comments on each column in the database (MySQL only).

Why?

Since the Rails philosophy uses the database as the “authoritative source” for the data model, it’s been troublesome to know where to store helpful developer comments. If you write them in the model files (where it would make sense to do it), you repeat a lot of work because the models don’t store column information.

Dave Thomas solved part of this problem by introducing the AnnotateModels plugin–with it you can take the basic information about each table and auto-generate a comment-header inside each model file. But still, where would you put comments? If you edit the auto-generated header then you run the risk of accidentally (or by necessity) overwriting your own comments when you re-generate the headers.

Enter ColumnComments

The ColumnComments plugin makes the necessary modifications to ActiveRecord to allow for an optional :comment in your table migrations. In addition, it includes the same feature provided by the AnnotateModels plugin (rake annotate_models), but also includes the database-stored comments in the auto-generated header.

Here’s an example of the output:

# Schema as of Thu Apr 27 12:07:32 MDT 2006 (schema version 10)
#
#  id                                      integer(11)         not null
#  login                                   string(255)
#    A user-generated login name.
#
#  salted_password                         string(40)          default(), not null
#    Encrypted using SHA1 and the "salt" column
#
#  email                                   string(60)          default(), not null
#    Mandatory email address for each user

Adding Comments to the Database

There are two times when you might want to add comments to your columns: while you create a new table or after a table already exists.

While You Create a New Table

  def self.up
    create_table "users" do |t|
      t.column "first_name", :string, :comment => "The member's given name."
      t.column "last_name", :string, :comment => "The member's family name."
    end
  end

After a Table Already Exists

  def self.up
    column_comment "tags", "id", "The unique ID of any tag in the system."
  end

Or, if you want to mass-assign comments (for example, just after you install the plugin on an already-built app):

  def self.up
      column_comments({
        :users => {:first_name => "User's given name", :last_name => "Family name"},
        :tags  => {:id => "Tag IDentifier"}})
  end

Get It From Our Subversion Repository

syncPEOPLE maintains an open source subversion repository for plugins and other useful code that we’ve open-sourced. ColumnComments is released under the MIT license and can be installed from:

svn://syncid.textdriven.com/svn/opensource/column_comments/trunk

Textmate is Expensive…

I was witness to this gem on the freenode.net #textmate channel:

quellhorst: textmate is expensive
rhacer: textmate isn’t expensive at all.
quellhorst: it is when you have to buy a mac first
canadaduane: lol
quellhorst: which i did this week

Apple, you should be paying attention right now.

My Textmate Bundle

I’ve decided to put up a page for my personal for others to use. So far, it’s star (and only!) feature is the work I’ve been doing.

As I develop other features, I’ll upload newer versions. This is an “as-is” bundle–I’m just putting it out there in case you want to use / modify / steal / learn from its code.

UPDATE: I just re-made the screencast, this time in under 2 MB. Check it out on the bundle page.

ActiveRecord::Errors#to_xml

Here’s a method that will allow you to call to_xml on an ActiveRecord::Errors object. We’re using this to pass errors between web apps via a web service api.

class ActiveRecord::Errors
  def to_xml(options = {})
    options[:indent] ||= 2
    options.reverse_merge!({ :builder =>
      Builder::XmlMarkup.new(:indent =>
        options[:indent]), :root => “errors” })
    options[:builder].instruct! unless options.delete(:skip_instruct)

    options[:builder].__send__(options[:root].to_s.dasherize) do |xml|
      @errors.each do |key, value|
        xml.__send__(key.to_s.dasherize) do |xm|
          for message in value
            xm.message(message)
          end
        end
      end
    end

  end
end

Textmate Snippets Like

So here’s an idea I’ve been tossing around on the Textmate IRC channel and mailing list: What if Textmate supported multiple arbitrary simultaneous carets (MASC)? Allan Odgaard didn’t like the idea on first inspection, because it involves using the mouse–something that he is philosophically opposed to (for obvious reasons–it’s a text editor, after all). But I think there’d be many people who would receive this feature with eager arms and an appropriate amount of salivation.

So what is the idea? Here’s my original mailing list post:

What if you could add multiple carets to the current document by command-clicking? I imagine this to work in a way almost identical to that of the current snippet mechanism. For example: choose three caret locations, start typing, and in each location your text will show up. Press tab and the caret will focus in on the second of the two locations, highlighting the newly typed text (if any). Tab again and it moves to the third.

It would also be ideal to be able to highlight multiple selections in the same way–kind of like a powerful replace mechanism. Command-click and select three words in the document, then type one new word, and incrementally, all three words change to the new one.

Here is a screencast of MASC in action (Quicktime, 27 MB):

Textmate - multiple arbitrary simultaneous carets


Download

Update: The bundle is available as a disk image here. Key bindings and the “ß” marker character are subject to change. Use alt-s to mark and alt-f to convert markers to a snippet.

Update #2: No longer uses the beta marker. Uses special angle brackets instead.

Update #3: There is now a that has updated information and a new (shorter) screencast.

Small fix to TextMate Footnotes Plugin

It seems that the TextMate Footnotes Plugin has been hiding a bug for a while–the clickable line numbers have occasionally had the RAILS_ROOT part of the path included twice, causing the links to fail.

Here’s a screenshot of the type of link that’s been failing (note the “#1″):

Rails Error with Backtrace

It seems to be working now. Get the latest from “svn://syncid.textdriven.com/svn/opensource/textmate_footnotes/trunk”.

Thanks go out to a new Mac user, Jed Hurt, for this correspondence:

You know, I was using TextMate for migrations last night. I realized that when making a new table is was typing ‘mcc[tab]‘ over and over again. Would there be any way to DRY this up? Maybe make it so that if you are currently on an mcc line (after it has already been expanded and you’ve given the column a name and type) and you press a key combo, it makes another mcc on the following line, and places your cursor in the ‘name’ field of the new mcc declaration?

From his suggestion, we now have the “mccc” or “Migration Create Column Continue” snippet, which is a snippet that creates a snippet. Watch the screencast here:

This feature is available in the Textmate Bundle Subversion Repository. Or, you can wait until the next beta release of Textmate.

Update: The screencast is working now.

Ruby Gotcha: "or" Precedence

This one got me today:

feed_bridge_is_valid = !state[:feed] or (state[:feed] and @feed_bridge.valid?)

It turns out that the “=” assignment has a higher precedence than the “or” operator, so if (in the example above) !state[:feed] turns out to be false then feed_bridge_is_valid will be assigned a false value, and the entire expression will return the value of (state[:feed] and @feed_bridge.valid?).

Here’s one solution:

feed_bridge_is_valid = (!state[:feed] or (state[:feed] and @feed_bridge.valid?))

And I think you can also solve the problem by using the “||” version of the “or” operator too.

Textmate Footnotes Plugin 1.0

This plugin saves you time when moving between your browser and Textmate. It adds clickable links to the Rails backtrace when an error occurs, and clickable “footnotes” to the bottom of each page (in development mode only). When clicked, these links open the correct file in Textmate and move your cursor to the precise line of interest.

In your plugins directory, install with:

svn export \\
svn://syncid.textdriven.com/svn/opensource/textmate_footnotes/trunk \\
textmate_footnotes

This plugin was also featured in the syncPEOPLE on Rails bundle for Textmate.

Simple URI Validation

Here’s a way to validate a url if you really want to make sure it’s real (i.e. this goes above and beyond merely checking that a regexp says it’s ok–instead, it goes out and pings the server.)

Create a file, validates_uri_existence_of.rb in your app’s lib folder:

require 'open-uri'

class ActiveRecord::Base
  def self.validates_uri_existence_of(*attr_names)
    configuration = { :message => "is not a valid web address" }
    configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash)
    validates_each attr_names do |m, a, v|
      begin
        # Try to open the URI
        open v
      rescue
        # Report the error if it throws an exception
        m.errors.add(a, configuration[:message])
      end
    end
  end
end

Add ” require ‘validates_uri_existence_of’ ” to your environment.rb. Then in your model:

class Repository < ActiveRecord::Base
  validates_uri_existence_of :url
end