-
ActiveRecord::Base.with_scope { :only => 'in your model' } - Rails 2.0 a feature a day #4
Today's (un)feature covers the oft-abused and misunderstood
with_scopemethod. The excellent err.the_blog had a post on how you should be usingwith_scope(yes, only use it in your ActiveRecord models, not in your controllers or filters). Yes that blog post may have been written a long time back but it's still applicable to Rails 2.0.With Rails 2.0, the
with_scopemethod is nowprotected, meaning that you can no longer use it in your controllers (unless you use the#send(:with_scope), or#send!(:with_scope)in Ruby 1.9). I'm not even going to show you how you could use it in your controllers since you really shouldn't (unless you know what you are doing).Here's what
with_scopeallows you to do:class SiteOwner < ActiveRecord::Base # Returns list of distinct active sites. def active_sites(*args) with_scope :find => { :select => 'DISTINCT(site)', :conditions => { :active => true } } do find(*args).collect(&:site) end end endAnd even better example, stolen from the err.the_blog post mentioned above, is re-using scopes:
class Movie < ActiveRecord::Base def self.find_playing(*args) with_playing do find(*args) end end def self.find_playing_by_id with_playing do find_by_id(*args) end end def self.with_playing with_scope :find => { :conditions => [ state = ? AND visible = ?, NOW_PLAYING, true ] } do yield end end endI tend to use that a lot combined with
method_missingmagic (also from the err.the_blog post) to allow me to do things likeMovie.find_playing_by_name:def self.method_missing(method, *args, &block) if method.to_s =~ /^find_(all_)?playing_by/ with_playing do super(method.to_s.sub('playing_', ''), *args, &block) end else super(method, *args, &block) end endIf you prefer to get these for free, check out the the Rails scope-out plugin implements many of the ideas in the err.the_blog post.
About the contributor, Josh Peek
Josh Peek (WorkingWithRails profile) was the man behind the "protection" of with_scope. This is not Josh's greatest contribution to Rails, of course - Josh has been a very productive and active Rails contributor since the very beginning. A winner of one of the monthly Rails Hackfest, you can read up on the ever-elusive (I say "elusive" because he doesn't have a blog - it's just patch after patch after patch) Josh Peek in this post-Rails Hackfest interview.
Josh is also the man behind the deprecation and "plugin-izing" of dynamic scaffolding and pagination (yes, those are gone from Rails 2.0) among a great number of other patches and test coverage improvements. Right now, Josh appears to be working on refactoring ActionView.
Outro
Apologies for the late-on-arrival Rails 2.0 a feature a day posts, but such is life. Feature #5 follows immediately after :)
-
Concatenate your stylesheets and JavaScripts in 3 seconds - Rails 2.0 a feature a day #3
I don't think I need to explain how concatenating your 5 stylesheets and 12 JavaScript files into single files each makes your webpages load faster. Unless you're using multiple asset hosts (Rails 2 allows for multiple asset hosts), then it becomes tricky to say for sure which method is better, but I digress.
There're a bunch of really useful plugins that help you bundle your JS and CSS files, most of them offering minifying/packing of JS and stripping of comments and whitespace in CSS as well. I was a big fan (still love it) of Asset Packager, but newer plugins like PackR and Bundle-fu work great too.
With Rails 2.0 though, unless you want the minifying/packing/comment-stripping features, you can do away with installing a packager plugin. Remember your trusty
javascript_include_tagandstylesheet_link_tag? They now accept a:cacheoption:# :cache => true creates a cached javascript named all.js. <%= javascript_include_tag :all, :cache => true %> # This becomes: # <script type="text/javascript" src="/javascripts/all.js"></script> # You can name your javascript too, by passing a name instead of true. <%= javascript_include_tag 'jquery', 'swfobject', 'application', :cache => 'base' %> # This becomes: # <script type="text/javascript" src="/javascripts/base.js"></script> # Same deal for stylesheets... # :cache => true creates a stylesheet named all.css. <%= stylesheet_link_tag :all, :cache => true %> # This becomes: # <link href="/stylesheets/all.css" media="screen" rel="Stylesheet" type="text/css" /> # You can name your stylesheet like for javascript_include_tag. <%= stylesheet_link_tag 'yui/reset-fonts-grids', 'application', :cache => 'styles' %> # This becomes: # <link href="/stylesheets/styles.css" media="screen" rel="Stylesheet" type="text/css" />Of course, this only takes place if caching is turned on (
ActionController::Base.perform_caching), which is on by default in the production environment and off in the development environment.For those who want to look at some code, check out the changeset.
About the contributor, DHH
Well, DHH hardly needs any mention. David has_one Wikipedia entry. Nice little touch on this new feature though, I love the convenience.
I promise, like I'd said before, to cover contributions by someone less "mainstream" in the next Rails 2 "feature a day" blog post.
-
gchartrb = Ruby + Google Chart API
Google Chart API looks quite useful. I've used Gruff to generate graphs before but this looks way easier (and much lighter on the server without all that ImageMagick magic!) I'll have to play with it someday - if only I didn't have to mess with those URL params!
Well, Deepak Jois from singapore.rb (the local Ruby user group) recently announced a Ruby wrapper around the Google Chart API: gchartrb (rdocs). You can get it as a rubygem, just run:
sudo gem install gchartrbLooking good so far and I dig the familiarity - Deepak says the API is Gruff-inspired and it tells. If you need a charting solution (for reporting or whatever), this is a gem to consider.
Ah yup, there'll be no Rails 2 feature today - just submitted a couple of Ruby 1.9 compatibility patches for Rails and feeling a little bummed out now (dependency handling is not that pleasant in Rails). Rails' Ruby 1.9 compatibility needs you!
-
Declarative exception-handling in your controllers - Rails 2.0 a feature a day #2
I know I said I was going to try and keep the features in "a feature a day" to those not mentioned in popular places, but this feature is one of the few nicest features in Rails made by a contributor outside of the Rails core so I couldn't resist mentioning it.
First, an example
class CommentsController < ActionController::Base rescue_from Comment::Spammy, :with => :moderate_comment rescue_from ActiveRecord::RecordNotFound, :with => :redirect_if_not_found protected def moderate_comment # Handle the exception, like placing the comment in a moderation queue # and then rendering a different action. end def redirect_if_not_found # Redirect somewhere... end endCompare this to what you had to do with Rails 1.2.6 and earlier:
class CommentsController < ActionController::Base def rescue_action_in_public(exception) case exception when Comment::Spammy # Handle the exception as above. when ActiveRecord::RecordNotFound # Redirect somewhere... else super end end endMuch clearer and reads almost like English. I don't think I even need to explain what the code in the 1st example is trying to do.
Let's try another example
Here's another common use-case: dealing bad create/update actions. With
rescue_from, you can simply do this:class ApplicationController < ActionController::Base rescue_from ActiveRecord::RecordInvalid do |exception| render :action => (exception.record.new_record? ? :new : :edit) end endNo need for actions to be coded like this anymore!
def create @comment = comment.create!(params[:comment]) rescue ActiveRecord::RecordInvalid render :action => :new endThis patch was committed in revision 7597 after a short discussion on the syntax and implementation over at the ticket. (More improvements on the implementation over at ticket #9645 for the interested.)
About the contributor, Norbert Crombach
Norbert Crombach (Rails Trac username: norbert, WorkingWithRails profile) is another regular Rails contributor hailing from the Netherlands. The
rescue_fromis, in my opinion, one of his more significant contributions (and what a great one it is too).Some final words
While the new declarative
rescue_frommacro is useful, I've found myself liking the merb way of dealing with exceptions, where exception handling is delegated to an Exception controller. You can use layouts, templates, helpers and filters just like in ye plain olde controller. I'm still not so sure if it's worth making a patch for Rails to emulate this behavior yet though!Update: Rails 2.0 a feature a day #3 is now available Concatenate your stylesheets and JavaScripts in 3 seconds
-
Time#advance now uses :weeks, :hours, :minutes, :seconds options - Rails 2.0 a feature a day #1
Time#advanceandDateTime#advancejust got, erm, more betterer in Rails 2.0.In Rails 1.2.6 and earlier,
Time#advanceandDateTime#advanceonly accepted:year,:monthand:dayoptions. You'd think:hours,:minutesand:secondswould work too but due to a bug in the code (see ticket #9818), you get something like this instead:time = Time.utc(2000,10,1,10,30,45) # => Sun Oct 01 10:30:45 UTC 2000 time.advance(:days =>3, :hour => 2) # => Wed Oct 04 02:00:00 UTC 2000Notice how the hour is changed instead of being advanced.
Now, thanks to Geoff Buesing's efforts, passing
:hours,:minutesand:secondsoptions work as you'd expect:time = Time.local(2005,2,28,15,15,10) # => Mon Feb 28 15:15:10 0800 2005 time.advance(:hours => 5, :minutes => 20, :seconds => 25) # => Mon Feb 28 20:35:35 0800 2005Hell, you can even
#advanceby:weeks(see ticket #9866):time = Time.local(2005,2,28,15,15,10) # => Mon Feb 28 15:15:10 0800 2005 time.advance(:weeks => 2) # => Mon Mar 14 15:15:10 0800 2005+1 for consistency and properly behaving code. Thanks to this #advance patch, Geoff was also able to re-use #advance to refactor some other parts of ActiveSupport's time-related extensions.
About the contributor, Geoff Buesing
Geoff Buesing is a regular Rails contributor and I've known him to be the date/datetime/time guru - just look at his submitted patches if you're not convinced. Not too long ago Geoff was given well-deserved commit rights (congrats Geoff)! You won't find him on the Ruby on Rails Core team page yet though - it's probably out of date.
Check out his blog at mad.ly where he writes mostly his adventures with on unobtrusive JavaScript in Rails.
subscribe via RSS