The ELC Community Blog
A knowledge exchange on Ruby on Rails and Agile Development
Scope Controller Model Plugin
by jsiegel on August 10, 2007
ScopeControllerModel plugin was created to DRY up controller and model logic when working a common data model pattern. Particularly, when access to model data is restricted to the current_user or their account. Supposing you have:
class Invoice < ActiveRecord::Base belongs_to :account end class Account < ActiveRecord::Base has_many :invoices end
In this circumstance, there are two models: accounts and invoices. We want invoices to be restricted to the account of the currently logged in user, but also leverage scaffold_resource to make our site building nice and snappy.
A scaffold_resourced default controller will make reference to @invoices = Invoice.find(:all); however, this returns all invoices when we only want the invoices for a particular user. Likewise, Invoice.create(param[:invoice]) creates an invoice and has an inherent insecurity that a user can overload form data to create the invoice into the account of their choice. Enter ScopeControllerModel:
class InvoiceController < ApplicationController
scope_controller_model :invoice, :conditions => { :account => Proc.new { |c| c.send(:current_user).account } }
This code snippit shows ScopeControllerModel being used to enforce a condition throughout all model find and create calls.
Install ScopeControllerModel by:
./script/plugin install http://svn.elctech.com/svn/public/plugins/scope_controller_model
Feedback welcome.
Quick update!
After some suggestions, we've modified the plugin to support a few new bits of functionality. In order to allow particular controller actions to bypass the scope_controller_model filtering, it now supports an API similar to before_filter. Also, callbacks are now supported as well as Proc's when establishing the object that actually associates to the scope. Example below:
class InvoiceController < ApplicationController
scope_controller_model :invoice, :conditions => { :account => :current_user_account }, :except => :new
def new
@invoice = Invoice.new
end
def show
@invoice = Invoice.find(params[:id])
end
private
def current_user_account
current_user.account
end
end
All in all a simple update, but hopefully a useful one! As always, feedback welcome!
Timeline
- Ruby on Rails Enterprise - PC World
- Ruby on Rails, C# Use Increasing
- Ruby on Rails Keynote at RailsConf
- Reconciling TextMate With redgreen Tests
- Rails with JRuby and Glassfish
- Scope Controller Model Plugin
- Congratulations to Buy.com, Shoperion and us!
- Migrate Test DB Rake Plugin
- FunnyOrDie.com in Time's 50 Best Websites of 2007
- TuneCore and Public Enemy in New York Times
- Conditional Action Caching with Cache_Fu
Comments
This is exactly what I need. I’ve spent the last few days modifying acts_as_paranoid to get the functionality this plugin appears to already offer. Thank you so much!
God bless!
Blake
You can apply an account restriction to a lot of models by modifying scope_controller_model to accept an array of model names. Change line 17 onward in controller.rb to: if model.is_a?(Array) model.collect { |model| scope_controller_model(model, options) } else … end
Thanks Blake! I’ve just made an update to the plugin following your suggestion.
What about passing in a table name? There are times when I’m selecting on a model but I want to add the scope to one of the :include models.
An an example would the the authentication plugin where the finder is called on Role but I want to scope the roles_users table.
I’ve been doing some of this in my project, where :table_name is optional.
So, will this allow for true account separation when it comes to building our apps? What other tools would I need to implement something like Basecamp or Shopify, where everyone has their own wholly controllable account with unique URL, etc?
Uh, that is ugly.
Don’t understand why this
doesn’t satisfy you.