Pivotal Labs

Access Control & Permissions in Rails

edit Posted by Nick Kallen on Thursday July 26, 2007 at 02:55AM

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)

edit Posted by Nathan Wilmes on Wednesday July 18, 2007 at 09:24PM

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

edit Posted by Nick Kallen on Wednesday July 18, 2007 at 07:30PM

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

edit Posted by Brian Takita on Tuesday July 17, 2007 at 05:27PM

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

edit Posted by Nick Kallen on Monday July 16, 2007 at 04:47PM

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

edit Posted by Felix Morio on Wednesday July 11, 2007 at 06:09PM

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

edit Posted by Chad Woolley on Thursday July 05, 2007 at 09:25PM

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

edit Posted by Chad Woolley on Thursday July 05, 2007 at 09:23PM

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

edit Posted by Chad Woolley on Thursday July 05, 2007 at 09:19PM

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_after methods.

  • 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 define blank?.