Undefined method when updating boolean - ruby-on-rails

I'm currently working on a todo app manager.
My task model have a description and a boolean, which is false by default.
When clicking on 'done', the boolean should switch to true.
However, I have the error undefined local variable or method `tasks'
Here's my task controller :
def index
#tasks = Task.all
#task = Task.new
end
def complete
#task = tasks.find(params[:id])
#task.completed = true
#task.save
redirect_to root_path
end
def incomplete
#task = tasks.find(params[:id])
#task.completed = false
#task.save
redirect_to root_path
end
and my index :
<h3>Tâches à réaliser</h3>
<% #tasks.incomplete.each do |task| %>
<p><%= "#{task.description} | Créé le #{task.created_at.day}/#{task.created_at.month}" %>
<%= link_to "Done", complete_task_path(task), method: :put %><%= link_to ' Modifier', edit_task_path(task) %><%= link_to ' X', task, method: :delete, data: { confirm: 'Are you sure?' } %></p>
<% end %>
<h3>Tâches réalisées</h3>
<% #tasks.completed.each do |task| %>
<p><%= task.description %>|
<%= link_to "Undo", incomplete_task_path(task), method: :delete %></p>
<% end %>
and my routes :
resources :tasks
root 'tasks#index'
match '/tasks/complete/:id' => 'tasks#complete', as: 'complete_task', via: :put
match '/tasks/complete/:id' => 'tasks#incomplete', as: 'incomplete_task', via: :delete
I've tried to change variables but this error is still the same.
Does anyone know what's wrong in here ?

The error here is pretty explicit: you're calling amethod or variable that wasn't defined.
Look at the first line in your complete and incomplete methods:
#task = tasks.find(params[:id])
tasks wasn't defined anywhere, hence the error. The #find method is, in ActiveRecord, a class method, so what you want to do is
#task = Task.find(params[:id])

Related

Not sure what to add to my routes to get icalendar gem link

My index page shows
<%= link_to 'Subscribe', icalendar_url(protocol: :webcal, format: :ics) %>
On my controller I have added
respond_to do |format|
format.html
format.ics do
cal = Icalendar::Calendar.new
cal.x_wr_calname = 'Awesome Rails Calendar'
cal.event do |e|
e.scheduled_date = #clients.scheduled_date
e.summary = '#clients.work_needed'
e.description = '#clients.comments'
end
cal.publish
render plain: cal.to_ical
end
end
And added nothing to my client.rb
What should I do I get the error
undefined method `icalendar_url' for #<#<Class:0x007fbb1a4b92b8>:0x007fbb32fc8560>
My mistake was that I was trying to fix the issue in routes, the route was wrong in the link nothing to do with routes
I had a link set as
<%= link_to "CSV", calendar_path(format: "csv"), :class => 'glyphicon glyphicon-file' %>
The middle calendar_path wasn't my controller, my controller was clients_path
With a simple change
<%= link_to "CSV", clients_path(format: "csv"), :class => 'glyphicon glyphicon-file' %>
it works !

RoR - how to make an .each loop on views pass a variable to the controller

Im trying to add votes to comments on my blog application. But i cant seem to be able to send the variable within the below loop to the controller:
<% #comments.each do |c| %>
<h1><%= c.title %></h1>
<p><%= c.content %></p>
<p><%= c.user.username %></p>
<% if current_user.id == c.user_id %>
<%= link_to "Edit", edit_comment_path(#post, c)%>
<%= link_to "Delete", delete_comment_path(#post, c), method: :delete, data: { confirm: "Are you sure?" }%>
<% end %>
<% if current_user.id != c.user_id %>
<p>Review this comment</p>
<%= link_to like_path(c), method: :put do %>
Upvote
<%= c.upvote %>
<% end %>
<%= link_to dislike_path(c), method: :put do %>
Downvote
<%= c.downvote %>
<% end %>
<% end %>
<% end %>
controller:
...
def upvote
object_comment = Comment.find(#comment.id)
object_comment.increment!(:upvote)
redirect_to show_path(#post)
end
def downvote
object_comment = Comment.find(#comment.id)
object_comment.increment!(:downvote)
redirect_to show_path(#post)
end
....
Routes:
Rails.application.routes.draw do
....
#Comments
post '/post/:post_id', to: 'comment#create', as: 'new_comments'
get '/post/:post_id/:comment_id/edit', to: 'comment#edit', as: 'edit_comment'
put '/post/:post_id/comment/:comment_id', to: 'comment#update', as: 'update_comment'
delete '/post/:id/:comment_id/', to: 'comment#destroy', as: 'delete_comment'
put 'like', to: "comment#upvote", as: 'like'
put 'dislike', to: "comment#downvote", as: 'dislike'
end
I would like to receive the 'c' stated on the each loop as a variable on the upvote and downvote methods to replace the #comment within object_comment = Comment.find(#comment.id), in order to increment the votes. is this possible?
As it is right i obviously receive the following error:
NoMethodError (undefined method `id' for nil:NilClass):
Your *_path methods do not know what to do with that single argument. (If you want to pass an object to your links, you might want to research RESTful routing. However, to solve it with your current routes, I suggest
<%= link_to "Downvote #{c.downvote}" dislike_path(id: c.id) %>
It should then be possible to access this id attribute in the controller via params.
def dislike
#comment = Comment.find(params[:id])
...
end
A couple of other things to think about:
It's very unusual to perform a put action on clicking a link. You might want to consider doing this as a get action instead.
Does the downvote method return something which is different for each comment? If not, it's standard practise to include it in the controller's helper.
You can get id in parameters, please check below methods
def upvote
object_comment = Comment.find(params[:id])
object_comment.increment!(:upvote)
redirect_to show_path(#post)
end
def downvote
object_comment = Comment.find(params[:id])
object_comment.increment!(:downvote)
redirect_to show_path(#post)
end
....
I think you're doing it wrong. You'll need to save the user's action on the comment with a model CommentVote for example that belongs to user and comment.
Then you'll need to do something like:
class CommentVote
belongs_to :comment
belongs_to :user
enum type: [:upvote, :downvote]
def self.vote(type:, comment_id:, user_id:)
comment_vote = CommentVote.where(comment_id: comment_id, user_id: user_id).first_or_initialize
comment_vote.type = type
comment_vote.save
end
end
And then do a after_save :update_counts in the CommentVote model:
def update_counts
self.comment.upvote = self.comment.comment_votes.upvote.count
self.comment.downvote = self.comment.comment_votes.downvote.count
self.comment.save
end
and you'll call it like: CommentVote.vote(type: :upvote, comment_id: 1, user_id: 1)

Couldn't find Item with 'id'=; Destroy action

I needed to customize my controllers so I did this:
routes:
post 'add_item', to: 'walls#create'
delete 'remove_item', to: 'walls#destroy'
destroy action WallsController:
def destroy
#item = Item.find params[:id]
#item.destroy
redirect_to :back
end
view:
<% #items.each do |item| %>
<%= item.name %> <%= link_to "X", remove_item_path(item), method: :delete %>
<% end %>
Create action works just fine but still getting error with destroy action: Couldn't find Item with 'id'=
Many thanks for help
remove_item_path is expecting an item id as argument.
link_to "X", remove_item_path(item.id), method: :delete
This works:
link_to "X", remove_item_path(id: item.id), method: :delete

change log in button to user name ruby

How do I make it so when a user logs in the log in button is changed to their email in the nav bar
class SessionsController < ApplicationController
def new
end
def create
#guestaccount = Guestaccount.find_by_email(params[:session][:email])
if #guestaccount && #guestaccount.authenticate(params[:session][:password])
session[:guestaccount_id] = #guestaccount.id
redirect_to '/guest?'
else
flash.now[:danger] = "Invalid email/password combination"
render 'new'
end
end
def destroy
session[:guestaccount_id] = nil
redirect_to '/guest?'
end
end
this is my nav bar
<%= button_to "Returning Guest ", guestlogin_path, :method => "get", class: "button round success" %>
<% if session[:guestaccount_id] %>
<%= Guestaccount.find(session[:guestaccount_id]).email %>
<% else %>
<%= button_to "Returning Guest ", guestlogin_path, :method => "get", class: "button round success" %>
<% end %>
Will do it. Feel free to adjust styling and content within the if/else blocks. If you have a current_user, current_guestaccount, or similar method, I would use that instead of the session and .find call.
You can define a current_guestaccount method in your ApplicationController:
class ApplicationController < ...
# Use this before internal/non-request (index/show/create/etc) controller methods
protected
# Usable in your controllers. E.g. authentication, loading associated data.
def current_guestaccount
# Return nil if the session value isn't set, don't query the DB
return nil unless session[:guestaccount_id]
# #x ||= y
# will make y run only once if it returns a successful value,
# essentially caching it for the entire request
#current_guestaccount ||= Guestaccount.find(session[:guestaccount_id])
end
# This makes current_guestaccount, a controller method, accessible in your views.
helper_method :current_guestaccount
end
Then, in your view you can use
<% if current_guestaccount %>
<%= current_guestaccount.email %>
<% else %>
<%= button_to "Returning Guest ", guestlogin_path, :method => "get", class: "button round success" %>
<% end %>
Which will use 1 SELECT query for the whole request instead of multiple. You can also use classes and HTML nodes within your view:
<% if current_guestaccount %>
<span class="guest-email"><%= current_guestaccount.email %></span>
<% else %>
<%= button_to "Returning Guest ", guestlogin_path, :method => "get", class: "button round success" %>
<% end %>
To adjust the styling later with CSS.
Similar to what Benjamin Mann's said, however please do not put ORM queries in the view template...
If the user is logged user should be stored in the controller.
<% if current_user %>
<%= current_user.email %>
<% else %>
<%= button_to "Returning Guest ", guestlogin_path, :method => "get", class: "button round success" %>
<% end>

Why do I get 'no route matches', for a route that exists?

This is the error:
No route matches {:action=>"send_to_client", :controller=>"stages"}
It corresponds to this line:
<%= link_to "<span class='icon send-to-client-icon' title='Send to Client' id='send-to-client'> </span>".html_safe, send_to_client_stage_path(#stage), :id => stage.id, :confirm => "This will send #{stage.name.capitalize} to #{stage.client.email}. Are you sure you are ready?" %>
In this _show_table.html.erb
<%
if #upload != nil
stage = #upload.stage
end
%>
<h1 class="panel-header">Images</h1>
<% if stage == nil %>
<div class="images_menu">
<%= link_to "<span class='icon send-to-client-icon' title='Send to Client' id='send-to-client'> </span>".html_safe, send_to_client_stage_path(#stage), :id => stage.id, :confirm => "This will send #{stage.name.capitalize} to #{stage.client.email}. Are you sure you are ready?" %>
<span class="icon compare-icon" data-url="<%= compare_stage_path(stage)%>" title="Compare Images" id="compare-images"> </span>
</div>
<% end %>
Here is my routes.rb:
resources :stages do
member do
get :step
get :compare
get :send_to_client
end
end
The issue is that this partial _show_table.html.erb is in the view folder of my uploads model...not the stages model.
When I execute that link_to in the stages model, it works fine. Once I take it out into the uploads model it throws that error.
Why would that be the case ?
Edit1: Here is the send_to_client action of the stages controller:
def send_to_client
stage = Stage.find(params[:id])
ClientMailer.send_stage(stage).deliver
if ClientMailer.send_stage(stage).deliver
flash[:notice] = "Successfully sent to client."
redirect_to("/")
else
flash[:notice] = "There were problems, please try re-sending."
redirect_to("/")
end
end
Rails will raise an ActionController::RoutingError if you use send_to_client_stage_path(nil).
You're mixing up #stage and stage. If you don't define #stage in the controller action, it will be nil and the error raises. In this case, just use #upload.stage.
Like:
<% if #upload.stage %>
<%= link_to "...", send_to_client_stage_path(#upload.stage), :confirm => "..." %>
<% end %>
If you want to use #stage, just define it in the action with #stage = #upload.stage and use it instead of #upload.stage:
<% if #stage %>
<%= link_to "...", send_to_client_stage_path(#stage), :confirm => "..." %>
<% end %>
It should probably be
send_to_client_stage_path(stage)
instead of
send_to_client_stage_path(#stage)
And it should be "unless", not "if" here, right?
<% unless stage.nil? %>
Also, dont forget you can use "unless", it's nicer sometimes
if #upload != nil
stage = #upload.stage
end
->
stage = #upload.stage unless #upload.nil?

Resources