Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

My tests are passing. But, undefined method `reviews' for nil:NilClass when calling the page

Good afternoon people

I'm trying to build a tobacco review application, for no other reason than to learn some more stuff. I've been trying to build upon what I’ve learned in the Rails Tutorial book, however I keep coming up against brick walls and gotchas that I don't understand. I can normally get over the undefined method `something' for nil:NilClass and other issues i have, but this one's really get me stumped because I’ve got my tests passing.

My Tests

review_test.rb

require 'test_helper'

class ReviewTest < ActiveSupport::TestCase

# Define a user for the test
def setup
@user = users(:shaun)
@review = @user.reviews.build( rating: 5, 
                               comment:"This is a super snus",
                               tobacco_id: 1)
end

# Test should be valid
test "should be valid" do
    assert @review.valid?
end

# Test the user id is present
test "user id should be present" do
    @review.user_id = nil
    assert_not @review.valid?
end

# Test the tobacco id is present
test "tobacco id should be present"  do 
    @review.tobacco_id = nil
    assert_not @review.valid?
end

# Rating should be present
test "rating should be present" do
    @review.rating = "   "
    assert_not @review.valid?
end

# Comment should be present
test "comment should be present" do 
    @review.comment = "    "
    assert_not @review.valid?
end

# Verify that the first review in the DB is the same as fixture review
test "order should be most recent first" do
    assert_equal reviews(:most_recent), Review.first
end

end

user_test.rb

# Test that a review is destroyed when a user is destroyed
test "associated reviews should be destroyed" do 
@user.save
@user.reviews.create!( rating: 5,
                       comment: "I love this suns, it's so good!",
                       tobacco_id: 1)
assert_difference 'Review.count', -1 do 
    @user.destroy
end
end

Fixtures

reviews.yml

one:
rating: 5
tobacco_id: 1
comment: "This is a super snus"
created_at: <%= 3.hours.ago %>

two:
rating: 5
tobacco_id: 1
comment: "This is the best snus in the world"
created_at: <%= 2.hours.ago %>

three:
rating: 5
tobacco_id: 1
comment: "I will always buy this snus"
created_at: <%= 1.hours.ago %>

 most_recent:
 rating: 5
 tobacco_id: 1
 comment: "I have a shed load of this snus, it's great!"
 created_at: <%= Time.zone.now %>

Models

review.rb

belongs_to :tobacco

tobacco.rb

has_many :reviews

user.rb

has_many :tobaccos, dependent: :destroy
has_many :reviews, dependent: :destroy

Controller reviews_controller.rb

def new
@review = Review.new
end

This is what i have for the create action in the controller but i can't tell if i have this correct because i can't get the page to load yet. Any suggestions on the create action would be greatly appreciated to save asking another question if I have this wrong, which I probably have!

def create
    @tobacco = Tobacco.find(params[:tobacco_id])
    @review = @tobacco.reviews.new(params[:review].permit(:tobacco_id, :comment))
    @review.user = User.find(current_user.id)
    @review.save
    redirect_to tobacco_path(@tobacco)
end

Routes

resources :tobaccos do 
resources :reviews, except: [:show, :index]
end

show.html.erb

<%= link_to "Write a Review", new_tobacco_review_path(@tobacco) %>

new.html.erb

<%= form_for([@tobacco, @tobacco.reviews.build]) do |f| %>
<%= render 'fields', f: f %>
<%= f.submit "Save Review", class: 'btn btn-primary' %>
<% end %>

When I get to the page to write a review, i get the error

Showing C:/Sites/apps/review/app/views/reviews/new.html.erb where line #11 raised:

undefined method `reviews' for nil:NilClass

It's telling me it's line 11

<%= form_for([@tobacco, @tobacco.reviews.build]) do |f| %>

but whatever I try I get a different error. I know it's a rookie mistake but i can't see it.

Thanks in advance

Shaun

UPDATE

I've got it working and saving to the database now, i did it like this In the reviews_controller.rb the new and create look like this

def new
    @tobacco = Tobacco.find(params[:tobacco_id])
end


def create

@tobacco = Tobacco.find(params[:tobacco_id])
@review = Review.new(review_params)
@review.user_id = current_user.id
@review.tobacco_id = @tobacco.id

if @review.save
  redirect_to @tobacco
else
  render 'new'
end
end

The review_params are

def review_params
    params.require(:review).permit(:rating, :comment, :tobacco_id)
end

That all looks right to me, and it works so i hope it's all ok.

What i'm stuck on now is editing a review that belongs to a tobacco. This is the first time i've worked on nested routes. I just looked at a couple tutorials but nothing has really helped me to fix this. I am not sure how can I pass these keys...

On the show page i've got this

<%= link_to "Edit", edit_tobacco_review_path(@tobacco, @review) %>

The show method tobaccos_controller.rb looks like this

def show
    @tobacco = Tobacco.find(params[:id])
    @reviews = Review.where(tobacco_id: @tobacco.id)
end

In the reviews_controller.rb i've this

def edit
    @tobacco = Tobacco.find(params[:tobacco_id])
    @review  = Review.find(params[:id])
end

And the error i'm getting is this

ActionController::UrlGenerationError in Tobaccos#show
No route matches {:action=>"edit", :controller=>"reviews", :id=>nil, :tobacco_id=>"8"} missing required keys: [:id]

Looking at this i should be doing something in the Tobaccos#show action, but i can't think what.

I'm almost home and dry, can you see what i've done wrong? Thanks :)

UPDATE NUMBER TWO show.html.erb It's not the whole show page, but it's the important part

<% if @reviews.blank? %>
    <h3>No reviews yet! Would you like to be the first?</h3>
    <%= link_to "Write Review", new_tobacco_review_path(@tobacco), class: "btn btn-danger" %>
<% else %>
    <% @reviews.each do |review| %>
 <div class="reviews">
    <p><%= review.comment %></p>
    <p><%= review.user.name %></p>
    <%= link_to "Edit", edit_tobacco_review_path(@tobacco, @review) %>
</div>
   <% end %>
<%= link_to "Write Another Review", new_tobacco_review_path(@tobacco), class: "btn btn-danger" %>
<% end %>

UPDATE NUMBER 3 edit.html.erb

<%= form_for([@tobacco, @tobacco.reviews.build]) do |f| %>
<%= render 'fields', f: f %>
<%= f.submit "Update Review", class: 'btn btn-primary' %>
<% end %>
<%= link_to "Back", tobacco_path(@tobacco), 
class: "btn btn-default btn-block margin-top-25" %>

_fields.html.erb

<%= f.label  :rating %>
<%= f.select :rating, [['1', 1], 
                   ['2', 2], 
                   ['3', 3], 
                   ['4', 4], 
                   ['5', 5]
                  ] ,class: 'form-control' %>

<%= f.label :comment %>
<%= f.text_field :comment, class: 'form-control' %>
like image 472
Shaun Avatar asked Oct 31 '22 22:10

Shaun


1 Answers

ActionController::UrlGenerationError in Tobaccos#show No route matches {:action=>"edit", :controller=>"reviews", :id=>nil, :tobacco_id=>"8"} missing required keys: [:id

You are iterating @reviews as review, so

this line

<%= link_to "Edit", edit_tobacco_review_path(@tobacco, @review) %>

should be

<%= link_to "Edit", edit_tobacco_review_path(@tobacco, review) %>
like image 173
Pavan Avatar answered Nov 15 '22 06:11

Pavan