Living on the Edge (or what's new in Edge Rails) #1 - API changes and PerformanceTests

Posted by Chu Yeow June 20, 2008 @ 03:05 PM

As Gregg Pollack mentioned a week or so ago, I’ll be keeping a weekly-or-so column about noteworthy changes on edge Rails. This is the first time Living on the Edge (of Rails) is appearing on the official Ruby on Rails weblog, so you’ll have to bear with my short introduction.

Living on the Edge is a weekly column I used to put up on my own blog after some prodding by Gregg Pollack of Rails Envy way back in December of 2007. I used to be a rather active Rails contributor back then so it was a no-brainer. Gregg and Jason were awesome enough to feature it weekly in their podcast.

And now it’s here, so try your best to be a tough crowd so I can tune these blog posts so that they are actually useful to you – when I was blogging these on my tiny personal blog it wasn’t that vital but now the audience is significantly larger. Leave your suggestions and criticisms in the comments – they are greatly appreciated!

Anyway, there’s been a ton of new features, API changes and performance improvements in the past 2 weeks or so since Rails 2.1 was released, so rather than dumping all into one mega-post, I’ve decided to break it into 2 posts for new features/API changes and performance improvements. In this post, I’m gonna talk about some of the new features and API changes.

Minor API changes

Let’s start jump straight in with some minor API changes.

link_to now takes a block

The link_to helper now takes a block argument for those occasions when you have really long hyperlink text with variables in them:

<% link_to(@profile) do %>
  <strong><%= @profile.name %></strong> -
  <span>Status: <%= @profile.status %></span>
<% end %>

Some people would find it cleaner than:

<%= link_to "<strong>#{@profile.name}</strong> -- <span>Status: #{@profile.status}</span>", @profile %>

Credit goes to Sam Stephenson (of Prototype fame) and DHH for this change.

Changeset details

ActiveRecord::Base#merge_conditions is now part of the public API

Jeremy Kemper has made ActiveRecord::Base#merge_conditions a public method.

This is pretty useful if you have conditions from multiple sources or like to combine any conditions for any reason.

Post.merge_conditions(
  {:title => 'Lucky ☆ Star'},
  ['rating IN (?)', 1..5]
)
=> "(`posts`.`title` = 'Lucky ☆ Star') AND (rating IN (1,2,3,4,5))"

Do note though that this merges with a SQL boolean AND only (no ORs).

Changeset details

Associations now take a :validate option

Association macros now accept a :validate option like so:

class Anime > ActiveRecord::Base
  has_many :characters, :validate => true
end

This tells ActiveRecord to validate the characters association when saving your Anime model – just like how :validates_associated works. The default is false, which is the current behavior in Rails 2.1 and earlier, so no need to fret. This works for all the other association macros as well (has_one, belongs_to, has_and_belongs_to_many).

Thumbs up to Jan De Poorter and Pratik Naik for this, which also fixes a nasty bug.

Changeset detailsTicket

ActiveSupport::StringInquirer and convenience Rails.env.development? methods

David Heinemeier Hansson (henceforth abbreviated as DHH – sorry!) recently added an ActiveSupport::StringInquirer String subclass that allows you to do this:

s = ActiveSupport::StringInquirer.new('awesome')
=> "awesome" 
s.awesome?
=> true
s.sucks?
=> false

An immediate use of this is when you are checking the environment your app is running in: Rails.env is wrapped in a StringInquirer so you can use query methods like Rails.env.development? and Rails.env.production?.

Changeset details

Core extensions: Object#present? and Enumerable#many?

DHH also added some core extensions that while trivial, could make your code more readable. First up is Object#present?, which is essentially !Object#blank?

[].present?
=> false
[1, 2].present?
=> true
"".present?
=> false
"i'm here".present?
=> true

An Enumerable#many? extension was also added that is simply a boolean test for enumerable.size > 1:

[].many?
=> false
[:just_me].many?
=> false
[:just_me, 'my_friend'].many?
=> true

Object#present? changesetEnumerable#many? changeset

Declarative block syntax for writing tests

DHH was inspired by Jay Fields when he committed this bit of syntatic sugar: you can now write your tests (Test::Unit) in declarative block style like so:

test "an anime should be invalid if any of its characters are invalid" do
  # Your usual test code here.
end

I seldom use Test::Unit (except when submitting Rails patches) and prefer RSpec – this declarative style of writing tests is definitely more readable.

All Rails-generated test stubs now use this new syntax.

Changeset details

Performance tests

Jeremy Kemper has been hard at work optimizing and improving the performance of Rails, so it’s no surprise that he has also introduced a new type of integration test: the performance test.

You can use the performance test generator (added by Pratik in 23232a) to generate a performance test stub.

script/generate performance_test LoginStories

Running the performance test requires ruby-prof >= 0.6.1, which is still unreleased but you can get at it the development version by checking out the source and installing the gem yourself (I suggest you get Jeremy’s fork of ruby-prof for now). It’s interesting to note that with the 0.6.1 release, ruby-prof supports profiling tests cases written using Test::Unit.

Moving on… Put in some test code (request a few controller actions – whatever user story you want to test performance of) and run the test. You’ll get output like this (together with the usual ruby-prof profiling output in the test/tmp/performance directory of your Rails app):

> ruby performance/login_stories_test.rb 
Loaded suite performance/login_stories_test
Started
LoginStoriesTest#test_homepage (32 ms warmup)
        process_time: 11 ms
              memory: unsupported
             objects: unsupported
.
Finished in 0.870842 seconds.

The memory and objects results are “unsupported” because I haven’t patched my Ruby interpreter for memory profiling support. You’d need certain Ruby interpreter patches to enable memory and GC profiling. I wish I could tell you more about how to do so, but I’m treading unfamiliar ground here. There are some details here on how to patch Ruby for memory profiling. I leave it for wiser folks to explain how to do this :)

Changeset details

Outro

That’s it so far for new feature/API changes in Rails since Rails 2.1 – performance improvements are coming up in the next post and I’ve also intentionally left out mention of the Rack support that has been partially merged into edge.

If there were any errors or you have any suggestions on how to make this column better, please point them out in the comments. Any info on patching your Ruby interpreter for memory profiling support is also greatly welcome. If I’ve left out anything that I’d considered not noteworthy enough but you disagree, let me know in the comments too.

Posted in Edge | 15 comments

Comments

  1. Justin Mecham on 20 Jun 15:28:

    This is a great addition to the official weblog!

  2. Trevor Turk on 20 Jun 16:24:

    Agreed – this is a very valuable resource. Thank you!

  3. iGEL on 20 Jun 16:37:

    In the paragraph “Core extensions: Object#present? and Enumerable#many?”, there are two return values missing. But its easy to figure out, what values it would return, so no big loss. ;)

    Thanks for this article…

  4. Jamie Conlon on 20 Jun 16:52:

    aye very useful addition to this blog, thanks for writing this =)

  5. Terry Schmidt on 20 Jun 17:04:

    This is a godsend. The information here is incredibly useful and this will just add to that effort.

    The way in which the information is presented is clear and straight to the point. I am very excited to see some of the new developments that are occurring in Edge and can’t wait to try them out myself. Great work Chu!

  6. Jim Cropcho on 20 Jun 19:55:

    I think this is a great addition to the core Rails blog! Good job guys!

  7. zabbadapp on 20 Jun 22:51:

    Isn’t Object#present? doing exactly the same thing as Enumerable#any? already is?

    [].any? => false

    => true ””.any? => false “i’m here”.any? => true

  8. Alex Sharp on 20 Jun 23:20:

    Ditto on what Terry said. Keep up the great work, both on the weekly article and the great changes this week! I especially like link_to block-style and merge_conditions. Great job everyone.

  9. Glenn Ford on 20 Jun 23:27:

    Thank you so much! I’m subscribing to this feed now, definitely something I want to read more of.

  10. Chu Yeow on 21 Jun 03:53:

    @iGEL: Thanks for spotting that – that was my copy n paste (from the terminal) blunder. Fixed!

    @zabbadapp: Yes that is correct, but Object#present? is more generic since it can be used on every object whereas Enumerable#any? is specific to enumerables.

    @everyone else: Thank you so much for the positive feedback! If anyone has any negative things to say, don’t be shy – I certainly want to hear it!

  11. Mike on 21 Jun 22:01:

    Could someone explain how:

    class Anime > ActiveRecord::Base has_many :characters, :validate => true end

    differs from:

    class Anime > ActiveRecord::Base has_many :characters validates_associated :characters end

  12. @mike: on 23 Jun 09:16:

    not at all. so don’t worry.

    if you have more associations than just “characters”, it would validate all of them.

  13. ooops on 23 Jun 09:17:

    actually, that’s not true. it’s just nicer to read.

  14. Ryan Sobol on 27 Jun 17:01:

    +1 for ActiveSupport::StringInquirer wrapping RAILS[‘ENV’] (why can this be changed during runtime in the first place?)

    +1 link_to accepting a &block

    +1 :validate option for associations

    +10 for performance tests via integration testing!

  15. roger matching ties on 28 Jul 13:24:

    Here here on Jeremy Kemper optimizing rails. Please please :) -R