August 2008
Monthly Archive
Sun 31 Aug 2008
Posted by mike under
RailsComments Off
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.
Sat 30 Aug 2008
Posted by mike under
UncategorizedComments Off
Technorati Profile
Yep. it’s just that simple.
Tue 26 Aug 2008
Posted by mike under
RailsComments Off
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.
Tue 12 Aug 2008
Posted by mike under
Rails ,
RubyComments Off
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.
Sat 9 Aug 2008
I may write up a better review of the new Canon A650 that I got for my birthday once I figure out a little more about how to use all of the fine features and get a little more experience. I’ve been too busy to take very many pictures recently, but I did experiment with the panorama stitching feature while on vacation in Waves, NC.

The image is large (9MB) so don’t click if you are on dial-up. Does dial-up still exist?
Upon putting the camera into panorama mode, the next step is to indicate whether you will shoot moving left or moving right. The camera then lets you take up to 26 pictures and keeps showing the last 1/3 of the previous picture on the large LCD screen as an aid to lining up the next shot.
Having taken all the pictures they should then be downloaded to a folder on the computer. This panorama comprises 13 images shot at 3 MP resolution (the camera does 12.1 MP) and then stitched using the Canon software on Mac OS X. The stitching process took about 4 seconds. I think it did a pretty reasonable job and I’m excited to try it out some more.
Tue 5 Aug 2008
Posted by mike under
RailsComments Off
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.
Fri 1 Aug 2008
Posted by mike under
Rails ,
RubyComments Off
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!')