How to get user name of Liker? - ruby-on-rails

The example line in here didn't work. I tried 20 varieties of it.
views/comments/_comments.html.erb
<% #comments.each do |comment| %>
<%= User.find(comment.user_id).name %> # Gives user name of commenter
<%= simple_format comment.content %>
<%= pluralize(comment.likes, 'like') %>
<%= link_to content_tag(:span, '', class: 'glyphicon glyphicon-thumbs-up') +
' Like it', like_comment_path(:id => comment.id), method: :post %>
<%= User.find(like.user_id).name %> # How to get user name of liker?
<% end %>
comments_controller.rb
def like
#comment = Comment.find(params[:id])
comment_like = current_user.comment_likes.build(comment: #comment)
if comment_like.save
#comment.increment!(:likes)
#comment.create_activity :like
#user = User.find(params[:id])
flash[:success] = 'Thanks for liking!'
else
flash[:error] = 'Two many likes'
end
redirect_to(:back)
end
comment_like.rb
class CommentLike < ActiveRecord::Base
belongs_to :comment
belongs_to :user
validates :user, uniqueness: { scope: :comment }
end
comment.rb
class Comment < ActiveRecord::Base
include PublicActivity::Common
# tracked except: :update, owner: ->(controller, model) { controller && controller.current_user }
has_many :comment_likes
has_many :likers, through: :comment_likes, class_name: 'User'
belongs_to :commentable, polymorphic: true
belongs_to :user
end

A comment has many likes. So you are looking for the comment likers.
class Comment
has_many :comment_likes
has_many :likers, through: :comment_likes, class_name: 'User', source: :liker
end
class CommentLikers
belongs_to :liker, class_name: 'User', foreign_key: :user_id
belongs_to :liked_comment, class_name: 'Comment', foreign_key: :comment_id
end
class User
has_many :comment_likes
has_many :liked_comments, through: :comment_likes, class_name: 'Comment', source: :liked_comment
end
Then in your views:
<% #comments.each do |comment| %>
<% comment.likers.each do |user| %>
<%= user.name %>
<% end %>
<% end %>
EDIT 1
Your question is so well explained in the rails guides:
http://guides.rubyonrails.org/association_basics.html
But just to give you a short explanation:
You need to associate the comments through the comment likes to the users. To do you need to tell rails to look for the user in the table using the user_id.
That's what the through: :comment_likes, class: 'User' do.
EDIT 2
Check the source code to see more options:
http://apidock.com/rails/ActiveRecord/Associations/ClassMethods/has_many

1) If like variable declared in controller it should be global: #like
2) Don't use User.find(comment.user_id).name or User.find(like.user_id).name.
Use it like comment.user.name, #like.user.name

The short answer
In your controller, change
comment_like = current_user.comment_likes.build(comment: #comment)
if comment_like.save
with
#comment_like = current_user.comment_likes.build(comment: #comment)
if #comment_like.save
In your view, change
<%= User.find(like.user_id).name %>
with
<%= #comment_like.user.name %>

Related

several has_many through instances, of diffrent categories, in the same form

I've been working on a simple scenario : users can join one group of each type. I am tring to build a form that will show all types, and under each type's name- the chosen group for that type, or a select box to choose a group of that type, if the user is not a member of one.
So far, I only could come up with a seperate form for each type - rather unconvinient. I'v Been tring to solve this for several days. I found explanations for uniqness of instances, collection_select and has_many through forms but I can't find a way to a combine solution.
Models:
class User < ActiveRecord::Base
has_many :memberships
has_many : groups, through: :memberships
end
class Group < ActiveRecord::Base
has_many : memberships
has_many :users, through: :memberships
belongs_to :group_type
end
class Membership < ActiveRecord::Base
belongs_to :user
belongs_to :group
validates :user_id, uniqueness: { scope: [:group_id, :group_type] }
end
class GroupType < ActiveRecord::Base
has_many :groups
end
View:
<% #types = GroupTypes.all %>
<% #types.each do |type| %>
<%= '#{#type.name}' %>
<% #active_group = user.groups.where(type :type) %>
<% if #active_group.exist? %>
<%= '#{#active_group}' %>
<%= link_to 'Leave', [group.user], method: :delete, data: { confirm: 'Are you sure?' } %>
<% else %>
<%= form_for (Membership.new) do |f| %>
<%= f.hidden_field :user_id, value: #user.id %>
<%= f.collection_select :group_id, Groups.all.where(type :type), :id, :name
<%= f.submit %>
<%end>
<%end>
<%end>
controlller:
Class MembershipController < ApplicationController
def create
#user = User.find(params[:user_id])
#group = Group.find(params[:group_id])
#membership = user.membership.create(group :#group )
#user. memberships << membership
redirect_to user_register_path
end
def destroy
#user = User.find(params[:user_id])
#user.groups.find_by(group : params[:group_id]).delete
redirect_to user_register_path
end
private
def membership_params
params.require(:membership).permit(:user_id, :group_id)
end
end
Not sure if it is working properly, but as I wrote I am not happy with the idea of a form for each cathegory. was wondering if anyone could advise on a solution for that basic problem.
Thanks!
not a complete answer but I thought of posting
the whole idea is by DRYING up your code you can easily see solution to your problems
1) DROP the TypeGroup model
class Group < ActiveRecord::Base
has_many : memberships
has_many :users, through: :memberships
has_many :types, class_name: "Group",
foreign_key: "type_id"
belongs_to :type, class_name: "Group"
end
migration
class CreateTypes < ActiveRecord::Migration[5.0]
def change
create_table :groups do |t|
t.references :type, index: true
t.timestamps
end
end
end
2) your controller#new
def new
#active_groups = current_user.groups.map{ |group| group.types}
#types = Type.all
end
3) use form helpers
def user_group?
type.group.user == current_user
end
4) DRY your form
<% #types.each do |type| %>
<%= '#{#type.name}' %>
<% if user_group? %>
// show your form
<%end>
// etc etc
<%end>
also I never use this architecture, of showing the child form and using it to query for the parent, but usually I always start from the parent and build a nested form

allowing user to create answers to multiple questions in one form

I have a model relationship set up like this:
# User model
class User < ActiveRecord::Base
belongs_to :company
has_many :answers
# groups
has_many :group_memberships
has_many :group_questions, through: :groups, source: :questions
has_many :groups, through: :group_memberships
# question
has_many :question_participants, as: :questionable
has_many :questions, through: :question_participants
# questions created by admin
class Question < ActiveRecord::Base
belongs_to :company
has_many :question_participants
has_many :answers
has_many :users, through: :question_participants,
source: :questionable, source_type: 'User'
has_many :groups, through: :question_participants,
source: :questionable, source_type: 'Group'
has_many :companies, through: :question_participants,
source: :questionable, source_type: 'Company'
end
# user answers to questions
class Answer < ActiveRecord::Base
belongs_to :user
belongs_to :question
end
now my answer stores user_id, reply (their answer) and question id.
What I'd like to be able to create now is a form that allows a user to answer all the questions asked to them, and submit the answers.
I've setup my form like this on the view file (answers#new):
<%= form_for #answer do |f| %>
<% current_user.all_questions.each do |question| %>
<%= f.hidden_field :question_id, value: question.id %>
<p><%= question.name %>
<%= f.text_field :reply %>
<% end %>
<% end %>
which doesn't work, and I get why - issue is i don't know how to make it work with multiple save.
I hope I got your problem. Here is what I suggest:
<%= form_for #answer do |f| %>
<% current_user.all_questions.each do |question| %>
<%= hidden_field_tag 'questions[][id]', question.id %>
<p><%= question.name %>
<%= text_field_tag 'questions[][reply]' %>
<% end %>
<% end %>
Now in the controller action you can access params[:questions] and you'd get smth like this: [{"id"=>"1", "reply"=>"some text 1"}, {"id"=>"2", "reply"=>"some text 2"}]:
# your_controller.rb
def create_answers
answers = params[:questions].map do |question|
current_user.answers.create(question_id: question[:id], reply: question[:reply])
end
if answers.any(&:invalid?)
flash[:error] = 'Some answers were not accepted'
redirect_to :back
else
redirect_to home_path
end
end

Using a Many-to-Many Relationship with the Public Activity gem in Rails

I have the scenario where an author has and belongs to many books, vice versa. Following the instructions for setting up associations in a one-to-many relationship works fine but when a many-to-many relationship introduced I get this error message whenever I try to create or update my book model.
undefined method `author' for #<Book:0x007fb91ae56a70>
As far as setting up how authors are chosen for a book I'm using the code provided by the token-input railscast here with a few alterations.
class Author < ActiveRecord::Base
has_many :authorships
has_many :books, through: :authorships
def self.tokens(query)
authors = where("name like ?", "%#{query}%")
if authors.empty?
[{id: "<<<#{query}>>>", name: "Add New Author: \"#{query}\""}]
else
authors
end
end
def self.ids_from_tokens(tokens)
tokens.gsub!(/<<<(.+?)>>>/) {create!(name: $1).id}
tokens.split(',')
end
end
class Book < ActiveRecord::Base
attr_reader :author_tokens
include PublicActivity::Model
tracked owner: :author
has_many :authorships
has_many :authors, through: :authorships
def author_tokens=(ids)
self.author_ids = Author.ids_from_tokens(ids)
end
end
Form View
<%= form_for(#book) do |f| %>
...
<div class="field">
<%= f.text_field :author_tokens, label: 'Author', input_html: {"data-pre" => #book.authors.to_json} %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
There is no author relationship in your Book model.
What
tracked owner: :author
does is basically calling method author on your Book instance. You should try :authors
But!
That won't solve your problem because owner can only be one. So you can do something like:
tracked owner: proc {|_, book| book.authors.first }
to set the owner to the first author the book has.
class Author < ActiveRecord::Base
has_many :author_books, inverse_of: :author, dependent: :destroy
accepts_nested_attributes_for :author_books
has_many :books, through: :author_books
end
class Book < ActiveRecord::Base
has_many :author_books, inverse_of: :book, dependent: :destroy
accepts_nested_attributes_for :author_books
has_many :authors, through: :author_books
end
class AuthorBook < ActiveRecord::Base
validates_presence_of :book, :author
end
============= view ==============
<%= form_for #book do |f| %>
<%= f.text_field :title %>
<%= f.fields_for :author_books do |f2| %>
<%# will look through all author_books in the form builder.. %>
<%= f2.fields_for :author do |f3| %>
<%= f3.text_field :name %>
<% end %>
<% end %>
<% end %>

Rails conditional_select for nested attributes

I'm trying to setup the following: A User has many Groups through Memberships, a Group has many Events, and an Event has many Posts.
On my view to show a group with all of its events, I want a user to be able to write a new post by selecting the correct group from a drop down, writing a comment and submit. I'm currently using a collection_select to create the post, but the event_id is not getting passed to ActiveRecord, i.e. posts are created, but they do not have event_ids (or even comments):
class User < ActiveRecord::Base
has_many :memberships
has_many :groups, through: :memberships
has_many :posts
end
class Membership < ActiveRecord::Base
belongs_to :group
belongs_to :user
end
class Group < ActiveRecord::Base
has_many :memberships
has_many :events, dependent: :destroy
has_many :users, through: :memberships
end
class Event < ActiveRecord::Base
belongs_to :group
has_many :posts
end
class Post < ActiveRecord::Base
belongs_to :event
belongs_to :user
end
class GroupsController < ApplicationController
def show
#define new post
#new_post = Post.new
end
end
class PostsController < ApplicationController
def create
if #post = Post.create(params[post_params])
flash[:success] = "Post Created!"
else
redirect_to group_url
end
end
private
def post_params
params.require(:post).permit(:event_id, :comment)
end
end
<h1>New Post:</h1>
<%=form_for([#new_post]) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class = "field">
<%= f.label :event_name %>
<%= f.collection_select(:event_id, Event.all, :id, :title) %>
</div>
<div class = "field">
<%= f.text_area :comment, placeholder: "New Post..." %>
</div>
<%=f.submit "Submit", class: "btn btn-large btn-primary" %>
<%end%>
I have a feeling that because the routes are nested, group_id never is passed to the Posts controller, and so can never be set. But I'm sure there's a lot more wrong than that...
can you try to pass Post.create(post_params) instead of Post.create(params[post_params])
post_params is actually a full hash extracted from the params so you should not pass it to params again
If you want to add user_id
you should add to your view something like this
<%= f.hidden_field :user_id, value: current_user.id %>

rails undefined method `reviewer' for #<Review

I'm creating review to my posts, all works but i dont know how to show who wrote the review
i'm trying this:
class User < ActiveRecord::Base
has_many :posts
has_many :reviewers, :class_name => 'Post', :foreign_key => 'reviewer_id'
end
class Post < ActiveRecord::Base
belongs_to :user
belongs_to :category
has_many :reviews
belongs_to :reviewers, class_name: 'User', :foreign_key => 'reviewer_id'
default_scope -> { order('created_at DESC') }
end
class Review < ActiveRecord::Base
belongs_to :post
belongs_to :user
end
class ReviewsController < ApplicationController
def new
#post = Post.find(params[:post_id])
#review = #post.reviews.new(post_id:params[:post_id])
end
def create
#post = Post.find(params[:post_id])
#review = #post.reviews.build(review_params)
if #review.save
flash[:success] = "Ваш отзыв добавлен"
redirect_to post_path #post
else
render 'new'
end
end
private
def review_params
params.require(:review).permit(:post_id, :body, :reviewer_id).merge(:reviewer_id => current_user.id)
end
end
and my view
<% #post.reviews.each do |review| %>
<p>
<strong>reviewer:</strong>
<%= review.reviewer.email %>
</p>
<p>
<strong>review:</strong>
<%= review.body %>
</p>
<% end %>
my migration
class CreateReviews < ActiveRecord::Migration
def change
create_table :reviews do |t|
t.text :body
t.references :post, index: true
t.references :reviewer, index: true
t.timestamps
end
end
end
but rails given error undefined method `reviewer' for #
Help please dsfsdf
I think that you have a pluralization issue:
A post have many reviews by many reviewers (one for each review). But you are storing the foreign key within the post so you written the problematic line:
belongs_to :reviewers, class_name: 'User', :foreign_key => 'reviewer_id'
The issue here is that it is a singular association with a plural name.
I think that you are trying to say here that a
class Post
have_many :reviewers, class_name: 'User', through: :reviews
end
But as you are trying to access the reviewers from the review what you really need is to add:
class Review
belongs_to :reviewer, class_name: 'User'
end
Then you can access the reviewers from the review as expected:
<% #post.reviews.each do |review| %>
<p>
<strong>reviewer:</strong>
<%= review.reviewer.email %>
</p>
<p>
<strong>review:</strong>
<%= review.body %>
</p>
<% end %>
There is also an error in User:
has_many :reviewers, :class_name => 'Post', :foreign_key => 'reviewer_id'
As it should be:
has_many :reviews, :foreign_key => 'reviewer_id'
You need to be using .user. Check the belongs_to in your model.
Review.first.user
As a previous poster pointed out, your user association for Review is :user, so your view should probably look like this:
<% #post.reviews.each do |review| %>
<p>
<strong>reviewer:</strong>
<%= review.user.name unless review.user.nil? %>
</p>
<p>
<strong>review:</strong>
<%= review.body %>
</p>
<% end %>

Resources