Named Scope: It's Not Just for Conditions, Ya Know?

Posted by ryan
at 10:47 AM on Wednesday, August 20, 2008

Named scopes in Rails are great, everybody knows that. They’re usually used to create granular, chainable sets of SQL conditions that nicely encapsulate your domain query logic. Here’s a simple example:

1
2
3
4
5
6
7
8
9
10
11
12
class Article < ActiveRecord::Base
  
  # Get all articles that have been published
  named_scope :published, :conditions => ['published = ?', true]

  # Get all articles that were created recently
  named_scope :recent, lambda { { :conditions => ['created_at >= ?', 1.week.ago] } }

end

# Get all recently created articles that have been published
Article.published.recent #=> [<Article id: ...>, <..>]

However, as much as I use named_scope for this purpose, I also use it for some smaller and still useful functions. For instance, I find that I often need to just fetch the first X number of results for any particular query. Instead of having to call find with the :limit option you could create the following named_scope:

1
2
3
4
5
6
7
8
9
class Article < ActiveRecord::Base
  
  # Only get the first X results
  named_scope :limited, lambda { |num| { :limit => num } }

end

# Get the first 5 articles - instead of Article.find(:all, :limit => 5)
Article.limited(5) #=> [<Article id: ...>, <..>]

Hey, any less typing I’ll take, and I find myself using this limited named_scope a lot. But let’s pimp it a little so that you don’t always have to supply the number, and make it default to the per_page value that exists on the class if you’re using will_paginate.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Article < ActiveRecord::Base
  
  # Only get the first X results.  If no arg is given then try to
  # use the per_page value that will_paginate uses.  If that
  # doesn't exist then use 10
  named_scope :limited, lambda { |*num|
    { :limit => num.flatten.first || (defined?(per_page) ? per_page : 10) }
  }

  def per_page; 15; end

end

# Get the first 15 articles
Article.limited #=> [<Article id: ...>, <..>]

# Get the first 5 articles
Article.limited(5) #=> [<Article id: ...>, <..>]

Note that we have to use the variable length *num argument in the lambda to allow for no arguments.

Cool, so we’ve got a handy little tool for our toolbox now. Here’s another one I find myself using that isn’t strictly a conditional scope – ordered:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Article < ActiveRecord::Base
  
  # Order the results by the given argument, or 'created_at DESC'
  # if no arg is given
  named_scope :ordered, lambda { |*order|
    { :order => order.flatten.first || 'created_at DESC' }
  }

end

# Get all articles ordered by 'created_at DESC'
Article.ordered #=> [<Article id: ...>, <..>]

# Get all articles ordered by 'updated_at DESC'
Article.ordered('updated_at DESC') #=> [<Article id: ...>, <..>]

Be careful with this one, however, as with_scope (which is really what is powering named_scope) doesn’t know how to handle multiple order clauses. So, you can only used ordered once per call chain.

I’ve bundled these scopes up into a “utility scopes:http://github.com/yfactorial/utility_scopes” plugin/gem if you think they look useful to you. I’ve also added some class-level convenience initializers to let you override the default values (like the default limit and default order clause):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Article < ActiveRecord::Base

  # This class's default ordering (if not specified
  # defaults to 'created_at DESC'
  ordered_by 'published_at DESC'
  
  # By default, return 15 results (if not specified
  # defaults to 10
  default_limit 15

end

# Get the first 15 articles ordered by 'published_at DESC'
Article.ordered.limited #=> [<Article id: ...>, <..>]

# Get the first 15 articles ordered by 'popularity ASC'
Article.ordered('popularity ASC').limited #=> [<Article id: ...>, <..>]

# Get the first 20 articles ordered by 'popularity ASC'
Article.ordered('popularity ASC').limited(20) #=> [<Article id: ...>, <..>]

Need a little something else? How about this with scope I’ve included which will eager load the specified associations:

1
2
3
4
5
6
7
8
9
class Article < ActiveRecord::Base
  has_many :comments
  has_many :contributors, :class_name => 'User'
end

# Get the first 10 articles along with their comments, comment authors and article contributors
# This is equivalent to
# Article.limit(10).find(:all, :include => [{ :comments => :author }, :contributors])
Article.limit(10).with({ :comments => :author }, :contributors)

You can get all these goodies yourself by doing the following in your Rails 2.1 app. In config/environment.rb specify the gem dependency:

1
2
3
4
5
Rails::Initializer.run do |config|
  # ...
  config.gem "yfactorial-utility_scopes", :lib => 'utility_scopes', 
    :source => 'http://gems.github.com/'
end

And then to get the utility_scopes gem actually installed on your system:


rake gems:install GEM=yfactorial-utility_scopes

Or you can just install the gem as you normally would:


sudo gem install yfactorial-utility_scopes -s http://gems.github.com

Independent of whether or not you find these scopes useful, remember that named_scope is all up in your queries’ bidness – not just your queries’ conditions

Have some utility scopes you find to be indispensable? Let me know here or send me a request on github (user is yfactorial).

tags: ruby, rubyonrails

Rails 2.1 Released - Summary of Features

Posted by ryan
at 9:59 AM on Monday, June 02, 2008

Those of you that have purchased the Rails 2 Peepcode can download a free update with these features explained in further detail.

Rails 2.1 was released over the weekend during RailsConf. As always, here’s a list of the latest and greatest features in this release:

Major Rails 2.1 Features and Changes

(sorted by date added – most recent first)

Chow down.

tags: ruby, rubyonrails

"loaded_specs" Gem Error

Posted by ryan
at 3:30 PM on Tuesday, May 13, 2008

For those of you that have run into this gem error when running the latest rails:

./script/../config/../vendor/rails/railties/lib/initializer.rb:175:in `install_gem_spec_stubs': undefined method `loaded_specs' for Gem:Module (NoMethodError)
        from ./script/../config/../vendor/rails/railties/lib/initializer.rb:175:in `reject!'
        from ./script/../config/../vendor/rails/railties/lib/initializer.rb:175:in `install_gem_spec_stubs'
        from ./script/../config/../vendor/rails/railties/lib/initializer.rb:89:in `send'
        from ./script/../config/../vendor/rails/railties/lib/initializer.rb:89:in `run'
        from ./script/../config/boot.rb:46:in `load_initializer'
        from ./script/../config/boot.rb:38:in `run'
        from ./script/../config/boot.rb:11:in `boot!'
        from ./script/../config/boot.rb:109
        from script/dbconsole:2:in `require'
        from script/dbconsole:2

the solution is a quick and dirty:

gem update --system

Or, if you currently have rubygems v0.8.4 or earlier installed:

gem install rubygems-update
update_rubygems

Just a public service announcement from the “Ryan’s been bitten by this one” dept.

What's New in Edge Rails - In Chinese!

Posted by ryan
at 3:05 PM on Tuesday, January 08, 2008

The virus that is Rails is spreading. Thanks to Yudi, my posts on new features in edge rails have all been translated into Chinese. Surely a good sign for Rails and Ruby.

Thanks to Yudi for doing the unenviable task of wading through my diatribes, I hope your hard work is appropriately rewarded.

tags: ruby, rubyonrails

Rails 2.0 Final Released! - Summary of Features

Posted by ryan
at 7:43 AM on Friday, December 07, 2007

It appears that, after a short release candidate period, Rails 2.0 (.1) final has now been released. In celebration of such an event, I’ve compiled a list of all the features of Rails 2 that I’ve written about so far. Soak it in, baby.

Though I’ve made my best effort to keep all the feature descriptions up-to-date, I can’t guarantee their complete accuracy. For a more exhaustively and thoroughly vetted list of features presented in a more refined manner you may just have to suck it up and buy the Peepcode min-book PDF on Rails 2. “It’s good stuff”, says the author!

Major Rails 2 Features and Changes

(sorted by date added)

For another great feature overview, check out the official rails blog announcement.

Anybody ready for Rails 3?

tags: ruby, rubyonrails

Ruby on Rails Podcast

Posted by ryan
at 3:29 PM on Friday, November 23, 2007

For those that want to hear the grating sound of my voice, Geoffrey Grosenbach just put up a conversation we had earlier this week over at the Ruby on Rails podcast. It’s a pretty lengthy episode, clocking in at around 25 minutes – so only give it a listen if you’re looking for a sure-fire way to get to sleep.

tags: ruby, rubyonrails

ActiveResource Available as a Gem 36

Posted by ryan
at 9:30 AM on Thursday, April 26, 2007

In the past, whenever I’ve spoken to a group about ActiveResource, one of the questions that regularly comes up is if ActiveResource is available as a gem. Trying to install it from the svn tree is a little less than straight forward. With ARes now scheduled to be a part of Rails it will be installable as a gem. For now, though, prior to the official Rails release you can get ARes installed with:


gem install activeresource --source http://gems.rubyonrails.org

(Thanks to DHH for pointing this out in my last post)

When Rails is released you will be able to use the more conventional:


gem install activeresource

tags: ruby, rubyonrails, REST

What I'll Be Attending at RailsConf

Posted by ryan
at 8:46 PM on Wednesday, April 25, 2007

It’s interesting to see that, from a non-scientific summary of the sessions available at rails conf, scalability and REST are the topics du jour for the Rails community.

Here’s what I think I’ll be attending come May 18th, what about you?

Ping me over at conferencemeetup.com if you want to say hello, or share a brew, during the aforementioned festivities.

rubyonrails, REST, railsconf2007

REST & ActiveResource

Posted by ryan
at 10:09 AM on Wednesday, March 14, 2007

See here from some syntactical changes that occurred after this post was published

I gave a little chat last night at the Central Virginia Ruby Enthusiast’s Group – otherwise known as the Richmond Ruby Brigade – about REST, ActiveResource and how to build client services from the marriage of the two.

It was a really good group and I’d encourage anybody in the central virginia area to go check them out if they get the chance. It sounds like they’ve got some good speakers lined up in the next few meetings to make it worth your while (and they had free pizza last night – what a grade-A operation!)

For those that are interested, here’s the PDF version of my presentation. As you can imagine much of the value is lost without having the dialogue to accompany the presentation, but I think it somewhat stands on its own. Most of the examples are drawn from my ongoing work on ContactsAPI – a site meant to further the adoption and education of ActiveResource and REST in general. Check it out if you have the time, but again, it’s a work in progress.

tags: ruby, rubyonrails, cvreg

Rails Hackfest 2007 Announcement 12

Posted by ryan
at 2:21 PM on Monday, January 29, 2007

The Rails Hackfest 2007 list of winners is up and it looks like I’m fortunate enough to be listed. Much gratitude to Derek (like we’re on a first-name basis) for sponsoring the contest.

tags: rubyonrails, rails, railsconf2007

What's New in Edge Rails: 1.month.from_now.no_longer.effed

Posted by ryan
at 10:10 AM on Friday, January 26, 2007

Brian Donovan has a great writeup of his new Duration fix to the Rails’ Time convenience methods that, until now, have been slightly broken.

Here’s the summary. Before (bad):

1
2
3
4
Time.now
#=> Thu Jan 25 21:01:31 -0800 2007
1.month.from_now
#=> Sat Feb 24 21:01:34 -0800 2007

Oops – apparently one month only equals 30 days, not really one month. But now, in edge:

1
2
3
4
Time.now
#=> Thu Jan 25 21:01:31 -0800 2007
1.month.from_now
#=> Sun Feb 25 21:01:34 -0800 2007

And check out this little inspect goodie:

1
2
>> 3.weeks
#=> 21 days

And just so you’re not mislead by this post’s title, this fix applies to all the time helpers, not just month.from_now. Read the full rundown to become fully enlightened.

tags: rubyonrails, rails

Rails Cookbook Released (w/ Rails 1.2 Content)

Posted by ryan
at 8:21 AM on Friday, January 19, 2007

The aforementioned Rails Cookbook from O’Reilly, to which I made a minute contribution, has been released… including Rails 1.2 relevant content like ActiveResource and RESTful development.

Go getcha one.

(Here is a full TOC for the curious amongst you)

So You Want to Learn ActiveResource...

Posted by ryan
at 11:10 PM on Wednesday, January 17, 2007

But you can’t find much info on the subject? Stay tuned to this little teaser to learn ActiveResource in a live, real-world scenario.

Rela.tv and Gr.egario.us Taken Down

Posted by ryan
at 10:03 PM on Wednesday, January 10, 2007

As part of a larger consolidation effort I have taken down Rela.tv and gr.egario.us. They were mostly play apps for me and I’m moving on to other things. As my boss tells me, “you don’t want to dilute your brand…”

For the curious or nostalgic amongst you, you can get the source for both from my subversion repository:

Rela.tv source

svn co https://saucyworks.devguard.com/svn/projects/rela.tv/trunk rela.tv

gr.egario.us source

svn co https://saucyworks.devguard.com/svn/projects/gr.egario.us/trunk gr.egario.us

And if either of these domain names appeals to you, let me know. I’m willing to transfer them over to you if you have a project or cause that speaks to me (and even if you don’t have such a cause…)

tags: , , ,

How to Turn Deprecation Warnings Off in Rails

Posted by ryan
at 4:19 PM on Monday, December 04, 2006

I was talking with a friend of mine last week about the new Rails deprecation warnings that you’ll see when you do naughty things like access @cookies from your controller or use deprecated methods like ActiveRecord’s find_all. Sometimes it’s useful to turn off those warnings – especially when they’re coming from a part of your application you don’t control (i.e. a third-party library).

To turn off all deprecation warnings, just do the following:

ActiveSupport::Deprecation.silenced = true

Or, if you want to perform something other than spit out deprecation warnings you can re-route them however you want within a block:

ActiveSupport::Deprecation.behavior = Proc.new { |msg, stack| MyLogger.warn(msg) }
Or, if you want to be more granular, you can silence specific parts of your code that you know reference deprecated code:

def bad_action
  ActiveSupport::Deprecation.silence { p "Referencing #{@cookies} is bad" }
end

Hopefully this will get you past any annoying deprecations you can’t do much about (without giving you an easy out for those that you can do something about).