It seems that I'm calling authorize @artist
way too many times in artists_controller.rb
Honestly, I think what you have there is fine.
There are a few ways that you could try to be clever about this and "automatically call authorize
" for each controller action, but (warning: opinion-based answer) from past experience I've found that such attempts to make it more DRY adds significant confusion. Especially when you end up writing some controller action(s) that don't need authorising, or need authorising in an unusual way.
There's a lot of code duplication in my artist_policy.rb
One step at time... Here's the original:
class ArtistPolicy < ApplicationPolicy
attr_reader :user, :artist
def initialize(user, artist)
@user = user
@artist = artist
end
def create?
if user.admin? || user.moderator? || user.contributor?
true
elsif user.banned?
false
end
end
def update?
if user.admin? || user.moderator? || user.contributor? && user.id == @artist.user_id
true
elsif user.banned?
false
end
end
def destroy?
if user.admin? || user.moderator? || user.contributor? && user.id == @artist.user_id
true
elsif user.banned?
false
end
end
end
It's unnecessary to define your own initialize
method like that, so long as you're happy to reference the more generic variable name: record
, instead of artist
(which should be defined in the ApplicationPolicy
):
class ArtistPolicy < ApplicationPolicy
def create?
if user.admin? || user.moderator? || user.contributor?
true
elsif user.banned?
false
end
end
def update?
if user.admin? || user.moderator? || user.contributor? && user.id == record.user_id
true
elsif user.banned?
false
end
end
def destroy?
if user.admin? || user.moderator? || user.contributor? && user.id == record.user_id
true
elsif user.banned?
false
end
end
end
Next, in situations like this, it's fine to reference one policy rule from another - so long as they apply equally to user types:
class ArtistPolicy < ApplicationPolicy
def create?
if user.admin? || user.moderator? || user.contributor?
true
elsif user.banned?
false
end
end
def update?
if user.admin? || user.moderator? || user.contributor? && user.id == record.user_id
true
elsif user.banned?
false
end
end
def destroy?
update?
end
end
Next, note that the record.user_id
is the logged in user, for the create action! So you can simplify this even further:
class ArtistPolicy < ApplicationPolicy
def create?
if user.admin? || user.moderator? || user.contributor? && user.id == record.user_id
true
elsif user.banned?
false
end
end
def update?
create?
end
def destroy?
create?
end
end
And lastly, the logic in that method is actually little wrong. (You could have picked this up with tests...) If a user is an admin and they are banned, then you still presumably want this to return false
, not true
. With this in mind, we can fix + simplify the code again to:
class ArtistPolicy < ApplicationPolicy
def create?
return false if user.banned?
user.admin? || user.moderator? || user.contributor? && user.id == record.user_id
end
def update?
create?
end
def destroy?
create?
end
end