When, a Rails plugin
Posted by Dan Croak
Feb 15
Ever write this?1 2 3 4 5 6 7 8 9 10 |
before_filter :authorize protected def authorize unless logged_in? session[:return_to] = request.request_uri redirect_to login_url and return false end end |
1 2 3 4 5 6 7 8 |
before_filter :authorize, :unless => logged_in? protected def authorize session[:return_to] = request.request_uri redirect_to login_url and return false end |
This is more expressive. The conditional logic is no longer hidden in the method.
The authorize method now has a single responsibility: to authorize. The before_filter, responsible for performing some action before your controller action is called, is now smarter about when it is supposed to run.
Introducing When
The When Rails plugin adds :if and :unless modifiers to before_filters, most ActiveRecord callbacks, and validations.
Get it:
piston import https://svn.thoughtbot.com/plugins/when/trunk vendor/plugins/when
Then start improving your controllers:
1 2 3 4 5 6 7 8 9 10 11 12 |
before_filter :deny_access, :unless => :admin? protected def deny_access flash[:failure] = "You do not have access to that page." redirect_to home_url end def admin? logged_in? and current_user.admin? end |
1 2 3 4 5 6 7 |
before_create :encrypt_password, :unless => lambda {|user| user.password_confirmation.blank?} after_save :send_alerts, :if => :alerts? validate_on_create :add_unsupported_type_error, :unless => lambda {|document| SUPPORTED_FILE_TYPES.include? document.file_type} |
Coding without ifs?
Jared makes a strong argument that this is actually removing conditional logic from your program. I tend to agree.
It reminds me of defining constants in your environments so you don’t write conditional logic that checks the RAILS_ENV. That kind of control flow belongs in the framework.
Whether this is removing conditional logic is moot, though. It just feels right, and isn’t that why we write Ruby?
Update ... After releasing this, we found out this is already in Rails trunk. Awesome! It’s a great feature and should be in the framework. So, if you’re not running on Edge, use the when plugin to get this feature now. Then, remove it when you upgrade to the next version of Rails, when you’ll get :ifs and :unlesses “for free.” Happy coding!
Comments on this post
Feb 15
Josh Peek said,
The refactoring of ActiveSupport::Callbacks adds conditionals everywhere (that uses the module). However, controller filters have not been moved to it, yet. Its kind of difficult because of the complexity of the filter logic.
Feb 15
Pete Forde said,
Oh man, that’s some sweet sticky right there.
Feb 15
Dan Croak said,
Josh, you’re breaking my heart! I just checked out http://svn.rubyonrails.org/rails/trunk and see what you’re talking about ...
Well, for those folks (like us) who don’t run on edge, at least we’ll have this benefit a little earlier. Also, its comforting knowing that the test coverage for this little plugin is nuts:
792 tests, 1536 assertions, 0 failures, 0 errors
Feb 15
Mark Wilden said,
Actually, the improved #authorize does more than just authorize – it saves the user’s place, purely for his convenience. That has nothing to do with authorization, but it’s the obvious place to put this code.
One could make the point that you’re now
splittingthe logic between #authorize and the before_filter call. What the usual “authorize” does (including this one) is handle everything to do withensuringthe user is authorized, and what to do if he isn’t, and where to go afterwards. So a better name might be #ensure_authorized.///ark
Feb 15
Matt Jankowski said,
Could something like this be added to #verify as well?
What if you could pass arbitrary symbols to #verify, instead of only using it’s built in conditionals?verify :logged_in?, :redirect => :login_url, :add_flash => { :failure => 'Log in first' }For any filters which were only doing redirection and flash setting, that’s pretty straightforward.
Feb 15
justin said,
That would have looked more impressive up on Github… too bad…
Feb 15
anonymous said,
“After releasing this, we found out this is already in Rails trunk. Awesome! It’s a great feature and should be in the framework. So, if you’re not running on Edge (like us), use the when plugin to get this feature now.”
If your running edge, then why didn’t you see this in trunk?
Feb 15
Chad Pytel said,
@anonymous: I guess the wording we used in the update is ambigious, we meant it to mean that we are not running on Edge.
Feb 16
Keith said,
Sorry to be negative, but this is actually in my opinion a poor refactoring. Instead of making it clearer as you claim, you now have a misnamed method. “Authorize” now simply redirects to the login page – it does not “authorize” anything. You can rename the method to something more expressive, like “bounce_to_login” or something (not a great name either…), however in either case you are now requiring the developer reading this to look at the entire file to understand the logic. In a larger controller, where the method being called from the filter might be at the bottom, it could be easy to miss. The “unimproved” code is completely understandable at first glance. Again, at the very least, the method should have a name that says what it does.
I’m not against using conditionals in filters at all, but this particular example as written is not really an improvement.
Feb 17
Oleg said,
I honestly don’t understand how putting complex conditionals right there on the filter improves readability of the code. :if => logged_in? might be fine but the whole lambda thing seriously should be crammed in the method.
Feb 17
Dan Croak said,
@anonymous: what Chad said. I edited that line to be less ambiguous.
@Keith: Fair point. I wouldn’t be against renaming :authorize to :login if it fit the tastes of the development team.
@Oleg: In general, the lambda option is important to have in the arsenal. If what you write in the lambda is short and expressive enough, there is no need to create a method with an intention-revealing name later in the model or controller. Particularly in a fat model, you don’t want to have to bounce down 100 lines to find out what :password_confirmed? actually does. Similar to Keith’s comment, I believe taste may dictate which option (method vs. lambda) you prefer in a particular scenario.
Feb 17
Floyd said,
Whoa, hold on, are you saying there is no objectively correct solution to the problem of method naming? Jesus, no, just always use ‘each’ or something—anything than leave it at a matter of taste.
Feb 20
Steve Tooke said,
Merb’s controller filters now have conditionals too. Inspired by this post.
See Here
Sorry, comments are closed for this article.
© 2000 - 2009 by thoughtbot, inc.
written by a bushel of tiny robots
Come “ride the toad” on Hoptoad, the app error app.
Thunder Thimble: Brand monitoring for social media.
Widgetfinger: Simple content management for simple websites.
Tee-Bot, funny shirts your friends won't understand!
Umbrella Today: “It’s like totally the simplest weather report ever, Julie.”
Thoughtbot
thoughtbot is a technology consulting firm that provides web application development and design services. We focus on building modern systems, embracing good ideas and delivering elegant solutions.
Interested in learning Rails?
Sign up for our beginning or advanced training.
Archives