0

I have this in models/ability.rb

class Ability
  include CanCan::Ability

  def initialize(user)
    user ||= User.new   

    if user.role? :registered
      can :read Post
    end

  end

When I do this on rails console

#this returns a user with a role: "registered" attribute   
user = User.first
post = Post.first
ability = Ability.new(user)

### This returns false ###
ability.can?(:read, post)
#=> false

This spec I have written to test the ability also fails while i expect it to pass.

describe User, :type => :model do
  let(:post) {create(:post)}
  describe "abilities" do
    subject(:ability){Ability.new(user)}
    let(:user){nil}

     context "when is a registered user" do

       ## the default value for the role attribute in the user factory is "registered"
       let(:user) {create(:user)}
       it {is_expected.to be_able_to :read, post}
     end
  end
end

I can access and read posts in both /posts and /posts/:id when I am authenticated as a registered user on the browser, I have no idea why it is failing in both rails console and rspec.

Optimus Pette
  • 3,250
  • 3
  • 29
  • 50
  • try inserting `byebug` just before `user ||= User.new`, and then run your usual rails console commands. When byebug console show up, inspect the `user` variable first (`puts user`), then check its ability: `user.role? :registered` – Jay-Ar Polidario Jan 13 '16 at 13:19
  • @Jay-ArPolidario I am not familiar with byebug, but when i do this in rails console `user.first` and then `user.role`, it outputs `"registered"` – Optimus Pette Jan 13 '16 at 13:44
  • I do not know what user-role gem you used or this is your own implementation (I am referring to `user.role? :registered`) Even if you say `user.role` outputs "registered", it doesn't automatically mean `user.role? :registered` is true (depending on your implementation). If you have byebug in your Gemfile, try inserting this `byebug` just above `user ||= User.new`, then save that ability.rb file, then reload your rails console. After that try again the commands you typed into rails console. After that byebug console should appear. From, there type `user.role? :registered`, tell me the output – Jay-Ar Polidario Jan 13 '16 at 14:00
  • the byebug window does not appear on rails console but it does appear on the server log, when i type `puts user` nothing happens – Optimus Pette Jan 13 '16 at 14:08
  • If `user.role? :registered` return true (then that means there's no problem in Ability.rb). If that's the case, I couldn't think yet of any other reason it would have failed. I'd guess some callback somehow overrides the `ability` variable. – Jay-Ar Polidario Jan 13 '16 at 14:08
  • The byebug window should appear after you enter `ability = Ability.new(user)` I just tested it just now that it appears. – Jay-Ar Polidario Jan 13 '16 at 14:10
  • `user.role? :registered` returns `true`. – Optimus Pette Jan 13 '16 at 14:11
  • Ok... Hmmm.. I will try to replicate a fresh rails app with a User and Post model just to try if I will also have the same result as yours. Be right back. – Jay-Ar Polidario Jan 13 '16 at 14:12
  • @Jay-ArPolidario I'm using rails 5.0 beta, thanks. – Optimus Pette Jan 13 '16 at 14:14
  • `ability.can?(:read, post)` gave me `true` instead. My specs: Rails 4.2.5, cancancan-1.10.1, Ruby 2.2.1. For reference, here's my replication procedure: `rails new rails_test`, then ran `rails g model user name role`, then ran `rails g post title content`, then created a dummy user and post record with user.role = 'registered', I slightly modified your ability.rb's `if user.role? :registered` into `if user.role == 'registered'`, then ran your rails console commands you typed. Now, my guess is this has something to do with Rails 5. Check your cancancan version as well just in case – Jay-Ar Polidario Jan 13 '16 at 14:24
  • It is not a problem with rails, I have downgraded rails in the application to 4.2.5 and i get the same result. – Optimus Pette Jan 13 '16 at 20:38
  • That is strange. Is your cancancan version the same as mine? If it is the same, this is the last debugging procedure that I could think of: go to rails console and then run your usual commands. Then after running `ability = Ability.new(user)`, run this line: `puts ability.instance_variable_get(:@rules).to_yaml` It should show you the whole list of ability rules,here's mine to compare -- - !ruby/object:CanCan::Rule match_all: false base_behavior: true actions: - :read subjects: - !ruby/class 'Post' conditions: {} block: expanded_actions: - :read - :index - :show => nil – Jay-Ar Polidario Jan 14 '16 at 10:20
  • when i run that i get `=> "--- \n...\n"` as the output – Optimus Pette Jan 14 '16 at 11:25
  • nothing in between \n and \n? I used `puts` at the start that' s why mine didn't have \n – Jay-Ar Polidario Jan 14 '16 at 11:28
  • Yes there is nothing between `\n...\n` – Optimus Pette Jan 14 '16 at 11:33
  • @Jay-ArPolidario actually when i use `puts` the out put is `=> nil` – Optimus Pette Jan 14 '16 at 11:35
  • ok. I know the problem now, but I don't still know the reason. I tried to replicate the `nil` you got, by removing every line of code inside def initialize of ability.rb, meaning no rules whatsoever. After that, I got the `nil` and \n... \n that you have. Inspecting further, the instance variable @rules inside the `ability` variable is nil, but this is expected because I removed everything in `def initialize`. Now, my theory goes that somehow your `ability` variable doesn't attach the rules you have inside `def initialize`. see next comment for a possible debug procedure. – Jay-Ar Polidario Jan 14 '16 at 11:42
  • In your `def initialize`, at the end of the method, add the line `byebug`, then rerun your rails console commands (dont forget to reload rails console). After byebug console shows up, enter the following: `puts self.instance_variable_get(:@rules)`. To compare this is what I got: `--- - !ruby/object:CanCan::Rule match_all: false base_behavior: true actions: - :read subjects: - !ruby/class 'Post' conditions: {} block: nil ` If yours is similar, then it means after `ability` variable has been initialized, for some yet unknown reason, its rules get deleted. – Jay-Ar Polidario Jan 14 '16 at 11:46
  • If however, it is still `\n \n` or `nil`, then move the `byebug` line of code from the end of the method into just below the `can :read, post`. After that rerun your console commands again. After byebug show up, inspect the rules once more by `puts self.instance_variable_get(:@rules)`. If nothing still shows up (i.e. nil or \n \n), then I can't think of any other way to solve this, as it just means that the rules do not get added even after the line `can :read, post`, which it should have – Jay-Ar Polidario Jan 14 '16 at 11:51
  • The byebug console dont show up when i use rails console. – Optimus Pette Jan 14 '16 at 11:51
  • Sorry, for which comment of mine were you referring to? 1) when byebug was at the end of `def initialize` method? or 2) when `byebug` was after the line `can :read, post`? – Jay-Ar Polidario Jan 14 '16 at 11:53
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/100663/discussion-between-optimus-pette-and-jay-ar-polidario). – Optimus Pette Jan 14 '16 at 11:56

1 Answers1

1

Following our discussion, we concluded that the problem is either

  1. Rails didn't load the Ability class, or
  2. A code somewhere somehow overrides the Ability class.

The workaround-solution is to manually load the Ability file by appending the following at the end of the application.rb

require "#{Rails.root}/app/models/ability.rb"
Community
  • 1
  • 1
Jay-Ar Polidario
  • 6,463
  • 14
  • 28