ActiveRecord::Base#find shortcut
I use ActiveRecord::Base#find a lot in the Rails console. A lot. As a result, I’ve started doing the following:
1 2 3 |
class <<ActiveRecord::Base alias_method :[], :find end |
That little snippet saves me up to five entire keystrokes, every time I need to do a find!
1 2 3 |
prs = Person[5] prs = Person[:first] prs = Person[:all, :conditions => { :name => "Jamis" }] |
And, thanks to the existing hash and array semantics, it loses none of find’s readability for most cases. Good stuff!
Reader Comments
Wow, this is like a bucket of cold water. Except.. good cold water. I’m sure a lot of people are thinking “why didn’t I think of this?” right about now.
Are there any downsides to this? I guess I wonder why it isn’t just in core.
4 Apr 2007
I’ve been doing it for ages and I think it should be in the core.
4 Apr 2007
Elegant and intuitive, I like it.
4 Apr 2007
That’s really cool! Thanks for the tip.
One thing to be aware of, this doesn’t work when doing finds through associations:
@category.products[:all, ...] # won’t work
However, calling “find” on the association will still work.
4 Apr 2007
I added a similar method directly to my User class so I could quickly look up users by id, email address or subdomain. Never thought to add it to AR directly.
4 Apr 2007
Just last night, I was doing some work in the console, and I thought to myself, “I’m tired of typing User.find(..., I bet there is a way to make a shortcut, I’ll look into that later.”
Funny how you read my mind! Or maybe I read yours? Hmm…
4 Apr 2007
How about:
Person5 Person.first Person.all(:name => ‘Jamis’)
Tasty! :)
4 Apr 2007
Can this go in .irbrc?
4 Apr 2007
John, sure, just make sure there’s a guard around it so it only applies when running from script/console.
I put mine in config/environment.rb, generally. That way I can use it in my code, as well as in the console.
4 Apr 2007
That’s one of those Post-It note ideas. Ten seconds after the first time you see it, you’re wondering how you ever managed to survive without it.
4 Apr 2007
Sorry for two comments in a row, I’ve just been thinking about this. I think this absolutely belongs in Rails core. Here’s why.
A lot of what Rails does turns SQL into memory management. Just like you don’t need to keep track of pointers and address space any more with garbage-collected languages, you don’t need to know SQL to write trivial Rails apps. Obviously writing a complex Rails app very often can require SQL, but SQL to some extent disappears when using Rails.
find is a method name which refers to the implementation of the method. #[] is the default collection access method name, and it’s actually much more appropriate, because it masks the implementation and refers only to the purpose of the method. #[] tells you what the method does; #find tells you how the method does it. I think #[] should be the encouraged idiom, and #find relegated to history.
4 Apr 2007
Thanks, Giles. Here’s one counter argument for why it shouldn’t be in core.
It can’t be employed without surprises. Consider: Person[5] works great, but account.people[5] doesn’t (it just returns the 5th person in the list, rather than the person with id 5). Whereas Person.find(5) and account.people.find(5) both work as expected. That disconnect cannot be resolved without breaking the array semantics of plural associations.
That’s one argument, anyway. I’m actually kind of sitting the fence on this one. It’s certainly useful; perhaps someone could throw together a (trivial) plugin for it?
4 Apr 2007
This isn’t a very strong argument, but there is the fact that Person5 is a class method and account.people5 is an instance method. So it shouldn’t be expected to do exactly the same thing anyway.
Sorry about the bold text, btw. I was trying to do the hash-mark thing for instance methods.
4 Apr 2007
Just last night I was yearning for this!
4 Apr 2007
Sorry for the lapse to newbiedom, but where would you put this shortcut? In its own file? Included in every model?
4 Apr 2007
Luke, config/environment.rb is a good place for snippets like this.
4 Apr 2007
I think Og uses this as its default find method and I thought it was nice… But I had no idea you could do what you’ve done with [:first, :conditions => ...]... That is unreal!
5 Apr 2007
Wicked cool Jamis. I’ve been annoyed by all the typing in script/console as well, but never stopped to think there could be a better way. I actually vote for pluginizing it and keeping it out of core. I think it will just complicate future feature changes, where handling this needs to always be a consideration. Nice work as always.
6 Apr 2007
Hey Jamis. Why did you use “alias_method” instead of “alias”?
8 Apr 2007
Same as Roman, I always wonder exactly what is the different between alias_method and alias.
As far as I understand ‘alias’ is not a method… But apart from that I don’t know why one would use one instead of the other.
9 Apr 2007
Mostly, I prefer
alias_method
because I like its syntax better than that ofalias
. Otherwise,alias
andalias_method
function identically; if you look at the implementation ofalias_method
, you’ll see it calls the same function internally asalias
does. Becausealias
is an actual keyword and not a method call, it is probably slight faster, if that matters to you. Beyond that, I think it’s largely a matter of taste.Oh, and note that alias_method returns the module/class that it was called for, while alias returns nil, so you could potentially chain alias_method with other method calls.
9 Apr 2007
It is funny that you bring this up, Jamis. The other day as I was typing find I thought to myself, “wait, can I just use brackets here?”
The conclusion I came to was that there would be inconsistent semantics and also less uniformity Enumerable.find … which I believe is the part of Ruby that ActiveRecord::Base.find was modeled after.
In some ways it seems that AR find is moving more in the direction of taking a block as its argument… does anyone know what is coming down the pipe in the next version of Rails?
9 Apr 2007
would using [] make AR slower ?
i.e. it’s an extra lookup ?
10 Apr 2007
weepy, it won’t make any significant difference. The speed of the find itself is the bottleneck, not the extra method lookup.
If you are doing tons and tons of finds in a tight loop, you could try benchmarking it to see what the difference is, but I’ll be surprised if it makes much of a blip at all.
10 Apr 2007
Jamis: WRT Giles’ suggestion: There’s no need to break the semantics of #[]. If we’re duck-typing anyways. There’s no reason that a has_many needs to return an Array. It’s almost against the Ruby way. :-)
That’s what I do in the DataMapper anyways. Return a HasManyAssociation instance that’s enumerable. So account.people5 isn’t a true Array indexer, but aside from the performance hit of iterating through the loader to find the existing object with that key, there’s no database call or anything, so it’s still comparatively inexpensive since it would only be used at a high level (the application level).
weepy: Not at the application level. The most expensive loops are going to be in the object instantiation mechanisms. The methods relating to query-generation make up a small fraction of the cost in comparison. So I’d avoid it in framework internals, but at the level most people will be working at the expense is not worth thinking twice about.
15 Apr 2007