Access Control & Permissions in Rails
Access Control is a simple idea. We want company employees to be able to delete inappropriate content; but random Users cannot. Here I propose one way to implement Access Control that has the particular advantage of being very general, very concise, and unlikely to be violated. I call it RESTful Access Control.
Til the End of Time (or Time.now)
Sooner or later, every test-driven developer discovers that they need a superpower - the power to control time. Let's say you're working on a scheduling system. You're going to want to write tests that say, "Assume that it's 2PM on January 3rd and this user does THIS. What happens as a result? What happens at 4PM on January 3rd as a result?"
One of the joys of writing in Rails is the sheer power you have to change your application universe at almost any level you choose. As a result, we've come up with a variety of ways to solve this problem. As it turns out, with great power comes great responsibility. (I'm not sure why I'm going all comic book right now, but let's run with it).
Creating Multiple Models in One Action
One of the issues in my previous post The Controller Action that sparked some interest is the handling of the creation of multiple Models in one Action. In this post I shall elaborate on this problem in some detail, first considering the cases where in constructing the Dependent Object no data are needed from the querystring/params, and secondly where such data are necessary (and where we have a complicated nested Form). Let's head right in to an example.
With No Extra Params to Worry About
Your site has Groups and Memberships. Our Business Rule is: when a user creates a Group, she should also be a Member of that Group. Following the Controller Formula, we know the solution in advance:
class GroupsController < ActionController::Base
def create
group = logged_in_user.groups.build(params[:group])
raise SecurityTransgressionError.new unless logged_in_user.can_create?(group)
if group.save
...
end
end
end
This leaves unresolved where to put our Business Rule...
Sake for Gems Downloads List
I have a few gems on Rubyforge and I want to track how many of them were downloaded. I found Firefox's search tools lacking to find my gem rr.
To fix this issue, I made a sake task, named gems:downloads:list, that prints the gem downloads in text.
The source is on caboo.se.
You can install it by using:
sudo gem install sake sake -i http://pastie.caboo.se/79547.txt gems:downloads:list sake gems:downloads:list | less
This will give an output like:
------------------------------------------------ | Gem | Downloads | ------------------------------------------------ | rails | 1194471 | | activerecord | 1121778 | | actionpack | 1054718 | | activesupport | 990851 | | actionmailer | 960759 | | actionwebservice | 948640 | | rake | 860824 | | mysql | 593476 | | fcgi | 230394 | | mongrel | 220370 | | daemons | 167443 | | rmagick | 164537 | | gem_plugin | 153505 | | RedCloth | 147182 | | rubygems-update | 119615 | | net-ssh | 114369 | | sqlite3-ruby | 105796 | | fastthread | 95534 | | cgi_multipart_eof_fix | 95399 | | needle | 87718 |
Sake is way cool. It was just too easy to implement and deploy this. Have fun making your own sake tasks.
The Controller Formula
When I introduce a programmer to Rails I encourage them to read the article Skinny Controller, Fat Model. My only complaint about this article is that it applies the Skinny/Fat Pattern in a vague way, proceeding as if by intuition. I've found instead that there is a formulaic way to produce excellent Controller code--the Controller Formula
Skinny/Fat: Code Complexity vs. Abstraction Boundaries vs. The Formula
The Skinny/Fat pattern is typically stated in terms of Lines of Code. Your Controllers should have few Lines of Code; where there are many, move them to the Model. This rule has more exceptions than it has applications, so we can state the intent of the Pattern more precisely in terms of Abstraction Boundaries. Remove all Business Logic from your Controllers and put it in the model. Your Controllers are only responsible for mapping between URLs (including other HTTP Request data), coordinating with your Models and your Views, and channeling that back to an HTTP response. Along the way, Controllers may do Access Control, but little more. These instructions are precise, but following them requires intuition and subtle reasoning. The purpose of this post is to avoid subtle heuristics like Abstraction and Code Complexity. There is a Formula. Let's see an example that follows the Formula:
def create
model = Model.new(params[:model])
raise SecurityTransgressionError.new unless logged_in_user.can_update?(model)
if model.save
render ...
else
...
end
end
What's going on here? The Action channels user input to the model. It raises an Exception if the user lacks permission to perform this Action. Validation is performed by the model. Given the results, the Controller renders the appropriate Template to the HTTP Response.
Applying different error display styles
Rails' build-in view helpers (text_field, etc.) can automatically render errors. The default error display is easy to use and functional, but it only goes so far. Sometimes, it's necessary to display errors in different ways in different parts of your application.
Rails provides a way of changing error rendering in "ActionView::Base.field_error_proc". Unfortunately, the only way to change the error display is on a global basis, which makes the following code non-thread safe.
We wanted to apply different error styles to different views. A simple helper method, such as this one, will allow you to specify the error display you would like to use:
<code> def with_error_proc(error_proc) pre = ActionView::Base.field_error_proc ActionView::Base.field_error_proc = error_proc yield ActionView::Base.field_error_proc = pre end </code>
To define different error display styles, we can use a hash that contains various error procs:
<code>
ERROR_PROCS = {}
ERROR_PROCS[:errors_below] = Proc.new do |html_tag, instance|
html_tag+' error'
# renders errors below the field
end
</code>
Finally, to use this in your controller, just wrap the render method with #with_error_proc. It's also possible to use this directly in your view.
Standup 06/28/2007
Interesting Things
- You can do single key presses in selenium with key-press. This is very useful in some scenarios - for example, predictive typing in search fields.
Standup 06/27/2007
Interesting Things
- Don't forget about the 'net' tab in the FireBug firefox extension. This can take the place of other separate plugins such as Live HTTP Headers.
Standup 06/26/2007
Interesting Things
RSpec global before/after: In addition to Behaviour-scoped before and after method forms, Rspec also has global
prepend_before, prepend_after, append_before, append_aftermethods.RSpec Custom Expectation Matchers: Remember that you can write your own Custom Expectation Matchers to make your specs as expressive as desired. For example:
foo.should contain_text.Self-Signed SSL Cert Gotcha: Firefox and IE hate it when your Certificate Authority and Server Certificate have the same Common Name.
RailMail: RailMail "gives you a persisted view of any mail your application has sent... With railmail there is no need to set up testing email accounts while developing your application; just send out mail to any address and Railmail will capture it." Nice...
validates_presence_of in ActiveRecord calls
blank?. This means you can use it check for dependent objects if you defineblank?.







