I have a simple rails apps where users can create quotes (such as, “Two things are infinite: the universe and human stupidity; and I’m not sure about the universe.” ― Albert Einstein, etc).
I'd like only the users that created the quote, or the admin to be able to edit and delete the quote.
Currently I have a before filter that sets the user that created the quote that looks like this:
before_action :correct_user, only: :destroy
Here's my Quotes controller:
class QuotesController < ApplicationController
before_action :set_artist, only: [:show, :edit, :update, :destroy]
before_action :logged_in_user, only: [:create, :new, :destroy, :update, :edit ]
before_action :correct_user, only: :destroy
def index
@quotes = Quote.all.paginate(page: params[:page], per_page: 12)
end
def show
end
def new
@quote = Quote.new
end
def create
@quote = current_user.quotes.build(quote_params)
if @quote.save
flash[:success] = "Quote created!"
redirect_to @quote
else
render :new
end
end
def edit
end
def update
if @quote.update(quote_params)
flash[:success] = "Quote updated"
redirect_to @quote
else
render :edit
end
end
def destroy
@quote.destroy
flash[:success] = "Quote deleted"
redirect_back(fallback_location: browse_path)
end
private
def set_artist
@quote = Quote.find(params[:id])
end
def quote_params
params.require(:quote).permit(:content, :source, :topic_id, :speaker_id)
end
def correct_user
@quote = current_user.quotes.find_by(id: params[:id])
redirect_to root_url if @quote.nil?
end
end
What is the idiomatically-correct way to do this in Rails? Should I do something like this:
def correct_user
if user.admin?
@quote = current_user.quotes.find_by(id: params[:id])
else
@quote = current_user.quotes.find_by(id: params[:id])
end
redirect_to root_url if @quote.nil?
end
Is there a more succinct or Rails-way to do this that I'm missing? Also, how do you ensure that only the user that created the quote is able to delete it or edit it? Does my correct_user
method already cover that?
I think you could check if the user is admin or if the user.id is the same as the quote.user_id, in such case you return true, by using ||
you return true if any of the two expressions returns true, so you could do something like:
def correct_user
current_user.admin? || current_user.id == @quote.user_id
end
So you could create a helper method that redirects in case the user is not an admin or isn't the quote author/owner:
before_action :check_permission, only: %i[edit destroy]
def correct_user
current_user.admin? || current_user.id == @quote.user_id
end
def check_permission
redirect_back(fallback_location: browse_path) unless correct_user
end
With a before callback you could check in edit and destroy and any other if some of those two expressions are evaluated as true.