All the members of Los Lonely Geeks were hanging out in the Portland Campfire last night, contributing to the constant stream of YouTube videos, LOLCATS, LOST spoilers, and Ruby code. But in the middle of an intense academic colloquy regarding whether it is “I CAN HAS” or “I CAN HAZ,” I had a stroke of genius about something I’d been using for months: named_scope.
You’re probably familiar with Rails’ named_scope feature. It allows you create stackable “scopes” for your ActiveRecord queries (for example, User.active might find all those users whose account_expires date is after today or something). I’d always used it basically for that: scoping queries for finding records. But as I was creating a couple of new ones, I had a thought: I wonder if you can use them to create records with attributes, too.
Of course I had to find out.
Immediately, I opened script/console on an app and type a few things in:
>> Page.named_scope :awesome, {:conditions => {:title => "This is awesome."}}
=> (stuff)
>> Page.awesome.new
=> #<Page id: nil, title: "This is awesome.", created_at: nil, updated_at: nil>
Whoa. I always really like finding a new feature of something that I make a lot of use of. It’s like getting a whole new feature altogether.
So, of course, I shared this, which, of course, led to hacking. I tossed out the idea and Nicolas Sanguinetti (a.k.a., foca) tossed out a quick implementation within like 20 seconds (which now lives on as Kaleidoscope at his Github). We tinkered a bit after that, but in general the implementation didn’t change at all. When he put his implementation in Campfire, I’d just opened Textmate, so I jacked his implementation and within 30 minutes had built what I’m now calling foundry.
Foundry is another fixture replacement plugin (just like modelstubbing, fixturereplacement, factory_girl, machinist, and the trillions of others), but unlike most of these, it stays clear of ActiveRecord’s internals. It simply creates these scopes for you and lets you build objects off of them. For example, here’s a basic User factory:
model User do
factory :valid_user, :name => "Jeremy", :login => "jeremy"
end
…which allows you to do this in a test:
User.valid_user.create
You can also provide blocks and some other crazy stuff, and you can check out the README for more information. But its implementation is powerful in its simplicity, and as evidence of that, you can do really flexible stuff like this:
require 'faker'
module Unique
def unique(attr)
if [:name, :first_name, :last_name].include?(attr)
return Faker::Name.send(attr)
elsif [:company_name, :company_bs, :company_catch_phrase].include?(attr)
return Faker::Company.send(attr.to_s.gsub(/company_/, '')) # name is in user and company
elsif [:email, :free_email, :user_name, :domain_name].include?(attr)
return Faker::Internet.send(attr)
else
raise ArgumentError, "I'm not sure what random data you want by specifying #{attr}!"
end
end
end
module Foundry
class Runner
include Unique
end
end
That will provide a unique method to your factories which you can use to generate names and such from Faker. Now, in your factories you can do:
# In your foundries.rb or whatever
model User do
factory :unique do
{:conditions => {:name => unique(:name), :login => unique(:user_name), :password => "1234"}}
end
end
…and every time you generate a record, you’ll get unique data:
User.unique.create
# => #<User id: 12, name: "Wanda Sharp", login: "delia", :password => "1234", created_at: ..., updated_at: ...>
User.unique.create
# => #<User id: 13, name: "Alec Eichmann", login: "adam", :password => "1234", created_at: ..., updated_at: ...>
User.unique.create
# => #<User id: 14, name: "Derek Gleichner", login: "jared", :password => "1234", created_at: ..., updated_at: ...>
Awesome, eh? You can even chain these up just like you would normally do with named_scope's. So, you could have User.valid_user.with_role("admin").with_permissions(permissions) or something like that.
You can check out foundry at my Github, and if you dig it, follow it or fork it from its page.


3 Comments
Awesome, I love using named_scopes and this is a great way to provide clarity of intention in unit testing.
Named Scope sucks.
This is brilliant. I totally drank the cool aid on Thoughtbot’s Shoulda and Factory Girl, but I always wanted a way to use these kind of semantics when building test objects.
Awesome.
Make your voice heard
Sorry, but comments are closed for this item.