Why do i get this error (NoMethodError) - ruby-on-rails

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.

Related

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

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|

how to set id from independent page using dropdown list ? or how can i save the belongs_to, has_many value?

I'm beginner in ruby on rails. I want to create Brand - Product list. I would like to add product names and brand name (should be selected in dropdown list) from an independent page. Every product has a name and a brand Id. ı will set the brand name which is selected from drop down list to a variable. after that, ı will add the product name the user have written in independent page and the brand Id to product table.
routes.rb
Rails.application.routes.draw do
get 'welcome/index'
get 'add_product/index'
# For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
resources :brands do
resources :products
end
root 'welcome#index'
end
.._create_brands.rb
class CreateBrands < ActiveRecord::Migration[6.0]
def change
create_table :brands do |t|
t.string :title
t.timestamps
end
end
end
.._create_products.rb
class CreateProducts < ActiveRecord::Migration[6.0]
def change
create_table :products do |t|
t.string :name
t.references :brand, null: false, foreign_key: true
t.timestamps
end
end
end
brand.rb
class Brand < ApplicationRecord
has_many :products, dependent: :destroy
validates :title, presence: true,
length: { minimum: 2 }
end
product.rb
class Product < ApplicationRecord
belongs_to :brand
end
add_product/index.html.erb (add_product has view and controller)
<h1>Add a new product</h1>
<%= form_with(model: #product) do |f| %>
<p>
<%= f.label :name,"Product name: " %><br>
<%= f.text_field :name %>
</p>
<%= f.collection_select(:brand_id, Brand.all, :id, :title) %>
<p>
<%= f.submit "Add a product" %>
</p>
<% end %>
add_product_controller.rb
class AddProductController < ApplicationController
def index
end
def new
#product = Product.new
end
def create
#product = Product.new(product_params)
if #product.save
redirect_to #product
else
render :new
end
end
private
def product_params
params.require(:product).permit(:name,:brand_id)
end
end
when i run the rails routes in console,
Prefix Verb URI Pattern Controller#Action
welcome_index GET /welcome/index(.:format) welcome#index
add_product_index GET /add_product/index(.:format) add_product#index
brand_products GET /brands/:brand_id/products(.:format) products#index
POST /brands/:brand_id/products(.:format) products#create
new_brand_product GET /brands/:brand_id/products/new(.:format) products#new
edit_brand_product GET /brands/:brand_id/products/:id/edit(.:format) products#edit
brand_product GET /brands/:brand_id/products/:id(.:format) products#show
PATCH /brands/:brand_id/products/:id(.:format) products#update
PUT /brands/:brand_id/products/:id(.:format) products#update
DELETE /brands/:brand_id/products/:id(.:format) products#destroy
brands GET /brands(.:format) brands#index
POST /brands(.:format) brands#create
new_brand GET /brands/new(.:format) brands#new
edit_brand GET /brands/:id/edit(.:format) brands#edit
brand GET /brands/:id(.:format) brands#show
PATCH /brands/:id(.:format) brands#update
PUT /brands/:id(.:format) brands#update
DELETE /brands/:id(.:format) brands#destroy
root GET / welcome#index
independent page picture
if i go to brans and clicked the show i can add product with only name. but i want to add a product from independent page. so how can i do it? Can i set brand_id value from dropdownlist? than can i save product name and brand id?

Show contents of table but limit to those matching signed-in user

In my quiz game Rails project, I have a table for "Participations" that stores information on the user, the quiz category, and the quiz score after a user completes the test.
class CreateParticipations < ActiveRecord::Migration
def change
create_table :participations do |t|
t.references :user
t.string :category
t.boolean :finished, default: false
t.integer :current_question_index, default: 0
t.integer :score, default: 0
t.timestamps
end
end
end
In my user.rb, I specify an association that a user has_many :participations, which allows a user to play multiple quizzes while storing categories/scores in the table.
If I want to show a user a table of his results (so return all Participations results, but only for those that match the user) in a view, can I call that without generating a new controller?
You can just do like below
#in the controller action
#user_participations = Participation.where(user_id: current_user.id)
and just call #user_participations in the view.
#config/routes.rb
resources :users do
resources :participations, path: "results", only: :index #-> url.com/users/:user_id/results
end
#app/controllers/participations_controller.rb
class ParticipationsController < ApplicationController
def index
#user = User.find params[:user_id]
#participations = #user.participations
end
end
#app/views/participations/index.html.erb
<% #participations.each do |participation| %>
<%= participation.score %>
<% end %>
--
If you have your associations set up correctly, you should be using the associative method (#user.participations), which will basically do what Pavan has suggested:
#app/models/user.rb
class User < ActiveRecord::Base
has_many :participations
end
#app/models/participation.rb
class Participation < ActiveRecord::Base
belongs_to :user
end
Users controller
has_many :participations
def participations
#user = User.find(params[:id])
#participations = #users.participations.build(params[:participation_id])
end
end
ParticipationsController
belongs_to :user
In your routes file you can create the route by
GET 'users/:id/participations', to: 'users#participations', as: 'user_participations'
That will give you a user_participations_path route
So if you wanted to link to it you could add
<%= link_to 'Show user games!', user_participations_path(#participations.user) %>
Then in views/users/participations
<% #participations.each do |participation| %>
<%= participation.inspect %>
<% end %>
Let me know how you go!
EDIT
Please not that the has_many and belongs_to declarations should be in the user.rb and participation.rb models respectively. Thanks to #richpeck for picking up the mistake.

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 %>

Ruby on Rails - Associating a service with a shop. How to integrate this relationship in the 'create' method

I'm creating an app where I have a 'Shop' model, which is associated with a 'Service' model (For example, a shop might offer different services which can be added by the shop owner only - such as an oil change, new tires, window tinting etc.)
I believe I have the relationship sorted:
class Service < ActiveRecord::Base
belongs_to :shop
end
and
class Shop < ActiveRecord::Base
belongs_to :user
has_many :services
end
However I'm not sure how to add this association in the 'Service' create method so that 'service' belongs only to 'shop'
I'd also like to display all related services on the Shop 'show' page.
I'm in the 'Show' view of Shop, and I want to add a service, how can I pass the shop_id into the service and what files would I have to change?
The create method is just the bog standard scaffold one:
def create
#service = Service.new(service_params)
#service.save
respond_with(#service)
end
And the database looks like this:
Shops:
class CreateShops < ActiveRecord::Migration
def change
create_table :shops do |t|
t.string :name
t.text :description
t.string :phone
t.string :email
t.string :website
t.integer :user_id
t.timestamps
end
end
end
and Services:
class CreateServices < ActiveRecord::Migration
def change
create_table :services do |t|
t.string :category
t.integer :shop_id
t.timestamps
end
end
end
Any help would be very appreciated. I guess i'm just not getting how to pass the relationship information correctly.
Cheers!
First you will need a set of nested routes for shops & services:
# config/routes.rb
resources :shops do
resources :services
end
This creates routes for services nested under shop:
Prefix Verb URI Pattern Controller#Action
shop_services GET /shops/:shop_id/services(.:format) services#index
POST /shops/:shop_id/services(.:format) services#create
new_shop_service GET /shops/:shop_id/services/new(.:format) services#new
edit_shop_service GET /shops/:shop_id/services/:id/edit(.:format) services#edit
shop_service GET /shops/:shop_id/services/:id(.:format) services#show
PATCH /shops/:shop_id/services/:id(.:format) services#update
PUT /shops/:shop_id/services/:id(.:format) services#update
DELETE /shops/:shop_id/services/:id(.:format) services#destroy
shops GET /shops(.:format) shops#index
POST /shops(.:format) shops#create
new_shop GET /shops/new(.:format) shops#new
edit_shop GET /shops/:id/edit(.:format) shops#edit
shop GET /shops/:id(.:format) shops#show
PATCH /shops/:id(.:format) shops#update
PUT /shops/:id(.:format) shops#update
DELETE /shops/:id(.:format) shops#destroy
If look at the parameters for the services routes you can see that there is a :shop_id parameter.
We can use this in our ServicesController to set the shop before each action:
class ServicesController < ApplicationController
before_action :set_shop
# GET /shops/:shop_id/services/new
def new
#service = #shop.services.new
end
# POST /shops/:shop_id/services
def create
#service = #shop.services.new(service_params)
# ... #todo validate and save
end
private
def service_params
params.require(:service).permit(:category)
end
def set_shop
#shop = Shop.find(params[:shop_id])
end
end
In your views you need to change your forms somewhat so that form_for uses the correct path.
<!-- views/services/new.html.erb -->
<h1>Add a service to <%= #service.shop.name %></h1>
<%= form_for([#service.shop, #service]) do |f| %>
<div>
<%= f.label :category %>
<%= f.text_field :category %>
</div>
<%= f.submit %>
<% end %>

Resources