Allowing user to favorite a post - ruby-on-rails

I'm working on allowing a user to favorite a post.
I've created a model called favorite.
belongs_to :user
belongs_to :post
it stores the user_id and post_id.
I've also created a FavoritesController
class FavoritesController < ApplicationController
def create
#post = Post.find(params[:post_id])
current_user.favorite(#post)
end
def destroy
#post = Post.find(params[:id])
current_user.unfavorite(#post)
end
end
the form I have on my Posts#index is:
<%= form_for current_user.favorites.build do |favorite| %>
<%= hidden_field_tag :post_id, f.id %>
<%= favorite.button do %>
<i class="fa fa-star-o"></i>
<% end %>
<% end %>
my user model looks like this:
# Favorites a post.
def favorite(post)
favorite.create(post_id: post)
end
# Unfavorites a post.
def unfavorite(post)
favorite.find_by(post_id: post).destroy
end
when I try to click on favorite I get:
wrong number of arguments (0 for 1)
Parameters:
{"utf8"=>"✓",
"authenticity_token"=>"HjiANQUqTQVEqy0yzfLFMlnC8RsTiY5kVlvIUnD5OSIaSYSi4ELSuC95vRMIBA/6W+KvzCWMMXQ==",
"post_id"=>"7",
What am I doing wrong here? Also is there a better way to do this?

If you're just trying to get a 'voting/favoriting' system working, I suggest using a gem like https://github.com/ryanto/acts_as_votable or something of the like.

Related

gem devise how to add comment created user email?

I am using gem devise for creating users profile
Each user can create a comment. I need to add the user name beside each comment something like this <%= #comment.user.name %>
in user.rb
has_many :comments, dependent: :destroy
in comment.rb
belongs_to :users
in comment controller
before_action :find_comment ,only:[:show,:update,:edit,:destroy]
def new
#user =User.find(params[:id])
#comment = #user.comments.build
end
def create
#user =User.find(params[:id])
#comment = #user.comments.build(comment_params)
#comment.user = current_user
if #comment.save
redirect_to doctor_path(:id => #user.id)
end
end
private
def find_comment
#comment = Comment.find(params[:id])
end
def comment_params
params.require(:comment).permit(:text)
end
user controller
def show
#user = User.find(params[:id])
end
user show.html.erb
<% for item in #user.comments %>
<% if item.text.present? %>
<%= item.text %><br>
<%= #comment.user.name %>
<br><hr>
<% end %>
I got this error
undefined method `user' for nil:NilClass
You could do it the other way around, in your show method:
#comments = Comment.all
in your show view:
<% #comments.each do |comment| %>
<%= comment.text %>
<%= comment.user.name %>
<% end %>
Since your question is not really clear I'll specifiy that if you want to show just the comments posted by the user:
def show
user_id = User.find(params[:id]).id
#comments = Comment.where(user_id: user_id)
end
Just some quick rules to start with
A user has many comments, this will be the relationship between the user and a comment that the user has made.
You already have this
A user has many profile comments, this is the relationship between a user and the comments that have been left for that user on their profile
Now you have that distinction things start to be come clearer.
Start by creating a single xref table to act as the go between users and comments that have been left for a profile and call it profile_comments
this profile_comments table needs a user_id and a comment_id of type integer to store the primary keys from user and comments tables, where the user_id is the id of the user that is having a comment left about them on their profile
You can now setup a profile_comment model that with the following relationships
belongs_to comment
belongs_to user
So now you need to change your user model relationships to the following
user.rb
has_many :comments
has_many :profile_comments, dependent: :destroy
comment.rb
belongs_to :user #not users as you have defined in your question
has_many :profile_comments, dependent: :destroy
and the new profile_comment.rb model needs the two belongs_to clauses for comment and user
profile_comment.rb
belongs_to :user
belongs_to :comment
Now when you create a comment you need to assign to to the user, and also to the profile_comment
So now your comments controller needs to setup these relationships so instead of
before_action :find_comment ,only:[:show,:update,:edit,:destroy]
def new
#user =User.find(params[:id])
#comment = #user.comments.build
end
def create
#user =User.find(params[:id])
#comment = #user.comments.build(comment_params)
#comment.user = current_user
if #comment.save
redirect_to doctor_path(:id => #user.id)
end
end
You need something like this
def create
#user =User.find(params[:id])
#comment = current_user.comments.build(comment_params)
#profile_comment = ProfileComment.new
#user.profile_comment < #profile_comment
#comment.profile_comment < #profile_comment
if #comment.save
redirect_to doctor_path(:id => #user.id)
end
end
Your update action will need to also change accordingly
Now in your view instead of
<% for item in #user.comments %>
<% if item.text.present? %>
<%= item.text %><br>
<%= #comment.user.name %>
<br><hr>
<% end %>
<% end %>
You want this, it's a little complex because you need to get from the profile comment to the comment then to the user that created the comment
<% #user.profile_comments.each do | profile_comment |%>
<%comment = profile_comment.comment%>
<% if comment.text.present? %>
<%= comment.text %><br>
<%if comment.user.blank?%>
No user assigned to this comment
<%else%>
<%= comment.user.name #or email or whatever%>
<%end%>
<br><hr>
<% end %>
<% end %>
Although text is a reserved word and is an actual column type so you might want to change the column name text to something else
Any questions on this feel free to get back to me but I won't be around for the next 24 hours. Hope it's clear and helps you understand what has gone wrong with your initial setup
In the controller and the view I have assumed that the current_user is the person making the comment and #user is the person that is being commented on. Just switch that round if I have that wrong

Adding comments to article in Rails 5 (deeply nested resources)

I have a Rails app that has three main models: Qn, Ans, and Comments. I've been doing okay with 1-level-deep nested resources, but these three resources are nested deeply (Comments is shallowly nested) and they are all displayed in one single view, which makes it very confusing.
In a url like: http://localhost:3000/questions/2, the user can see all #question.answers displayed using a loop. In each of those answers, the user can see the answer.comments displayed using a loop. Below each answer, the user can also submit a new comment.
But after attempting several times to implement a 1) loop displaying all comments and 2) form for new comment, I always get some error along the lines of:
undefined method `model_name' for {:url=>"/questions/4/answers/2/comments/new"}:Hash
So I tried to pass in params #commentable instead of the answer, or point to the specific controller and action and so on, but none of these methods worked. I am guessing that I have an issue with my controllers to begin with, but I cannot seem to figure out what.
routes.rb (top ommited)
# Resources
resources :sessions
resources :users
resources :bookmarks # to be implemented later
resources :questions do
resources :answers do
resources :comments, shallow: true
end
end
Question model
class Question < ApplicationRecord
has_many :answers
has_many :bookmarks #later
end
Answer model:
class Answer < ApplicationRecord
belongs_to :question
has_many :comments, as: :commentable
has_many :likes, as: :likeable
validates :answercontent, length: {minimum: 50}
end
Comment model:
class Comment < ApplicationRecord
belongs_to :commentable, polymorphic: true
end
The show.html.erb (of QuestionsController)
<% #question.answers.each do |answer| %>
// ommited
<!-- Comments -->
<% answer.comments.each do |comment| %>
<%= comment.content %>
<br>
<% end %>
<!-- Submit new comment -->
<%= form_for(url: new_question_answer_comment_path, comment: {answer_id: answer.id, question_id: #question.id}) do |f| %>
<%= f.text_area :content %>
<%= f.submit "Submit" %>
<% end %>
<% end %>
QuestionsController (new, create, destroy ommited for brevity)
class QuestionsController < ApplicationController
def index
#questions = Question.all
end
def show
#question = Question.find(params[:id])
#answers = Answer.all
# Delete only appears when num_ans is 0
#deletable = (current_user== User.find(#question.user_id)) && (#question.answers.all.size==0)
end
private
def question_params
params.require(:question).permit(:picture_url, :country, :educational_level, :topic)
end
end
AnswersController (edit, update, destroy ommited for brevity)
class AnswersController < ApplicationController
def create
#question = Question.find(params[:question_id])
#answer = #question.answers.create(answer_params)
#answer.question_id = #question.id
#answer.user_id = current_user.id
if #answer.save
redirect_to #question
else
render :new
end
end
private
def answer_params
params.require(:answer).permit(:user_id, :question_id, :answercontent)
end
end
CommentsController
class CommentsController < ApplicationController
before_filter: load_commentable
def index
#commentable = Answer.find(params[:answer_id])
#comments = #commentable.comments
end
def new
#comment = #commentable.comments.new
end
def create
#comment = #commentable.comments.new(params[:comment])
if #comment.save
redirect_to #commentable
else
render :new
end
end
# From RailsCast ep.154
private
def load_commentable
resource, id = request.path.split('/')[1,2]
#commentable = resource.singularize.classify.constantize.find(id)
end
end
The routes
are quite messy right now so I will just post where the comments are:
question_answer_comments GET /questions/:question_id/answers/:answer_id/comments(.:format) comments#index
POST /questions/:question_id/answers/:answer_id/comments(.:format) comments#create
new_question_answer_comment GET /questions/:question_id/answers/:answer_id/comments/new(.:format) comments#new
edit_comment GET /comments/:id/edit(.:format) comments#edit
comment GET /comments/:id(.:format) comments#show
PATCH /comments/:id(.:format) comments#update
PUT /comments/:id(.:format) comments#update
DELETE /comments/:id(.:format) comments#destroy
Thanks in advance for the help.
Update:
To give you more info on what solutions I attempted:
1. Passing in two params like:
<%= form_for([answer, #comment], url: new_question_answer_comment_path(answer.id, #question.id)) do |f| %>
Gave me:
First argument in form cannot contain nil or be empty
Using #commentable (which is basically the answer) gives me an error saying that 'the id in #commentable.id doesn't exist as #commentable is nil'.
So I think the problem is that answer or #commentable is nil. But I specified it in the loop and in the controller too. So what else may I try?
form_for expects record as first argument, in your case it should be a comment instance. Also new_question_answer_comment_path expects values for question_id and answer_id keys, as you are creating a new comment, the route should be question_answer_comments not new_question_answer_comment so your form_for should be
<%= form_for Comment.new,url: question_answer_comments_path(#question,answer) do |f| %>
<%= f.text_area :content %>
<%= f.submit "Submit" %>
<% end %>
or just
<%= form_for [Comment.new,#question,answer] do |f| %>
<%= f.text_area :content %>
<%= f.submit "Submit" %>
<% end %>

Couldn't find Subscriber with 'id'= - Rails Association

Currently I have a Subscriber model and Comments model that belongs to Subscriber. Right now I need to link two models together so that my Subscriber has many comments on it. what I want is so that if I write this in the console I'll get my answer -> Subscriber.find(1).comments.first right now that returns nil because it doesn't know how to find the id of the subscriber that is leaving the comment. How can I give the application the proper code so that I can link the two? I'll post code for clarity.
CONTROLLER:
class CommentsController < ApplicationController
def new
#comment = Comment.new
end
def create
#subscriber = Subscriber.find(params[:subscriber_id])
#comment = #subscriber.comments.build(comments_params)
if #comment.save
flash[:notice] = "Thank you!"
redirect_to subscribers_search_path(:comments)
else
render "new"
end
end
private
def comments_params
params.require(:comment).permit(:fav_drink, :subscriber_id)
end
end
As you can see I'm trying to find the :subscriber_id when I create the comment. That is where my problem is. How can I connect it
ERROR:
MODELS:
class Comment < ActiveRecord::Base
belongs_to :subscriber
end
class Subscriber < ActiveRecord::Base
has_many :comments
end
Another aspect I should be clear about is that I have no current subscriber because this app is used for checking in a customer so the app does not log the user in it just checks them in with their phone number. Let me know if you need more info.
VIEWS:
<h1>new</h1>
<%= form_for #comment do |form| %>
<div class="form-group">
<p>
<%= form.label :fav_drink %>
<%= form.text_field :fav_drink %>
<%= form.hidden_field :subscriber_id %>
</p>
<%= form.submit "Send", class: "btn btn-primary" %>
</div>
<% end %>
Method 1
You subscriber id is inside the comment hash in your params. So you need to find subscriber like this
#subscriber = Subscriber.find(params[:comment][:subscriber_id])
#If you're taking this approach, you need to remove :subscriber id from your comment_params
Lik this
def comment_params
params.require(:comment).permit(:fav_drink)
end
##subscriber.comments.build will take care of the subscriber_id field for you, so its pointless rewriting it
Method 2
Or you directly create your comment.
#comment = Comments.new(comments_params)
#notice this already has, the subscriber_id, so we don't need to find
#subscriber and then do build on it
Let me know if that helps

Rails id parameter not persisting

So, essentially, I have two models, Ticket
class Ticket < ActiveRecord::Base
belongs_to :event
end
And Event:
class Event < ActiveRecord::Base
belongs_to :club
has_many :tickets
accepts_nested_attributes_for :tickets
end
They are associated with each other, and I have done the necessary migrations.
In the events/show view, it shows the event and then at the end I have a link to create a ticket, with this event's name passed as an id:
<%= link_to 'Add tickets', new_ticket_path(:id => #event.name) %>
This renders properly in the new ticket page, I have tested it in the new ticket view with <%= params[:id] %> and it comes up correctly.
The tickets_controller's create method is as follows:
def create
#ticket = Ticket.new(ticket_params)
#ticket.event_id = params[:id]
...
end
But when testing back in the events/show view
<% #tickets = Ticket.all %>
<% #tickets.each do |ticket| %>
<p><%= ticket.event_id %>--</p>
<% end %>
All of the event_id's come up empty.
Any suggestions?
The problem is you're passing the :id param to the new method, and then expecting it to persist to the create method
Params are HTTP-based values -- they don't last longer than a single request. You'll have to either use a nested resource, or set the event_id attribute in your new form:
Route
#config/routes.rb
resources :events do
resources :tickets #-> /events/:event_id/tickets/new
end
This will keep the params[:event_id] param consistent (as it's being passed in the URL), allowing you to use it in your create action like so:
def create
#ticket = Ticket.new(ticket_params)
#ticket.event_id = params[:event_id]
...
end
This is the conventional way to do this
Attribute
If you want to just test your code, you should set the event_id param in your new action form:
<%= form_for [#event, #ticket] do |f| %>
<%= f.hidden :event_id, params[:id] %> #-> sets params[:ticket][:event_id]
<% end %>
This will allow you to set the event_id param in your strong_params method in your create action:
def new
#event = Event.find params[:event_id]
#ticket = Ticket.new
end
def create
#ticket = Ticket.new(ticket_params)
#ticket.save
end
private
def ticket_params
params.require(:ticket).permit(:event_id)
end

Using Rails 3's nested resources to correctly associate models

I am trying to get a better handle on Rail's nested resources, and made a test app with models School and Class. In my routes.rb file, I have:
resources :schools do
resources :classes
end
With the following models relationship for School and Class:
class School < ActiveRecord::Base
attr_accessible: name
has_many :classes
end
and
class Class < ActiveRecord::Base
attr_accessible: name, school_id
belongs_to :school
end
I am having difficulty getting the school_id associated with the posts created under an URL like /schools/1/posts/new. More precisely, I would like to define a helper method like current_school that could take the first half of the URI where it contains the school_id, to allow me to write functions in the controller like current_school.posts.all that will automatically pull all the posts associated with school_id = what is in the URL. Thanks!
*Edit
Here is what I have in ClassController:
class ClassesController < ApplicationController
def index
#classes = current_school.classes.all
end
def new
#class = current_school.classes.build
end
def create
#class = current_school.classes.build(params[:post])
if #class.save
redirect_to root_path #this will be further modified once I figure out what to do
else
redirect_to 'new'
end
end
private
def current_school
#current_school ||= School.find(params[:school_id])
end
end
And in the new.html.erb file:
<div class="span6 offset3">
<%= form_for([#school, #class]) do |f| %>
<%= f.label :name, "class title" %>
<%= f.text_field :name %>
<%= f.submit "Create class", class: "btn btn-large btn-primary" %>
<% end %>
</div>
When you nest your resources, you get several helper methods for free as explained here. The method you're looking for would be written as one of:
new_school_class_path(#school)
new_school_class_path(#school_id)
While your classes index page would be:
school_classes_path(#school)
school_classes_path(#school_id)
In your ClassesController, you would do something like:
def index
#classes = current_school.classes
end
def new
#class = current_school.classes.build
end
private
def current_school
#current_school ||= School.find(params[:school_id])
end

Resources