1

Background

I am following an outdated beginner's Ruby course and got stuck. During this course, I am building a Ruby on rails application similar to Yelp, so it is for listing and reviewing restaurants. Currently, I am linking the reviews to the restaurants. My ruby version is: ruby 3.0.0p0 (2020-12-25 revision 95aff21468) [x86_64-darwin22] My rails version is: Rails 7.0.4.2

Steps

I am linking the reviews to the restaurants by executing the following steps:

  • Run rails generate migration AddRestaurantIDToReviews restaurant_id:integer
  • Run rake db:migrate
  • Add belongs_to :restaurant to reviews.rb
  • Add has_many :reviews to restaurants.rb
  • Check rails routes
  • Adjusted routes.rb file
  • Adjusted reviews_form.html.erb
  • Adjusted review_controller.rb file
  • set_restaurant and set_restaurant before action
  • Add @review statement for restaurant_id (line …)
  • Removed links that point to old URLs
  • Restart server

Error message

This resulted in the following error: undefined method `reviews_path' for #ActionView::Base:0x00000000025800 and this is triggered by this line of code: <%= form_with(model: [@resturant, @review], local: true) do |form| %> (from the reviews _form.html.erb file). In this thread a similar issue is explained, but I still can't figure it out.

Could someone help me with this? And if you need more info, please let me know!

Review.rb

class Review < ApplicationRecord
    belongs_to :user
    belongs_to :restaurant
end

Restaurant.rb

class Restaurant < ApplicationRecord
    mount_uploader :image, ImageUploader
    has_many :reviews
end

Routes.rb

Rails.application.routes.draw do
  devise_for :users
  resources :restaurants do 
    resources :reviews, except: [:show, :index] 
  end
  get 'pages/about'
  get 'pages/contact'
  root 'restaurants#index'
  # Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html

  # Defines the root path route ("/")
  # root "articles#index"
end

**Routes**
` Prefix Verb   URI Pattern                                                                                       Controller#Action
                        new_user_session GET    /users/sign_in(.:format)                                                                          devise/sessions#new
                            user_session POST   /users/sign_in(.:format)                                                                          devise/sessions#create
                    destroy_user_session DELETE /users/sign_out(.:format)                                                                         devise/sessions#destroy
                       new_user_password GET    /users/password/new(.:format)                                                                     devise/passwords#new
                      edit_user_password GET    /users/password/edit(.:format)                                                                    devise/passwords#edit
                           user_password PATCH  /users/password(.:format)                                                                         devise/passwords#update
                                         PUT    /users/password(.:format)                                                                         devise/passwords#update
                                         POST   /users/password(.:format)                                                                         devise/passwords#create
                cancel_user_registration GET    /users/cancel(.:format)                                                                           devise/registrations#cancel
                   new_user_registration GET    /users/sign_up(.:format)                                                                          devise/registrations#new
                  edit_user_registration GET    /users/edit(.:format)                                                                             devise/registrations#edit
                       user_registration PATCH  /users(.:format)                                                                                  devise/registrations#update
                                         PUT    /users(.:format)                                                                                  devise/registrations#update
                                         DELETE /users(.:format)                                                                                  devise/registrations#destroy
                                         POST   /users(.:format)                                                                                  devise/registrations#create
                      restaurant_reviews POST   /restaurants/:restaurant_id/reviews(.:format)                                                     reviews#create
                   new_restaurant_review GET    /restaurants/:restaurant_id/reviews/new(.:format)                                                 reviews#new
                  edit_restaurant_review GET    /restaurants/:restaurant_id/reviews/:id/edit(.:format)                                            reviews#edit
                       restaurant_review PATCH  /restaurants/:restaurant_id/reviews/:id(.:format)                                                 reviews#update
                                         PUT    /restaurants/:restaurant_id/reviews/:id(.:format)                                                 reviews#update
                                         DELETE /restaurants/:restaurant_id/reviews/:id(.:format)                                                 reviews#destroy
                             restaurants GET    /restaurants(.:format)                                                                            restaurants#index
                                         POST   /restaurants(.:format)                                                                            restaurants#create
                          new_restaurant GET    /restaurants/new(.:format)                                                                        restaurants#new
                         edit_restaurant GET    /restaurants/:id/edit(.:format)                                                                   restaurants#edit
                              restaurant GET    /restaurants/:id(.:format)                                                                        restaurants#show
                                         PATCH  /restaurants/:id(.:format)                                                                        restaurants#update
                                         PUT    /restaurants/:id(.:format)                                                                        restaurants#update
                                         DELETE /restaurants/:id(.:format)                                                                        restaurants#destroy
                             pages_about GET    /pages/about(.:format)                                                                            pages#about
                           pages_contact GET    /pages/contact(.:format)                                                                          pages#contact
                                    root GET    /                                                                                                 restaurants#index
        turbo_recede_historical_location GET    /recede_historical_location(.:format)                                                             turbo/native/navigation#recede
        turbo_resume_historical_location GET    /resume_historical_location(.:format)                                                             turbo/native/navigation#resume
       turbo_refresh_historical_location GET    /refresh_historical_location(.:format)                                                            turbo/native/navigation#refresh
           rails_postmark_inbound_emails POST   /rails/action_mailbox/postmark/inbound_emails(.:format)                                           action_mailbox/ingresses/postmark/inbound_emails#create
              rails_relay_inbound_emails POST   /rails/action_mailbox/relay/inbound_emails(.:format)                                              action_mailbox/ingresses/relay/inbound_emails#create
           rails_sendgrid_inbound_emails POST   /rails/action_mailbox/sendgrid/inbound_emails(.:format)                                           action_mailbox/ingresses/sendgrid/inbound_emails#create
     rails_mandrill_inbound_health_check GET    /rails/action_mailbox/mandrill/inbound_emails(.:format)                                           action_mailbox/ingresses/mandrill/inbound_emails#health_check
           rails_mandrill_inbound_emails POST   /rails/action_mailbox/mandrill/inbound_emails(.:format)                                           action_mailbox/ingresses/mandrill/inbound_emails#create
            rails_mailgun_inbound_emails POST   /rails/action_mailbox/mailgun/inbound_emails/mime(.:format)                                       action_mailbox/ingresses/mailgun/inbound_emails#create
          rails_conductor_inbound_emails GET    /rails/conductor/action_mailbox/inbound_emails(.:format)                                          rails/conductor/action_mailbox/inbound_emails#index
                                         POST   /rails/conductor/action_mailbox/inbound_emails(.:format)                                          rails/conductor/action_mailbox/inbound_emails#create
       new_rails_conductor_inbound_email GET    /rails/conductor/action_mailbox/inbound_emails/new(.:format)                                      rails/conductor/action_mailbox/inbound_emails#new
      edit_rails_conductor_inbound_email GET    /rails/conductor/action_mailbox/inbound_emails/:id/edit(.:format)                                 rails/conductor/action_mailbox/inbound_emails#edit
           rails_conductor_inbound_email GET    /rails/conductor/action_mailbox/inbound_emails/:id(.:format)                                      rails/conductor/action_mailbox/inbound_emails#show
                                         PATCH  /rails/conductor/action_mailbox/inbound_emails/:id(.:format)                                      rails/conductor/action_mailbox/inbound_emails#update
                                         PUT    /rails/conductor/action_mailbox/inbound_emails/:id(.:format)                                      rails/conductor/action_mailbox/inbound_emails#update
                                         DELETE /rails/conductor/action_mailbox/inbound_emails/:id(.:format)                                      rails/conductor/action_mailbox/inbound_emails#destroy
new_rails_conductor_inbound_email_source GET    /rails/conductor/action_mailbox/inbound_emails/sources/new(.:format)                              rails/conductor/action_mailbox/inbound_emails/sources#new
   rails_conductor_inbound_email_sources POST   /rails/conductor/action_mailbox/inbound_emails/sources(.:format)                                  rails/conductor/action_mailbox/inbound_emails/sources#create
   rails_conductor_inbound_email_reroute POST   /rails/conductor/action_mailbox/:inbound_email_id/reroute(.:format)                               rails/conductor/action_mailbox/reroutes#create
rails_conductor_inbound_email_incinerate POST   /rails/conductor/action_mailbox/:inbound_email_id/incinerate(.:format)                            rails/conductor/action_mailbox/incinerates#create
                      rails_service_blob GET    /rails/active_storage/blobs/redirect/:signed_id/*filename(.:format)                               active_storage/blobs/redirect#show
                rails_service_blob_proxy GET    /rails/active_storage/blobs/proxy/:signed_id/*filename(.:format)                                  active_storage/blobs/proxy#show
                                         GET    /rails/active_storage/blobs/:signed_id/*filename(.:format)                                        active_storage/blobs/redirect#show
               rails_blob_representation GET    /rails/active_storage/representations/redirect/:signed_blob_id/:variation_key/*filename(.:format) active_storage/representations/redirect#show
         rails_blob_representation_proxy GET    /rails/active_storage/representations/proxy/:signed_blob_id/:variation_key/*filename(.:format)    active_storage/representations/proxy#show
                                         GET    /rails/active_storage/representations/:signed_blob_id/:variation_key/*filename(.:format)          active_storage/representations/redirect#show
                      rails_disk_service GET    /rails/active_storage/disk/:encoded_key/*filename(.:format)                                       active_storage/disk#show
               update_rails_disk_service PUT    /rails/active_storage/disk/:encoded_token(.:format)                                               active_storage/disk#update
                    rails_direct_uploads POST   /rails/active_storage/direct_uploads(.:format)                                                    active_storage/direct_uploads#create`

Review _form.html.erb

<%= form_with(model: [@resturant, @review], local: true) do |form| %>
  <% if review.errors.any? %>
    <div style="color: red">
      <h2><%= pluralize(review.errors.count, "error") %> prohibited this review from being saved:</h2>

      <ul>
        <% review.errors.each do |error| %>
          <li><%= error.full_message %></li>
        <% end %>
      </ul>
    </div>
  <% end %>

  <div class="form-group">
    <%= form.label :rating, style: "display: block" %>
    <%= form.number_field :rating, class: "form-control" %>
  </div>

  <div class="form-group">
    <%= form.label :comment, style: "display: block" %>
    <%= form.text_area :comment, class: "form-control" %>
  </div>

  <div>
    <%= form.submit class: "btn btn-primary" %>
  </div>
<% end %>

Reviews_controller.rb

class ReviewsController < ApplicationController
  before_action :set_review, only: %i[ edit update destroy ]
  before_action :set_restaurant
  before_action :authenticate_user!

  # GET /reviews/new
  def new
    @review = Review.new
  end

  # GET /reviews/1/edit
  def edit
  end

  # POST /reviews or /reviews.json
  def create
    @review = Review.new(review_params)
    @review.user_id = current_user.id
    @review.restaurant_id = @restaurant.id

    respond_to do |format|
      if @review.save
        format.html { redirect_to root_path, notice: "Review was successfully created." }
        format.json { render :show, status: :created, location: @review }
      else
        format.html { render :new, status: :unprocessable_entity }
        format.json { render json: @review.errors, status: :unprocessable_entity }
      end
    end
  end

  # PATCH/PUT /reviews/1 or /reviews/1.json
  def update
    respond_to do |format|
      if @review.update(review_params)
        format.html { redirect_to review_url(@review), notice: "Review was successfully updated." }
        format.json { render :show, status: :ok, location: @review }
      else
        format.html { render :edit, status: :unprocessable_entity }
        format.json { render json: @review.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /reviews/1 or /reviews/1.json
  def destroy
    @review.destroy

    respond_to do |format|
      format.html { redirect_to reviews_url, notice: "Review was successfully destroyed." }
      format.json { head :no_content }
    end
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_review
      @review = Review.find(params[:id])
    end

    def set_restaurant
      @restaurant = Restaurant.find(params[:restaurant_id])
    end

    # Only allow a list of trusted parameters through.
    def review_params
      params.require(:review).permit(:rating, :comment)
    end
end

Stefan
  • 109,145
  • 14
  • 143
  • 218
jimW
  • 23
  • 5
  • Sounds very similar to this one: https://stackoverflow.com/questions/46919115/nested-resources-w-rails-5-1-form-with – can you check whether the [proposed solution](https://stackoverflow.com/a/46919792/477037) solves your problem? – Stefan Feb 09 '23 at 09:57
  • 2
    reviews are nested resource inside restaurants, so it should be `restaurant_reviews_path` – marmeladze Feb 09 '23 at 10:21
  • This is most likely a sneaky nil error. Resolving `[@resturant, @review]` into a path helper is done by the [polymorphic routing helpers](https://api.rubyonrails.org/v7.0.4/classes/ActionDispatch/Routing/PolymorphicRoutes.html) and when called with an array the array is compacted to remove nils. There is nothing in the code here which explains why `@resturant` could be nil so you'll have to do some debugging and set some breakpoints. – max Feb 09 '23 at 10:44
  • But @marmeladze is correct that you're using the wrong helper in the other places in your controller and you should be using `redirect_to [@resturant, @review], ...` or change to shallow nesting. – max Feb 09 '23 at 10:49
  • You can try something like **form_with(model: @review, url: [@resturant, @review])**, because looks like the issue that you trying to fix is the url/path – railsman Feb 09 '23 at 10:50
  • @railsman that doesn't actually make any difference at all besides being more typing. When you call `form_with(model: [@resturant, @review])` the `model` option is passed to the polymorhic routing helpers while the last element is used as the object wrapped by the form builder. – max Feb 09 '23 at 10:53
  • Could it be that you're embedding the form on another view such as the `/restaurants/index.html.erb` view where the `@resturant` instance variable is not set? – max Feb 09 '23 at 10:57
  • @max the problem that he is facing is of url and there can be more than one solution to the problem. And I don't want to get argument on StackOverflow as everyone is here to help. On that note I'll drop off from this thread – railsman Feb 09 '23 at 11:07
  • @railsman I just pointed out that its a wild goose chase and not an actual solution which is something thats easy to verify by checking the documentation or implementation of form_with. You gotta learn that not everything is personal in programming. https://api.rubyonrails.org/v7.0.4/classes/ActionView/Helpers/FormHelper.html#method-i-form_with – max Feb 09 '23 at 11:19

1 Answers1

0

The url: restaurant_reviews_path addition to the form_with made the difference.

<%= form_with(model: @review, url: restaurant_reviews_path, local: true) do |form| %>
Stephen Ostermiller
  • 23,933
  • 14
  • 88
  • 109
jimW
  • 23
  • 5