Benchmarking your Rails tests (updated)

Posted by Jon
on Friday, April 03

Update: stubbing a single integration point shaved 22 seconds off of my unit tests, reducing test time from 35 seconds to 13. See below.

The first step to faster tests is knowing what is slow. Fortunately, this is dead simple with the test_benchmark plugin by Tim Connor, and originally built by Geoffrey Groschenbach. Install the plugin, and when you run your tests via Rake, you’ll see handy output showing you the slowest tests, and the slowest test classes.

Step 1: Install the plugin.

script/plugin install git://github.com/timocratic/test_benchmark.git

Step 2: Run your tests

rake test

Here is a bit of output when I run the unit tests for FanChatter:

Finished in 34.838173 seconds.

Test Benchmark Times: Suite Totals:
25.393 MailReceiverTest
4.520 PhotoTest
1.429 REXMLTest
0.961 TeamTest
0.846 MessageTest

Pretty useful information. Almost 75% of our unit testing time is taken up in the MailReceiverTest. So if we want to speed up our tests, we need to make our MMS testing faster. Looking at that code, I see this line over and over:


MailReceiver.receive(fixture_mms(:fixture_name))

This method reads a test email message from the filesystem, and runs it through our mail parsing method. This is basically an integration test, hitting at least two integration points. So if we can remove these bottlenecks, we can reasonably expect a fairly large improvement in our unit test speed.

I think we could realistically reduce our unit testing time from 34 seconds to <15 seconds just by refactoring this one test method.

Other options

The test_benchmark plugin fires whenever you run your tests with rake. Tim recently patched the plugin to not fire when run with autotest, which is great. Personally, though, I don’t want to see this benchmark information every time I run my tests. So I added the following line to my test.rb environment file:

ENV['BENCHMARK'] ||= 'none'

Now, the benchmarks don’t run by default. If I want to see them, I call:

rake test BENCHMARK=true

And if to see full tests, showing the time it takes to run every test in the system, just call:

rake test BENCHMARK=full

That’s it. You still have to speed up your tests, and there are many ways to do that (from mocking to simply reducing the number of calls to expensive methods), but knowing what’s slow is half the battle.

The stirring conclusion (update)

I spent a few minutes optimizing these slow tests today. First, I tried rearranging the tests to reduce unnecessary calls to the slow method (MailReceiver.receive(message)). I was able to speed MailReceiverTest from about 25 seconds to 17. Not bad, but still slow.

The real problem is that this method saves a photo. It creates a Photo record that includes a file, treated sort of like an upload, like this:

1
photo.uploaded_data = mms.file

This is what was slow. But my unit tests don’t actually deal with the file being saved to the filesystem; they test other things, like the right records being created, confirmation emails being sent, etc.

So I decided to try bypassing this file save/upload by stubbing the uploaded_data= method. I put the following at the top of my test class:

1
2
3
def setup
    Photo.any_instance.stubs(:uploaded_data=)
  end

And voila! MailReciverTest went from 25 seconds to 17 seconds to 3 seconds.

10 Cool Things in Rails 2.3

Posted by Luke Francl
on Monday, March 30

This was presented to the Ruby Users of Minnesota on March 30, 2009.

Here’s a quick look at 10 new Rails features that I think are cool. Not all of them are huge new features, but instead help solve annoying problems. I’ve also created a simple application that demonstrates most of these features. You can get it at BitBucket

1. Rails Boots Faster in Development Mode

This is something all Rails developers can appreciate. In development mode, Rails now lazy loads as much as possible so that the server starts up much faster.

This is so fast, instead of replying on reloading (which doesn’t pick up changes to gems, lib directory, etc) one developer wrote a script (does anyone have the link for this?) that watches for file system changes and restarts your script/server process.

Using an empty Rails app, I got the following (totally non-scientific) real times for time script/server -d:

Rails 2.2: 1.461s
Rails 2.3: 0.869s

Presumably this difference would grow as more libraries were used, because Rails 2.3 will lazy load them. However I was too lazy to build up equivalent Rails 2.2 and 2.3 applications to try that out.

2. Rails Engines Officially Supported

Inspired by Merb’s slices implementation, Rails added official support for Engines, which are self-contained Rails apps that you can install into another application. Engines can have their own models, controllers, and views, and add their own routes.

Previously this was possible using the Engines plugin, but Engines would often break between Rails versions. Now that they are officially supported, this should be less frequent.

There are still some features from the unofficial Engines plugin that are not part of Rails core. You can read about that at the Rails Engines site.

3. Routing Improvements

RESTful routes now use less memory because formatted_* routes are no longer generated, resulting in a 50% memory savings.

Given this route:

map.resources :users

If you want to access the XML formatted version of a user resource, you would use:

user_path(123, :format => 'xml')

In Rails 2.3, :only and :except options to map.resources are not passed down to nested routes. The previous behavior was rather confusing so I think this is a good change.

1
2
3
4
map.resources :users, :only => [:index, :new, :create] do |user|
  # now will generate all the routes for hobbies
  user.resources :hobbies
end

4. JSON Improvements

ActiveSupport::JSON has been improved.

to_json will always quote keys now, per the JSON spec.

Before:

{123 => 'abc'}.to_json
=> '{123: "abc"}'

Now:

{123 => 'abc'}.to_json
=> '{"123": "abc"}'

Escaped Unicode characters will now be unescaped.

Before:

ActiveSupport::JSON.decode("{'hello': 'fa\\u00e7ade'}")
=> {"hello"=>"fa\\u00e7ade"}

Now:

ActiveSupport::JSON.decode("{'hello': 'fa\u00e7ade'}")
=> {"hello"=>"façade"}

See ticket 11000 for details.

5. Default scopes

Prior to Rails 2.3, if you executed a find without any options, you’d get the objects back unordered (technically, the database does not guarantee a particular ordering, but it would typically be by primary key, ascending).

Now, you can define the default sort and filtering options for finding models. The default scope works just like a named scope, but is used by default.

1
2
3
class User < ActiveRecord::Base
  default_scope :order => '`users`.name asc'
end

The default options can always be overridden using a custom finder.

User.all # will use default scope
User.all(:order => 'name desc') # will use passed in order option.

Example:

1
2
3
4
5
6
7
8
9
User.create(:name => 'George')
User.create(:name => 'Bob')
User.create(:name => 'Alice')

puts User.all.map { |u| "#{u.id} - #{u.name}" }

3 - Alice
2 - Bob
1 - George

Note how the default order is respected.

6. Nested Transactions

Pass :requires_new => true to ActiveRecord::Base.transaction and a nested transaction will be created.

1
2
3
4
5
6
7
User.transaction do
  user1 = User.create(:name => "Alice")

  User.transaction(:requires_new => true) do
    user2 = User.create(:name => "Bob")
   end
end

This is actually emulated using save points because most databases do not support nested transactions. Some databases (SQLite) don’t support either save points or nested transactions, so in that case this works just like Rails 2.2 where the inner transaction(s) have no effect and if there are any exceptions the entire transaction is rolled back.

7. Asset Host Objects

Since Rails 2.1, you could configure Rails to use an asset_host that was a Proc with two arguments, source and request.

For example, some browsers complain if an SSL request loads images from a non-secure source. To make sure SSL always loads from the same host, you could write this (from the documentation):

1
2
3
4
5
6
7
ActionController::Base.asset_host = Proc.new { |source, request|
  if request.ssl?
    "#{request.protocol}#{request.host_with_port}"
  else
    "#{request.protocol}assets.example.com"
  end
}

This works but it’s kind of messy and it’s difficult to implement complicated logic. Rails 2.3 allows you to implement the logic in an object that responds to call with one or two parameters, like the Proc.

The above Proc could be implemented like this:

1
2
3
4
5
6
7
8
9
10
11
class SslAssetHost
  def call(source, request)
    if request.ssl?
      "#{request.protocol}#{request.host_with_port}"
    else
      "#{request.protocol}assets.example.com"
    end
  end
end

ActionController::Base.asset_host = SslAssetHost.new

David Heinemeier Hansson has already created a better plugin that handles this case: asset-hosting-with-minimum-ssl. It takes into account the peculiarities of the different browsers to use SSL as little as possible, reducing load on your server.

8. Easily update Rails timestamp fields

If you’ve ever wanted to update Rails’ automatic timestamp fields created_at or updated_at you’ve noticed how painful it can be. Rails REALLY didn’t want you to change those fields.

Not any more!

Now you can easily change created_at and updated_at:

1
2
3
4

User.create(:name => "Alice", :created_at => 3.weeks.ago, :updated_at => 2.weeks.ago)

=> #<User id: 3, name: "Alice", created_at: "2009-03-08 00:06:58", updated_at: "2009-03-15 00:06:58">

Remember, If you don’t want your users changing these fields, you should make them attr_protected.

9. Nested Attributes and Forms

This greatly simplifies complex forms that deal with multiple objects.

First, nested attributes allow a parent object to delegate assignment to its child objects.

1
2
3
4
5
6
7
8
9
10

class User < ActiveRecord::Base
 has_many :hobbies, :dependent => :destroy

  accepts_nested_attributes_for :hobbies
end

User.create(:name => 'Stan', 
            :hobbies_attributes => [{:name => 'Water skiing'},
                                    {:name => 'Hiking'}])

Nicely, this will save the parent and its associated models together and if there are any errors, none of the objects will be saved.

Forms with complex objects are now straight-forward. To use this in your forms, use the FormBuilder instance’s fields_for method.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<% form_for(@user) do |f| %>
  <div>
    <%= f.label :name, "User name:" %>
    <%= f.text_field :name %>
  </div>

  <div>
    <h2>Hobbies</h2>

    <% f.fields_for(:hobbies) do |hf| %>
      <div>
  <%= hf.label :name, "Hobby name:" %>
  <%= hf.text_field :name %>
      </div>
      <% end %>
  </div>

  <%= f.submit 'Create' %>
<% end %>

One catch is that a form is displayed for every associated object. New objects obviously have no associations so you have to create a dummy object in your controller.

1
2
3
4
5
6
7
8
9
10
class UsersController < ApplicationController
  def new
    # In this contrived example, I create 3 dummy objects so I'll get
    # 3 blank form fields.
    @user = User.new
    @user.hobbies.build
    @user.hobbies.build
    @user.hobbies.build
  end
end

There are a lot of options for nested forms including deleting associated objects, so be sure to read the documentation. Ryan Daigle also has a great write-up.

10. Rails Metal \m/

You can now write very simple Rack endpoints for highly trafficked routes, like an API. These are slotted in before Rails picks up the route.

A Metal endpoint is any class that conforms to the Rack spec (i.e., it has a call method that takes an environment and returns the an array of status code, headers, and content).

Put your class in app/metal (not generated by default). Return a 404 response code for any requests you don’t want to handle. These will get passed on to Rails.

There’s a generator you can use to create an example Metal end point:

script/generate metal classname

In my sample app, I have what I would consider the “minimally useful” Rails Metal endpoint. It responds to /users.js and returns the list of users as JSON.

1
2
3
4
5
6
7
8
9
10
11
class UsersApi
  def self.call(env)
    # if this path was /users.js, reply with the list of users
    if env['PATH_INFO'] =~ /^\/users.js/
      [200, {'Content-Type' => 'application/json'}, User.all.to_json]
    else
      # otherwise, bail out with a 404 and let Rails handle the request
      [404, {'Content-Type' => 'text/html'}, 'not found']
    end
  end
end

If you want a little bit more help, you can use any other Rack-based framework, for example Sinatra.

For more details on how Rails Metal works, check out Jesse Newland’s article about it.

Thanks for reading! For more details about new features in Rails 2.3, read the excellent release notes

xss_terminate now Rails 2.2 ready; code now on GitHub

Posted by Luke Francl
on Friday, December 19

I had some coffee a bit too late yesterday and it inspired me to a boost of productivity on xss_terminate, my plugin that escapes HTML from your models when you save them.

xss_terminate now supports Rails 2.2. It is backwards compatible with Rails 2.0 and 2.1.

I also moved the plugin source to GitHub, and incorporated a bug fix from redinger.

You can install it using

script/plugin install git://github.com/look/xss_terminate.git

Upgrading Phusion Passenger when Apache is installed from source

Posted by Luke Francl
on Thursday, October 30

I have a confession to make.

I hate the way Debian-based distributions handle Apache. sites-available, sites-enabled, esoteric commands to enable and disable sites, config files moved around from their usual places. Ugh. I just don’t like it. So I usually install Apace from source.

Here’s what you need to do to upgrade Phusion Passenger on Debian/Ubuntu when you’ve installed Apache from source.

When you run passenger-install-apache2-module, it will try to find Apache, and fail.

You need to set the following environment variables:

export APXS2=/usr/local/apache2/bin/apxs
export APR_CONFIG=/usr/local/apache2/bin/apr-1-config

With these set, the installer can find Apache and everything will work appropriately. Hopefully this information will help somebody out.

Via the Passenger documentation and Google Group.

Is your Rails application safe?

Posted by Eric Chapweske
on Monday, September 22

Rails provides many great security features. It’s design can also create significant security holes. In the case of ActiveRecord’s mass assignment vulnerability, the security issues are more servere and widespread than many of us recognize.

Nearly every open source Rails application I’ve seen is vulnerable, and most closed source ones as well. There’s some great solutions for protecting your application from attack, but first, the problem:

The Problem

By default ActiveRecord allows visitors access to any writer method, that is, any method ending with an equal sign. This comes courtesy of the ActiveRecord::Base#attributes= method, which is used internally by the main methods that handle creating and updating records, including new(), create(), and update_attributes().

The way most applications are designed means that whatever data a visitor sends to the server will likely find its way through the attributes=() method, and if not protected, ActiveRecord will happily update the records based on what was sent. In less technical terms: ActiveRecord is insecure by default.

As an example, let’s look at a request against vulnerable code:

1
2
3

# The request
$ curl -X PUT -d "order[price_in_cents]=0" example.com/orders/225
app/models/order.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

class Order < ActiveRecord::Base
  # Table name: orders
  #  id          :integer(11)     not null, primary key 
  #  price_in_cents     :integer(11)
  #  user_id     :integer(11)     
  #  state       :string(255)               

  has_many :line_items
  
  acts_as_state_machine :initial => :pending

  state :pending
  state :paid
    
  def name
    ... 
  end
  
  def shipped_on=(shipping_date)
    ...
  end

end
app/controllers/orders_controller.rb
1
2
3
4
5
6
7
8
9

class OrdersController < ApplicationController
  ...

  def update
    @order.update_attributes(params[:order])
  end

end
Pop quiz: which Order instance methods are exposed to the world?
  • Attributes generated from its table: price_in_cents=, user_id=, state=
  • Attributes generated by association macros: line_item_ids=
  • Other defined writer methods: shipped_on=

Ruby’s dynamic nature and ActiveRecord’s changing API make this excercise more of a guess than anything else. Does Rails 2.1 dynamicly generate different writer methods? Will Rails 2.2? How about the plugins and libraries the application relies on?

Theoritically, this isn’t a problem since ActiveRecord provides a solution out of the box: “Sensitive attributes can be protected from this form of mass-assignment by using the attr_protected macro. Or you can alternatively specify which attributes can be accessed with the attr_accessible macro”

The Reality

Naturally, profesional developers experienced with the framework use attr_accessible/attr_protected and don’t suffer from these problems. As a quick poll, here’s a few of the more popular open source code bases:

  1. Insoshi is second only to Rails as a top forked project on github. It’s a social networking app developed by a seed funded startup whose team includes the author of a very well-reviewed book on developing social applications in Rails.
  2. Mephisto, the Rails-based blogging application, which Railspikes runs on.
  3. Anonymous App is a large Rails project with seasoned developers. I’m withholding its details since it has security issues that are still being addressed.
  4. Rubyflow, the codebase of Peter Cooper’s very useful Ruby news aggregation site.
  5. Spree is a rapidly-maturing ecommerce project and powers RailsEnvy’s new screencast store.

Good projects. Professional developers. Every project except Mephisto is vulnerable. Any forum thread in Insoshi will raise exceptions and be unusable after the user_id is changed to a non-existent user. A similiar approach worked on Anonymous and Rubyflow. Since these projects lacked any strategy for handling this kind of problem, it’s highly probable that much more damaging attacks exist. One example: Spree’s public exposure of the 'state' attribute allowed me to make my order appear as though it was paid for when I hadn’t even entered my payment information. While these projects vary in terms of risk, in each case the cost of solving this issue is cheap when compared to the cost of cleaning up after an attack.

I’m singling out these applications because they’re popular and open source, but every project I’ve developed has experienced the same security issues. The only thing that seems to change is how much data is vulnerable and how important it is. It’s a difficult problem to manage. Retrofitting security on existing code is a very unpleasant experience. It’s easy to forget when developing new applications. Educating other developers on the problem has proved unreliable.

As an aside, I’m impressed by the response of the developers on these projects. Insoshi, Rubyflow, and Spree addressed the issue almost instantly after being informed. It was a reminder to me of how lucky I am to be involved in such a passionate, professional community. Michael Hartl of Insoshi went so far as to write a mass assignment auditing plugin and offers some great advice on how he ended up tackling the problem.

A solution

  1. Don’t use attr_protected. I haven’t seen a compelling use case for it. It’s functionality is confusing. It should probably be removed from ActiveRecord.
  2. Do use attr_accessible. Its white list approach forces an explicit decision on the mass assignablity of attributes. A rule of thumb: if an attribute shouldn’t be in a user submitable form, it shouldn’t be accessible.
  3. Review and audit. Even with attr_accessible, a developer can still shoot themselves in the foot without code audits and reviews. Even if the application is secure today, holes will eventually be introduced into the code. In addition to peer review, automated auditing tools are a great, inexpensive way to find such security problems.
  4. Make it automatic. Disable mass assignment by default, requiring attr_accessible to be specified for each attribute. I’ve taken this approach on maybe 5 projects now. Here’s how to do it:
config/initializers/disable_mass_assignment.rb
1
2

ActiveRecord::Base.send(:attr_accessible, nil)

It’s worked quite well, with the exception of two cases where I had to retrofit it on larger applications. That was a nightmare. I’ve been tinkering with a plugin that aims to reduce some of the problems caused by attr_accessible, and make retrofitting a more pleasant experience. It’s not production ready, but I think there’s some small improvements in it worth stealing.

The downside: it’s pretty much a guarantee that you’ll run into confusing bugs during development. This is a major problem for developers new to the framework, and is annoying for the more experienced. ActiveRecord used to raise exceptions in development when mass assignment was attempted with an inaccessible attribute. This was great, but there were a few complaints, and conflicts with ActiveResource, so the change was pulled.

A better solution?

An alternative approach worth exploring is the route taken by Merb, which decided this is the controller’s problem, and has a plugin providing params_accessible functionality. There’s a similar plugin for Rails . This approach may be especially appreciated by developers who want to add some level of protection to an existing application, since less code needs to change.

I’ve hesitated to use this on applications that use ActiveRecord, which has a bad habit of making methods part of the public api when they should be privately scoped (those ending in _id, _ids, _count, most enumerables, etc) Because of this, attr_accessible serves double duty by discouraging public use of writer methods that should be private. Not really the best excuse, and I’d like to give the params_protected approach a try on my next Rails project.

Regardless of the solution, the cost of designing applications to handle potential mass assignment abuse from the beginning is so much cheaper than attempting to retroactively address the issue. Rails should step up and encourage such design decisions. Whether it’s something as extreme as disabling mass assignment from the start, or an unobtrusive change like adding a commented out attr_accessible line in generated models, the risk shouldn’t be ignored.

Security Tools

There’s a few other related tools that look promising for developing securer code:
  • Tarantula: A fuzzing plugin that spiders your application looking for problems. Via Stuart Halloway’s post on Revelance’s blog: “It crawls your rails app, fuzzing inputs and analyzing what comes back. We have pointed Tarantula at about 20 Rails applications, both commercial and open source, and have never failed to uncover flaws.” Aaron Bedrak’s Rails Security Audit PDF on Peepcode devotes significant space to getting this up and running. It also covers a few of the common mistakes developers can make when using a framework like Rails, and that alone may make it a worthwhile read.
  • ratproxy: Happened upon this on Google’s excellent security blog . From their announcement post: “[ratproxy] is designed to transparently analyze legitimate, browser-driven interactions with a tested web property and automatically pinpoint, annotate, and prioritize potential flaws or areas of concern.”
  • Audit Mass Assignment: Scans ActiveRecord models looking for potential mass assignment mistakes.
  • Find Mass Assignment: Searches controller actions for likely mass assignment, and then find the corresponding models that don’t have attr_accessible defined.
References

Upgrading an existing application to Phusion Passenger and Ruby Enterprise Edition

Posted by Luke Francl
on Friday, July 25

Whew. That’s a long title. I am talking about taking an existing Rails application deployed with Apache and Mongrel and upgrading it to use Phusion Passenger and Ruby Enterprise Edition.

Here’s what I did.

Install Ruby Enterprise Edition

First, if you’re going to use Phusion Ruby Enterprise Edition (I wanted to because of the recent Ruby security problems) I recommend starting with that first. Installation is straight-forward and does not conflict with your existing Ruby installation.

It supposedly can pick up gems installed with your old copy of Ruby, but I couldn’t figure out how to get that to work, so I had to install some gems again. You do that like this:

sudo /opt/ruby-enterprise-1.8.6-20080709/bin/gem install gem-name

We vendor all our gems except for a few that require native compilation or I just plain can’t get working in the vendor directory. I had to install hpricot, mime-types, and image_science.

The reason to start with Ruby Enterprise Edition is that you’ll have to reconfigure Passenger to use it if it’s not installed already.

Install Passenger

Next, install Passenger. Again, this is straight-forward. However, make sure you install it with the Ruby Enterprise Edition binary.

sudo /opt/ruby-enterprise-1.8.6-20080709/bin/gem install passenger

Then build the Apache module. You may need to install the Apache development header files if they’re not already there.

sudo /opt/ruby-enterprise-1.8.6-20080709/bin/passenger-install-apache2-module

This will give you some lines of code to put in your httpd.conf file. Since you used Ruby Enterprise Edition to run the command, the PassengerRuby variable, the PassengerRoot, and the location of the mod_passenger.so will be inside your /opt/ruby-enterprise-X.X.X-YYYYMMDD directory tree.

Update Rails configuration

Now you need to remove mod_proxy_balancer and mod_rewrite from your application’s Apache config file. The DocumentRoot you had before ought to be fine—Passenger can detect when Apache is serving up a Rails application.

This is what I had in my config file before:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

  # Configure mongrel_cluster 
  <Proxy balancer://tumblon_cluster>
    BalancerMember http://127.0.0.1:8000
    
    BalancerMember http://127.0.0.1:8001 
  </Proxy>

  RewriteEngine On
  
  # Prevent access to .svn directories
  RewriteRule ^(.*/)?\.svn/ - [F,L]
  ErrorDocument 403 "Access Forbidden"

  # Check for asset hosts
  RewriteRule %{REMOTE_HOST} ^assets\d.* [L]

  # Check for maintenance file and redirect all requests
  RewriteCond %{DOCUMENT_ROOT}/system/maintenance.html -f
  RewriteCond %{SCRIPT_FILENAME} !maintenance.html
  RewriteRule ^.*$ /system/maintenance.html [L]

  # Rewrite index to check for static
  RewriteRule ^/$ /index.html [QSA] 

  # Rewrite to check for Rails cached page
  RewriteRule ^([^.]+)$ $1.html [QSA]

  # Redirect all non-static requests to cluster
  RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME} !-f
  RewriteRule ^/(.*)$ balancer://tumblon_cluster%{REQUEST_URI} [P,QSA,L]

Obviously, we are not using mod_proxy_balancer at all, so that can just go (also watch out for the last RewriteRule, wich uses the balancer).

The RewriteRules – for the most part – aren’t needed because Passenger handles cached pages automatically. Also, Passenger conflicts with mod_rewrite and turns it off by default, so they don’t work anyway.

I don’t have an alternative for the maintenance page, but I don’t use that anyway.

Bonus tip: Set your Rails environment. If you application runs in anything other than production, you need to set it here. I have a staging server that runs in its own environment, so I set it like so:

RailsEnv staging

Update Capistrano configuration

Only one more thing: update your deployment recipe. You don’t need to restart mongrel_cluster because there won’t be one.

I’m still in the dark ages using Capistrano 1.4.1, so this is the task I came up with:

1
2
3
4
5
6
7
8
9
10

desc "Restart Phusion Passenger (restarts Apache)"
task :restart_app, :roles => :app do
  sudo "#{apache_ctl} restart"
end

desc "Override the built-in restart task to restart Apache"
task :restart, :roles => :app do
  restart_app
end

Overriding restart is important because that’s what Capistrano will run. The default deprec version I was using restarted mongrel_cluster. This one restarts Apache.

You can restart Passenger by touching RAILS_ROOT/tmp/restart.txt but I prefer just to restart Apache.

And that’s about all there is to it.

Where Merb is, and where it's going: interview with Ezra Zygmuntowicz

Posted by Jon
on Thursday, June 05

Merb has come along way since Luke first examined it here over a year ago. The philosophy remains the same: “All you need. Nil you don’t.” But the implementation has changed a bit, and it is far more mature.

Like Rails, Merb is a MVC web framework written in Ruby. Unlike Rails, Merb doesn’t tell you what ORM to use – you can use ActiveRecord, but DataMapper and Sequel are a bit more in line with the Merb philosophy. Similarly, Merb supports several Javascript libraries and templating languages, and doesn’t recommend one over another. Merb strikes a balance between Rails (large and full-featured) and Camping or Sinatra (tiny), but it is closer to Rails; one gets the sense that a large, complex site could be reasonably maintainable in Merb, unlike Camping or Sinatra.

Merb got a lot of attention at RailsConf this weekend, and will probably get even more at RubyFringe and RubyConf, and it seemed like time to review it again, about a year after our first look. Ezra Zygmuntowicz, creator of Merb, was kind enough to give us an interview.

Rail Spikes: How much production use has Merb seen so far?

Ezra: A lot. We have about 40 or so Merb apps hosted here at EngineYard. A few of them are doing 2-8 million pageviews/day with Merb rock solid at 35Mb ram on a 64 bit system.

Rail Spikes: Merb is thread-safe, unlike Rails. What advantages does thread-safety bring to Merb?

Ezra: It allows for long requests to not block the process, effectively letting multiple requests get served at once. This can drastically reduce the latency any one user gets because requests do not get backed up behind longer requests waiting for their chance to run.

Rail Spikes: So do Merb apps typically use a single mongrel/thin instance, or multiple instances?

Ezra: Merb apps still typically use multiple processes, but they require less processes then an equivalent Rails app by far.

Rail Spikes: Is the only advantage of multiple instances to make use of multiple CPU cores? And will a natively threaded Ruby implementation allow us to use a single app server per box?

Ezra: Yes, it is to use multiple cores and to spread out the load. Natively threaded ruby implementation will allow us to use much fewer processes, but running an app out of a single process is still a bad idea in case the instance crashes, so load balancing over a few processes helps increase uptime if there is an issue with any single process.

Rail Spikes: Does one need to use a thread-safe ORM like DataMapper to take advantage of this, or does ActiveRecord::Base.allow_concurrency = true work just as well?

Ezra: Yes – you need a thread safe ORM like DataMapper or Sequel. ActiveRecord::Base.allow_concurreny = true does not work well and should be avoided for now.

Rail Spikes: Merb has long been agnostic about things like ORM, templating language, and Javascript/ajax libraries. Will that continue, or will Merb ever “prefer” one systems over others? And is Merb test framework agnostic, or does it prefer rspec?

Ezra: It will continue as much as possible. We do favor rspec over test/unit so that bias does show a bit. I also personally prefer DataMapper as my ORM. But Merb will definitely remain ORM agnostic as I think that going forward there are going to be all kinds of interesting non-relational data stores to build web apps on. Merb will be right there at the forefront easily using any new persistence layer that pops up.

Rail Spikes: You’ve written that Merb and Rails can get along. Would you use Rails instead of Merb for some projects? If so, what and why?

Ezra: Rails is a more complete end to end solution; there is a large ecosystem and plugins for almost everything. But Rails is also a large bloated codebase so if you want to step outside the golden path of the framework you will often run away screaming. ;) Merb is more of a platform for you to build whatever it is you want on it. The framework code is simple and to the point so it is very easy to extend and bend Merb to your will when you run into a problem that the framework has not thought of or dealt with before. In the end I think this makes for a more scalable framework for serious hackers to build interesting new stuff I have never thought of.

So Rails may get you up and running quicker, but Merb will scale further as your application needs to go “off the rails” so to speak.

Rail Spikes: Beyond the docs, what is the best resource for a Rails developer looking to learn Merb?

Ezra: There is a Merb PeepCode that is pretty good, and the merbunity.com site is good. The Merb wiki is starting to have a lot of information. There is a book coming out soon from Manning called Merb in Action, written mostly by Michale Ivey and Yehuda Katz with me as an advisor/proofreader.

Rail Spikes: Can you point us to any speed benchmarks?

Ezra: I don’t have any prerolled benchmarks at this point. But I can say that in general Merb’s routing, dispatching, request parsing, filters and rendering are dramatically faster then ActionPack. I encourage folks to run their own benchmarks as microbenchmarks are rarely helpful.

In general Merb can serve a hello world action at 2500req/sec on ebb and can serve a simple template wrapped in a layout action at 1700req/sec or so on a Macbook Pro.

Rail Spikes: What is the 12-month plan for Merb?

Ezra: Merb 1.0 will be released sometime this summer along with DataMapper 1.0. I don’t have a long term 12 month plan for Merb, but I think that 1.0 will be a super solid platform for folks to build on top of. I don’t believe in having Merb become a kitchen sink framework, I’d rather see a tight/fast/documented core and let everything else be plugins so we don’t end up bloating the core framework.

Rail Spikes: Thanks!

Rails gets more mature

Posted by Luke Francl
on Friday, May 02

Rails 2.1 is right around the corner. I’ve been following the new features in Edge Rails and eagerly looking forward to this release. Rails 2.1 includes a number of features that will make developers’ lives easier. Here’s a few of my favorites.

Necessary directories created if they don’t exist

Neither Mercurial nor Git track empty directories. This is a pain with Rails, because you have to create a file in the log directory to make sure it gets created when you check out code, otherwise Rails won’t start. This is no longer needed, because Rails will create necessary directories if they don’t exist.

Time zone support

Time zones are a huge pain in any application, in any language because they are just plain confusing. But ya gotta do it. In Rails, the solution used to be using the TzTime and TzInfoTimeZone plugins. Rails 2.1 adds support for tracking Time objects with their time zone. This is going to make everyones’ lives a lot easier. Check out Geoff Buesing’s in-depth tutorial.

Partial updates and “dirty” tracking

Two features that I knew and loved in our home-brew ORM from my former life as a Java developer have made it into Rails.

With dirty objects you can know if you need to persist an object, and which attributes have changed, and what an attribute’s previous value was. This will be great for user messages and validations!

In Rails 2.1, ActiveRecord can update only the attributes which have changed. This can (sometimes) put your objects into an inconsistent state, but partial updates improve performance, especially when you have big TEXT or BLOB attributes that haven’t changed. Use optimistic locking to prevent users from stomping on each others’ changes.

Timestamped migrations

With all this distributed SCM going on, the classic problem of messed up migrations gets way worse. I talked about solutions to this in my talk at acts_as_conference, one of which was timestamped migrations. Timestamped migrations allow interleaved migrations. As long as those migrations don’t conflict with each other, they can be applied in any order. This has been added to Rails. Nice!

Better gem dependency and unpacking

I am a big fan of the vendor everything approach to gems because I got burned way too many times by missing gems.

But it doesn’t always work (for example, gems which must be natively compiled are a problem), and you have to install one of the various vendor everything plugins—and everyone seems to use a different one. In Rails 2.1, gem unpacking is built in with rake gems:unpack GEM=gemname. (more info)

And for those gems that don’t work, you can list them as a dependency. Your app will fail to start if the gem is not installed. Fail early, fail often!

Text helpers usable outside the view

You can now use helpers without including them into your class. Hurray!

5 little-known Rails methods

Posted by Eric Chapweske
on Wednesday, April 23

While the next release of Rails appears to be coming up, there’s still plenty of small, useful features from previous releases that aren’t widely used.

A few of my favorites:
  1. query_attribute
  2. polymorphic_path
  3. debug
  4. rake -T `query` ( Not Rails specific, but still handy! )
  5. extract_options!

1. ActiveRecord’s query_attribute

Query methods are available for each of a record’s attributes, providing for a cleaner way to check for the presence of an attribute.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# == Schema Information
# Schema version: 17
#
# Table name: users
#
#  id                    :integer(11)     not null, primary key 
#  first_name            :string(255)     
#  last_name             :string(255) 

# Original
class User < ActiveRecord::Base
  def named?
    !first_name.blank? && !last_name.blank?
  end
end

# Refactored to use query_attribute
class User < ActiveRecord::Base
  def named?
     first_name? && last_name?
  end
end

2. Indifferent links with polymorphic paths.

Rails has polymorphic edit/new/formatted path routing available out of the box. Providing an array will namespace the path with those array parameters. Available Methods: (edit|new|formatted|)polymorphic_path(record_or_hash_or_array)

1
2
3
4
5
6
7
8
9
10

# Before:
<% if @record.is_a?(User) %>
<%= user_path(@record) %>
<% elsif @record.is_a?(Friend)
<%= friend_path(@record) %>
 ... etc.

# After:
<%= polymorphic_path(@record) %>
Quite a few options are supported, and other Rails methods take advantage of polymorphic routing:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# Paths can be namespaced:
#=> admin/users/5/edit
edit_polymorphic_path([:admin, @record])

# Polymorphic urls are also internally used by helpers:
# redirects to store_path(@store)
redirect_to @store
  
# builds a form with an action to 'new_admin_stores_path'
#=> <form action="admin/stores/new" ... />
form_for([:admin, Store.new])

# <a href="/stores/5">A store in Minneapolis, MN</a>
link_to @store.name, @store

3. debug

Especially useful when starting out a project, this is quick way to understand what objects are being used in the view.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15


<%= debug @user %>

# Yields this in the view:
# - !ruby/object:User 
#  attributes: 
#    salt: 7be4287a1b27426fa6e5b6d733c707dd66425e82
#    updated_at: 2008-04-22 19:01:23
#    crypted_password: abb611def895dac923ba8ea59a78451f77473d5e
#    id: "1"
#    first_name: Eric
#    last_name: Chapweske
#    created_at: 2008-04-06 01:45:33
#  attributes_cache: {}

4. rake -T task

This is a handy Rake feature, and not limited to Rails. Can’t remember the exact syntax for a particular rake task? Trim the results generated with `rake -T` with an optional search parameter.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

$rake -T

rake annotate_models                 # Add schema information (as comments)...
rake audit:purge                     # Removes Audit records older than 2 m...
rake db:abort_if_pending_migrations  # Raises an error if there are pending...
... etc.
rake tmp:sockets:clear               # Clears all files in tmp/sockets

// Searching by the task's name

$rake -T db:migrate:r

rake db:migrate:redo   # Rollbacks the database one migration and re migrat...
rake db:migrate:reset  # Resets your database using your migrations for the...

5. extract_options!

While not needed very often, Rails comes bundled with a method to extract the options from methods that utilize the splat operator. This method removes the last object from an array if it’s a Hash, otherwise an empty hash is returned.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16


class Story < ActiveRecord::Base

  # Example: 
  # Story.published_and_tagged_with('deep', 'thoughts', :order => 'created_at desc') 
  # The generated options for this method look like this: 
  #=> { :include => :tags, :order => 'created_at desc' }
  def self.published_and_tagged_with(*tag_names)
    options = tag_names.extract_options!
    options[:include] ||= :tags
    
    ...
  end
end

References

I wasn’t able to find any write ups on the above methods, so reading the source code may be the best path if you’re curious about their exact implementations.

Auto-escaping HTML with Rails

Posted by Luke Francl
on Monday, January 28

One of the things I don’t like about Rails is that it doesn’t auto-escape HTML in user input. Forget one h call in your template and you’re screwed. Worse yet, before Rails 2.0, strip_tags and sanitize were flawed. Fortunately that’s been fixed. Django added auto-escaping even though it was a backwards incompatible change, but so far there doesn’t seem to be similar movement on the Rails front.

But I’m all about automating manual processes. So let’s fix this problem.

Sanitize before saving or before displaying? Or both?

Should you sanitize text before saving it or before displaying it?

It’s nice to not need to worry about doing anything extra in your views. However, if a field escapes your notice, you may be open for an attack.

I think your first line of defense should be model-level sanitization, but auto-escaping HTML is good backup. Doing both covers your bases at a cost of extra processing.

Introducing xss_terminate

xss_terminate is a plugin in that makes stripping and sanitizing HTML stupid-simple. It’s install and forget. And you can forget about forgetting to h() your output, because you won’t need to anymore. It’s based on acts_as_sanitized by Alex Payne but updated for Rails 2.0, and with some new features.

I like acts_as_sanitized but it’s not being maintained any more so Alex gave me the OK to take his code and do something different with it. Here’s what makes xss_terminate different:

  • It works with Rails 2.0.
  • It’s automatic. It is included with default options in ActiveReord::Base so all your models are sanitized. Period.
  • It works with migrations. Columns are fetched when model is saved, not when the class is loaded.
  • You can decide whether to sanitize or strip tags on a field-by-field basis instead of model-by-model.
  • HTML5lib support if Rails’s HTML parser isn’t doing it for you.

Here’s how you use it.

To install: script/plugin install http://xssterminate.googlecode.com/svn/trunk/xss_terminate

Strip HTML tags from all the fields in a model

1
2
class Article < ActiveRecord::Base
end

Done. All models have tags stripped by default.

Sanitize HTML from some fields

1
2
3
class Article < ActiveRecord::Base
  xss_terminate :sanitize => [:body]
end

Use HTML5lib to sanitize HTML from some fields

HTML5lib is a new library for parsing HTML for Python and Ruby. Its goal is to parse HTML like browsers do, so it’s very fault-tolerant. If you want to use it, gem install html5 and use the :html5lib_sanitize option. This is thanks to code by Jacques Distler.

1
2
3
class Article < ActiveRecord::Base
  xss_terminate :html5lib_sanitize => [:body]
end

But I don’t want to strip HTML at all from that field!

1
2
3
class Article < ActiveRecord::Base
  xss_terminate :except => [:title, :body]
end

Putting it all together

And of course, you can put these options together. Remember, fields are stripped of tags by default, so that’s assumed unless you override it.

1
2
3
class Article
  xss_terminate :except => [:author_name], :sanitize => [:title], :html5lib_sanitize => [:body]
end

Report bugs at the xss_terminate Google Code site.

Extra credit: Use Erubis

Erubis catches 80% of HTML escaping screw ups by making them impossible. You can use it in conjunction with xss_terminate or other XSS plugins to give yourself an extra layer of protection. (See our post on setting up Erubis with Rails 2.0.)

With Erubis, code like <%= "<script>alert('pwnd')</script>" %> can be auto-escaped.

However, all Rails helpers which generate HTML must be called with <%== %> so the HTML is not escaped. This leaves an opening for attacks like this:

<%== link_to user.name, "/some/url" %>

If user.name contains XSS you’re pwnd.

So while Erubis is a marked improvement over Erb it’s not a cure-all. That’s why I like to use both approaches.

Other approaches

There’s been a lot of discussion about Rails and XSS lately, so I’m hopeful that the situation will get better. Here’s a couple other XSS protection projects you can check out:

  • SafeERB – Throws exceptions if you try to display tainted strings. Call h() to untaint.
  • xss-shield – automatically h() strings unless marked as “safe”.
  • sanitize_params – strip HTML from your parameters before they hit your models.
  • AntiSamy – another whitelist-based approach (not available for Rails)

Also, check out Is your Rails App XSS Safe? and Never Untaint by Stu Halloway and Jacques Distler’s posts about making Instiki XSS-safe: XSS and XSS 2 (these are must read).

Rendering with Erubis and Rails 2.0

Posted by Eric Chapweske
on Monday, December 10

Update: ActionView has been refactored in Rails 2.0.2, making Erubis’ Rails helper, and the “Create an Erubis Initializer” section of this article, obsolete. See the comments for a Rails 2.0.2 compatible initializer. Thanks for the tip, Jason!

Erubis is a drop in replacement for Erb. Among its many features are a few notable improvements in terms of speed and security (it optionally supports auto-html escaping).

Sample Erubis Syntax:
1
2
3
4
5
# Erubis with auto HTML escaping enabled:

Hello, <%= current_user.name %> # equivalent to h(current_user.name)

<%== render :partial => 'user' %>

Installing Eribus:

1. Install the gem


gem install erubis

2. Create an Erubis initializer

app/config/initializers/erubis.rb
1
2
3
4
5
6
7
8
9
10
11
# Via http://www.kuwata-lab.com/erubis/users-guide.05.html#topics-rails
# The above link also references an optional patch that can be applied.

require 'erubis/helpers/rails_helper'

# These are optional settings:
Erubis::Helpers::RailsHelper.init_properties = { :escape => true, :escapefunc => 'h' }

# Erubis::Helpers::RailsHelper.engine_class = Erubis::Eruby # or Erubis::FastEruby
# Erubis::Helpers::RailsHelper.show_src = false
# Erubis::Helpers::RailsHelper.preprocessing = true

3. Create custom rescue templates

The default Rails debug views need to be slightly modified to support Eribus. This problem only pops up in a few spots, but Eribus doesn’t handle inline statements:

1
2
3
4
5
6
7
# Default Rails sample:
<%= request.parameters["controller"].capitalize if request.parameters["controller"] %>

# Erubis compatible rewrite:
<% if request.parameters["controller"] %>
<%= request.parameters["controller"].capitalize %>
<% end %>

If auto-escaping is enabled, all instances of <%= need to be replaced with <%== and <%=h replaced with <%=.

Step 1: Download these auto escaped, Eribus-friendly templates and put them in app/views/rescues

Step 2: Redefine the rescues path to point to the modified templates:

controllers/application.rb
1
2
3
4
# For Erubis compatible debug templates  
def rescues_path(template_name)
  "#{view_paths.first}/rescues/#{template_name}.erb"
end

Using Erubis

Auto escaped Erubis vs Erb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Erubis doesn't handle inline parsing well.
# While in Erb, this would work:
# <%=h status = current_user.status if returning_user? %> 
# It needs to be broken out in Erubis:

<% if returning_user? %>
  <%= current_user.status %> # Auto HTML escaped
<% end %>
                                                                          
Here's a list of your friends, <%= current_user.first_name %>        # Auto-escaped
<ul>
  <%== render :partial => 'friend', :collection => @friends %>       # Not escaped
</ul>
<p><%== submit_tag "Add a friend", :class => "button" %></p>         # Not escaped
<% end %>
A few things to remember:
  • Erubis doesn’t handle inline statements well
  • If auto escape is enabled, use the <%== operator when rendering partials and helpers
  • Update app/views/layout templates
References

Textmate: remove unnecessary files from Rails project

Posted by Jon
on Tuesday, November 13

Have you ever done a cmd-T or a global find in Textmate while working on a Rails project, and found more noise than signal in the results? For example, when trying to navigate to the routes.rb file, here is what I see:

routing.rb
routes.rb - config
routes.rb - configs
routes.rake
routing_test.rb
routing_error.html
routing_assertions.rb

This is because Textmate loads my entire vendor/ directory when I open up a project, including the entire Rails source. I can find the right file (it is routes.rb - config), but this breaks my focus and keeps searching and navigation from being transparent.

Similarly, log files are loaded into Textmate by default, which is terrible if you want to search across the entire site; I’ve had searches take 60 seconds or crash Textmate just because they were digging through enormous log files.

There is a simple way to fix this. Go to Preferences, and choose “Advanced” and then “Folder References”. You’ll see two regular expressions. They both start with a !, so any files that match the pattern will be excluded from Textmate. To stop searching through your vendored copy of Rails, just add this after the first parenthesis in the folder pattern:

vendor\/rails|

So the front of my folder pattern now looks like this:

!.*/(vendor\/rails|\.[^/]*

And to stop including log files, add this after the opening parenthesis in the file pattern:

.\.log|

The | is an “or” separator, so if you want to add more patterns, separate them by pipes. Pretty simple.

You can do quite a bit more, like excluding binary files (.jpg, .png, etc.), excluding plugins, etc. Just follow the same patterns as above.

iPhone subdomains with Rails

Posted by Luke Francl
on Thursday, November 08

iPhone! It seems like everyone has one, and those who don’t have one are talking about it. (I fall into the latter category.)

I recently attended an Apple iPhone Tech Talk with some of my colleagues from Slantwise. It was well worth it. I highly recommend going if the talk comes to your area. If you can’t attend, you can watch the videos online. Getting into the nitty-gritty of how to develop for the iPhone and iPod Touch was very interesting, but to me the most useful aspect of the class with the information on the iPhone web application user interface guidelines.

Most examples I’ve seen for how to do a special view for the iPhone suggest something like this:

1
2
3
4
5
6
7
8
9
10
class ApplicationController < ActionController::Base  

  before_filter :adjust_format_for_iphone

  def adjust_format_for_iphone
    if request.env["HTTP_USER_AGENT"] && request.env["HTTP_USER_AGENT"][/(iPhone|iPod)/]
      request.format = :iphone
    end
  end
end

However, Apple’s user interface guidelines for the iPhone suggest against doing user agent sniffing. The reason is that iPhone users are used to being able to use the entire web. They don’t want a limited subset.

Another problem is that when Apple releases a new device, your code will need to be updated to work with it. This actually happened when the iPod Touch was released. Some iPhone sites didn’t work on the iPod Touch because (unlike the code above) they only sniffed for “iPhone”.

When I was at the Apple iPhone Tech Talk, Apple suggested the best way to develop web applications for the iPhone was to provide the full version of your site, with a link to the iPhone web app. The iPhone version should focus on discrete functionality, and look like a native iPhone application. But if an iPhone user ever needs to use the “real” site, it’s just a clicks away. Examples of sites doing this include Facebook and Amazon)

Fortunately, this is still easy to do with Rails.

1
2
3
4
5
6
7
8
9
10
class ApplicationController < ActionController::Base  

  before_filter :adjust_format_for_iphone

  def adjust_format_for_iphone
    if request.subdomains.first == "iphone"
      request.format = :iphone
    end
  end
end

You can test that your subdomain detection works with something like this:

1
2
3
4
5
def test_hitting_app_using_iphone_subdomain_should_set_iphone_virtual_mime_type
  @request.host = "iphone.test.host"
  get :index
  assert_equal :iphone, @request.format.to_sym
end

This is kind of a drag to develop with, so when not in production mode, I sniff based on the user agent like the old way.

1
2
3
4
5
6
7
8
def adjust_format_for_iphone
  if request.subdomains.first == "iphone" || 
     (RAILS_ENV != "production" && 
      request.env["HTTP_USER_AGENT"] && 
      request.env["HTTP_USER_AGENT"][/(iPhone|iPod)/])
    request.format = :iphone
  end
end

With this, I can use iPhoney to test my code on localhost, but the sniffing isn’t used when deployed.

For those times when you do need user agent detection, Apple recommends testing the “Mobile/XX” part of MobileSafari’s user agent string. This will work across iPhone, iPod Touch, and future MobileSafari devices.

Mozilla/5.0 (iPhone; U; CPU like Mac OS X; en) AppleWebKit/XX (KHTML, like Gecko) Version/ZZ Mobile/WW Safari/YY

Here’s an AbstractRequest#iphone? method. You can use in in your views to display a message to iPhone users telling them about your iPhone-optimized site or web application (like Amazon does). But I’m not sure if this is the “Rails way” to do this. Let me know in the comments.

1
2
3
4
5
6
7
module ActionController
  class AbstractRequest
    def iphone?
      self.env["HTTP_USER_AGENT"] && self.env["HTTP_USER_AGENT"][/(Mobile\/.+Safari)/]
    end
  end
end

And of course, I used the above method to refactor my adjust_format_for_iphone method.

For more on this topic, check out the iPhone Dev Center. You have to be an ADC member to look at the content, but signing up is free.

Update: Check out Ben’s Slash Dot Dash article on iPhone on Rails – Creating an iPhone optimised version of your Rails site using iUI and Rails 2 for some new tips and tricks.

Bringing the Rails Magic to Facebook

Posted by Eric Chapweske
on Friday, November 02

While there’s a few plugin options for the Rubyist looking to write a Facebook application, none of them quite fit our needs. I opted to write one for internal use at Slantwise. Why? A fundemental difference between this code and the publicly available solutions is that its Rails-centric.

If you’re not planning on writing your app in Rails, this isn’t the library for you. The benefits to the Rails programmer is that they now have a Facebook interface that’s borderline indisguishable from other Rails code—meaning its understandable, enjoyable, and doesn’t require hours of pouring through Facebook’s API documentation.

Ruby, where art thou?

This first sample shows some of the library’s low-level functionality, and its pretty similiar to the other solutions out there. It also demonstrates the inherent problems with simply wrapping API calls.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
  # Retrieves photos from the first album that's found for the current user on Facebook
  # Then publishes some of the photos as a news update in Facebook
  ...
  #Retrieve the photos
  recommended_album   = Facebook::API::Albums.get(:uid => current_user.facebook_uid).first
  @recommended_photos = Facebook::API::Photos.get(:aid => album[:aid])

  # Assign all the necessary API params for publishing to Facebook.
  # This is a more complex (and abbreviated) API call
  # A typical update call will take at least a few lines of code
  feed_body_template = render_to_string(:partial => 'facebook/_body_template')
  feed_body_general  = render_to_string(:partial => 'facebook/_body_general')
  ...
  recommended_photos_feed = { :actor => current_user.facebook_uid, 
                              :body_template => body_template,
                              :body_general   => ... ,
                              ... }
  
  #Publish the feed to Facebook
  Facebook::API::Feed.publish_templatized_action @recommended_photos_feed

Direct API calls are great in some instances, but not in most. Writing something like the following could easily take someone new to the Facebook API hours to figure out. It also takes the magic out of working in Rails—increasing development time and developer frustration and ultimately resulting in a lesser product. Let’s bring that magic back and put a smile on our developer’s face while we’re at it:

Hello, old friend

1
2
3
4
5
  
  recommended_album   = current_user.facebook.albums.find_by_name 'Halloween'
  @recommended_photos = recommended_album.photos

  update_facebook 'recommended_photos.feed'

The first line of code is a Facebook API call. It utilizes a basic ActiveRecord connection that’s adapted to run through the Facebook API, so the results behave exactly the same as any other ActiveRecord request.

The update_facebook method is a specialized version of render that looks for a template named ‘recommended_photos.feed’, fills it with data, and sends it to Facebook. In this case, the ’.feed’ extension maps to the Facebook API call Feed.publishTemplatizedActionOfUser. The rendering action is easily configurable to support any extension/API call combination.

Interested?

The plan is to release a development version of the plugin within the next week or two to iron out any glaring bugs, followed very shortly thereafter with a production version that you can freely use in your projects. I’ll announce the plugin release here when it’s available.

Get Involved

While I think I’m off to a good start, I’m looking for help. I’m on the look out for highly useful features. If you’re developing a Facebook application and have any problem areas, send me an example of some problem code and I’ll look into incorporating a solution into the plugin.

My First Rails App

Posted by Luke Francl
on Saturday, October 27

I’ve recently have the “opportunity” to do some work on the first Rails app I developed professionally, as well as an existing codebase that’s in a similar state.

We’ve all been there. Trying to understand old code, refactoring, adding features and tests. It’s painful. And considering how much Rails has changed in the last 2 years, it’s no wonder that working with apps written for older versions is no fun.

Here’s a few “worst practices” that I’ve encountered in my own and others’ old code:

  • Using url_for instead of named routes and RESTful routes. Just say no to relying on Rails default, implicit routing. I even delete the default route from routes.rb.
  • <% @objects.each do |obj| %>...<% end %> instead of <%= render :partial => 'object', :collection => @objects %>
  • Lots of if blocks in application.rhtml instead of creating new layouts.
  • Not using content_for :whatever to insert code in layouts.
  • Not following Rails convention, like misnamed controllers: UserController instead of UsersController
  • Practically any use of scaffolding (my beef with scaffolding: code generation results in a lot of views you ultimately won’t need, which then get in your way.).
  • Not using Rails association methods: Thingy.find(owner.thingy_id) instead of owner.thingy
  • Similarly, not using the built in create and build methods on associations: Employee.create(:employer => employer, :name => "Bob") instead of employeer.employees.create(:name => "Bob")
  • Security issues. XSS and attributes that should be attr_protected first among them.
  • Bad or non-existent tests.
  • Deprecated code. It probably wasn’t your fault, but given the pace of change in Rails, working with an old app almost certianly means working with code that’s now deprecated.

Do you have any of your own favorite newb mistakes? Add ‘em in the comments.

Photo by chaim zvi.