Upgrading to 1.1

ryanb edited this page Aug 13, 2010 · 6 revisions

CanCan version 1.1 includes several new features along with a few backwards incompatible changes. See below for details, and be sure to check out the updated README and Wiki pages.

New Features

Conditions Hash

Abilities can now be restricted using a conditions hash instead of a Ruby block. For example, if you previously had this.

can :update, Article do |article|
  article && article.user == user && article.visible?
end

You can now do this.

can :update, Article, :visible => true, :user_id => user.id

The block is still available but I encourage you to use condition hashes whenever possible because it can be used in database queries.

See Defining Abilities with Hashes for more information.

Fetching Records

One of the biggest limitations of CanCan in the past was that it was not possible to use the Ability logic in a database query to fetch only the readable records. With the addition of condition hashes to define abilities it is now possible to use that in the database through the added accessible_by scope in Active Record.

# in controller
@articles = Article.accessible_by(current_ability)

This will only fetch the articles which the current user has read access to. If you are not using Active Record you can access the conditions hash directly.

current_ability.conditions(:read, Article)

See Fetching Records for more information.

RSpec Matcher

If you are testing the Ability class through RSpec there is a new be_able_to matcher available. This checks if the can? method returns true.

require "cancan/matchers"
# ...
ability.should be_able_to(:read, Article)

Also see Testing Abilities.

Additional can? Arguments

It is now possible to pass additional arguments to the can? method. These will be passed into the block. This is useful for passing additional information about the request.

# in controller or view
can? :read, article, request.remote_ip

# in ability
can :read, Article do |article, remote_ip|
  # ...
end

See Accessing Request Data for an alternative solution.

AccessDenied Exception

You can now access additional information about the unauthorized action inside the CanCan::AccessDenied exception. This is useful for changing the behavior in the rescue_from clause.

exception.action # => :read
exception.subject # => Article

You can also change the default error message from here. This will be returned as the message if one wasn’t specified when the exception was raised.

exception.default_message = "Unauthorized!"
exception.message # => "Unauthorized!

See Exception Handling for more information.

The authorize! method

An authorize! method was added to the controller. Instead of this.

unauthorized! if cannot? :read, @article

Do this.

authorize! :read, @article

This performs the can? check and raises the exception if needed. You can pass a custom message with the :message option.

authorize! :read, @article, :message => "Not authorized to read this article."

Backwards Incompatibility

Remove unauthorized!

The unauthorized! method has been removed. Use authorize! instead as shown above. If you need more custom behavior you can raise the exception manually.

raise CanCan::AccessDenied.new("Cannot read article.", :read, @article)

Rename :class to :resource

If you used the :class option in the load_and_authorize_resource methods then use :resource instead. This change allows for a symbol to be passed into :resource if there is no class to use.

Cache current_ability

If you defined the current_ability method in the controller you’ll now need to cache it like so.

def current_ability
  @current_ability ||= CustomAbility.new(current_account)
end