New Plugin: xls_fixtures

Posted by trevor Tue, 17 Oct 2006 17:54:46 GMT

Here’s a little something I whipped up that’s proved useful.

Yaml is a pretty good format for storing fixture data but it can be a real pain to set up a bunch of fixtures with repeating or incrementing data. There’s a much better tool out there for that – Excel.

One way you can ease the pain of generating reams of fixture data is to build it all up in Excel and export your spreadsheets as csv files and tell Rails to use csv fixtures. Unfortunately I couldn’t find a way to have a moniker (my name for the non-indented line in your yaml fixtures) in the csv files so that I could refer to fixtures by name in my tests.

So what I’ve done is to write a little plugin and rake task that converts xls files into yaml fixtures. See:

http://svn.protocool.com/public/plugins/trunk/xls_fixtures/

Note that it requires the ‘parseexcel’ gem (as in “gem install parseexcel”) and you need to arrange your rows and columns as per the README file in the plugin.

Posted in code | 2 comments

Pastie: It's "italics good"

Posted by trevor Tue, 25 Jul 2006 17:25:42 GMT

You come across interesting stuff by browsing pastie but this tops them all.

http://pastie.caboo.se/5900/wrap

no comments

A little (more) syntactic sugar in Rails

Posted by trevor Mon, 12 Jun 2006 17:48:56 GMT

This little extension to Ruby’s Array class is making the rounds:


class Array
  def sum
    inject(0) { |m, v| m + v }
  end
end

# Use it thusly:
[1,2,3,4,5].sum # => 15

Nice. But what if we want to sum a member of each array element, such as the ‘price_cents’ attribute on a bunch of ActiveRecord objects?


@order.line_items.collect {|x| x.price_cents}.sum
# or (my preference) using Symbol's to_proc method:
@order.line_items.collect(&:price_cents).sum

Even with the sugar that Symbol#to_proc adds, it’s just a bit… well, blech. We can do better:


class Array
  def sum
    if block_given?
      inject(0) { |m, v| m + yield(v) }
    else
      inject(0) { |m, v| m + v }
    end
  end
end

# Now we can say:
@order.line_items.sum(&:price_cents)

That’s just so much clearer.

Update: a recent changeset in the rails trunk adds a ‘sum’ method to Enumerable. It’s different from what I present above in that it only works with a block – which is okay for me because that’s my main usage pattern anyhow.

Posted in code | no comments

A Rails tale of caution: functional tests hanging with controllers in subdirectories

Posted by trevor Tue, 30 May 2006 23:27:05 GMT

This afternoon I was hit by a real headscratcher – documented here for future googlers looking for information on: functional tests hanging controllers subdirectories :-)

I added in some tests for controllers that were in a subdirectory (as in “./script/generate controller boo/hoo“) and suddenly rake started hanging.

I could run individual functional tests just fine but as a suite – forget it.

The source of the problem turned out to be twofold:

  1. in test_helper.rb we alias the get and post test methods so that we can optionally capture all of our output and run it through a validator.
  2. the require line that ./script/generate creates for loading test_helper.rb in your functional tests is relative to the current file’s directory. For functional tests in subdirectories you get a require argument ending with ”/../../test_helper” rather than the one ending with ”/../test_helper” as you do in top-level functional tests.

Even though those two different require lines point to the same file, from require’s perspective they are different – so the file is loaded twice.

And that’s a big no-no if you are doing a simple method alias without checks against doing it twice.

The bulletproof solution is to have tests use File.expand_path() on the argument to require. But if the generators keep spitting it out as they do now it’s one of those “pushing water uphill” situations. I smell a patch.

For now, I’m just wrapping my alias calls with a check of self.instance_methods to make sure the alias hasn’t already been done.

Posted in code | no comments

Living under a rock? Google and Sketchup

Posted by trevor Mon, 29 May 2006 01:16:40 GMT

A few years back, while I was building my house, I downloaded a demo copy of Sketchup to help visualize how everything would look. It’s a fantastic tool for 3D design, one of those rare pieces of software that just makes sense.

Unfortunately it’s also software that costs about $500 USD and as much as I wanted to, I simply couldn’t justify the expense. My love affair with Sketchup was doomed to be just a few short weeks as the demo period quickly slipped away.

Sketchup did leave me with a legacy though: Ruby. It ships with a ruby interpreter and has hooks to extend the software with Ruby scripts. Had I not seen the Ruby/Sketchup integration I probably wouldn’t have given Rails a second look when I stumbled across it a year and a half (gosh!) ago.

Okay, that’s enough nostalgia. The real point here isn’t whether I owe a debt of gratitude to a 3D modeling program for more than a year of fun, full-time work with Rails…

The real point here is that Google bought Sketchup and they are releasing cut-down free versions. I think that’s pretty big news and I have absolutely no idea how I missed it.

They’ve already released the free version for PC and apparently a free version for OS X is in the works as well.

I am so chuffed about this. It’s going to make designing my kids’ backyard playstructure a breeze.

Snippet: using 'inherited' to set filters in controllers

Posted by trevor Fri, 26 May 2006 22:51:37 GMT

One of my apps has a set of controllers in an ’/admin’ directory. They all must have a :login_required before_filter and they all must define an authorized? method that checks if the current user is an administrator.

For a couple of reasons the idea of having a special “AdminController” which everything under ’/admin’ would inherit from (and that set up the filter and authorized? method) kind of irked me.

First, there was the name clash – AdminController becomes ‘admin’ in routes and that clashes with my directory called ‘admin’, so I’d have to choose a name like “AdminBaseController” or some-such. Blech.

Second, I just didn’t like the extra file for the base controller. Call me picky, I can take it.

What I wanted was a way to say “if the controller is in the /admin directory then it needs this filter and this method”.

Class#inherited to the rescue. Here’s what I put in /app/controllers/application.rb:

# For all controllers in the 'Admin' namespace we set
# the login_required before_filter and define an authorized?
# method that checks user.is_administrator? 
def self.inherited(subclass)
  # call super first - otherwise any before_filters we add are lost
  super
  if subclass.name.split('::').first == 'Admin'
    subclass.before_filter :login_required
    subclass.send(:define_method, :authorized?, Proc.new {|user| user.is_administrator?})
    subclass.send(:protected, :authorized?)
  end
end

This is fine for my present needs. If controllers under /admin start to need any more shared behavior I’ll just bite the bullet and do the AdminBaseController thing – but for now this is clean and works.

Posted in code | no comments

Best Practices: a strong case for attr_accessible part 2

Posted by trevor Wed, 24 May 2006 22:31:58 GMT

This is a followup of part 1 which you should read before continuing here.

So… are the requirements satisfied? No.

It’s possible for any user to delete a project that they don’t actually own. More specifically, it’s possible for a user to arbitrarily assume ownership of any project they know the id of. After that, the project is theirs to do with as they choose.

The code that allows you to assume project ownership is here:

class UserController < ApplicationController
  def save
    current_user.update_attributes(params[:user])
  end
end

class User < ActiveRecord::Base
  attr_protected :is_administrator
  has_many :projects
end

Spotted it yet?

Read more...

Posted in code | 9 comments

Best Practices: a strong case for [redacted] part 1

Posted by trevor Wed, 24 May 2006 03:10:00 GMT

Tonight I decided to tip over my blog-post idea pickle jar and see if anything interesting fell out. This is the first one that didn’t get crumpled up and thrown in the trash (gotta love that pickle jar).

Instead of just blathering on I’d like to show you a few lines of code and invite you to answer a pop quiz:

class UserController < ApplicationController
  def save
    current_user.update_attributes(params[:user])
  end
end

class ProjectController < ApplicationController
  def destroy
    @project = Project.find(params[:id])
    if ( @project.user_id == current_user.id ||
      current_user.is_administrator? )
      @project.destroy
    end
  end
end

class User < ActiveRecord::Base
  attr_protected :is_administrator
  has_many :projects
end

class Project < ActiveRecord::Base
  belongs_to :user
end

Before anyone gets too bent out of shape – don’t worry about the stuff you don’t see. Everything you need is there.

Here’s the requirements we’ve been given:

  1. Users should not be able to grant themselves administrative capability
  2. Only administrators or the project’s owner can delete a project

And here’s the pop quiz: are those requirements satisfied? Why or why not?

Bonus points (for what they’re worth) will be given if you can tell me what I’m going to make “a strong case for” in the title of this post.

I’ll post my answers tomorrow (Wednesday) at 10:00 PM GMT.

[Edit] someone asked so I’ll clarify: the current_user method in the controllers always returns the correct user.

[Edit] another clarification: we can assume that actions not shown in ProjectController such as edit/save perform the same checks as ProjectController#destroy before doing anything.

[Edit] Part 2 is available now.

Posted in code | 11 comments

In The Smoking Room

Posted by trevor Wed, 26 Apr 2006 05:59:40 GMT

Note: there’s cussing in this post – you’ve been warned. And yes, I have shamelessly lifted from Daring Fireball.

Read more...

1 comment

pandora.com is uncanny

Posted by trevor Thu, 13 Apr 2006 19:24:00 GMT

I’m not sure how much I buy into the whole Pandora Music Genome Project thing.

Still, this morning I gave it a try and I have to say, it’s uncannily accurate at figuring out my tastes – it’s been quite a few songs since I had to click “I don’t like it”.

What’s even better is that apart from my starting point (Iron and Wine by the way) all but one of the tracks played were from acts I’d never heard before.

And whether I buy into it or not, it’s pretty interesting to click “Why did you play this song?”

Based on what you’ve told us so far, we’re playing this track because if features mellow rock instrumentation, a subtle use of vocal harmony, mixed acoustic and electric instrumentation, a vocal aesthetic and a minor key tonality.

Seriously? Not because 734 other people who bought ‘Woman King’ also bought this album? Go figure.

Older posts: 1 2