why am i getting undefined method `destroy' for nil:NilClass? - ruby-on-rails

I want to delete something posted by a user. But it's not working the way it normally does despite the fact that I'm mirroring a setup I've used in other apps.
My html:
<div class="section-wrap">
<% #colors.each do |color| %>
<div class="swatch">
<div class="colorblock" style="background-color: <%= color.backgroundcolor %>">
</div>
<h2>PANTONE<sup>2</sup></h2>
<p><%= color.name %></p>
</div>
<div class="controls">
<% if current_user %>
<%= link_to 'Delete', #color, method: :delete %>
<% end %>
</div>
<% end %>
</div>
My controller action:
def destroy
#color.destroy
redirect_to root_path
end
My routes.rb (in which I should not have to specify the delete route):
resources :colors
delete 'colors', to: 'colors#destroy'
And then rake routes gives the route I'd expect:
DELETE /colors/:id(.:format) colors#destroy
But I get undefined method 'destroy' for nil:NilClass

It doesn't look like you've loaded #color, and instance variables are automatically initialized to nil (in contrast with local variables, which raise an exception if you use them without assigning them). Hence, Ruby complains that destroy is undefined on nil. Load #color first:
#color = Color.find(params[:id])
If you're using Color.find_by_id somewhere else in your controller (eg. a before_action), perhaps the ID just doesn't correspond to an existing Color. Unlike find, find_by_id will return nil when the record is missing.

2 issues:
#View
<% #colors.each do |color| %>
<%= link_to 'Delete', color, method: :delete %> #-> #color doesn't exist
<% end %>
#Controller
def destroy
#color = Color.find params[:id]
#color.destroy
redirect_to root_path
end
The above should work for you.

Related

My Rails button is not updating the database

I created a button where users can input stuff in a field and then press the button to update the database (put request) which can be seen here in show.html.erb:
<% provide(:title, #user.name) %>
<div class="row">
<aside class="col-md-4">
<section class="user_info">
<h1>
<%= gravatar_for #user %>
<%= #user.name %>
<br>
<%= #user.email %>
<% if #errors %>
<p>THE FORM COULD NOT BE SAVED </p>
<ul id='errors'>
<% #errors.each do |error| %>
<li><%= error %></li>
<% end %>
</ul>
<% end %>
<br>
<% if is_admin? %>
<% if !#user.admin %>
<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= form_for(#user) do |f| %>
<%= f.label :wistia_project_id %>
<%= f.text_field :wistia_project_id, class: 'form-control' %>
<%= f.submit "Save", :action => "set_wistia_project_ID", :method => :patch, :form_class => "form-control" %>
<% end %>
</div>
</div>
<% end %>
<% end %>
</h1>
</section>
</aside>
</div>
The function is in user_controller.rb:
# Sets wistia_project_ID.
def set_wistia_project_ID
#user = User.find(params[:id])
#user.set_project_id
unless #user.valid?
#errors = #user.errors.full_messages
render :show
end
end
That function calls another function, just to separate things more clearly. This other function lives in user.rb:
# Sets the wistia_project_ID.
def set_project_id!(val)
self.wistia_project_ID = val # self is necessary here
save # or self.save, but the self is unnecessary here
end
My routes.rb:
.
.
.
resources :users do
member do
patch 'set_wistia_project_ID'
end
end
My problem is that right now, when you press the button, it says: Completed 500 Internal Server Error in 26ms (ActiveRecord: 0.7ms)
and
NoMethodError (undefined method `set_project_id' for #<User:0x000055b1a0914ab8>
2019-06-26T14:46:34.940086+00:00 app[web.1]: Did you mean? wistia_project_id):
Zavitoski got it right. I suggest, however, that you're doing a number of things more fundamentally wrong. Given that you're early in your rails journey, I hope you don't mind if I point a few things out.
First, and to be nit-picky, yes, you created a button. But, it is not a button "where users can input stuff in a field and then press the button to update the database". You created a button on a form. And you created a field on that form. The user can input stuff into the field. And when clicked, the button submits the form which includes the information in the field.
Now, on that form, you did:
<%= form_for(#user) do |f| %>
<%= f.label :wistia_project_id %>
<%= f.text_field :wistia_project_id, class: 'form-control' %>
<%= f.submit "Save", :action => "set_wistia_project_ID", :method => :patch, :form_class => "form-control" %>
<% end %>
There are a few things wrong with:
:action => "set_wistia_project_ID"
First, set_wisteria_project_ID is not a very ruby-ish action name. set_wistia_project_id would be more like it. Also, you're using old-form key-value formatting. And, you can use a symbol instead of a string for your action name so your code is prettier. Something, perhaps, like:
<%= f.submit "Save", action: :set_wistia_project_id, method: :patch, form_class: "form-control" %>
But, that's a mistake, too. Because you don't need a set_wistia_project_id action. (It's an action or a method, not a function.) You already have the update action. And form_for is smart enough to submit to this action if #user is an instance of User. So, really, you should do:
<%= form_for #user do |f| %>
<%= f.label :wistia_project_id %>
<%= f.text_field :wistia_project_id, class: 'form-control' %>
<%= f.submit "Save", form_class: "form-control" %>
<% end %>
I'm not sure what form_class is, but I'll trust that it's correct.
Now, in your UsersController, just do:
class UsersController < ApplicationController
def update
#user = User.find(params[:id])
if user.update(user_params)
# do something successful
else
# do something unsuccessful
end
end
private
def user_params
# NOTE: You'll probably want to permit other stuff here, too.
params.require(:user).permit(:wistia_project_id)
end
end
Get rid of this:
class User < ApplicationRecord
# Sets the wistia_project_ID.
def set_project_id!(val)
self.wistia_project_ID = val # self is necessary here
save # or self.save, but the self is unnecessary here
end
end
Because you're just duplicating the update method. And, you probably want that attribute to be wistia_project_id, not wistia_project_ID. (Again, you never see _ID as the suffix in rails core and you might as well be conventional.) And, if you make sure you have your association set up correctly, ActiveRecord should make sure that wistia_project_id is actually a valid value.
And write your routes.rb like this:
resources :users
Because you don't need all that set_wistia_project_id business.
It appears that you are not calling the function by the name you defined, neither passing the parameter (project_id) needed.
def set_wistia_project_ID
#user = User.find(params[:id])
#user.set_project_id!(params[:wistia_project_id])
unless #user.valid?
#errors = #user.errors.full_messages
render :show
end
end
This should use the function you created and pass the parameter from the form.

Acts_As_Votable Gem Automatically Liking Everything

I'm using the acts_as_votable gem to like and unlike "Deals" in my Ruby on Rails project. My user is set to act_as_voter and my deal is set to acts_as_votable, but for some reason everything is set to like as soon as a new user is created, and they can't unlike the deal. For some reason my list of deals all have an unlike button and it doesn't actually do anything but refresh the page. Here's some of my code.
app/views/catalog/index.html.erb
<ul class="deals_list">
<% #deals.each do |deal| %>
<li>
<div>
...
<div class="favorite">
<% if account_signed_in? and current_account.accountable_type == "Personnel" %>
<%= image_tag("dark-favorite.png") %>
<% if deal.liked_by current_account %>
<%= link_to unlike_deal_path(deal), method: :put do %>
Unlike
<% end %>
<% else %>
<%= link_to like_deal_path(deal), method: :put do %>
Like
<% end %>
<% end %>
<% end %>
</div>
</li>
<% end %>
</ul>
app/controllers/deals_controller.rb
def like
#deal = Deal.find(params[:id])
#deal.liked_by current_account
redirect_back(fallback_location: catalog_index_url)
end
def unlike
#deal = Deal.find(params[:id])
#deal.unliked_by current_account
redirect_back(fallback_location: catalog_index_url)
end
config/routes.rb
resources :deals do
member do
put 'like', to: "deals#like"
put 'unlike', to: "deals#unlike"
end
end
Be sure and read the entire Readme because you're using the library wrong.
To check if a voter has voted on a model, you can use voted_for?. You can check how the voter voted by using voted_as_when_voted_for.
I zeroed in on your problem because I was expecting to see a "?" after the deal.liked_by call, which would indicate a boolean result (by convention, not always the case).
So use this instead:
<% if current_account.voted_for? deal %>

Ruby on rails. Mailboxer, move multiple conversations to trash. Undefined method for #<Array

I am struggling with sending multiple items to the trash folder using checkboxes. I get an
undefined method `move_to_trash' for #<Array:0x007...etc...
move_to_trash works fine on single conversations.
I have a checkbox next to each conversation rendered by a partial, and a button to delete all checked conversations.
Anyway, my conversations controller:
def trash_multiple
#convo = mailbox.conversations.find(params[:trash_id])
#convo.move_to_trash(current_user)
redirect_to mailbox_inbox_path
end
The checkbox which is in a partial, placed next to each conversation:
<%= check_box_tag "trash_id[]", conversation.id %>
The id's are correct.
The form:
<div class="message-cont">
<div class="col-md-8">
<%= form_tag trash_multiple_conversations_path, method: :post do %>
<%= submit_tag "Trash selected" %>
<div class="panel-body">
<% if is_conversation %>
<%= render 'conversations/form' %>
<% else %>
<div class="msg-cnter">
<%= render partial: 'conversations/conversation', collection: messages %>
</div>
<% end %>
<% end %>
</div>
</div>
</div>
And my routes:
resources :conversations do
member do
post :reply
post :trash
post :untrash
end
collection do
get :trashbin
post :empty_trash
post :trash_multiple
end
end
Any hints on getting this to work for an array would be greatly appreciated, thanks.
SOLUTION:
changing controller to:
def trash_multiple
params[:trash_id].each do |element|
#convo = mailbox.conversations.find(element)
#convo.move_to_trash(current_user)
end
redirect_to mailbox_inbox_path
end
as stated by #wpp has fixed this.
move_to_trash works fine on single conversations.
My guess is that you want to invoke the move_to_trash method on each element of the array:
array.each do |element|
element.move_to_trash
end
or shorter:
array.map(&:move_to_trash)
Try this
#convo.each {|c| c.move_to_trash(current_user) }

Undefined method `add' for Hash

I'm implementing this shopping cart gem, but having problems when I add the item to cart in the controller. I tried executing the code using rails console and it works fine. Not sure why I'm getting this error from the controller.
Error:
products_controller.rb:
helper_method :add_to_cart
def add_to_cart
#cart = session[:active_cart]
#product = Product.find(params[:product_id])
#cart.add(#product, 99.99)
end
routes.rb
post '/add_to_cart/:product_id' => 'products#add_to_cart', :as => 'add_to_cart'
views/products/index.html
<% products.each do |product| %>
<%= button_to "Add to Cart", add_to_cart_path(:product_id => product.id), :method => :post %>
<a href="<%= addresses_path(:brand => product.brand.id, :product_id => product.id) %>" class="list-group-item">
<%= image_tag product.image.url(:square), class: "product-list-group-item" %>
<%= product.name %>
<span class="badge">$<%= number_with_precision(product.price, precision: 2) %></span>
</a>
<% end %>
#cart = session[:active_cart] this is returning a hash and that's why you get the mentioned error when you call this:
#cart.add(#product, 99.99)
Because there is no add method implemented for hash object.
I suggest you to inspect the #cart object in your controller like this:
#cart = session[:active_cart]
puts #cart.inspect
puts #cart.class
and then you will see, it's a hash object and you should be able to extract the required cart object from that hash.
The main issue is to get the correct #cart object from the session. Once you do that, then, it should work :)

Rendering a partial in rails. Specifying the partial for a resource gives an error, but not specifying a partial works fine. What gives?

I've got this working now quite accidentally, but I don't understand what causes it to break when I explicitly specify what partials are to be used for rendering the resource/s. Can anyone explain it?
The index template for my Posts controller contained the following line, which was giving me an error:
<%= render partial: 'posts', collection: #posts %>
The error (in my browser) said:
NoMethodError in Posts#index
Showing /Users/applebum/Sites/rails_projects/eventful2/app/views/posts/_posts.html.erb where line #1 raised:
undefined method `any?' for #<Post:0x000001064b21f0>
Extracted source (around line #1):
1: <% if posts.any? %>
2: <div id="posts">
3: <% posts.each do |post| %>
4: <%= render partial: "posts/post", locals: { post: post } %>
Changing the problem line to
<%= render #posts %>
made the error disappear and the posts appear (displayed nicely in markup from the appropriate partials) as I had wanted and expected them to.
Here's my _posts.html.erb partial:
<% if posts.any? %>
<div id="posts">
<% posts.each do |post| %>
<%= render partial: "posts/post", locals: { post: post } %>
<% # render :partial => "comments/comments", :collection => post.comments %>
<% end %>
</div>
<% end %>
And the _post.html.erb partial it's referring to, if that matters:
<div class="post" id="post_<%= "#{post.id}" %>">
<div class="post_inner">
<%= link_to avatar_for(post.user, size: "small"), post.user.profile %>
<div class="post_body">
<div class="user-tools">
<% if can? :destroy, post %>
<%= link_to '<i class="fi-x"></i>'.html_safe, post, :method => :delete, remote: true, :class => "delete", :confirm => "Are you sure you want to delete this post?", :title => post.content %>
<% end %>
</div>
<h5 class="username">
<%= link_to post.user.name, post.user.profile %>
<span class="timestamp">• <%= time_ago_in_words(post.created_at) %> ago</span>
</h5>
<div class="content">
<%= post.content %>
</div>
<ul class="foot">
<li>Like<li>
<li>Share</li>
</ul>
</div>
</div>
</div>
And the relevant bits from the controller:
class PostsController < ApplicationController
respond_to :html, :js # Allow for AJAX requests as well as HTML ones.
before_filter :load_postable
load_and_authorize_resource
def index
#post = Post.new
#posts = #postable.posts
end
private #################
def load_postable
klass = [User, Event].detect { |c| params["#{c.name.underscore}_id"] } # Look for which one of these there's a ***_id parameter name for
#postable = klass.find(params["#{klass.name.underscore}_id"]) # Call find on that, passing in that parameter. eg Event.find(1)
end
Can anyone explain to me what's going on here? I couldn't find anything in the Layouts and Rendering guide at rubyonrails.org.
Thanks!
Your error comes from assuming :collection and #posts mean the same thing when rendering. From Rails Docs (point 3.4.5):
Partials are very useful in rendering collections. When you pass a collection to a partial via the :collection option, the partial will be inserted once for each member in the collection
So, if you use that, for each post, you will be doing post.any? which fails as any? isn't defined for a single post.
From the same docs, you should check if render returns Nil to see if the collection is empty:
<h1>Posts</h1>
<%= render(#posts) || "There are no posts." %>
PD: Use the partial to render only one post, not all of them.
GL & HF.

Resources