ArgumentError in Topics#show Possible Devise error? - ruby-on-rails

I'm a bit of a rails newb here and need some help figuring this out. I have an argument error that is thrown every time I try and create or edit a new "topic."
ArgumentError
Here is the code for the "show:"
<h1><%= #topic.title %></h1>
<%= link_to "Edit", edit_topic_path(#topic), class: 'btn btn-success' %>
<%= link_to "Delete Topic", #topic, method: :delete, class: 'btn btn-danger', data: {confirm: 'Are you sure you want to delete this topic?'} %>
<% if policy(Bookmark.new).create? %>
<%= link_to "New Bookmark", new_topic_bookmark_path(#topic), class: 'btn btn-success' %>
<% end %>
<% #topic.bookmarks.each do |bookmark| %>
<div class="media-body">
<div class="row">
<div class="col-md-2">
<div class="container">
<img src="http://icons.better-idea.org/icon?url=<%= bookmark.url %>&size=120">
<div class="media-heading">
<%= link_to bookmark.name, topic_bookmark_path(#topic, bookmark) %>
</div>
</div>
</div>
</div>
<div class="col-md-1">
<%= render partial: 'likes/like', locals: {bookmark: bookmark} %>
</div>
</div>
<% end %>
Here is the "topics" controller:
class TopicsController < ApplicationController
def index
#topics = Topic.all
end
def show
#topic = Topic.find(params[:id])
end
def new
#topic = Topic.new
end
def create
#topic = Topic.new(topic_params)
if #topic.save
flash[:notice]= "Topic was saved."
redirect_to #topic
else
flash.now[:alert]= "The topic could not be saved. Please try again"
render :new
end
end
def edit
#topic = Topic.find(params[:id])
end
def update
#topic = Topic.find(params[:id])
#topic.assign_attributes(topic_params)
if #topic.save
flash[:notice]= "The topic was saved sucessfully."
redirect_to #topic
else
flash.now[:alert]= "There was an error saving the topic. Please try again."
render :edit
end
end
def destroy
#topic = Topic.find(params[:id])
if #topic.destroy
flash[:notice]= "\"#{#topic.title}\" was deleted successfully."
redirect_to topics_path
else
flash.now[:alert] = "There was an error in deleting this topic."
render :show
end
end
def topic_params
params.require(:topic).permit(:title)
end
end
Update(1): New error after deleting "policy" New Error
Here is the "application policy that uses Pundit:
class ApplicationPolicy
attr_reader :user, :record
def initialize(user, record)
raise Pundit::NotAuthorizedError, "must be logged in" unless user
#user = user
#record = record
end
def index?
false
end
def show?
scope.where(:id => record.id).exists?
end
def create?
user.present?
end
def new?
create?
end
def update?
user.present? && ( record.user == user )
end
def edit?
update?
end
def destroy?
user.present? && (record.user == user)
end
def scope
Pundit.policy_scope!(user, record.class)
end
class Scope
attr_reader :user, :scope
def initialize(user, scope)
#user = user
#scope = scope
end
def resolve
scope
end
end
end

Problem is with Policy made with the Pundit gem. Can you check something called the BookmarkPolicy or something similar, or at least post that here. Did you forget to include Pundit in your controller ?

You're using the Pundit gem but I don't see the authorize method in your controller. From Pundit's documentation:
Supposing that you have an instance of class Post, Pundit now lets you
do this in your controller:
def update
#post = Post.find(params[:id])
authorize #post
if #post.update(post_params)
redirect_to #post
else
render :edit
end
end
The authorize method automatically infers that Post will have a
matching PostPolicy class, and instantiates this class, handing in the
current user and the given record.

The source code in the screenshot and the code you posted is not the same. Your code:
<% if policy(Bookmark.new).create? %>
Screenshot:
<% if (Bookmark.new).create? %>
Rails is correctly reporting that Bookmark.new does not have a create? method, because it's missing the policy() method call.
Is your file saved? Are you sure you're changing the correct file?

Related

How to Edit a Pic Comment without redirecting to an Edit view page in Rails

I am building an app for practice,(Instagram replica), and I am having a really hard time getting a feature to work.
What I want to happen is, a user is able to edit or delete their own comments about a picture. I was able to get the delete feature to work, but I am unable to figure out the 'Edit comment' feature. I want the user to be able to edit the comment from within the picture show page. Code is below.
pics_controller.rb
class PicsController < ApplicationController
before_action :find_pic, only: [:show, :edit, :update, :destroy, :upvote]
before_action :authenticate_user!, except: [:index, :show]
before_action :require_same_user, only: [:edit, :update, :destroy]
def index
#pics = Pic.all.order("created_at DESC")
end
def show
end
def new
#pic = current_user.pics.build
end
def create
#pic = current_user.pics.build(pic_params)
if #pic.save
redirect_to #pic, notice: "Your pic has been posted!"
else
render :new
end
end
def edit
end
def update
if #pic.update(pic_params)
redirect_to #pic, notice: "Awesome! Your Pic was updated!"
else
render :edit
end
end
def destroy
if #pic.destroy
redirect_to root_path
end
end
def upvote
#pic.upvote_by current_user
redirect_back fallback_location: root_path
end
private
def pic_params
params.require(:pic).permit(:title, :description, :image)
end
def find_pic
#pic = Pic.find(params[:id])
end
def require_same_user
if current_user != #pic.user
flash[:danger] = "You can only edit or delete your own pictures"
redirect_to root_path
end
end
end
comments_controller.rb
class CommentsController < ApplicationController
def create
#pic = Pic.find(params[:pic_id])
#comment = #pic.comments.create(params[:comment].permit(:name, :body))
redirect_to pic_path(#pic)
end
def edit
#pic = Pic.find(params[:pic_id])
#comment = #pic.comments.find(params[:id])
redirect_to #pic
end
def update
#comment = #pic.comments.find(params[:id])
#comment.update_attributes(comment_params)
if #comment.save
redirect_to #pic
end
end
def destroy
#pic = Pic.find(params[:pic_id])
#comment = #pic.comments.find(params[:id])
#comment.destroy
redirect_to pic_path(#pic)
end
def show
#pic = Pic.find(params[:pic_id])
end
private
def comment_params
params.require(:comment).permit(:body)
end
end
And here is the (_comment.html.erb) partial being called from the show page
<div class="card" style="width: 18rem;">
<div class="card-header">
<span class="badge badge-dark"><%= comment.name %></span>
</div>
<ul class="list-group list-group-flush">
<p><%= comment.body %></p>
</ul>
</div>
<% if user_signed_in? && comment[:body].present? %>
<p><%= link_to 'Delete Comment', [comment.pic, comment], method: :delete, class: "btn btn-danger",
data: { confirm: "Are you sure?" } %></p>
<p><%= link_to 'Edit Comment', edit_pic_comment_url(#pic, comment), class: 'btn btn-primary' %></p>
<% end %>
Any help is greatly appreciated. TIA
You need to ajaxify for that scenario. The steps will be:
Create the edit link with data-remote=true option. It'll enable the link to hit on the server with a ajax request. Also, add a editable div.
Edit
<div id="comment_#{comment.id}_form"></div>
In the edit method respond the request using js. Like:
def edit
respond_to do |format|
format.js # actually means: if the client ask for js -> return edit.js
end
end
Create edit.js file. In there, render the comment _form.html.erb on the defined div with ID - comment_4_form (Lets assume you're now editing comment with id 4).
It's not elaborated I know. The solution will be much bigger if I elaborate. But you are good to go if you understand the cycle.

Getting user phone number and sending a message in ruby on rails

I am working on a messaging app in ruby and i am currently encountering a blocker which i can not fix. I have been using tutorials for this and i think part of the reason i cannot find the solution is because of that. My app allows the users to log in and sign up,they can then add,view and edit contact. Finally the can send a message to different recipients. The problem is, i cannot get the recipients in the contacts and send them a message. I am only able to select my name as a user(which is not what its intended to do). I have attached the controllers used here:
contacts_controller
class ContactsController < ApplicationController
def index
#contacts = Contact.all
end
def new
#contact = Contact.new
end
def create
#contact = Contact.new(contact_params)
if #contact.save
flash[:success]= "new contact successfully added!"
redirect_to contacts_path
else
render 'new'
end
end
def edit
#contact = Contact.find(params[:id])
end
def update
#contact = Contact.find(params[:id])
permitted_columns = params.require(:contact).permit(:name, :company, :email, :phone)
#contact.update_attributes(permitted_columns)
redirect_to contacts_path
end
def destroy
#contact = Contact.find(params[:id])
#contact.destroy
redirect_to contacts_path
end
private
def contact_params
params.require(:contact).permit(:name, :company, :email, :phone)
end
end
messages_controller
class MessagesController < ApplicationController
def index
#messages = Recipient.where(:user_id => current_user.id).order('created_at DESC')
end
def new
#message = Message.new
#recipients = Contact.all
end
def create
#message = current_user.messages.build(message_params)
if #message.save
flash[:success]= "Message sent!"
redirect_to contacts_path
else
flash[:alert]= "sorry!message unsent"
render :new
end
end
private
def message_params
params.require(:message).permit(:body, :sender_id, user_tokens:[])
end
end
users_controller
class UsersController < ApplicationController
def index
end
def create
user = User.new(user_params)
if user.save
session[:user_id] = user.id
redirect_to '/contact'
else
flash[:register_errors] = user.errors.full_messages
redirect_to '/'
end
end
private
def user_params
params.require(:user).permit(:fname, :lname, :email, :password, :password_confirmation)
end
end
sessions_controller
class SessionsController < ApplicationController
def create
user = User.find_by(email:login_params[:email])
if user && user.authenticate(login_params[:password])
session[:user_id] = user.id
redirect_to '/contact'
else
flash[:login_errors] = ['invalid username or password']
redirect_to '/'
end
end
def destroy
session[:user_id] = nil
redirect_to '/', notice: 'Successfully logged out!'
end
private
def login_params
params.require(:login).permit(:email,:password)
end
end
The _recipient.html.erb is rendered by the new.html.erb. Here is the code:
<div class="container vertical-center">
<div id ="stream">
<%= form_for :message, url:messages_path do |f| %>
<%= f.text_area :body, id: "url", placeholder: "Message", class: "message_body" %>
<div id="stream-list" class="follow-list">
<ul>
<% #recipients.each do |contact| %>
<label for="user<%=contact.id%>" >
<li id="stream_item">
<span class="list-group-item"><%= contact.name %></span><%= check_box_tag "message[user_tokens][]",user.id, #message.users.include?(user), id: "user#{user.id}" %>
</li>
</label>
<br>
<% end %>
</ul>
</div>
<div class="stream-footer">
<%= f.button :submit, class: "btn btn-success" %>
<% end %>
</div>
</div>
</div>
Here is the error when i try to write a message
It's not very clear why you use local variable user in your view template. I think it's just an error and contact variable is supposed to be used instead:
<span class="list-group-item"><%= contact.name %></span><%= check_box_tag "message[user_tokens][]", contact.id, #message.users.include?(contact), id: "user#{contact.id}" %>
Also, a small HTML error: ul tag should contain li tags; other tags are not allowed as direct descendants. So I would also rewrite that list as:
<ul>
<% #recipients.each do |contact| %>
<li id="stream_item">
<label for="user<%=contact.id%>" >
<span class="list-group-item"><%= contact.name %></span><%= check_box_tag "message[user_tokens][]", contact.id, #message.users.include?(contact), id: "user#{contact.id}" %>
</label>
</li>
<br>
<% end %>
</ul>

Rails: link_to delete from different controller

Right now I have a like button that allows you to like foods. When you try to unlike the food, I get this error:
The action 'destroy' could not be found for UsersController
I'm not sure why it is looking for the destroy action in the users controller. My only guess is because the button is on the user show page, so I assume it defaults to that controller, but how would I access the delete method from my votes controller?
Shared like form
<% unless current_user.votes.empty? || current_user.votes.pluck(:food_id).include?(food.id) %>
<%= form_for #vote do |f| %>
<%= f.hidden_field 'food_id', food.id %>
<%= f.hidden_field 'user_id', food.user.id %>
<%= f.submit "Vote", :class => "like_button" %>
<% end %>
<% else %>
<% vote = food.votes.where(user_id: current_user.id).first %>
<div class="unlike_button">
<%= button_to "Unlike", vote, method: :delete %>
</div>
<% end %>
class VotesController < ApplicationController
def index
#votes = Vote.all
end
def new
#vote = Vote.new
end
def create
#vote = Vote.new(vote_params)
if #vote.save
puts #vote
flash[:notice] = "Thanks for voting!"
redirect_back(fallback_location: root_path)
else
puts "No"
flash[:notice] = "Something went wrong"
redirect_back(fallback_location: root_path)
end
end
def destroy
#vote = Vote.find(params[:id])
if #vote.destroy!
flash[:notice] = "Unvoted!"
redirect_back(fallback_location: root_path)
end
end
private
def vote_params
params.require(:vote).permit(:food_id, :user_id)
end
end
class Vote < ApplicationRecord
belongs_to :user
belongs_to :food
end

Rails: ActionController::InvalidAuthenticityToken

I'm currently working on a project involving users, likes, and posts. I have a like/unlike button that I finally got to work some of the time, but on certain user's profiles when I go to unlike a post, I get thrown this error, which says that it is coming from my destroy action in my likes controller:
ActionController::InvalidAuthenticityToken
I'm using devise, but don't know if that has to do with the cause of the issue.
Right now this is what I'm working with:
<h4>All of <%= #user.email %>'s posts:</h4>
<% #user.posts.order('created_at DESC').each do |post| %>
<li><%= post.content %></li>
<% unless current_user.likes.pluck(:post_id).include?(post.id) %>
<%= form_tag likes_path do %>
<%= hidden_field_tag 'post_id', post.id %>
<%= submit_tag "Like", :class => "like_button" %>
<% end %>
<% else %>
<% like = post.likes.where(user_id: current_user.id).first %>
<div class="unlike_button">
<%= form_tag like_path(like) do %>
<%= hidden_field_tag 'post_id', post.id %>
<%= button_to "Unlike", like_path(post), method: :delete %>
</div>
<% end %>
class LikesController < ApplicationController
def create
#post = Post.find(params[:post_id])
#like = Like.new(user_id: current_user.id, post_id: #post.id)
if #like.save
flash[:success] = "Post Liked!"
redirect_back(fallback_location: root_path)
else
flash[:notice] = "Couldn't like post"
redirect_back(fallback_location: root_path)
end
end
def destroy
#like = Like.find(params[:id])
#like.destroy
flash[:success] = "Post unliked"
redirect_back(fallback_location: root_path)
end
end
class PostsController < ApplicationController
def index
#posts = Post.all
#user = User.find(params[:user_id])
end
def new
#post = Post.new
#user = User.find(params[:user_id])
end
def create
#post = current_user.posts.build(post_params)
if #post.save
flash[:success] = "Posted!"
redirect_to user_path(current_user)
else
flash[:notice] = "Post could not be submitted"
redirect_to users_path
end
end
private
def post_params
params.require(:post).permit(:content)
end
end
There is a comment in application_controller.rb..
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
so ,you may try changing..
protect_from_forgery with: :exception
to this
protect_from_forgery with: :null_session
Hope it helps :)
I think I have figured it out.. At least have gotten it to work. I wasusing a form_for helper as well as button_to helper. I deleted the form_for helper and just stuck with
<%= button_to "Unlike", like_path(like), method: :delete %>
and it is now working
What helps me solve this problem is adding the Forward Slash in the URL
From:
= bootstrap_form_tag url: 'signup_with_phone' do |form|
To:
= bootstrap_form_tag url: '/signup_with_phone' do |form|

Only allow signed_in user to comment in the post

Currently, I am using devise gem, I want only a signed_in user to comments in the post and display the flash message like "you must log_in to comment for the post" for the not sign_in user if they comment in the post.
This is my comments_controller.rb file
class CommentsController < ApplicationController
before_action :correct_user, only: :destroy
def create
#post = Post.find(params[:post_id])
#comment = #post.comments.create(params[:comment].permit(:name, :body))
#comment.user_id = current_user.id
#comment.save
redirect_to post_path(#post)
end
def destroy
#post = Post.find(params[:post_id])
#comment = #post.comments.find(params[:id])
#comment.destroy
redirect_to post_path(#post)
end
private
def comment_params
params.require(:comment).permit(:name, :body)
end
def correct_user
#comment = current_user.comments.find_by(id: params[:id])
if #comment.nil?
flash[:alert] = "Not your comment!"
redirect_to :back
end
end
end
Below is my _comment.html.erb file under views/comments folder
<div class="comment clearfix">
<div class="comment_content">
<p class="comment_name"><strong><%= comment.name %></strong></p>
<p class="comment_body"><%= comment.body %></p>
<p class="comment_time"><%= time_ago_in_words(comment.created_at) %> Ago</p>
</div>
<% if user_signed_in? %>
<p><%= link_to 'Delete', [comment.post, comment],
method: :delete,
class: "button",
data: { confirm: 'Are you sure?' } %>
</p>
<% end -%>
and my comment model
class Comment < ApplicationRecord
belongs_to :post
belongs_to :user
end
In your new or create action you can use. You can also make it as a before_action filter
unless user_signed_in?
flash[:notice] = "you must log_in to comment for the post"
redirect_to #your login page
end
Spend some time going through documentation http://github.com/plataformatec/devise

Resources