How to fix "undefined method" error in destroy function - ruby-on-rails

I am creating a page where a logged in user can view a list of their upcoming reservations and cancel a reservation if need be.
I've tried this as the destroy function:
def destroy
#trip = Reservation.find(params[:id])
#trip.destroy
flash[:alert] = "This booking has been cancelled."
redirect_to your_trips_path
end # destroy/ cancel a booking
which produces the following error
undefined method `space_reservation' for #<#<Class:0x00007f8198a191d8>:0x00007f8195341160>
Did you mean? space_reservations_url
Here is the relevant code in the controller.rb
class ReservationsController < ApplicationController
before_action :authenticate_user!
before_action :set_reservation, only: [:approve, :decline, :destroy]
...
def your_trips
#today = DateTime.now
#trips = current_user.reservations.order(start_date: :desc)
end
def destroy
#reservation = Reservation.find(params[:id])
#reservation.destroy
flash[:alert] = "This booking has been cancelled."
redirect_to your_trips_path
end # destroy/ cancel a booking
Routes.rb
resources :spaces, except: [:edit] do
member do
get 'listing'
delete :delete_image_attachment
get 'preload'
get 'preview'
get 'get_dates'
get 'get_times'
put :deactivate
put :activate
get 'browse_spaces'
end
resources :reservations, only: [:create]
resources :calendars
end
resources :reservations, only: [:approve, :decline, :destroy] do
member do
post '/approve' => "reservations#approve"
post '/decline' => "reservations#decline"
delete 'destroy'
end
end
View
<div class="panel-body">
<% #trips.each do |trip| %>
...
<div class="col-md-3 text-right trips-index">
<% if trip.start_date && trip.end_date > #today %>
<%= link_to 'Cancel',
space_reservation(trip),
method: :delete,
class:"btn btn-danger",
data: { confirm: 'Are you sure?' } %>
<% end %>
</div>
</div>
<hr/>
<% end %>
</div>
I would like for the reservation to be deleted when the cancel button is clicked and to no longer appear in the list of reservations. Does anyone spot what I did wrong in the function? Thanks.

The error message says that the space_reservation method is undefined.
In your view, you called that undefined method here:
<%= link_to 'Cancel',
space_reservation(trip),
method: :delete,
class:"btn btn-danger",
data: { confirm: 'Are you sure?' } %>
<% end %>
Assuming everything else works, if you either create the space_reservation method or find another way to resolve the issue, then it should work.

Related

Delete method Rails - No route matches [GET] "/bookmarks/11"

My delete method for bookmarks does not seem to work, is this a problem with rails 7 or am I doing something wrong. It states that there is no Get method. I am using turbo-pack methods vs webpack
below is all relevant documents
routes.rb
Rails.application.routes.draw do
root 'lists#index'
resources :lists, except: [:edit, :update] do
resources :bookmarks, only: [:new, :create]
end
resources :bookmarks, only: :destroy
end
bookmarks_controller.rb
class BookmarksController < ApplicationController
before_action :set_bookmark, only: :destroy
before_action :set_list, only: [:new, :create]
def new
#bookmark = Bookmark.new
end
def create
#bookmark = Bookmark.new(bookmark_params)
#bookmark.list = #list
if #bookmark.save
redirect_to list_path(#list)
else
render :new
end
end
def destroy
#bookmark.destroy
redirect_to list_path(#bookmark.list), status: :see_other
end
private
def bookmark_params
params.require(:bookmark).permit(:comment, :movie_id)
end
def set_bookmark
#bookmark = Bookmark.find(params[:id])
end
def set_list
#list = List.find(params[:list_id])
end
end
show.html.erb
<div class="row">
<% #list.movies.each do |movie|%>
<% bookmark = Bookmark.find_by(list: #list, movie: movie) %>
<div class="col-12 col-lg-4">
<div class="card text-center text-white" style="background-color:#ffa82e00;">
<div class="card photo" class="card-img-top">
<%= image_tag(movie.poster_url, class: "card-img-top" )%>
<div class="card-body">
<h5 class="card-title text-dark"><%= movie.title%></h5>
<p class="card-text text-body"><%= movie.overview %></p>
<p class="card-text text-body"><%= bookmark.comment%></p>
<%= link_to "delete", bookmark_path(bookmark), data: { turbo_method: :delete, turbo_confirm: "Are you sure you want to remove #{movie.title} from your #{#list.name} list"}, class: 'text-danger' %>
</div>
</div>
</div>
</div>
<% end %>
<%= link_to "Bookmark now", new_list_bookmark_path(#list), class: "btn btn-primary" %>
Just use button_to instead which creates an actual form element and works even without relying on JavaScript:
<%= button_to "delete",
bookmark,
data: {
turbo_confirm: "Are you sure you want to remove #{movie.title} from your #{#list.name} list"
},
method: :delete,
class: 'text-danger'
%>
This has additional benefits to accessibility and the historical reasons why Rails used links and JS to turn clicking a link into a form submission are largely irrelevant in 2023. This is a feature that Turbo just provides for backwards compatibly.
As to why it doesn't work:
Turbo isn't properly included.
You have an error in your javascript thats preventing the event handler from doing its job.
You have a conflicting event handler.
It's the wrong phase of the planets...
I think it's because you are using link_to which will send a GET request. Try using button_for instead.

The action 'destroy' could not be found for ListingsController

UPDATE - I'VE NOW SOLVED THIS PROBLEM - I created a partial for the each Course item and rendered them from the main listing view. Thanks for all your help, I'm not really sure why it worked but it did END OF UPDATE
Apologies if this looks like a repeat posting but I've tried applying solutions to similar questions and they haven't worked, I'm stuck! Any suggestions welcomed, thank you.
Problem
I have a 'Courses' model which belongs to a 'Listings' model. The courses are created and deleted on a page belonging to Listing i.e. "/listing/2/courses"
Error Message
No route matches [DELETE] "/listings/2/courses"
Courses Controller def destroy detail
class CoursesController < ApplicationController
before_action :authenticate_user!, except: [:show]
before_action :set_listing
before_action :set_course, except: [:index, :new, :create]
def destroy
#course = #listing.courses.find(params[:id])
#course.destroy
flash[:notice] = "Course deleted!"
redirect_back(fallback_location: request.referer)
end
private
def set_course
#listing = Listing.find(params[:listing_id])
#course = Course.find(params[:id])
end
def set_listing
#listing = Listing.find(params[:listing_id])
end
def course_params
params.require(:course).permit(:name, :curriculum_type, :summary, :address, :course_places, :start_date, :finish_date, :price)
end
end
listing/listingID/courses page detail
<%= #listing.courses.each do |f| %>
<div class="jumbotron">
<ul>
<li>Name = <%= f.name %></li>
<li>Type of course = <%= f.curriculum_type %></li>
<li>Number of places = <%= f.course_places %></li>
<li>Start Date = <%= f.start_date %></li>
<li>Finish Date = <%= f.finish_date %></li>
<li>Price (£) = <%= f.price %></li>
<%= link_to "Delete Course", listing_courses_path(#listing, #course), method: :delete %>
</ul>
</div>
<% end %>
Routes.rb detail
resources :users, only: [:show]
resources :listings, except: [:edit] do
member do
get 'listing'
get 'pricing'
get 'description'
get 'photo_upload'
get 'amenities'
get 'location'
get 'courses'
end
resources :courses, except: [:edit] do
member do
get 'listing'
get 'pricing'
get 'description'
get 'photo_upload'
get 'amenities'
get 'location'
end
end
end
<%= link_to listing_course_path(#listing,f), :method => :delete, :data => { :confirm => 'Are you sure?' } %>
or try
<%= link_to listing_course_path(#listing,id: f.try(:id)), :method => :delete, :data => { :confirm => 'Are you sure?' } %>
route.rb
resources :listings do
resources :courses
end
By default, destroy method expects an ID as it's a member route. You are using listing/listingID/courses route without an ID. For that you need to define listing and/or courses as singular resources (read this) like:
resource :courses do
:
end
as described in this answer or make destroy a collection route like so:
resources :courses, except: [:edit] do
delete :destroy, on: :collection
:
rest...
end
Try and see if this works.
By the way, this looks a bit redundant as you are iterating over each #listing.courses and calling courses#destroy where you are destroying all the courses of #listing anyhow. So, why do #listing.courses.each in the first place. You should have either used a listing#destroy_courses method or remove #listing.courses.each iteration.
Update path in link_to to listing_course_path(#listing, #course) from listing_courses_path(#listing, #course)
<%= link_to "Delete Course", listing_course_path(#listing, f), method: :delete %>

configuring devise to be read only for users but full CRUD for Admin in Rails 4.2.4

I am a junior developer building an e-commerce web application using rails 4.2.4, Devise and a Pin scaffolding.
Right now, users can signup, sign in and then CREATE, READ UPDATE, DESTROY a pin on the index.html.erb.
Issue: I do not want users who sign in to be able to CRUD ( ONLY READ ).
I want to set this up so I as an ADMIN Can only create, update or destroy. Users and guests can only read.
I have been struggling with this for weeks, and would be grateful for help as to how I could achieve this please.
This is my PinsController
class PinsController < ApplicationController
before_action :set_pin, only: [:show, :edit, :update, :destroy]
before_action :correct_user, only: [:edit, :update, :destroy]
before_action :authenticate_user!, except: [:index, :show]
def index
if params[:search].present? && !params[:search].nil?
#pins = Pin.where("description LIKE ?", "%#{params[:search]}%").paginate(:page => params[:page], :per_page => 15)
else
#pins = Pin.all.order("created_at DESC").paginate(:page => params[:page], :per_page => 15)
end
end
def show
end
def new
#pin = current_user.pins.build
authorize(#pin)
end
def edit
end
def create
#pin = current_user.pins.build(pin_params)
if #pin.save
redirect_to #pin, notice: 'Pin was successfully created.'
else
render :new
end
end
def update
if #pin.update(pin_params)
redirect_to #pin, notice: 'Pin was successfully updated.'
else
render :edit
end
end
def destroy
#pin.destroy
redirect_to pins_url
end
private
# Use callbacks to share common setup or constraints between actions.
def set_pin
#pin = Pin.find_by(id: params[:id])
end
def correct_user
#pin = current_user.pins.find_by(id: params[:id])
redirect_to pins_path, notice: "Not authorized to edit this pin" if #pin.nil?
end
# Never trust parameters from the scary internet, only allow the white list through.
def pin_params
params.require(:pin).permit(:description, :image)
end
end
This is my Index.html.erb
<h1>For Sale</h1>
<%= form_tag pins_path, method: :get do %>
<div class="field">
<%= label_tag :Description %>
<%= text_field_tag :search %>
<%= submit_tag "Search", name: nil, class: "btn btn-success btn-sm" %>
<%= link_to 'Clear', pins_path, class: 'btn btn-danger btn-sm' %>
<% end %>
<div id="pins" class="transitions-enabled">
<% #pins.each do |pin| %>
<div class="box panel panel-default">
<%= link_to image_tag(pin.image.url(:medium)), pin %>
<div class="panel-body">
<%= pin.description %>
<%= link_to 'Show', pin_path(pin) %>
<% if current_user && pin.user == current_user %>
<%= link_to 'Edit', edit_pin_path(pin) %>
<%= link_to 'Destroy', pin, method: :delete, data: { confirm: 'Are you sure?' } %>
<% end %>
</div>
</div>
<% end %>
</div>
<div class="center">
<%= will_paginate #pins, renderer: BootstrapPagination::Rails %>
</div>
<div class=text-right>
<% if user_signed_in? %>
<%= link_to 'Post a Free Ad', new_pin_path, class: "btn btn-warning btn-lg" %>
</div>
<% end %>
<br>
You want everybody (logged in users or just quests) to be able to access the index and the show method. All other methods are exclusively for administrators.
First step: We need a way to identify an administrator. Add a boolean admin attribute to your the User model. Run the following command on your command line to create a new migration:
$ rails g migration add_admin_to_users admin:boolean
Open the generated file and add default: false to it, it should look like this:
class AddAdminToUsers < ActiveRecord::Migration
def change
add_column :users, :admin, :boolean, default: false
end
end
Now run rake db:migrate to add that column to your database.
Next step it to grant your own user administration rights. Log into the rails console (with $ rails c). Find your user and update the admin flag on your user to true:
> user = User.find_by(email: 'your-eamiladdress#example.tld')
> user.admin = true
> user.save
> user.admin?
# => true
As you see Rails automatically adds a admin? method to the user. We use that method in the controller now:
before_action :find_pin, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!, except: [:index, :show]
before_action :ensure_admin, except: [:index, :show]
With this private methods:
def find_pin
#pin = Pin.find(params[:id]) # renders 404 in production when pin isn't found
end
def ensure_admin
unless current_user.admin?
redirect_to(pins_path, notice: 'Not authorized to edit this pin')
end
end
Use the same admin? method in the view to hide edit and destroy links from non-admins:
<%= link_to 'Show', pin_path(pin) %>
<% if current_user && current_user.admin? %>
<%= link_to 'Edit', edit_pin_path(pin) %>
<%= link_to 'Destroy', pin, method: :delete, data: { confirm: 'Are you sure?' } %>
<% end %>

Parameter is missing or the value is empty?

I am trying to access an "edit" link to edit an object, but I'm getting this error:
Param is missing or the value is empty: preview
Basically, I have 2 models that I linked through association:
Game model
Review model
I'm rendering reviews in the Game's show page. When I try to edit a review, it's saying I'm missing params or the value is empty in the Reviews controller.
The routes are also nested. How can I fix this?
Thanks in advance :)
routes.rb
Rails.application.routes.draw do
devise_for :users
root "games#index"
resources :games do
resources :news
resources :reviews, except: [:show, :index]
resources :previews, except: [:show, :index]
end
resources :platforms
resources :genres
end
show.html.erb (Linked to Games controller)
<% if #news.last.created_at > preview.updated_at %>
<p><%= link_to "edit", edit_game_preview_path(#game.id, preview.id) %></p>
<% end %>
<p><%= link_to "delete", game_preview_path(#game.id, preview.id), method: :delete %></p>
<% end %>
Reviews partial (Form)
<%= form_for [#game, #previews.new] do |r| %>
<h3 class="post_review">Preview this game</h3>
<p><%= flash[:notice_submit] %></p>
<p><%= r.text_field :title, placeholder: "Enter your tagline" %></p>
<p><%= r.text_area :content, placeholder: "Enter your review here" %></p>
<p><%= r.text_area :vote %></p>
<p><%= r.hidden_field :game_id, value: #game.id %></p>
<%= r.submit %>
<% end %>
Reviews controller
class PreviewsController < ApplicationController
before_action :authenticate_user!
before_action :set_preview, only: [:show, :edit, :update, :destroy]
before_action :set_game
def new
#preview = Preview.new
end
def create
#preview = Preview.new(preview_params)
#preview.user_id = current_user.id
#preview.game_id = #game.id
#preview.username = current_user.username
if #preview.save
redirect_to :back
flash[:notice_submit] = "Thanks for you comment!"
else
redirect_to :back
flash[:notice_submit] = "Either you've already voted, or you're not filling in all forms."
end
end
def edit
#preview.update(preview_params)
redirect_to #game
end
def destroy
#preview.destroy
redirect_to #game
end
private
def set_preview
#preview = Preview.find(params[:id])
end
def set_game
#game = Game.find(params[:game_id])
end
def set_user
#user = User.find(params[:user_id])
end
def preview_params
params.require(:preview).permit(:title, :content, :vote)
end
end
You are getting this error because in your preview_params you are requiring a preview object.
I think your controller logic for the edit action is invalid. For the edit action, you just need to set_preview and then render the edit template. The current logic in your edit action should go in an update action.
def edit
end
def update
#preview.update(preview_params)
redirect_to #game
end
Also the first line of your form should be:
<%= form_for [#game, #preview] do |r| %>

Rails - Votes not saving, and creating button that does not submit?

so I'm trying to create a video game review website for practice.
A game has many reviews, and votes. The idea is, in order to post a review, you must vote "Good" or "Bad" first, THEN submit a review. You can't post a text review without voting.
I'm trying to do this without the acts_as_voteable gem...
The data format for votes is boolean. "Good" is true, "Bad" is false.
How do I get the votes to save? below are my routes.rb, _review partial, reviews controller, and show page.
many thanks guys :)
edit****: also I'm trying to only one vote per user. I was thinking of using a token variable which equals to 1, and when a vote is cast, the token is -1. Is that a good approach? But the data type for vote is boolean, so how would that work -- or should I change the data type for vote from boolean to integer?
edit#2 -- so I added :vote into my params.
routes.rb
upvote_game_review_path
POST /games/:game_id/reviews/:id/upvote(.:format) reviews#upvote
downvote_game_review_path
POST /games/:game_id/reviews/:id/downvote(.:format) reviews#downvote
Rails.application.routes.draw do
devise_for :users
root "games#index"
resources :games do
resources :news
resources :reviews, except: [:show, :index] do
member do
post "upvote"
post "downvote"
end
end
end
resources :platforms
resources :genres
end
reviews_controller.rb
class ReviewsController < ApplicationController
before_action :set_review, only: [:show, :update, :edit, :destroy]
before_action :set_game
before_action :authenticate_user!
def new
#review = Review.new
end
def create
#review = Review.new(review_params)
#review.user_id = current_user.id
#review.game_id = #game.id
if #review.save
redirect_to #game
else
render "review"
end
end
def upvote
#review.vote.create = true
redirect_to #game
end
def downvote
#review.vote.create
#review.vote = false
redirect_to #game
end
def edit
#review.update(review.params)
end
def destroy
#review.destroy
redirect_to #game
end
private
def set_review
#review = Review.find(params[:id])
end
def set_game
#game = Game.find(params[:game_id])
end
def review_params
params.require(:review).permit(:comment, :vote)
end
end
_review partial <-- to create a new review
<%= form_for [#game, #reviews.new] do |r| %>
<h3 class="post_review">Review this game</h3>
<p>
<%= r.text_area :comment %>
</p>
<p>
<%= button_to "Good", upvote_game_review_path(#game.id, r) %>
</p>
<p>
<%= button_to "Bad", downvote_game_review_path(#game.id, r) %>
</p>
<p>
<%= r.hidden_field :game_id, value: #game.id %>
<p>
<%= r.submit %>
<% end %>
show.html.erb
<p><%= link_to "<< Home", games_path %></p>
<span><%= link_to "Edit", edit_game_path(#game) %></span>
<span><%= link_to "Delete", game_path(#game), method: :delete %></span>
<div class="game_summary">
<h2><%= #game.title %></h2>
<%= image_tag #game.image %>
<p>Release Date: <%= #game.release_date %> </p>
<p>Genre: <%= #game.genre_id %> </p>
<p>Platforms: <%= #game.platform_id %></p>
</div>
<%= link_to "Add News", new_game_news_path(#game) %>
<h2>News & Articles</h2>
<%= link_to "view all", game_news_index_path(#game) %>
<% #news.each do |n| %>
<ol>
<li><%= link_to n.title, game_news_path(#game.id, n.id) %></li>
</ol>
<% end %>
<div class="game_review submit">
<%= render "review" %>
</div>
<% #reviews.each do |review| %>
<p><%= review.comment %></p>
<p><%= link_to "delete", game_review_path(#game.id, review.id), method: :delete %></p>
<% end %>
You don't specify which review you're loading in. The reason is here:
before_action :set_review, only: [:show, :update, :edit, :destroy]
You don't pull in the request's review instance when you go to either of those actions. Further, it doesn't look like you're actually saving them.
So, two things I'd recommend:
Add those methods to your before_action:
before_action :set_review, only: [:show, :update, :edit,
:destroy, :upvote, :downvote]
(May not be necessary, write tests to confirm this!) Actually save the entity after you've changed its value.
def upvote
#review.vote.create = true
#review.save
redirect_to #game
end
def downvote
#review.vote.create unless #review.vote
#review.vote = false
#review.save
redirect_to #game
end

Resources