Living on the edge (of Rails) #19 - change_table for migrations and more

This week’s report covers changes from 29 April 2008 to 4th May 2008 (the day the corresponding Rails Envy podcast was recorded).

change_table for ActiveRecord migrations

Thanks to Jeff Dean, who also blogged about the new change_table feature in ActiveRecord migrations, you can now change a table with a block like so:

change_table :videos do |t|
  t.add_timestamps
  t.add_belongs_to :goat
  t.add_string :name, :email, :limit => 20
  t.remove_column :name, :email # takes multiple arguments
  t.rename :new_name
  t.string :new_string_column # executes against the renamed table name
end

Some key things to note:

  • add_XXX would add a new column for you, e.g. add_string would add a new string field.
  • Of course, add_timestamps would add the magic created_at and updated_at datetime fields.
  • remove_column now takes multiple arguments.
  • rename would rename the table.

Very nice, DRY enhancement, props to Jeff Dean once again.

Related changeset: http://github.com/rails/rails/commit/96980bd561d79824b6cb6efbcbecdcbf8785d452

ActiveRecord::Base.create takes a block like ActiveRecord::Base.new

Yup now you can also create ActiveRecord objects with a block argument just like you could for ActiveRecord::Base.new:

@person = Person.create(params[:person]) do |p|
  p.name = 'Konata Izumi'
  p.age = 17
end

Credit goes to Adam Meehan for this patch.

Related changeset: http://github.com/rails/rails/commit/dd120ede53eaf71dee76894998a81626b7a689fc

Bugfix: change_column should be able to use :null => true on a field that
formerly had false

You can now use change_column in your migrations to alter a column as nullable if it was previously NOT NULL.

This bugfix is courtesy of Nate Wiger.

Related changeset: http://github.com/rails/rails/commit/10ef65a3b054270ed3d458ec8eb7c2b9a3e638f7

As always, let me know of any suggestions or how I can improve the Living on the Edge (of Rails) series.

Living on the Edge (of Rails) in French

It looks like Cyril Mougel has translated my 2 latest posts to French: LotE #17 and #18. Thanks Cyril!

Living on the edge (of Rails) #18

This week’s report covers changes from 21 Apr 2008 to 27 Apr 2008 (the day the corresponding Rails Envy podcast was recorded).

Not much interesting to report this week - there were mostly a bunch of bugfixes and Ruby 1.8.7-compatibility commits.

Introduce ActiveResource::Base.timeout and rescuing from Timeout::Error in ActiveResource::Connection

These are 2 changes that I’d talked about earlier this week so I won’t repeat myself - take a read through ActiveResource timeouts and why it matters.

Smart integer datatype for the MySQL adapter in migrations

The MySQL adapter in Rails now maps the integer column type in your migrations to either smallint, int, or bigint depending on the :limit option.

This means that a migration like this:

def self.up
  create_table :searches do |t|
    t.integer :foo, :limit => 2
  end

will create your foo column as a smallint(2) MySQL datatype (instead of int(2) before). (More information MySQL numeric datatypes.)

Credit goes to DHH for this patch.

Related changeset: http://github.com/rails/rails/commit/a37546517dad9f6d9a7de6e1dba4d960909d71e8

As always, let me know of any suggestions or how I can improve the Living on the Edge (of Rails) series.

New in Rails: ActiveResource timeouts and why it matters

If you’ve ever used ActiveResource or for that matter, coded anything that involves interacting with a remote API, you’ve probably had the misfortune of having that API go down or become unresponsive. The experienced old geezers would already have forseen the problem and set a timeout on those API calls. For the rest of us, it’s a good time to get acquainted with the idea of fail-fast and how no response is better than a slow response.

So what happens when the API you’re accessing via ActiveResource or Net::HTTP (or whatever, really, so long as there’s a network socket involved!) becomes unresponsive?

Unfettered outbound remote connections can kill your server

ActiveResource (or Net::HTTP or whatever you’re using) would block while waiting for the API to return. What did we learn about blocking IO in Operating Systems 101? That your process or thread gets stuck waiting for a response that’s what. So now your Mongrel/Thin/Ebb/X web server process basically stuck waiting for a response from an API that might return a response in 30 seconds, or maybe it’ll never return at all. It won’t be long before all your web server processes get blocked and its time to “kill dash nine” (kill -9) some processes and bring out the 500 error page. Yes, your server has been incapacitated and your website is now basically offline.

im in ur serverz making thingz better


Timeouts and you

If you’re using Net::HTTP you can easily safeguard yourself against errors like this by setting a timeout on the socket connection. Like so:

def fetch_something_from_some_api(uri)
  http = Net::HTTP.new(uri.host, uri.port)
  http.read_timeout = timeout

  response = http.start { http.request(Net::HTTP::Get.new("#{uri.path}?#{uri.query}")) }
  response.is_a?(Net::HTTPSuccess) ? do_something_with(response.body) : nil
rescue Timeout::Error
  logger.warn("Timeout accessing #{uri}: #{$!}")
  nil
rescue
  logger.warn("Error accessing #{uri}: #{$!}")
  nil
end

Take note that you really do need to explicitly rescue from the Timeout::Error (or Interrupt) since it’s a child of the Interrupt class and not a StandardError.

Any good client library should also allow you to specify a timeout on the socket connection so read the API documentation.

So what’s this about ActiveResource?

So back to ActiveResource: Michael Koziarski was kind enough to commit my 2 patches (this and this) to ActiveResource so now on edge Rails, you can do this:

class Person < ActiveResource::Base
  self.site = 'http://api.people.com:3000/'
  self.timeout = 5
end

This sets the timeout to 5 seconds on the underlying Net::HTTP instance that ActiveResource uses. The default timeout (to be exact, the read_timeout) for Net::HTTP is 60 seconds on most Ruby implementations (that includes MRI). That’s far too long if your ActiveResource call is in a public-facing controller!

If you are using ActiveResource anywhere and haven’t safeguarded your application with a timeout, I think it’s a good idea to fix it by using edge Rails or at least applying the patch!

The 2nd ActiveResource patch introduces the ActiveResource::TimeoutError exception that you should rescue from whenever you make an ActiveResource call. When this happens, it’s up to your application to decide what to do (like show a placating we’re sorry message). At least your application is still able to do something!

Living on the edge (of Rails) #17

Not much going on this week on edge Rails. It does seem however that we do have a new Rails core member, Joshua Peek. Also, the new Rails’ bug tracker hosted on Lighthouse is ready for use, so be sure to submit your patches and bug reports there.

This week’s report covers changes from 14 Apr 2008 to 20 Apr 2008 (the day the corresponding Rails Envy podcast was recorded).

Conditional caches_page

The caches_page now takes an :if option for specifying when a page can actually be cached via a Proc. You can now do this, for example:

caches_page :index, :if => Proc.new { |c| !c.request.format.json? }

That will only cache your index page if the requested format is not JSON.

Credit goes to Paul Horsfall for this enhancement.

Related changeset: http://github.com/rails/rails/commit/14a40804a29a57ad05ca6bffbe1e5334089593a9

New ActionView::TestCase for testing view helpers

Remember how you can now use specialized TestCase classes for testing controllers and ActionMailer classes? Now you can do the same with your Rails view helpers with the new ActionView::TestCase class.

Here’s a quick example:

module PeopleHelper
  def title(text)
    content_tag(:h1, text)
  end

  def homepage_path
    people_path
  end
end

class PeopleHelperTest < ActionView::TestCase
def setup
  ActionController::Routing::Routes.draw do |map|
    map.people 'people', :controller => 'people', :action => 'index'
    map.connect ':controller/:action/:id'
  end
end

def test_title
  assert_equal "<h1>Ruby on Rails</h1>", title("Ruby on Rails")
end

def test_homepage_path
  assert_equal "/people", homepage_path
end

Credit goes to Josh Peek for this sweet little enhancement.

ActiveSupport::Cache’s mem_cache_store accepts options

Even though Memcache-client was added to ActiveSupport recently, it didn’t allow you to specify any configuration options beyond just the IP of the memcached server. Now you can pass along more configuration options like so:

config.action_controller.fragment_cache_store = :mem_cache_store, 'localhost', { :compression => true, :debug => true, :namespace => 'foo' }

This patch is courtesy of Jonathan Weiss.

Related changeset: http://github.com/rails/rails/commit/9e1d506a8cfedef2fdd605e4cbf4bf53651ad214

As always, let me know of any suggestions or how I can improve the Living on the Edge (of Rails) series.