Rails


This screen cast covers Scaling Rails apps using Rack and Metal and is an execellent tutorial on both subjects. Jason Pollack, one of the Rails Envy guys, does a superb job explaining how rack and metal work in Rails 2.3.

We are starting to use thrift and needed an ant build recipe. Here’s what we came up with. It works good and the only thing that looks like an abstraction leakage to me is that I needed to know the package, the java namespace, for the resultant generated thrift code and the name of one of the thrift generated files. The primary goal was to eliminate running the thrift generator when the generated code is newer than the .thrift files. There isn’t a one to one mapping between .thrift files and generated output so if any of the generated stuff is newer than any of the thrift then it all gets recreated.

I also didn’t want to have to copy the thrift output to someplace else, so a javac target was added to just treat the “gen-java” thrift output as a new source directory for direct java compilation. The normal ant target to compile the java code can now just depend on “thrift-gen”.

The big project our team has been working on for the last couple of months if finally public. It’s a fairly substantial rails app, about 40 models, lots of cool stuff. Most of the site is subscription based but you could checkout some of the info and look at the home page:

http://globalsports.net/

It’s a social networking site centered around sports videos and young athletes.

Rails controllers are just super nice. My MVC experience predates STRUTS by several years. So having done it by hand several times, and seen pretty much everything out there, I just find that Rails has a lot of nice features that make it super convenient to use: controller inheritance, before_filter, default routes, and much more.

But this week I got in a real jam with security.

I am inheriting a bunch of controllers from a plugin. All of the plugin controllers extend BaseController, which the plugin also supplies. In order to add or change some functions, I just create a controller by the same name, do what I want, extend BaseController and everything just works great.

Except when the controller in the plugin has a line like this:

before_filter :login_required, :except => [:edit, :destroy]

If in my version of the controller I need to change it so that the edit and destroy actions are also protected by the login_required filter, well that gets tricky real fast. If you think you can just add

before_filter :login_required

and you are covered, think again. If you want to try

before_filter :login_required, :only => [:edit, :destroy]

well that doesn’t work either. Some people have a proposed (with diff and tests no less) that an :exclude and :include tag. This seems sensible and would be a solution to the problem I had.

What I chose to do is to wrap the filter I wanted to apply more broadly in a new name.

class BaseController < ApplicationController
  before_filter :my_login_required

  protected

  def my_login_required
    login_required
  end

end

What that does is allow me to use my filter without regard to where it might collide with specifications from the inherited controllers. This makes it much easier to audit security in my application, is much less verbose, and pretty easy to understand.

In this case the filter that I wrapped was pretty lightweight. It didn't do any database lookups for normal cases. That's good because rails now doesn't know that login_required and my_login_required are really the same filter. So I'm paying the overhead of running it twice when I require my_login_required and the super class uses login_required. If it was a heavyweight method that would have caused a problem I would have to hack a little more to make sure that one of them could bail out early.

On a recent project we served up a flash/flex based client from a Rails app. The flash client needed to call back into the app to retrieve xml to populate some displays. Nice. Rails is good that way, especially if you have started from scaffolded resources which have all the format.xml responders already in them.

Unfortunately the dashes that rails ActiveSupport::CoreExtensions::Array::Conversions uses
(same goes for AS::CE::Hash::) takes the model attribute names and puts dashes into them
to create the xml tags.

So if you have a User model with user_name attribute and you just want to return
@user.to_xml, you are going to get

   <user>
      <user-name>value of name</user-name>
   </user>

Which looks pretty. But apparently the xml parser in flex can’t handle that. I couldn’t find any reference in the documentation on how to fix this, but when in doubt UTSL, baby. Which shows that there is a :dasherize option that can be provided.

  @user = User.find(params[:id])
  @user.to_xml(:dasherize => false)

Now the generated xml will have underscores instead of dashes. Flex developers everywhere should send DHH a nickel for that one. Ok, I don’t know who made the actual commit for it, but you’s should give David the nickel anyway.

In a Rails system with a pretty standard user model, views, and controller, I recently found that the users_controller was getting pretty large. There was a pretty significant amount of code that I wanted to add to deal with the users using a different set of views under some circumstances — and I didn’t want to add all of that to the users_controller.rb. What I wanted was to move these functions to a staff_controller.rb, use restful routing on it and just treat the user object differently.

This didn’t work out so well initially. Trying to use form_for in a view always reflects on the type of object you have and sends the results right back to the users_controller — not what I wanted. I tried a bunch of stuff to force the form_for to do what I wanted but no joy. What I wanted I thought was a way to alias the user model.

class Staff < User
end

Problem solved.

The User model had some nice named scopes for finders, for example:

  named_scope
          :recent,
          :conditions => ["created_at > ?", 2.weeks.ago]

And now I can do all of the following

  Staff.first
  Staff.recent
  Staff.find(params[:id])
  @staff = Staff.recent.find(:all,
              :conditions => ['name = ?',"jim"])
  @staff.last_login_at = Time.now
  @staff.save!

.. which since reflection now says they are staff objects, causes
all the view helpers that reflect on stuff to work properly and doesn’t
break any of the ActiveRecord stuff.

I got sucked into a trap. In a Rails migration it’s so easy to use an ActiveRecord::Base model object to update some stuff, and it looks nice and DRY. But it is extremely fragile. For example suppose your domain has a user and sponsor model. Each user has a sponsor. You start out this way:

   def self.up
      create table :users do |t|
         t.string :name
         t.string :password
         t.string :sponsor_name
         t.timestamps
      end
   end

Later you want to insist that every user in the system have a sponsor. You already have a bunch of test data that you are intellectually invested in, so you whip up a quick migration to make sure.

  def self.up
     User.find(:all, :conditions => 'sponsor_name IS NULL').each do |u|
         u.sponsor_name = "The Default"
         u.save!
      end
  end

Cool. Works good. You can go up and down. But some time later you realize the supreme error of your ways. You should have had a sponsor model and the user should have a sponsor_id, not a sponsor_name. Doh! What was I thinking. Time for another migration:

   def self.up
      add_column :users, :sponsor_id, :integer
      drop_column :users, :sponsor_name
   end

and the corresponding self.down method too, and the necessary belongs_to, has_one in the user and sponsor model classes. But now you can’t migrate the database up from bootstrap any more because your use of the the User model with u.sponsor_name above will not work. The user no longer has a sponsor_name.

The root of the issue is that the model objects are not versioned along with the database as it goes up and down. Since fixtures just use the same model objects that doesn’t help one bit.

The only reliable way we have come up with is to inject data using raw sql. So step two above
could change to

  def self.up
     execute 'update users set sponsor_name = "The Default" where sponsor_name IS NULL'
  end

That works, up and down the line, because the database sql IS in sync with the database at every step.

Have a better way to write less brittle migrations? Let me know in the comments.

The typical Ruby on Rails flash area is only drawn when there is a message to flash. I think something like this is fairly common (this example is haml, but that’s not the point) usually done in a partial, something like app/views/shared/_flash.html.haml:

- %w(info notice error).each do |type|
  - if flash[type.to_sym]
    .warning.flash_message{:class => "#{type.to_s}"}
      %span= h(flash[type.to_sym])
      %small
        %a{:href=>"#", :onclick=>"$$('div.flash_message')[0].hide();return false"} Close this message
 

The problem with this is that if you are Ajaxing a bunch of stuff you could use the flash area for user interaction and highlighting messages, but if it isn’t there, that could be a problem. So how about doing this instead so that the flash area is always drawn, just not displayed:

- %w(info notice error).each do |type|
  .warning.flash_message{ :class => "#{type}", :id => "flash_#{type}", :style => "display: #{flash[type.to_sym] ? 'block' : 'none'}" }
    %span
      - if flash[type.to_sym]
        = h(flash[type.to_sym])
    %small
      %a{:href=>"#", :onclick=>"$$('div.#{type.to_s}')[0].hide();return false"} Close this message

The flash closer is still in there so we don’t have to worry about the complexity of building that again every time, and the message is in a separate span element so it is easy to fill in. So then we can add a little bitty application helper (app/helpers/application_helper.rb)

  def flashnow(page,msg)
    page.select("#flash_notice span").first.replace("#{msg}")
    page.select("#flash_notice").first.show
  end

You might need to change the replace method for replaceHtml
depending on the version of prototype you are working with.
and call it from any view that is building an rjs response:

  flashnow(page,'Yeeha, cowboy, it worked!')