#212 Refactoring & Dynamic Delegator
May 03, 2010 | 7 minutes | Active Record, Refactoring, Rails 3.0
Learn how to refactor a set of conditional Active Record queries using a Dynamic Delegator.
- source codeProject Files in Zip (102 KB)
- mp4Full Size H.264 Video (11.9 MB)
- m4vSmaller H.264 Video (8.19 MB)
- webmFull Size VP8 Video (21.1 MB)
- ogvFull Size Theora Video (17.1 MB)
Awesome, I'm excited to see all the benefits of the new Active Record query interface, especially now that `scope` is a first class citizen.
Doesn't this really call for mutator methods in ARel (sorry for being lazy and not checking the rails-core)?
Would that be a cleaner solution?
Your DynamicDelegator pattern is a great place to use Rails returning() method as well.
returning(scoped) do |model|
model.method if condition?
(Again) issues with the video file when viewing with VLC (platform: Linux), same as last time. Used to work fine before you started experimenting with codecs ;-(
On the subject: Yes, you can do lots of tricks with Ruby (or any modern language). But what's the point? You don't gain much (in this example you don't get ANYTHING you didn't already have) but loose readability and maintainability. This is even more important in large(r) projects with lots of people - one can complain about the stupidity of the univerye as much as one likes, but "average programmers" simply won't EVER be as good as good ones, and you cannot afford hiring only top-experts for everything.
IMHO the place for such (and much worse) trickery is inside frameworks such as rails, where only experts work and where the benefit is greatest because it multiplies, but not in "end programmer" application code.
What a coincidence, I made a blogpost about the same technique a week ago: http://iain.nl/2010/04/viewtranslator-a-dynamic-dispatcher/
I agree with Micael Hasenstein: it's something interesting in gems to provide pretty APIs, but not as end code itself. Though ultimately one part of your code is the API for that other part.
@CodeOfficer: I think returning is not the right way to go. I think using tap is better, since it is included in Ruby itself (since 1.8.7).
Great, thank you Ryan. Just what I was waiting for!
CodeOfficer: Will look into that returning() method, thank you.
I don't really know everything, but shouldn't you use Arel entirely when doing where predicates, that is, something like :
products.where(products.table[:name].matches("%" + params[:name] + "%")) if params[:name]
Clever little class, but I think a name like AutomaticChainer might have better conveyed what it does.
Has anyone used Squirrel? For Rails 2 projects I like it to do conditional logic in my queries. Also searchlogic does cool stuff with named_scoped. For conditional logic I do like Squirrel better.
Hi Ryan, do u know a working way to autheticate Rails3 over openid or twitter?
@iain I'd agree if the site were called Rubycasts. Returning is still an expressive pattern for rails enthusiasts.
Thanks for this episode Ryan but I have to agree with M. Hasenstein and iain. I dont think there is much you gain from that. The readability decreases dramatically. Everybody could have changed the first lines of code but the last ones? I am not so sure about that. Not everthing must be DRY.
Anyway I am looking forward to the next cast!
Nice technique, but I think it's wasted in the example. A few characters saved, but a whole lot of readability lost. The code goes from 5 lines to many more over 2 files.
Codec is unhappy on VLC/SMPlayer (Ubuntu). Works fine in Totem (same as last week).
Thank You, Ryan, for another great screencast.
Does anyone know where there isn't a where! method to do part of what dynamicdelegator can do ?
Great cast, it made me think and is an elegant solution. I wish you made more casts like this - it is the ones I enjoy the most.
FYI: Method #scoped is not necessary since
MyModel.ancestors.map(&:to_s).grep /Arel/ # => ["Arel::Sql::ObjectExtensions", "Arel::ObjectExtensions"]
This means that MyModel class acts as a relation itself, and #where could be called on it directly.
Good screencast, Ryan. I can't wait until Ruby 1.9 becomes the standard -- supporting 1.8.x while still taking advantage of some of the goodies that 1.9 dangles in front of us is a real pain.
For another neat trick afforded by BasicObject, see the writeup I did on BasicObject#I at http://metautonomo.us/2010/04/10/ruby-1-9-basicobject-and-not/, too. :)
@hakunin: not quite. Arel adds some extensions to object via mixins, true (to_sql for example) but Ryan wants an ActiveRecord::Relation, which is different.
Thank You, Ryan, for another great screencast.
I think you're overdoing it with this one. More code, no gain at all. Just more obfuscation.
Thanks Ryan, love your site.
I'd never use this though. What's wrong with a bit of code repetition within the same method?
Once the search code is out of your controller and in your model, that's good enough, you've done well.
Imagine having to work on a project that's full of little "optimizations" like this, you would go crazy.
This Railscast helped me learn something that proved to be very useful. I applied this in a real scenario where it simplified a lot of what I had before.