How can I write a RESTful controller that requires one of N parent resources to exist?
We use CanCan. In our library application, a Book
or Magazine
can be checked out:
POST /books/123/loans # LoansController#new :book_id => 123
POST /magazines/456/loans # LoansController#new :magazine_id => 456
Books
and Magazines
have a parent class Stockable
, so the above is equivalent to:
POST /stockables/123/loans # LoansController#new :stockable_id => 123
POST /stockables/456/loans # LoansController#new :stockable_id => 456
However, not every Stockable
can be loaned:
# error: can't check out reference books from the library
POST /reference_books/789/loans # LoansController#new :reference_book_id => 789
# same error
POST /stockables/789/loans # LoansContr开发者_开发百科oller#new :stockable_id => 789
What's the right way to write the LoansController
with CanCan so that it can handle anything that's Loanable
, without needing to make a specific route for everything that's Loanable
?
I would just use ability.rb
to define what actions are / are not allowed for each object type. The following is assuming you're using STI, and so there is a stockable_type
column containing the name of the class each row represents in your db. If you aren't using STI you can still use another method to correctly determine object type (probably by passing a block to the can
and cannot
methods instead of using the hash syntax [check the CanCan wiki, Defining Abilities for more info]).
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # guest user (not logged in)
can [:read], :all
can :new, Loan, :stockable_type => ["Book", "Magazine"]
cannot :new, Loan, :stockable_type => "ReferenceBook"
end
end
Then, just catch CanCan::AccessDenied
in your LoansController
and you can redirect people attempting to checkout reference books.
rescue_from CanCan::AccessDenied do |exception|
redirect_to some_url, :alert => "You can't check out reference books!"
end
精彩评论