Ruby on Rails: undefined method `any?' for nil:NilClass - ruby-on-rails

I have a Photo Share web app and I am trying to add comments in photos. I can't spot any mistakes. Maybe in the controller class in index function is the problem. There is an undefined method error when I try to show-post comments below the photo. Error in HAML code.
Error: - if #photo_comments.any?
Controller:
class CommentsController < ApplicationController
def index
#photo_comments = Comment.where(photo_id: => photo_id)
end
def create
#comment = Comment.create(user_id: params[:user_id], photo_id: params[:photo_id], text: params[:comment][:text])
flash[:notice] = "Successfully added a comment"
redirect_to :back
end
private
def comment_params
params.require(:comment).permit(:user_id, :photo_id, :text)
end
end
Model:
class Comment < ActiveRecord::Base
belongs_to :user
belongs_to :photo
end
Database:
class CreateComments < ActiveRecord::Migration
def change
create_table :comments do |t|
t.integer :user_id
t.integer :photo_id
t.string :text
t.timestamps
end
end
end
View:
%p Comments
- if #photo_comments.any?
- #photo_comments.each do |comment|
.bold-text= "#{comment.user.email}: "
.normal-text= comment.text
%br
- else
.text No comments for this photo yet!
%br
%br
%p
= form_for Comment.new(), :url => user_photo_comments_path do |form|
= form.label :text, 'Add a Comment'
%br
= form.text_area :text
%br
= form.submit 'Post'
Routes:
Rails.application.routes.draw do
get '/' => 'home#index'
resources :users do
resources :photos do
resources :comments
end
resources :follows
end
resources :tags, only: [:create, :destroy]
get '/log-in' => "sessions#new"
post '/log-in' => "sessions#create"
get '/log-out' => "sessions#destroy", as: :log_out
end

This line seems a bit problematic:
#photo_comments = Comment.where(photo_id: => photo_id)
I can spot a couple of potential errors here:
hash syntax: you are mixing both styles, you should use photo_id: photo_id or (Ruby pre 1.9) :photo_id => photo_id
the method or variable photo_id seems not defined in that controller, maybe you would mean params[:photo_id]?

There is definitely a syntax error on this line:
#photo_comments = Comment.where(photo_id: => photo_id)
also photo_id is not defined anywhere in the controller so maybe it should instead look like:
#photo_comments = Comment.where(photo_id: params[:photo_id])
?
The error undefined_method often comes when calling a method on a nil value. In your case instance variable #photo_comments is nil thus giving you undefined_method error in views.

These two line make no sense:
- if #photo_comments.nil?
- #photo_comments.each do |comment|
If the instance variable #photo_comments is nil then iterate of it? Of course, you will get an undefined method 'each' for nil:NilClass in that case.
I guess you mean something like this:
- unless #photo_comments.nil?
- #photo_comments.each do |comment|

Related

Why do i get this error (NoMethodError)

I am having a problem with my ruby on rails cloud 9 code while my task is to create an article from the UI and save it to the database when I hit submit.
This is my image of my problem:
This is my cloud 9 code
routes.rb:
Rails.application.routes.draw do
# The priority is based upon order of creation: first created -> highest priority.
# See how all your routes lay out with "rake routes".
# You can have the root of your site routed with "root"
# root 'welcome#index'
resources :articles
root 'pages#home'
get 'about', to: 'pages#about'
end
Articles controller (articles_controller.rb):
class ArticlesController < ApplicationController
def new
#article = Article.new
end
end
new.html.erb in articles folder in views:
<h1>Create an article</h1>
<%= form_for #article do |f| %>
<p>
<%= f.label :title %>
<%= f.text_area :title %>
</p>
<% end %>
Article model (article.rb) :
class Article < ActiveRecord::Base
end
I have done a migration and this is my migrate file :
class CreateArticles < ActiveRecord::Migration
def change
#article = Article.new
create_table :articles do |t|
t.string :title
end
end
end
Your migration seems to be missing the title column
class CreateArticles < ActiveRecord::Migration
def change
create_table :articles do |t|
t.string :title
end
end
end
Also your model should inherit from ApplicationRecord
class Article < ApplicationRecord
end
Or ActiveRecord::Base if your Rails version is less than 5
class Article < ActiveRecord::Base
end
You should remove the line #article = Article.new from migration file.
This would create a instance of Article using new keyword while you'll run the migration and it will be a nil instance, that's why you got the above error
Article id: nill
Because the last record was nil and you're looking for title of that record which primary key also nil.

Rails4 Entering data from one controller to multiple models

I have someone of a unique problem. I have 3 tables in the database that I need to populate with data. All tables are in relation to each other. The first table's info will be static and populated from a hash. The second table is the table that is usually targeted with data.
I am having a tough time trying to add data into the second table using strong parameters. I get an error param is missing or the value is empty: entries
Modles:
client.rb
class Client < ActiveRecord::Base
has_many :entries
end
Entry.rb
class Entry < ActiveRecord::Base
belongs_to :client_name
has_many :extra_data
end
extra_data.rb
class ExtraData < ActiveRecord::Base
belongs_to :entries
end
class ClientsController < ApplicationController
before_action :set_client, only: [:show, :update, :destroy, :edit]
# submit for all intended purposes.
#
def new
#entries = Entry.new()
end
def create
#client = Client.new(CLEINT_ATTR)
if #client.save
#entries = Entry.new(submit_params)
redirect_to action: :index
else
flash.alert "you failed at life for today."
redirect_to action: :index
end
end
.
.
.
private
def submit_params
params.require(:entries).permit( :full_name,:email,:opt_in )
end
def set_client
#client = Client.find(params[:id])
end
end
form
<%= simple_form_for(:client, url: {:controller => 'clients', :action => 'create'}) do |f| %>
<%= f.input :full_name %>
<%= f.input :email %>
<%= f.input :opt_in %>
<%= f.button :submit, class: "btn-primary" %>
<% end %>
Routes:
Rails.application.routes.draw do
resources :clients do
resources :entries do
resources :extra_data
end
end
root 'clients#index'
end
In the Database Client data goes in with out a problem. I am having a problem getting the data from the form itself.
This answer is the culmination of a few different parts.
I figured out I was not saving any data into the model. So I needed to make another if statement.
def create
#client = Client.new(CLEINT_ATTR)
if #client.save
#entries = Entry.new(submit_params)
if #entries.save
flash[:alert] = "Failure! everything is working."
redirect_to action: :index
else
flash[:alert] = "Success! at failing."
end
else
flash[:alert] = "you failed at life for today."
redirect_to action: :thanks
end
end
Also changing the form from :entries Helped. I also had a typo in my permit statment. I had :opt_in when I needed to use :optin Thanks #tmc

Undefined method for nil:NilClass. Can't access to user's name or any other data when calling from another controller in ROR

I'm getting a "undefined method `name' for nil:NilClass" when I'm trying to get the users name of a comment from the articles controller. I've tryed to do the same request from the comments controller and it works just fine.
NoMethodError in Articles#show
Showing C:/xampp/htdocs/app/views/comments/_show.html.erb where line #2 raised:
undefined method `name' for nil:NilClass
I' using ruby -v 2.1.5 and rails -v 4.2.2
Here is my comments controller code:
class CommentsController < ApplicationController
before_filter :logged_in_user, only: [:create, :destroy]
def show
#comments= Comment.all
end
def new
#comment = Comment.new
end
def create
#article = Article.find(params[:article_id])
#comment = #article.comments.create(comment_params)
#comment.user_id = current_user.id
if #comment.save
flash[:success] = "Comment created!"
redirect_to article_path(#article)
end
end
private
def comment_params
params.require(:comment).permit(:body)
end
end
And my Articles controller code
class ArticlesController < ApplicationController
before_action :logged_in_user, only: [:create, :destroy]
before_action :correct_user, only: [:edit, :update, :destroy]
def show
#article = Article.find(params[:id])
#user = #article.user
#comments = #article.comments
end
...
private
def article_params
params.require(:article).permit(:title, :lead, :content, :hashtags, :front_picture, :category_id, :league_id, :front_pic_src)
end
end
And the views:
/articles => show
<!-- A little peace of my Articles view where I use "user" but under the #article -->
<h3> Article Autor</h3>
<div id="about-info">
<div class="post-profile-img-wrapper">
<%= link_to gravatar_for(#article.user, size: 50), #article.user %>
</div>
<span class="user"><%= link_to #article.user.name, #article.user %> </span>
</div>
<!-- And here I call the render for comments-->
<section class="comments">
<h2>Comments</h2>
<%= render 'comments/show' %>
</section>
And the rendered partial is _show.html.erb inside the comments view
<% #comments.each do|c| %>
<h1><%= c.user.name %></h1>
<% end %>
What am I doing wrong? I've tryed to create an instance variable in articles for user comments and it doesn't work.
The comments table has a foreing key but it's pointing to the article_id I don't know if this is the best way, but it shouldn't affect the controllers behaviour, right?
UPDATE
Here is my comments schema, it may help
create_table "comments", force: :cascade do |t|
t.text "body", limit: 65535
t.integer "article_id", limit: 4
t.integer "user_id", limit: 4
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "comments", ["article_id"], name: "index_comments_on_article_id", using: :btree
add_index "comments", ["user_id"], name: "index_comments_on_user_id", using: :btree
UPDATE FOR Models
Comment:
class Comment < ActiveRecord::Base
belongs_to :user
belongs_to :article
default_scope -> { order(created_at: :desc) }
validates :body, presence: true
end
Article:
class Article < ActiveRecord::Base
belongs_to :category
belongs_to :user
belongs_to :league
has_many :comments, :dependent => :destroy
default_scope -> { order(created_at: :desc) }
mount_uploader :front_picture, ArticleFrontUploader
validates :user_id, presence: true
validates :category_id, presence: true
validates :content, presence: true
#validate :picture_size
private
# Validates the size of an uploaded picture.
def picture_size
if front_picture.size > 5.megabytes
errors.add(:front_picture, "should be less than 5MB")
end
end
end
Related peace of user
class User < ActiveRecord::Base
has_many :microposts, dependent: :destroy
has_many :articles, dependent: :destroy
has_many :comments, dependent: :destroy
...
Thanks!
STEP ONE: Setting up a partial
You can try naming local variables when rendering a partial. For example, in /articles/_show.html.erb, you would enter the following at the (current) rendering line:
<%= render partial: 'comments/show', locals: {comments: #comments} %>
That will treat the /comments/_show.html.erb as a piece of a page, rather than a complete one. This is ideal for your situation, since the page itself is about articles, but you only want to render a part of the page to show comments.
The locals hash sets up variables so your partial can render the correct objects, assuming they are named in the current controller. (In this case, the current controller is ArticlesController.) After setting local values, your next step in the comments show page is to replace #comments with simply comments.
Now, say you have no comments associated with the article. It is the same as Comment.where(article_id: #article.id), which is an array. Calling each on an empty array will take the first element, pretty much nil, and raise an error. In the app I work on, the best way to handle this is to prepend the code block with the following:
unless comments.empty?
This makes sense, because why show contents for comments if there are none present? Mind you, if you plan to have a form for entering comments, place that form outside the unless statement, and below the comments show partial.
STEP TWO: Editing associations
I do not know how you want to set this up exactly, so rather than provide copy/paste code, I will just explain. In most cases, comments do not stand alone, they always belong to something. A comment immediately belongs to an article. In your models, your associations should have a user having a comment through an article. Given the code you provided, I believe the hierarcy is the following:
User
Article
Comment
You need a user to make an article. You need an article to make a comment. While a user is required to make a comment, it is not a direct association. Again, I will not provide you with the exact code, as you may want to handle this slightly differently, but along the same lines.
The mistake is in the _show.html.erb in the comments view. The comment has only a user_id attribute. So if you call #comment.user there is nothing to show since the comment doesn't have user attribute. You have to search for the user by the user_id
<% #comments.each do |c| %>
<h1><%= User.find_by_id(c.user_id).name %></h1>
<% end %>
Try this. It would solve the problem.
Pass the locals to partial
<%= render partial: 'comments/show', locals: { comments: #comments } %>
comments/_show.html.erb
<% comments.each do|c| %>
<h1><%= c.try(:user).try(:name) %></h1>
<% end %>

How to make a path to a paginated url?

When user #1 likes/comments on user #2, user #2 gets a notification:
notifications/_notifications.html.erb
<%= link_to "", notification_path(notification.id), method: :delete, class: "glyphicon glyphicon-remove" %> <%= link_to Comment.find_by(notification.comment_id).user.name, user_path(Comment.find_by(notification.comment_id).user.id) %> commented on <%= link_to "your activity", (notification.activity_id) %>
but upon user #2 clicking the notification it doesn't lead anywhere since I removed the activity_path, if I put it back before (notification.activity_id) we get an error:
No route matches
{:action=>"show", :controller=>"activities", > :id=>nil} missing required keys: [:id]
I don't know if this is possible, but upon clicking on the notification user #2 would be taken to the paginated page where the activity is. There are 20 activities per page via the gem will_paginate so if the activity that was commented on is on page 2 then upon clicking on the activity, user #2 should be directed to: http://0.0.0.0:3000/activities?page=2
This would at least narrow down where the comment is on the activity feed for the user.
activities/index.html.erb
<% #activities.each do |activity| %>
<%= link_to activity.user.name, activity.user %>
<%= render "activities/#{activity.trackable_type.underscore}/#{activity.action}", activity: activity %>
<% activity.activity_likers.each do |user| %>
<%= user.name %>
<% end %>
<%= link_to like_activity_path(:id => activity.id), class: "btn", method: :post do %>
<span class='glyphicon glyphicon-thumbs-up'></span> Like
<% end %>
<%= render "comments/comments", comments: activity.comments %>
<%= render "comments/form", new_comment: Comment.new(commentable_id: activity.id, commentable_type: activity.class.model_name), create_url: :activity_comments_path %>
<% end %>
<%= will_paginate #activities %>
activities_controller.rb
class ActivitiesController < ApplicationController
def index
#activities = Activity.order("created_at desc").paginate(:page => params[:page], :per_page => 20)
end
def show
redirect_to(:back)
end
end
Please let me know if you need anymore code if you find that this is possible to do :)
UPDATE
class NotificationsController < ApplicationController
def index
#notifications = current_user.notifications
#notifications.each do |notification|
notification.update_attribute(:read, true)
activity = Activity.find(notification.activity_id) #Gives "Couldn't find Activity with 'id'=". I then removed ".activity_id" to see what happens next.
index = Activity.order(created_at: :desc).index(activity)
page_number = (index / per_page.to_f).ceil #I am then told "undefined local variable or method `per_page'" so I tried making it :per_page to which I then get: "undefined method `to_f' for :per_page:Symbol"
end
end
def destroy
#notification = Notification.find(params[:id])
#notification.destroy
redirect_to :back
end
end
routes.rb
resources :activities do
resources :comments
resources :notifications
member do
post :like
post :notifications
end
end
UPDATE #2
notifications/index.html.erb
<% if !#notifications.blank? %>
<%= render partial: "notifications/notification", collection: #notifications %>
<% else %>
<p>No notifications... yet</p>
<% end %>
comment.rb
class Comment < ActiveRecord::Base
after_create :create_notification
has_many :notifications
has_many :comment_likes
has_many :likers, through: :comment_likes, class_name: 'User', source: :liker
belongs_to :commentable, polymorphic: true
belongs_to :user
belongs_to :activity
private
def create_notification
#activity = Activity.find_by(self.activity)
#user = User.find_by(#activity.user_id).id
Notification.create(
activity_id: self.activity,
user_id: #user,
comment_id: self,
read: false
)
end
end
_create_notifications.rb
class CreateNotifications < ActiveRecord::Migration
def change
create_table :notifications do |t|
t.references :activity, index: true
t.references :comment, index: true
t.references :user, index: true
t.boolean :read
t.timestamps null: false
end
add_foreign_key :notifications, :activities
add_foreign_key :notifications, :comments
add_foreign_key :notifications, :users
end
end
To determine on which page the activity is located, you could do the following:
activity = Activity.find(notification.activity_id)
index = Activity.order(created_at: :desc).index(activity)
The above will determine the index of the activity within all of the activities.
So, let's say you've got 85 activities. You've got 20 activities per page, so in this case you would have 5 pages, right? Alright, let's assume the above index returns 42. To calculate the page number you would have to do this (assuming that you've got a variable called per_page which is 20):
page_number = (index / per_page.to_f).ceil
You've got index 42. Which you'll have to divide by the number of activities per page (it needs to be a float!), so that would be 20.0. That results in 2.1. ceil that and you've got your page number, which would be 3.
So, to create the correct paginated path, you can now do this:
activities_path(page: page_number)
Update
Now that we know that the activity_id isn't correctly set on the notification we can fix that (also note that the comment_id isn't set either). Change the last part of your Comment model to this:
...
belongs_to :activity
validates :activity_id, presence: true
validates :user_id, presence: true
private
def create_notification
Notification.create(activity_id: self.activity_id, user_id: self.user_id, comment_id: self.id, read: false)
end
I've added two validations here to make sure the activity_id and user_id are set. Now as I said before the comment_id isn't set either. That's because the id is only assigned on save, on create you are just setting it up to be saved. So, change the after_create :create_notification to after_save :create_notification to be able to set the comment_id as well.
That should set the activity_id and comment_id. Now for the next part. Getting the correct page number which should be added to the link in your _notification.html.erb partial.
Add these methods to your Activity class:
def page_number
(index / per_page.to_f).ceil
end
private
def index
Activity.order(created_at: :desc).index self
end
Now change the path in your notifications/_notification.html.erb partial to:
activities_path(page: notification.activity.page_number)
Note: If you get an error about the per_page in the page_number method you probably haven't set the value per_page in the model itself (like this; Basically add self.per_page = 20 to your model right below class Activity < ActiveRecord::Base)
If you decide to do it like this, you can remove the , :per_page => 20 part in your ActivitiesController. If not, simply replace per_page.to_f with 20.to_f or 20.0.
Also, remove the 3 lines from your NotificationsController which you've commented out previously to find out whether the activity_id was set or not. You don't need them anymore, since we've placed them in the Activity class.

NameError in Model - search form

I am working on a search with Rails 4 - learning from a Rails cast (which is about version prior 3, apparently).
However, I get the following error:
NameError in GamesController#index undefined local variable or method
`search' for #
This is my code
view:
<%= form_tag games_path, :method => 'get', :id => "games_search" do %>
<%= text_field_tag :search, params[:search] %>
<%= submit_tag "Search", :name => nil %>
<% end %>
controller:
def search
#games = Game.search(params[:search])
end
model:
self.search(search)
if search
where('title LIKE ?', "%#{search}")
else
scoped
end
end
migration:
def change
create_table :games do |t|
t.string :title
t.date :releaseDate
t.text :description
t.integer :rating
t.timestamps
end
end
routes:
resources :games
What am I doing wrong?
Class Method
I remember using that Railscast to help me create a search facility
What you're looking at is what's known as a class method - a method which initiates a class, and creates a response for you.
Here's how a class method should work in Rails (also consider each "model" is a class):
#app/models/game.rb
Class Game < ActiveRecord::Base
def self.search(query)
... logic here
end
end
I believe the problem you have is that you haven't declared def for your method, although I don't know for sure as you haven't posted your entire model code
The model missed 'def'
**def** self.search(search)
if search
where('title LIKE ?', "%#{search}")
else
scoped
end
end
I should be awarded the n00b badge. Sorry!

Resources