Ruby on Rails. Getting various errors while trying to add a voting feature via the acts_as_votable gem on nested comments.
The acts_as_votable works with the posts, but not with the nested comments under the posts.
With :
<%= link_to "Like", like_post_comment_path(#post, #comment), method: :put do %>
<i class="fa fa-arrow-up" aria-hidden="true">Yes</i>
<%= #p.get_upvotes.size %>
<% end %>
I get the error:
No route matches {:action=>"upvote", :controller=>"comments", :id=>nil, :post_id=>"1"}, missing required keys: [:id]
With:
<%= link_to "Like", like_post_comment_path(#post, #comment.id), method: :put do %>
<i class="fa fa-arrow-up" aria-hidden="true">Yes</i>
<%= #p.get_upvotes.size %>
<% end %>
I get the error:
undefined method `id' for nil:NilClass
I have tried various combinations as the parameters. All produce an error.
My Comments controller:
class CommentsController < ApplicationController
before_action :find_post, only: [:upvote, :downvote, :create, :destroy, :edit, :update]
before_action :find_comment, only: [:upvote, :downvote :destroy, :update, :edit, :comment_owner ]
before_action :comment_owner, only: [:destroy, :edit, :update]
def new
#comment = Comment.new
#post = Post.new
end
def create
#comment = #post.comments.create(params[:comment].permit(:content))
#comment.user_id = current_user.id
#comment.save
if #comment.save
redirect_back(fallback_location: root_path)
else
render 'new'
end
end
def destroy
#comment.destroy
redirect_back(fallback_location: root_path)
end
def edit
#comment = Comment.find(params[:id])
#p = Post.find(params[:post_id])
end
def update
if #comment.update(params[:comment].permit(:content))
respond_to do |f|
if (#comment.save)
f.html { redirect_to "/explore", notice: "Comment edited!" }
elsif
f.html { redirect_to "/explore", notice: "Error: Your Comment is the same!." }
else
render 'edit'
#f.html { render 'edit'} ## Specify the format in which you are rendering "new" page
end
end
end
end
def upvote
#comment.upvote_from current_user
redirect_back(fallback_location: root_path)
end
def downvote
#comment.downvote_from current_user
redirect_back(fallback_location: root_path)
end
private
def find_post
#post = Post.find(params[:post_id])
end
def find_comment
#comment = #post.comments.find(params[:id])
end
def comment_owner
unless current_user.id == #comment.user_id
flash[:notice] = "You can't do that!"
redirect_back(fallback_location: root_path)
end
end
end
Comment model:
class Comment < ApplicationRecord
belongs_to :commentable, :polymorphic => true
belongs_to :user
acts_as_votable
# scope :desc
default_scope { order(created_at: :desc) }
end
Post model:
class Post < ApplicationRecord
belongs_to :user
has_many :comments, :as => :commentable
attr_accessor :post_id
acts_as_votable
validates :user_id, presence: true
validates :content, presence: true, length: { maximum: 100} # Questions are capped at 100 chars.
default_scope { order(cached_votes_score: :DESC) }
end
User model:
class User < ApplicationRecord
has_many :posts
has_many :comments, :as => :commentable
acts_as_voter
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :posts, dependent: :destroy
def user
user = User.find_by(params[:id])
end
Routes:
like_post_comment PUT /posts/:post_id/comments/:id/like(.:format) comments#upvote
dislike_post_comment PUT /posts/:post_id/comments/:id/dislike(.:format) comments#downvote
Related
I am using Devise as my authentication system and simple form. I get a NoMethodError in Groups#show and an undefined method 'name' for nil:NilClass error. I use model associations to tie the groups and posts together. When I do puts post.user.name it correctly displays in my terminal but that line causes the above error and it's referencing Groups#show for some reason. Any thoughts?
Routes
resources :groups do
resources :posts
end
Group Model
class Group < ActiveRecord::Base
validates :user_id, presence: true
belongs_to :user
has_many :posts
has_many :comments
has_many :attachments
end
Post Model
class Post < ActiveRecord::Base
validates :user_id, presence: true
validates :caption, presence: true
belongs_to :user
belongs_to :group
has_many :comments, dependent: :destroy
end
Group Controller
class GroupsController < ApplicationController
before_action :authenticate_user!
def new
#group = current_user.groups.build
end
def create
#group = current_user.groups.build(group_params)
#group.user_id = current_user.id
if #group.save
redirect_to groups_path
else
render :new
end
end
...
private
def group_params
params.require(:group).permit(:group_name, :description, :user_id)
end
end
Posts Controller
class PostsController < ApplicationController
before_action :set_post, only: [:show, :edit, :update, :destroy]
before_action :owned_post, only: [:edit, :update, :destroy]
before_action :authenticate_user!
def index
#posts = Post.paginate(page: params[:page], per_page: 3).order('created_at DESC')
#post = current_user.posts.build
#attachments = Attachment.all
end
...
def new
#post = current_user.posts.build
end
def create
#post = current_user.posts.build(post_params)
#group = Group.find(params[:group_id])
#post.group_id = #group.id
if #post.save
redirect_to groups_path
else
render :new
end
end
...
private
def post_params
params.require(:post).permit(:caption, :user_id)
end
def set_post
#post = Post.find(params[:id])
end
def owned_post
unless current_user == #post.user
redirect_to root_path
end
end
end
groups/show.html.erb
<%= render "posts/index" %>
...
posts/_index.html.erb
<%= render 'posts/form' %>
<%= render 'posts/posts' %>
...
posts/_form.html.erb
<%= simple_form_for([#group, #group.posts.build]) do |f| %>
...
posts/_posts.html.erb
<% #group.posts.each do |post| %>
<%= puts post.user.name %> ISSUE
<%#<%= render 'posts/post', post: post %>
<% end %>
SOLUTION: After asking on Reddit Rails, a generous user offered a solution that works. Apparently the <%= simple_form_for([#group, #group.posts.build]) do |f| %> creates a new post and adds it to the groups.posts array and so this causes issues when it iterates over _posts.html.erb and there is no user. More information can be found here: https://www.reddit.com/r/rails/comments/4lygix/unidentified_method_for_nil_class_devise_and/
But replace the above simple_form line of code with <%= simple_form_for([#group, Post.new(group: #group)]) do |f| %> seemed to do the trick, as suggested by the generous user in the reddit link above.
I am a complete beginner in Rails as such and I am trying to build a page to add extra profile data once the user logs in.
I am using Devise for authentication purposes and that works fine. I get this error and I have been stuck here.
undefined method `profiles'
Can you please help?
Codes
profiles_controller.rb
class ProfilesController < ApplicationController
before_action :authenticate_user!, only: [:new, :create, :show]
def new
#profile = current_user.profiles.build
end
def create
#profile = current_user.profiles.build(profile_params)
if #profile.save
format.html {redirect_to #profile, notice: 'Post was successfully created.'}
else
format.html {render 'new'}
end
end
def show
#profile = current_user.profiles
end
private
def profile_params
params.require(:profile).permit(:content)
end
end
The error seems to be coming from these lines in particular
def new
#profile = current_user.profiles.build
end
Other codes for reference:
/views/profiles/new.html.erb
<h1>Profiles#new</h1>
<p>Find me in app/views/profiles/new.html.erb</p>
<h3>Welcome <%= current_user.email %></h3>
<%= form_for(#profile) do |f| %>
<div class="field">
<%= f.label :content %><br />
<%= f.text_field :text, autofocus: true %>
</div>
<div class="actions">
<%= f.submit "Sign up" %>
</div>
<%end%>
routes.rb
Rails.application.routes.draw do
get 'profiles/new'
get 'profiles/create'
get 'profiles/show'
get 'profiles/update'
get 'pages/home'
get 'pages/dashboard'
devise_for :users, controllers: { registrations: "registrations" }
resources :profiles
root 'pages#home'
devise_scope :user do
get "user_root", to: "page#dashboard"
end
end
models/user.rb
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_one :profile, dependent: :destroy
end
models/profile.rb
class Profile < ActiveRecord::Base
belongs_to :user
end
I just figured it out!
As the relationship is has_one, we should be using
def new
#profile = current_user.build_profile
end
instead of
def new
#profile = current_user.profiles.build
end
according to the documentation -
http://guides.rubyonrails.org/association_basics.html#has-one-association-reference
1) If your user must have many profiles. Set in your app/models/user.rb has_many :profiles
2) In your ProfilesController in new method instead of #profile = current_user.profiles use #profile = Profile.new
3) In your routes.rb delete
get 'profiles/new'
get 'profiles/create'
get 'profiles/show'
get 'profiles/update'
because you have already used resources :profiles
4) To stay with rules of DRY you can render form from a partial. Just add in views/profiles/_form.html.erb with the same content in your new.html.erb and after this you can delete everything im new.htm.erb and paste <%= render "form" %>. In future it will help you to render edit form if you want.
5) In your ProfilesController you can add method index with all profiles
def index
#profiles = Profile.all
end
You are trying to call an undefined relationship:
def new
#profile = current_user.profiles.build
end
has_one :profile
You should be calling:
def new
#profile = current_user.build_profile
end
All links work well except for devise. If I try to click sign up or log in it takes me to the sign up/log in page, right?
From the URL it looks normal. localhost:3000/users/sign_up but the view is wrong. It's displaying the profile view instead of the devise sign up/log in view.
Routes code:
Rails.application.routes.draw do
resources :questions
get '/users/:id' => 'profile#profile'
devise_for :users
root 'home#index'
end
Profile controller:
class ProfileController < ApplicationController
def profile
end
end
User model:
class User < ActiveRecord::Base
has_many :questions
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
end
Question model
class Question < ActiveRecord::Base
belongs_to :user
end
It didn't do that at first, I think this bug happened when I edited the show.html.erb view in the questions folder.
Show code (question):
<% #questions.each do |q| %>
<% if user_signed_in? %>
<% if q.id == current_user.id %>
<%= link_to 'Edit', edit_question_path(#question) %>
<% end %>
<% end %>
<% end %>
Question controller:
class QuestionsController < ApplicationController
before_action :set_question, only: [:show, :edit, :update, :destroy]
respond_to :html
def index
#questions = Question.all
respond_with(#questions)
end
def show
#questions = Question.all
respond_with(#question)
end
def new
#question = Question.new
respond_with(#question)
end
def edit
end
def create
#question = Question.new(question_params)
#question.save
respond_with(#question)
end
def update
#question.update(question_params)
respond_with(#question)
end
def destroy
#question.destroy
respond_with(#question)
end
private
def set_question
#question = Question.find(params[:id])
end
def question_params
params.require(:question).permit(:title, :description, :image_url)
end
end
Try putting that get '/users/:id' => 'profile#profile' below the devise_for :users line.
I'm somewhat of a newbie with ruby on rails and went off of samurails.com single table inheritance with rails 4 tutorial to add different comment types. This worked great but the problem I'm running into is when I try to use polymorphic associations to get comments and the specific type to function under other models such as project and challenge. A regular comment works, but the specific types do not.
I haven't seen anything that clearly says how to make this work or another option of going about it so any help would be greatly appreciated.
class Comment < ActiveRecord::Base
has_merit
acts_as_votable
belongs_to :commentable, :polymorphic => true
belongs_to :user
belongs_to :commenttype
belongs_to :project
def self.types
%w(Question Idea Problem)
end
def commentable_type=(sType)
super(sType.to_s.classify.constantize.base_class.to_s)
end
scope :questions, -> {where(type: 'Question')}
scope :ideas, -> {where(type: 'Idea')}
scope :problems, -> {where(type: 'Problem')}
end
class Question < Comment
end
class Idea < Comment
end
class Problem < Comment
end
class Project < ActiveRecord::Base
belongs_to :user
has_many :comments, :as => :commentable, :class_name => "Comment"
has_many :questions, :as => :commentable, :class_name => "Question"
has_many :ideas, :as => :commentable, :class_name => "Idea"
has_many :problems, :as => :commentable, :class_name => "Problem"
delegate :questions, :ideas, :problems, to: :comments
end
class CommentsController < ApplicationController
before_action :set_commentable, only: [:index, :new, :create]
before_action :set_type
before_action :set_comment, only: [:show, :edit, :update, :destroy]
def index
#comments = type_class.all
end
def show
end
def new
#comment = type_class.new
end
def edit
end
def create
#comment = #commentable.comments.new(comment_params)
#comment.user = current_user
if #comment.save
redirect_to :back, notice: "#{type} was successfully added."
else
render action: 'new'
end
end
def update
if #comment.update(comment_params)
redirect_to #comment.commentable, notice: "#{type} was successfully updated."
else
render action: 'edit'
end
end
def destroy
#user = current_user
#comment = #commentable.comments.where(comment_user: current_user).first
#commentable.comment.destroy
respond_to do |format|
format.html { redirect_to #commentable, notice: "Comment was deleted." }
format.js
end
end
private
def set_comment
#comment = type_class.find(params[:id])
end
def set_type
#type = type
end
def type
Comment.types.include?(params[:type]) ? params[:type] : "Comment"
end
def type_class
type.constantize
end
def set_commentable
#commentable = find_commentable
end
# add more commentable models here
def find_commentable
if params[:challenge_id]
Challenge.find(params[:challenge_id])
else
end
end
def find_commentable
if params[:project_id]
Project.find(params[:project_id])
else
end
end
def comment_params
params.require(type.underscore.to_sym).permit(:body, :type, :user_id, :commentable_id, :commentable_type, :commentable, :comment_type)
end
end
module CommentsHelper
def sti_comment_path(type = "comment", comment = nil, action = nil)
send "#{format_sti(action, type, comment)}_path", comment
end
def format_sti(action, type, comment)
action || comment ? "#{format_action(action)}#{type.underscore}" : "#{type.underscore.pluralize}"
end
def format_action(action)
action ? "#{action}_" : ""
end
end
<%= form_for [commentable, Comment.new], :html => { :multipart => true } do |f| %>
<%= f.text_area :body, class: "form-control", placeholder: "What's on your mind?" %>
<%= f.label :type %><br>
<%= f.select :type, Comment.types.map {|r| [r.humanize, r.camelcase]}, {}, disabled: #type != "Comment" %>
<%= f.submit "Post", class: "btn pull-right" %>
name
metric
date_value
result_value
None of the above attributes are showing when I load the index page.
I know its because of this line: <% if averaged.user == current_user %>
More specifically it's because of the nested attributes of date_value and result_value (because if I take out those nested attributes the name & metric will show)
What do I need to add to the controller to allow the nested attributes to show on the index page, and in effect all the attributes?
I am using the cocoon gem and the devise gem.
Thanks in advance for your service!
<div id="values" class="panel panel-default">
<div class="panel-heading"><h4><b>AVERAGE</b></h4></div>
<!-- Table -->
<table>
<% #averaged_quantifieds.each do |averaged| %>
<% if averaged.user == current_user %>
<th class="value">
<%= link_to edit_quantified_path(averaged) do %>
<%= averaged.name %>
<% end %>
(<%= averaged.metric %>)
</th>
<tbody class="value">
<td><%= averaged.date_value.strftime("%m-%Y") %></td>
<td><%= averaged.result_value %></td>
</tbody>
<% end %>
<% end %>
</table>
</div>
controller
class QuantifiedsController < ApplicationController
before_action :set_quantified, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!, except: [:index, :show]
def index
#averaged_quantifieds = current_user.quantifieds.averaged
#instance_quantifieds = current_user.quantifieds.instance
#averaged_quantifieds = Result.all.order("date_value")
#instance_quantifieds = Result.all.order("date_value")
end
def show
end
def new
#quantified = current_user.quantifieds.build
#quantified = Quantified.new
end
def edit
end
def create
#quantified = current_user.quantifieds.build(quantified_params)
if #quantified.save
redirect_to quantifieds_url, notice: 'Quantified was successfully created'
else
render action: 'new'
end
end
def update
if #quantified.update(quantified_params)
redirect_to quantifieds_url, notice: 'Goal was successfully updated'
else
render action: 'edit'
end
end
def destroy
#quantified.destroy
redirect_to quantifieds_url
end
private
def set_quantified
#quantified = Quantified.find(params[:id])
end
def correct_user
#quantified = current_user.quantifieds.find_by(id: params[:id])
redirect_to quantifieds_path, notice: "Not authorized to edit this goal" if #quantified.nil?
end
def quantified_params
params.require(:quantified).permit(:categories, :name, :metric, :result, :date, results_attributes: [:id, :result_value, :date_value, :_destroy])
end
end
quantified.rb
class Quantified < ActiveRecord::Base
belongs_to :user
scope :averaged, -> { where(categories: 'averaged') }
scope :instance, -> { where(categories: 'instance') }
has_many :results
accepts_nested_attributes_for :results, :reject_if => :all_blank, :allow_destroy => true
CATEGORIES = ['averaged', 'instance']
end
result.rb
class Result < ActiveRecord::Base
belongs_to :quantified
belongs_to :user
end
user.rb
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :goals
has_many :values
has_many :quantifieds
has_many :results
end
Please let me know if you need any more code or comments to help bring this question to a resolution.