February 19th, 2008
Here’s this week’s update of the important changes that have been happening on edge Rails. There were lots of performance patches this week, credit mostly due to the guys from Acunote and their latest blog post on optimizing Rails.
This week’s report covers changes from 11 Feb 2008 to 17 Feb 2008 (the day the corresponding Rails Envy podcast was recorded).
ActiveRecord::Base#attributes-related optimizations
The ActiveRecord::Base#attributes_before_typecast no longer clones attribute unnecessarily.
Related changeset: http://dev.rubyonrails.org/changeset/8858
ActiveRecord::Base#attributes now no longer supports options. The ActiveRecord::Base#attributes method used to support :only and :except options, which are actually unnecessary since it returns a Hash. You can use Hash#slice or Hash#except (both ActiveSupport core extensions) directly on the hash.
Related changeset: http://dev.rubyonrails.org/changeset/8863
ActiveRecord associations optimized by avoiding named block arguments
Alexander Dymo is at it again optimizing ActiveRecord performance, this time by avoiding passing named block arguments. Turns out that it is faster and more memory efficient in Ruby to pass blocks like this:
def foo
bar { |*block_args| yield(*block_args) if block_given?
end
instead of passing the block around like so:
def foo(&&block)
bar(&block)
end
Occurrences of the more expensive way of passing blocks have been replaced in the ActiveRecord associations code so associations are lighter to use.
For a more detailed write-up, check out Alexander’s blog entry.
Related changeset: http://dev.rubyonrails.org/changeset/8865
ActiveRecord associations optimized by using symbol callbacks instead of string callbacks
Again from Alexander Dymo, this patch also improving ActiveRecord association performance. Internally, ActiveRecord associations code uses a lot of string callbacks (e.g. before_save "some_ruby_code_to_eval"). It turns out that that is significantly more expensive compared to simply using Module#define_method because when the callback string is evaluated, the binding needs to be passed along. Passing the binding takes up memory (which is bad, of course).
The internal associations code has been changed to use that define_method now, and if you’re using a lot of associations, you would save megabytes of memory! (Alexander’s blog post has more detailed benchmarks.)
Remember, when writing your own before and after filters, not to use a string that needs to be evaled!
Related changeset: http://dev.rubyonrails.org/changeset/8867
Improve ActiveRecord::Base#save performance by avoiding repeated calls to ActiveRecord::Base#connection
Another Alexander Dymo performance patch. This particular patch improves performance by getting the connection object just once and calling it directly, instead of needlessly calling several times. This should improve ActiveRecord::Base#save performance.
Related changeset: http://dev.rubyonrails.org/changeset/8871
ActiveRecord associations now support the :readonly option
ActiveRecord::Base#find supports the :readonly option to retrieve records that were read-only (i.e. you couldn’t save them). Now you can do the same for associations, like so:
has_many :comments, :readonly => true
This allows you to protect associated records from being saved.
Related changeset: http://dev.rubyonrails.org/changeset/8864
String#squish and String#squish! to remove consecutive chunks of whitespace
A String#squish extension has been added that trims whitespace from both ends of a string, and compressing consecutive whitespace groups within the string into 1 space each.
For example,
@description = %{ String#squish() allows me to write long strings
inside my Ruby code, without having to worry about indentation,
the page margin, spaces, tabs or newlines. "double" or 'single'
quotes are both fine, and #{interpolation} works. }.squish
Related changeset: http://dev.rubyonrails.org/changeset/8878
As always, let me know of any inaccuracies or any suggestions you may have in the comments, see you guys next week!
February 13th, 2008
It’s that time of the week again (every Wednesday for the overly observant, coinciding with the release of the Rails Envy podcast) for some choice updates of what changes have been happening on edge Rails. This week’s report covers changes from 4 Feb 2008 to 10 Feb 2008 (the day the Rails Envy podcast was recorded).
More efficent ActiveRecord::Base#attributes
ActiveRecord::Base#attributes returns a hash of all attributes with clones of their objects as values. This unnecessary cloning is expensive, and has now been removed. ActiveRecord::Base#attributes now returns a hash of values of the attributes.
Thanks to Juanjo Bazan, there have been 2 further patches (after the podcast was recorded) related ActiveRecord::Base#attributes that further improves ActiveRecord’s performance: removal of cloning from ActiveRecord::Base#attributes_before_typecast, and removing options (:only and :except options, specifically) from ActiveRecord::Base#attributes.
Related changeset: http://dev.rubyonrails.org/changeset/8824
ActiveResouce::Base#exists? now uses HTTP HEAD instead of a GET
A HTTP HEAD request is really all that is needed to check for existence (200 == exists, 404 == doesn’t exist). This has been changed to HEAD from a GET request, which is more efficient since all we retrieve are the HTTP headers and there’s no unnecessary instantiation of the ActiveResource object.
Related changeset: http://dev.rubyonrails.org/changeset/8827
Time zone support for ActiveRecord, and config.time_zone property for specifying a default Time Zone
ActiveRecord models now have time zone-aware attribute readers and writers. Times returned from ActiveRecord attribute readers now have a time_zone attribute themselves, allowing time zone-aware calculations.
You can now configure the default Time.zone value with config.time_zone in your environment.rb. Setting this will enable time zone-awareness for ActiveRecord models.
Related changeset: http://dev.rubyonrails.org/changeset/8806
A Template class has been introduced to ActionView
As part of the ActionPack refactoring that’s still ongoing, a ActionView::Template class has been introduced to encapsulate all the data relevant to the currently rendered template. This is largely an internal refactoring so Rails users shouldn’t be affected.
Related changeset: http://dev.rubyonrails.org/changeset/8805
Concrete sweeper classes
You can now use an explicit class when declaring a cache_sweeper. E.g.
class ListsController < ApplicationController
caches_action :index, :show, :public, :feed
cache_sweeper Sweepers::List, :only => [ :edit, :destroy, :share ]
end
Related changeset: http://dev.rubyonrails.org/changeset/8819
As always, let me know of any inaccuracies or any suggestions you may have in the comments - this week’s report is a little rushed because I’m a little late in posting it up!
February 6th, 2008
Here’s another week of noteworthy updates on the Rails trunk. Thanks once again to the Rails Envy podcast for featuring my updates, and even bigger thanks to the Rails contributors and committers who make things happen. This week’s Living on the Edge was covered in Rails Envy podcast #17.
The company I work for, Bezurk (yeah we know some of you hate the name “Bezurk” - that’s going to change), is currently looking for a Ruby developer to join our ranks. This person will be working mainly on Ruby-based web development projects. As we all know, “Rails doesn’t scale” (delivered in Jason Seifer-style - meaning it’s just a joke!) so we’d love anyone who’s up for a challenge and who’s also willing to work outside the Rails comfort zone (our current favorite: Ramaze). Our Ruby team currently comprises 3 experienced developers and 1 up and coming Rubyist.
We’re active Open Source contributors (Arun released ActiveCouch recently), while I am a Rails contributor (I’m ‘chuyeow’ on the Rails Trac) myself.
If all this sounds interesting enough to you, do check out the full job description and drop us an email. Yes, we’re serious when we said “+1 for DotA/Counterstrike skills)”. There’s a catch though - you have to work on location at our office in Singapore.
Anyway, let’s get back on topic - this weeks’ report covers changes from 28 Jan 2008 to 3 Feb 2008 (the day the Rails Envy podcast was recorded).
Saving, creating and updating ActiveRecord objects is more efficient
Thanks to Gleb Arshinov and Alexander Dymo with their blog post and patches about improving Rails performance by approaching it via minimizing garbage collection, saving, creating, and updating ActiveRecord objects is now significantly more efficient.
The expensive ActiveRecord::Base#attributes method is no longer called internally, so there’s no unnecessary cloning of attribute values. Less memory usage means less garbage collection, which means better performance!
Related changeset: http://dev.rubyonrails.org/changeset/8770
SQL calls and rendering are faster
This patch is courtesy of Gleb Arshinov and Alexander Dymo too.
Rails surrounds every SQL call and ActionView::Base#render with the ruby method Benchmark#realtime so that it can print out some benchmarking numbers. Benchmark#realtime allocates an unnecessary 45k of memory per call (who knew!). This has been monkey patched in Rails to a leaner implementation, reducing memory usage and increasing performance.
Related changeset: http://dev.rubyonrails.org/changeset/8771
MySQL adapter slight optimization
Table and column names in the MySQL adapter are now cached in instance variables, reducing memory allocations (and thus the need for GC).
Related changeset: http://dev.rubyonrails.org/changeset/8794
script/generate does Git!
script/generate now allows you to pass the ‘–git’ (or ‘-g’) option to add the generated files to git (much like how you could do the same with Subversion with ‘-c’ or ‘–svn’). This patch is the work of Steven Soroka so nice work Steven!
Related changeset: http://dev.rubyonrails.org/changeset/8772
map.resources :products, :as => ’something_else’
Here’s something that will make non-english Rails developers happy: you can now do, for example,
map.resources :comments, :as => 'comentarios'
so that you can keep having your model names independent of what URLs you’d actually like to appear on the site.
The above map.resources means that you can keep your resource and model names in English (Comment), while having URLs like this: /comentarios/123.
Related changeset: http://dev.rubyonrails.org/changeset/8785
:index option for form_for and fields_for to simplify multi-model forms
Inspired by a Railscasts episode where Ryan Bates mentions how ugly it is to have to pass :index => nil to prevent #text_field (and other form helpers) from inserting the model’s ID in the input name (e.g. project[task_attributes][123][name] when what you really want is project[task_attributes][][name]).
Meaning instead of
<% fields_for "project[task_attributes][]", task do |f| %>
<%= f.text_field :name, :index => nil %>
<%= f.hidden_field :id, :index => nil %>
<% end %>
you can now do:
<% fields_for "project[task_attributes][]", task, :index => nil do |f| %>
<%= f.text_field :name %>
<%= f.hidden_field :id %>
<% end %>
Related changeset: http://dev.rubyonrails.org/changeset/8786
No more emails in your production logs
You’d probably have noticed that ActionMailer logs entire email messages in the log, even in production. Well, it’s been changed to be less verbose now. At the :info log level, only the recipients are logged. You can still see the entire email at the :debug log level (which is the default for development environment).
Related changeset: http://dev.rubyonrails.org/changeset/8781
As always, let me know of any inaccuracies or any suggestions you may have in the comments. Oh, and if you liked this, do me a favor by submitting it to Ruby Reddit.
February 2nd, 2008
My fellow Rubyist at work, Arun Thampi, has released ActiveCouch, a Ruby library for CouchDB (if you don’t know what CouchDB is, read this). Check out the official blog announcement of ActiveCouch.
ActiveCouch tries to be an ActiveResource and ActiveRecord (yes, there is even an ActiveCouch::Migration class for creating CouchDB views) equivalent for representing CouchDB documents in Ruby. I wish I could write some coherent code examples here but I am not that up to date with the latest changes in ActiveCouch (despite my name being tagged as a committer to ActiveCouch, it is largely Arun’s awesome little baby).
Never fear though, since Arun and I are chowing down our own dog food - we’re currently porting several gigabytes worth of hotel data from our hacked together XML and Ferret-based library to use the ActiveCouch library. I’m sure we’ll get a more coherent API after this exercise, since we are no longer relying on specs and mocks, but working on a Real Application.
If you decide to try out ActiveCouch, do let us know of any feedback or bug reports at the ActiveCouch Google Code site. ActiveCouch is Open Source, so if you’ve patches that’s even better!
February 2nd, 2008
I have a rather old Linksys WRT54G (v2.2) wireless router that used to disconnect rather frequently for no apparent reason. It is almost guaranteed to get disconnected when I go crazy with using Axel to download files.
The worse part of the entire “router disconnected” thing is how it takes down my DSL connection (provided by my ISP, Singnet Broadband) with it - trying to reconnect the DSL modem takes upwards of 3 minutes (I imagine it takes awhile for the ISP’s network to realize that my DSL connection is really down).
A few months ago, I finally couldn’t stand it any longer and decided to install custom firmware on the router to see if it fixes the problem (which is quite prevalent). I was unwilling to go down this route considering my bad luck tinkering with firmware and drivers (having broken a cable modem and a near-functional Linux install before).
Anyway, long story short: I installed Tomato Firmware and am enjoying rock solid stability! I can run Axel (10 simultaneous connections) with 2 torrents running and still surf comfortably and even download more stuff through the plain old web browser interface.
Long story, er, long: I tried out DD-WRT first (since we use the same at work), didn’t like its bloated interface, and especially didn’t appreciate reading (old) allegations about how DD-WRT’s author tried to screw the Open Source community over. So I flashed the firmware to Tomato Firmware and ended up loving its minimalist interface and faster restart cycles (most of the time, since Tomato tries to restart as few services as it needs to when you make configuration changes, rather than reboot the entire router like DD-WRT does).
So, if you’re experiencing the same troubles with your Linksys router and haven’t had taken the leap of faith with custom firmware, I really recommend you do so. Big warning: Read the installation instructions! Not all WRT54G routers are compatible with Tomato Firmware.
More reading: Lifehacker has a walkthrough of Tomato Firmware’s features (and also one for DD-WRT).