I am using devise, and have a pieces model in my Ruby on Rails web app. A user has_many pieces. Right now, in my pieces controller, I have a method called correct_user, which allows only users who own the pieces to edit and destroy them. I want to add a line to this method which allows the user with a devise user id of 1 to be able to edit and destroy anyone else's pieces (if current_user.id ==1, then allow the edit/destroy actions). I have tried a few combinations, but I keep getting errors.
Here is my pieces controller: (the correct_user method is towards the end)
class PiecesController < ApplicationController
before_action :set_piece, only: [:show, :edit, :update, :destroy]
before_action :correct_user, only: [:edit, :update, :destroy]
before_action :authenticate_user!, except: [:index, :show]
respond_to :html
def index
#pieces = Piece.all
respond_with(#pieces)
end
def show
respond_with(#piece)
end
def new
#piece = current_user.pieces.build
respond_with(#piece)
end
def edit
end
def create
#piece = current_user.pieces.build(piece_params)
flash[:notice] = 'Piece was successfully created.' if #piece.save
respond_with(#piece)
end
def update
flash[:notice] = 'Piece was successfully updated.' if #piece.update(piece_params)
respond_with(#piece)
end
def destroy
flash[:notice] = 'Piece was successfully deleted.' if #piece.destroy
respond_with(#piece)
end
private
def set_piece
#piece = Piece.find(params[:id])
end
def correct_user
#piece = current_user.pieces.find_by(id: params[:id])
redirect_to pieces_path, notice: "Access Denied! Not authorized to edit this piece." if #piece.nil?
end
def piece_params
params.require(:piece).permit(:title, :image, :genre, :size, :price, :description, :status)
end
end
Thanks guys!
In correct_user, change the conditional on redirect_to to and return if #piece.nil? and current_user.id != 1, then add a new line below that: #piece = Piece.find(params[:id]) if current_user.id == 1, which will only run if the redirect_to ... and return does not.
(Also, you have current_user.pieces.find_by(id: ...), which can be just .find(...) for simplicity.)
Related
I have a user and article model where a user can have many models, and have restricted the delete function to user or admin. yet when i attempt to destroy the article, i get the following error:
undefined method `user' for nil:NilClass
and its pointing to this private function in my articles controller:
def require_same_user
if current_user != #article.user and !current_user.admin?
flash[:danger] = "You can only edit or delete your own articles"
redirect_to root_path
end
end
this is my whole controller file:
class ArticlesController < ApplicationController
before_action :set_article, only: [:edit, :update, :show]
before_action :require_user, except: [:index, :show]
before_action :require_same_user, only: [:edit, :update, :destroy]
def index
#articles = Article.paginate(page: params[:page], per_page: 5)
end
def show
end
def new
#article = Article.new
end
def edit
end
def update
#article.update(article_params)
if #article.save
flash[:success] = "Article successfully updated!"
redirect_to article_path(#article)
else
flash[:danger] = "Sorry, try again..."
render :edit
end
end
def create
#article = Article.new(article_params)
#article.user = current_user
if #article.save
flash[:success] = "New article created"
redirect_to articles_path
else
flash[:danger] = "Sorry, invalid values"
render :new
end
end
def destroy
#article = Article.find(params[:id])
#article.destroy
flash[:success] = "Article deleted"
redirect_to articles_path
end
private
def article_params
params.require(:article).permit(:name, :title, :description)
end
def set_article
#article = Article.find(params[:id])
end
def require_same_user
if current_user != #article.user and !current_user.admin?
flash[:danger] = "You can only edit or delete your own articles"
redirect_to root_path
end
end
end
the articles and users exist in db so what could this be? thanks in advance
You're setting the article using the set_article function, but in the case of the require_same_user function, it doesn't know what's the value of #article; so in that case, the value is nil, as it's not in the scope, nor figure as an instance variable created before.
def require_same_user
# Here there's no #article, so it's evaluated as nil,
# and nil doesn't have a method user.
if current_user != #article.user ...
...
One approach could be to set the set_article also to be executed before the require_same_user does it.
before_action :set_article, only: %i[edit update show require_same_user]
You could also divide your code in smaller pieces to be used back again whenever you need it:
def require_same_user
redirect_to_root_path unless article_owner? && admin?
end
def redirect_to_root_path
flash[:danger] = 'You can only edit or delete your own articles'
redirect_to root_path
end
def admin?
current_user.admin?
end
def article_owner?
current_user == #article.user
end
Make sure set_article runs before destroy action.
In your controller:
before_action :set_article, only: [:edit, :update, :show, :destroy]
I am building a fairly straightforward Rails 5 application. You create "Movies," and then can create "Reviews" for those movies. Rails is doing something odd. I have my application set up so instead of a "new" action and a corresponding view, I have the form to create new movies in a modal contained in the application.html.erb file. I then provide #newmovie = Movie.new in the controller for all the movie views, so the data is available everywhere.
I have validations for the movie object setup like this:
class Movie < ApplicationRecord
has_many :reviews
validates :title, :director, :poster, :synopsis, presence: true
end
When I fill out the form to create a new movie in the modal on my index view and intentionally leave fields blank (to trigger the validation), I get this:
instead of the form simply not accepting the input. What's going on here? I can't have this error happening like this. Previously, I had a "new" view. This did not happen in the previous setup. Help!
Here is my entire movies controller:
class MoviesController < ApplicationController
before_action :find_movie, only: [:show, :edit, :update, :destroy ]
before_action :authenticate_user!, only: [:new, :create, :edit, :update, :destroy]
before_action :find_newmovie, only: [:show, :index, :new, :edit]
def show
#reviews = #movie.reviews.all.order(created_at: :desc).paginate(:page => params[:page], :per_page => 3)
#review = Review.new
if #movie.reviews.blank?
#average_review = 0
else
#average_review = #movie.reviews.average(:rating).round(2)
end
end
def index
#movies = Movie.all.order(title: :asc).paginate(:page => params[:page], :per_page => 3)
#newmovie = Movie.new
end
def new
#movie = Movie.new
end
def create
#movie = Movie.create(movie_params)
#movie.user_id = current_user.id
if #movie.save
flash[:success] = "Your movie was created!"
redirect_to root_path
else
flash[:danger] = "There was a problem with your request"
render :new
end
end
def edit
end
def update
if #movie.update(movie_params)
flash[:success] = "Your movie was updated"
redirect_to movie_path
else
flash[:danger] = "There was a problem with your request"
end
end
def destroy
if #movie.destroy
flash[:success] = "Your movie was removed"
redirect_to movies_path
else
flash[:danger] = "There was a problem with your request"
render :index
end
end
private
def movie_params
params.require(:movie).permit(:title, :director, :poster, :synopsis, :user_id)
end
def find_movie
#movie = Movie.find(params[:id])
end
def find_newmovie
#newmovie = Movie.new
end
end
In new action of movies_controller, you missed to initalize the instance variable #newmovie.
add below code in movies_controller:
class MoviesController < ApplicationController
def new
#newmovie = Movie.new
end
...
end
I am building a small classifieds application for our community where folks can list products for sale using a form with params description and image only.
Both params are in the schema.rb
THE PROBLEM:
I have embedded the search bar on my index.html.erb and also created a show.html.erb which is blank. Currently the search bar does nothing except look pretty,
I am struggling to understand how to get the search wired up to interrogate the description param and deliver the results onto my show.html.erb.
NB: I would like the show.html.erb page and its search results to mirror my index page cosmetically and achieve this task without having to install any further gems.
class PinsController < ApplicationController
before_action :set_pin, only: [:show, :edit, :update, :destroy]
before_action :correct_user, only: [:edit, :update, :destroy]
before_action :authenticate_user!, except: [:index, :show]
def index
#pins = Pin.all.order("created_at DESC").paginate(:page => params[:page], :per_page => 15)
end
def show
end
def new
#pin = current_user.pins.build
end
def edit
end
def create
#pin = current_user.pins.build(pin_params)
if #pin.save
redirect_to #pin, notice: 'Pin was successfully created.'
else
render :new
end
end
def update
if #pin.update(pin_params)
redirect_to #pin, notice: 'Pin was successfully updated.'
else
render :edit
end
end
def destroy
#pin.destroy
redirect_to pins_url
end
private
# Use callbacks to share common setup or constraints between actions.
def set_pin
#pin = Pin.find_by(id: params[:id])
end
def correct_user
#pin = current_user.pins.find_by(id: params[:id])
redirect_to pins_path, notice: "Not authorized to edit this pin" if #pin.nil?
end
# Never trust parameters from the scary internet, only allow the white list through.
def pin_params
params.require(:pin).permit(:description, :image)
end
end
Change your index method to either have a search query or not.
def index
if params[:query]
#results = Advert.where('lower(description) LIKE ?', '%#{params[:query].downcase}%')
else
#results = Advert.all
end
end
You can also create filters using javascript or install either searchkick gem or if you need something REALLY simple install rails4-autocomplete
In my controller, I have a def that verifies if its the correct user. A normal user can only edit,update and destroy his own tweets.
before_action :correct_user, only: [:edit, :update, :destroy]
Now I would like to grant admin the rights to edit,update and destroy any tweet as well as admin's tweets
How do I combine both definitions?
private
def verify_is_admin
#card = #tweet.card.id
(current_user.nil?) ? redirect_to(root_path) : (redirect_to card_path(#card), notice: 'Not authorised to update this tweet' unless current_user.admin?)
end
def correct_user
#card = #tweet.card.id
#tweet = current_user.tweets.find_by(id: params[:id])
redirect_to card_path(#card), notice: 'Not authorised to update this tweet' if #tweet.nil?
end
Do you want to combine them in a new method or in one of the existing? If you want a new method you can do something like this:
def can_edit_tweet?
true if (currrent_user.admin? || correct_user)
end
and change your before_action to:
before_action :correct_user, only: [:edit, :update, :destroy, :can_edit_tweet?]
If you want to combine them into one you can edit the correct_user method:
def correct_user
#card = #tweet.card.id
#tweet = current_user.tweets.find_by(id: params[:id])
redirect_to card_path(#card), notice: 'Not authorised to update this tweet' if (#tweet.nil? && !current_user.admin?)
end
I've been putting the finishing touches on my app all day with the help of some useful answers here and would like to know how this feature can be executed. I have an idea set up in my post_controller file where I want to show the top 10 most recent posts created based on the date that they were created. I also plan on doing this for my comments as well laster on. I am showing all users post in the views/post/index.html.erb file. I wrote this line of code in the posts_controller: posts = Post.order('created_at DESC').limit(10). I've searched here thoroughly but don't understand how some other users got this to work, any insight? Thanks in advance.
posts_controller
class PostsController < ApplicationController
before_action :set_post, only: [:show, :edit, :update, :vote]
before_action :require_user, only: [:new, :create, :edit, :update, :vote]
before_action :require_creator, only:[:edit, :update]
def index
posts = Post.order('created_at DESC').limit(10)
#posts = Post.all.page(params[:page]).per_page(10)
end
def show
#comment = Comment.new
end
def new
#post = Post.new
end
def create
#post = Post.new(post_params)
#post.creator = current_user
if #post.save
flash[:notice] = "You created a post!"
redirect_to posts_path
else
render :new
end
end
def edit
end
def update
#post = Post.find(params[:id])
if #post.update(post_params)
flash[:notice] = "You updated the post!"
redirect_to post_path(#post)
else
render :edit
end
end
def vote
Vote.create(voteable: #post, creator: current_user, vote: params[:vote])
respond_to do |format|
format.js { render :vote } # Renders views/posts/vote.js.erb
end
end
private
def post_params
params.require(:post).permit(:url, :title, :description)
end
def set_post
#post = Post.find(params[:id])
end
def require_creator
access_denied if #post.creator != current_user
end
end
The posts variable you've declared in your index action will not be available in your view. What you need to do is update the line where you're retrieving the posts using instance variable as follows:
def index
#posts = Post.page(params[:page]).order('created_at DESC').per_page(10)
end