After two days, I’ve not been able to solve this problem on my own. It seems like it should be pretty simple, but I’m missing something. I’m creating a simple blog with Posts and Authors. Authors have a boolean admin column.
The line that is giving me an error right now is where I check permissions to show the edit button in a post.Current error is:
NoMethodError in Posts#show
Showing …/posts/show.html.erb where line #18 raised:
undefined method `stringify_keys’ for #
posts/show.html.rb
<% if @author.can? :update, @post %>
<p><%= link_to 'Edit', edit_post_path(@post), :class => 'btn' %> <%= link_to 'Destroy', @post, confirm: 'Are you sure?', method: :delete %></p>
<% end %>
application_controller.rb
class ApplicationController < ActionController::Base
protect_from_forgery
rescue_from CanCan::AccessDenied do |exception|
redirect_to root_url, :alert => exception.message
end
helper_method :current_author
def current_user
@current_ability ||= Author.new(current_author)
end
end
ability.rb
class Ability
include CanCan::Ability
def initialize(author)
author ||= Author.new # guest user (not logged in)
if author.admin?
can :manage, :all
else
can :read, :all
end
end
end
Also, from what I can tell, CanCan is included in the gem file correctly.
Two things.
First, you need to have a
current_usermethod in your controller, on which cancan relies. If you don’t have one, you maycurrent_userto yourcurrent_whatevermethod or@ability = Ability.new(current_whatever)and call yourcan?‘s on that generated ability in your views (like@ability.can? :edit, @post).Second, your
Abilityusescurrent_authoron both line 4 and 5, however you don’t have acurrent_authorin yourinitializemethod. You haveauthor, though. If noAuthorobject is available/given to the ability’s initializer, you use a non-persisted author instead (and not a AuthorAbility, unless your AuthorAbility is whatcurrent_userreturns orinitializein your ability gets as argument). Something like this:Edit based on the comments to keep it simpler:
Ideally you put a
current_usermethod inside your application controller and also make it available as helper in your views (because you may want to conditionally show/hide/change things in your view based on a logged-in user).If this is new to you, I suggest to have a look at an authentication gem. Devise also introduces this and authlogic describes this in its how-to and the example application. If you’re doing authentication from scratch, you just need to return the user based on the session.
edit 2. You actually need to understand what you do, which IMHO is not the case at the moment. You’re doing a bit of a mess here 😉
Problem 1:
current_userneeds to return the current author / user logged in (not an abilty nor fallback user nor something else) ornilif no author is logged in. So you can e.g. do<% if current_user %>in your view.@current_ability ||= Author.new(current_author)is plain wrong. The fallback from the ability class needs to stay in the ability class because cancan’s methods can only be applied to an object and not tonil. So withauthor ||= Author.newin your ability, you make sure that there is an object, even if no author is logged in (in which casecurrent_authorreturnsnil).Problem 2:
helper_method :current_authoractually does nothing because there is nocurrent_authormethod in your application controller. You need to somehow definecurrent_author.Problem 3: In your view, you’re calling
can?on an instance ofAuthorwhich is wrong.can?is a method ofAbility. So you’d need to use@my_ability.can?where @my_ability is an instance of e.g.Ability.new(Author.first). This is used if you need to work with multiple abilities or customized something, which is not the case here, so just usecan?directly without a receiver (like@author.can?).For testing purposes, I’d create the following:
So your
current_userreturns a valid user (I hope, you need to at least have one in your database stored, though) and then can sort out the ability issues. If your ability works, you implement your authentication. As a beginner, I’d either stick to authlogic or devise.