Hello I am learning rails and I have made a mistake that error message shows when I submit the article but can't seem to know where the problem is. I think it's a controller?
Github files of my code:
GitHub Project
users_controller.rb
class UsersController < ApplicationController
def index
#users = User.all
end
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
flash[:success] = "welcome to the alpha blog #{#user.username}"
redirect_to articles_path
else
render 'new'
end
end
def edit
#user = User.find(params[:id])
end
def update
#user = User.find(params[:id])
if #user.update(user_params)
flash[:success] = "Your account was updated successfully"
redirect_to articles_path
else
render 'edit'
end
end
def show
#user = User.find(params[:id])
end
private
def user_params
params.require(:user).permit(:username, :email, :password)
end
end
articles_controller.rb
class ArticlesController < ApplicationController
before_action :set_article, only: [:edit, :update, :show, :destroy]
# Helppppp!
def index
#articles = Article.all
end
def new
#article = Article.new
end
def edit
#article = Article.find(params[:id])
end
def create
#article = Article.new(article_params)
#article.user = User.new
if #article.save
flash[:success] = "Article was successfully created"
redirect_to article_path(#article)
else
render :new
end
end
def update
if #article.update(article_params)
flash[:success] = "Article was successfully updated."
redirect_to article_path(#article)
else
render 'edit'
end
end
def show
end
def destroy
#article.destroy
flash[:danger] = "Article was successfully deleted"
redirect_to articles_path
end
private
def set_article
#article = Article.find(params[:id])
end
def article_params
params.require(:article).permit(:title, :description)
end
end
Look at your method:
def create
#article = Article.new(article_params)
#article.user = User.new
...
end
You are trying to assign empty user to your article object. You should pass user_id as a param (don't forget to add it as a permitted param in article_params) and your code should be:
#article.user = User.find(article_params[:user_id])
Fixed it by defining current_user method in the application_controller.rb and added the following:
**application_controller.rb**
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
helper_method :current_user, :logged_in?
def current_user
#current_user ||= User.find(session[:user_id]) if session[:user_id]
end
def logged_in?
!!current_user
end
def require_user
if !logged_in?
flash[:danger] = "You must be logged in to perform that action"
redirect_to root_path
end
end
end
articles_controller.rb
class ArticlesController < ApplicationController
before_action :set_article, only: [:edit, :update, :show, :destroy]
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 new
#article = Article.new
end
def edit
end
def create
#article = Article.new(article_params)
#article.user = current_user #code that needs fix
if #article.save
flash[:success] = "Article was successfully created"
redirect_to article_path(#article)
else
render :new
end
end
def update
if #article.update(article_params)
flash[:success] = "Article was successfully updated."
redirect_to article_path(#article)
else
render 'edit'
end
end
def show
end
def destroy
#article.destroy
flash[:danger] = "Article was successfully deleted"
redirect_to articles_path
end
private
def set_article
#article = Article.find(params[:id])
end
def article_params
params.require(:article).permit(:title, :description)
end
def require_same_user
if current_user != #article.user
flash[:danger] = "You can only edit or delete your own article"
redirect_to root_path
end
end
end
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 let users to upload photos using paperclip, but there is no ownership in the photo. Which means, I don't know who uploaded the photos at all.
Here, I would like when someone uploads a picture you know which user uploaded it. i have a user_id column. but i dont know how to implement the code in the pic controller
How do I do that? Thanks!
class PicsController < ApplicationController
before_action :find_pic, only: [:show, :edit, :update, :destroy]
def index
#user = User.find( params[:user_id])
#pics = Pic.all.order("created_at DESC")
end
def show
end
def new
#pic = Pic.new
end
def create
#pic.user = current_user
#pic = Pic.new(pic_params)
if #pic.save
redirect_to #pic, notice: "Yes it was posted"
else
render 'new'
end
end
def edit
#user = User.find(params[:user_id])
#profile = #user.profile
end
def update
if #pic.update(pic_params)
redirect_to #pic, notice: "Congrates Pic was upaded"
else
render 'new'
end
end
def destroy
#pic.destroy
redirect_to users_path
end
private
def pic_params
params.require(:pic).permit(:title, :description, :profile_id)
end
def find_pic
#pic = Pic.find(params[:id])
end
end
class UsersController < ApplicationController
before_action :authenticate_user!
def index
#pics = Pic.all.order("created_at DESC")
end
def show
#user = User.find(params[:id])
#pics = User.find_by(user_name: params[:user_name])
end
end
class ProfilesController < ApplicationController
before_action :authenticate_user!
before_action :only_current_user
def new
#user = User.find( params[:user_id])
#profile = Profile.new
end
def create
#user = User.find(params[:user_id])
#profile = #user.build_profile(profile_params)
if #profile.save
redirect_to user_path( params[:user_id])
else
render action: :new
end
end
def edit
#user = User.find(params[:user_id])
#profile = #user.profile
end
def update
#user = User.find(params[:user_id])
#profile = #user.profile
if #profile.update_attributes(profile_params)
flash[:success] = "Profile Updated"
redirect_to user_path(params[:user_id])
else
render action: :edit
end
end
private
def profile_params
params.require(:profile).permit(:avatar, :user_name, :contact_email, :description)
end
def only_current_user
#user = User.find(params[:user_id])
redirect_to(root_url) unless #user == current_user
end
end
If the user can be identified during the upload process, you can try to pass the user_id in a hidden_field during upload. You said you already created the user_id column.
<%= f.hidden_field :user_id , value: #user.id %>
for this code to work you need to find the #user in your controller action. Similar to what you are doing already in your 'index' action: find user by the :user_id
If you are using devise for User you can use current_user.id instead.
I keep getting the error destroy action can not be found in the messagesController. But i have the destroy action setup i don't get why i am getting this message error. Can some one tell me why i can getting this error?
class MessagesController < ApplicationController
before_action :find_message, only: [:show, :edit, :update, :destroy]
def index
#messages = Message.all.order("created_at DESC")
end
def show
end
def new
#message = Message.new
end
def create
#message = Message.new(message_params)
if #message.save
redirect_to root_path
else
render 'new'
end
def edit
end
def update
if #message.update(message_params)
redirect_to message_path
else
render 'edit'
end
end
def destroy
#message.destroy
redirect_to root_path
end
end
private
def message_params
params.require(:message).permit(:title, :description)
end
def find_message
#message = Message.find(params[:id])
end
end
You missed an end closure for your create action. The right way should be this:
def create
#message = Message.new(message_params)
if #message.save
redirect_to root_path
else
render 'new'
end
end
def edit
end
Your create action is missing an end statement:
def create
#message = Message.new(message_params)
if #message.save
redirect_to root_path
else
render 'new'
end
# This is defined before create is closed
def edit
end
I keep getting a variety of error while trying to create and show errors in a simple Rails blog I'm trying to create.Let me know if you see anything obvious or if you need me to post more code as I've tried a number of things but to no avail. Thanks
The browser is giving me this error
Couldn't find User without an ID
in my "logged_in?" method which shows
def logged_in?
#current_user ||= User.find(session[:user_id])
end
Sessions Controller
class SessionsController < ApplicationController
def new
end
def create
user = User.find_by(email: params[:email])
if user && user.authenticate(params[:password])
session[:user_id] = user.id
flash[:success] = "You are logged in"
redirect_to root_path
else
render action: 'new'
flash[:error] = "There was a problem logging in. Please check your email and password"
end
end
end
def index
#users = User.all
end
def show
end
def new
#user = User.new
end
def edit
end
def create
#user = User.new(user_params)
if #user.save
session[:user_id] = #user.id
flash[:notice] = "You have registered, please login"
redirect_to login_path
else
render :new
end
end
def update
respond_to do |format|
if #user.update(user_params)
format.html { redirect_to #user, notice: 'User was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
def destroy
#user.destroy
respond_to do |format|
format.html { redirect_to users_url }
format.json { head :no_content }
end
end
private
def set_user
#user = User.find(params[:id])
end
def user_params
params.require(:user).permit(:first_name, :last_name, :email, :password, :password_confirmation)
end
end
Articles Controller
class ArticlesController < ApplicationController
http_basic_authenticate_with name: "dhh", password: "secret", except: [:index, :show]
def new
#article = Article.new
end
def index
#article = Article.all
end
def create
#article = Article.new(article_params)
if #article.save
redirect_to #article
else
render 'new'
end
end
def edit
#article = Article.find(params[:id])
end
def update
#article = Article.find(params[:id])
if #article.update(article_params)
redirect_to #article
else
render 'edit'
end
end
def show
#article = Article.find(params[:id])
end
def destroy
#article = Article.find(params[:id])
#article.destroy
redirect_to articles_path
end
private
def article_params
params.require(:article).permit(:title, :text, :image)
end
end
Application Helper
module ApplicationHelper
def logged_in?
#current_user ||= User.find(session[:user_id])
end
end
The problem you're facing is that session[:user_id] is nil. Usually a method which sets current user is called current_user. The logged_in? is not a good name for a method setting an user instance, because one would expect that a method ending with a question mark would return a true or false. And not an user instance.
Also, setting the current user is usually done with a before_filter. Additionally, you want to skip such before filter for action where you're setting the current user (i.e the current_user doesn't exist yet)
Finally, I would rather fail gracefully, if user is not found. You can achieve this by changing your code to User.find_by_id(session[:user_id])
While the user is not loggued, session[:user_id] is nil, and so User.find(session[:user_id]) generates the error. The method should be like this:
def logged_in?
#current_user ||= User.find(session[:user_id]) if session[:user_id].present?
end
Why would the logged_in? helper method try to assign a value to #current_user? I think that is a bad logic, it should just return a boolean result without modifying such a central instance. This is a proper way to do that:
def logged_in?
#current_user.nil? ? false : true
end
The responsibility of setting the #current_user falls to a method that you should place in application_controller.rb and make it a before_action so that it's executed before any controller action is triggered, that is:
# app/controllers/applicaton_controller.rb
class ApplicationController < ActionController::Base
protect_from_forgery
before_action :authenticate_user
# Your actions here
..
..
#
private
def authenticate_user
#current_user ||= User.find(session[:user_id]) if session[:user_id].present?
end
end
I am having a bit of trouble displaying the most recent created comments based from the time/date that they were created in my views/post/show.htmlerb file. I just got my posts_controller to display the most recent created posts from the def index action but now in my def show action the following code doesn't work:
#comment_date_order = Comment.find(params[:id]).comments.order('created_at DESC')
this is my full posts_controller.rb file:
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.page(params[:page]).order('created_at DESC').per_page(10)
end
def show
#comment = Comment.new
#comment_date_order = Post.find(params[:id]).comments.order('created_at DESC')
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
comments_controller.erb file:
class CommentsController < ApplicationController
before_action :require_user
def create
#post = Post.find(params[:post_id])
#comment = Comment.new(params.require(:comment).permit(:body))
#comment.post = #post
#comment.creator = current_user
if #comment.save
flash[:notice] = "Your comment was created!"
redirect_to post_path(#post)
else
render 'posts/show'
end
end
def edit
#comment = Comment.find(params[:id])
#post = Post.find(params[:post_id])
end
def update
#comment = Comment.find(params[:id])
if #comment.update(comment_params)
flash[:notice] = "You updated your comment!"
redirect_to post_path(#post)
else
render :edit
end
end
private
def comment_params
params.require(:comment).permit(:body)
end
def set_comment
#comment = Comment.find(params[:id])
end
end
First you would need a relation between Post and Comment if you dont already.
I would just create a def in the Post model.
class Post < ActiveRecord::Base
has_many :comments
def newest_comments
self.comments.order('created_at DESC')
end
end
That way you could also make a oldest_post method and uses it directly in the view
<%= #post.newest_post.each do |comment| %>
Also as best practice. Try and not to create too many instance vars in your controller. Remember fat models, skinny controllers.