Skip to content
This repository was archived by the owner on Dec 12, 2021. It is now read-only.
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: ryanb/cancan
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 0.2.1
Choose a base ref
...
head repository: ryanb/cancan
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 1.0.0
Choose a head ref
  • 8 commits
  • 11 files changed
  • 1 contributor

Commits on Dec 13, 2009

  1. turning load and authorize resource methods into class methods which …

    …set up the before filter so they can accept additional arguments
    ryanb committed Dec 13, 2009
    Copy the full SHA
    a5f9882 View commit details
  2. Adding :collection and :new options to load_resource method so we can…

    … specify behavior of additional actions if needed.
    ryanb committed Dec 13, 2009
    Copy the full SHA
    63634b4 View commit details
  3. Copy the full SHA
    94e031b View commit details
  4. Copy the full SHA
    cd217eb View commit details
  5. Copy the full SHA
    51fa61b View commit details
  6. Copy the full SHA
    a75aee7 View commit details
  7. Copy the full SHA
    ffa677b View commit details
  8. Copy the full SHA
    f7480d1 View commit details
16 changes: 16 additions & 0 deletions CHANGELOG.rdoc
Original file line number Diff line number Diff line change
@@ -1,3 +1,18 @@
1.0.0 (Dec 13, 2009)

* Don't set resource instance variable if it has been set already - see issue #13

* Allowing :nested option to accept an array for deep nesting

* Adding :nested option to load resource method - see issue #10

* Pass :only and :except options to before filters for load/authorize resource methods.

* Adding :collection and :new options to load_resource method so we can specify behavior of additional actions if needed.

* BACKWARDS INCOMPATIBLE: turning load and authorize resource methods into class methods which set up the before filter so they can accept additional arguments.


0.2.1 (Nov 26, 2009)

* many internal refactorings - see issues #11 and #12
@@ -6,6 +21,7 @@

* support custom objects (usually symbols) in can definition - see issue #8


0.2.0 (Nov 17, 2009)

* fix behavior of load_and_authorize_resource for namespaced controllers - see issue #3
32 changes: 28 additions & 4 deletions README.rdoc
Original file line number Diff line number Diff line change
@@ -10,7 +10,7 @@ See the RDocs[http://rdoc.info/projects/ryanb/cancan] and Wiki[http://wiki.githu

You can set it up as a gem in your environment.rb file.

config.gem "cancan", :source => "http://gemcutter.org"
config.gem "cancan"

And then install the gem.

@@ -21,7 +21,7 @@ Alternatively you can install it as a Rails plugin.
script/plugin install git://github.com/ryanb/cancan.git


== Setup
== Getting Started

First, define a class called Ability in "models/ability.rb".

@@ -52,10 +52,10 @@ You can also use these methods in a controller along with the "unauthorized!" me
unauthorized! if cannot? :read, @article
end

Setting this for every action can be tedious, therefore a before filter is also provided to automatically authorize all actions in a RESTful style resource controller.
Setting this for every action can be tedious, therefore the load_and_authorize_resource method is also provided to automatically authorize all actions in a RESTful style resource controller. It will set up a before filter which loads the resource into the instance variable and authorizes it.

class ArticlesController < ApplicationController
before_filter :load_and_authorize_resource
load_and_authorize_resource

def show
# @article is already loaded
@@ -150,6 +150,30 @@ The following aliases are added by default for conveniently mapping common contr
alias_action :edit, :to => :update


== Authorizing Controller Actions

As mentioned in the Getting Started section, you can use the +load_and_authorize_resource+ method in your controller to load the resource into an instance variable and authorize it. If you have a nested resource you can specify that as well.

load_and_authorize_resource :nested => :author

You can also pass an array to the :+nested+ attribute for deep nesting.

If you want to customize the loading behavior on certain actions, you can do so in a before filter.

class BooksController < ApplicationController
before_filter :find_book_by_permalink, :only => :show
load_and_authorize_resource

private

def find_book_by_permalink
@book = Book.find_by_permalink!(params[:id)
end
end

Here the @book instance variable is already set so it will not be loaded again for that action. This works for nested resources as well.


== Assumptions & Configuring

CanCan makes two assumptions about your application.
4 changes: 2 additions & 2 deletions cancan.gemspec
Original file line number Diff line number Diff line change
@@ -4,8 +4,8 @@ Gem::Specification.new do |s|
s.description = "Simple authorization solution for Rails which is completely decoupled from the user's roles. All permissions are stored in a single location for convenience."
s.homepage = "http://github.com/ryanb/cancan"

s.version = "0.2.1"
s.date = "2009-11-26"
s.version = "1.0.0"
s.date = "2009-12-13"

s.authors = ["Ryan Bates"]
s.email = "ryan@railscasts.com"
1 change: 1 addition & 0 deletions lib/cancan.rb
Original file line number Diff line number Diff line change
@@ -5,5 +5,6 @@ class AccessDenied < StandardError; end
end

require File.dirname(__FILE__) + '/cancan/ability'
require File.dirname(__FILE__) + '/cancan/controller_resource'
require File.dirname(__FILE__) + '/cancan/resource_authorization'
require File.dirname(__FILE__) + '/cancan/controller_additions'
144 changes: 102 additions & 42 deletions lib/cancan/controller_additions.rb
Original file line number Diff line number Diff line change
@@ -3,7 +3,109 @@ module CanCan
# This module is automatically included into all controllers.
# It also makes the "can?" and "cannot?" methods available to all views.
module ControllerAdditions
module ClassMethods
# Sets up a before filter which loads and authorizes the current resource. This performs both
# load_resource and authorize_resource and accepts the same arguments. See those methods for details.
#
# class BooksController < ApplicationController
# load_and_authorize_resource
# end
#
def load_and_authorize_resource(options = {})
before_filter(options.slice(:only, :except)) { |c| ResourceAuthorization.new(c, c.params, options.except(:only, :except)).load_and_authorize_resource }
end

# Sets up a before filter which loads the appropriate model resource into an instance variable.
# For example, given an ArticlesController it will load the current article into the @article
# instance variable. It does this by either calling Article.find(params[:id]) or
# Article.new(params[:article]) depending upon the action. It does nothing for the "index"
# action.
#
# Call this method directly on the controller class.
#
# class BooksController < ApplicationController
# load_resource
# end
#
# A resource is not loaded if the instance variable is already set. This makes it easy to override
# the behavior through a before_filter on certain actions.
#
# class BooksController < ApplicationController
# before_filter :find_book_by_permalink, :only => :show
# load_resource
#
# private
#
# def find_book_by_permalink
# @book = Book.find_by_permalink!(params[:id)
# end
# end
#
# See load_and_authorize_resource to automatically authorize the resource too.
#
# Options:
# [:+only+]
# Only applies before filter to given actions.
#
# [:+except+]
# Does not apply before filter to given actions.
#
# [:+nested+]
# Specify which resource this is nested under.
#
# load_resource :nested => :author
#
# Deep nesting can be defined in an array.
#
# load_resource :nested => [:publisher, :author]
#
# [:+collection+]
# Specify which actions are resource collection actions in addition to :+index+. This
# is usually not necessary because it will try to guess depending on if an :+id+
# is present in +params+.
#
# load_resource :collection => [:sort, :list]
#
# [:+new+]
# Specify which actions are new resource actions in addition to :+new+ and :+create+.
# Pass an action name into here if you would like to build a new resource instead of
# fetch one.
#
# load_resource :new => :build
#
def load_resource(options = {})
before_filter(options.slice(:only, :except)) { |c| ResourceAuthorization.new(c, c.params, options.except(:only, :except)).load_resource }
end

# Sets up a before filter which authorizes the current resource using the instance variable.
# For example, if you have an ArticlesController it will check the @article instance variable
# and ensure the user can perform the current action on it. Under the hood it is doing
# something like the following.
#
# unauthorized! if cannot?(params[:action].to_sym, @article || Article)
#
# Call this method directly on the controller class.
#
# class BooksController < ApplicationController
# authorize_resource
# end
#
# See load_and_authorize_resource to automatically load the resource too.
#
# Options:
# [:+only+]
# Only applies before filter to given actions.
#
# [:+except+]
# Does not apply before filter to given actions.
#
def authorize_resource(options = {})
before_filter(options.slice(:only, :except)) { |c| ResourceAuthorization.new(c, c.params, options.except(:only, :except)).authorize_resource }
end
end

def self.included(base)
base.extend ClassMethods
base.helper_method :can?, :cannot?
end

@@ -70,48 +172,6 @@ def can?(*args)
def cannot?(*args)
(@current_ability ||= current_ability).cannot?(*args)
end

# This method loads the appropriate model resource into an instance variable. For example,
# given an ArticlesController it will load the current article into the @article instance
# variable. It does this by either calling Article.find(params[:id]) or
# Article.new(params[:article]) depending upon the action. It does nothing for the "index"
# action.
#
# You would often use this as a before filter in the controller. See
# load_and_authorize_resource to handle authorization too.
#
# before_filter :load_resource
#
def load_resource
ResourceAuthorization.new(self, params).load_resource
end

# Authorizes the resource in the current instance variable. For example,
# if you have an ArticlesController it will check the @article instance variable
# and ensure the user can perform the current action on it.
# Under the hood it is doing something like the following.
#
# unauthorized! if cannot?(params[:action].to_sym, @article || Article)
#
# You would often use this as a before filter in the controller.
#
# before_filter :authorize_resource
#
# See load_and_authorize_resource to automatically load the resource too.
def authorize_resource
ResourceAuthorization.new(self, params).authorize_resource
end

# Calls load_resource to load the current resource model into an instance variable.
# Then calls authorize_resource to ensure the current user is authorized to access the page.
# You would often use this as a before filter in the controller.
#
# before_filter :load_and_authorize_resource
#
def load_and_authorize_resource
load_resource
authorize_resource
end
end
end

39 changes: 39 additions & 0 deletions lib/cancan/controller_resource.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
module CanCan
class ControllerResource # :nodoc:
def initialize(controller, name, parent = nil)
@controller = controller
@name = name
@parent = parent
end

def model_class
@name.to_s.camelize.constantize
end

def find(id)
self.model_instance ||= base.find(id)
end

def build(attributes)
if base.kind_of? Class
self.model_instance ||= base.new(attributes)
else
self.model_instance ||= base.build(attributes)
end
end

def model_instance
@controller.instance_variable_get("@#{@name}")
end

def model_instance=(instance)
@controller.instance_variable_set("@#{@name}", instance)
end

private

def base
@parent ? @parent.model_instance.send(@name.to_s.pluralize) : model_class
end
end
end
38 changes: 27 additions & 11 deletions lib/cancan/resource_authorization.rb
Original file line number Diff line number Diff line change
@@ -2,9 +2,10 @@ module CanCan
class ResourceAuthorization # :nodoc:
attr_reader :params

def initialize(controller, params)
def initialize(controller, params, options = {})
@controller = controller
@params = params
@options = options
end

def load_and_authorize_resource
@@ -13,29 +14,44 @@ def load_and_authorize_resource
end

def load_resource
self.model_instance = params[:id] ? model_class.find(params[:id]) : model_class.new(params[model_name.to_sym]) unless params[:action] == "index"
unless collection_actions.include? params[:action].to_sym
if new_actions.include? params[:action].to_sym
resource.build(params[model_name.to_sym])
elsif params[:id]
resource.find(params[:id])
end
end
end

def authorize_resource
@controller.unauthorized! if @controller.cannot?(params[:action].to_sym, model_instance || model_class)
@controller.unauthorized! if @controller.cannot?(params[:action].to_sym, resource.model_instance || resource.model_class)
end

private

def model_name
params[:controller].split('/').last.singularize
def resource
@resource ||= ControllerResource.new(@controller, model_name, parent_resource)
end

def parent_resource
parent = nil
[@options[:nested]].flatten.compact.each do |name|
parent = ControllerResource.new(@controller, name, parent)
parent.find(@params["#{name}_id".to_sym])
end
parent
end

def model_class
model_name.camelcase.constantize
def model_name
params[:controller].split('/').last.singularize
end

def model_instance
@controller.instance_variable_get("@#{model_name}")
def collection_actions
[:index] + [@options[:collection]].flatten
end

def model_instance=(instance)
@controller.instance_variable_set("@#{model_name}", instance)
def new_actions
[:new, :create] + [@options[:new]].flatten
end
end
end
Loading