please try to understand my question:
when i shut down the webrick server and again restart it ,then in my index view file there are some records after starting server if i click on any of three option (show,edit,delete)it gives me error "undefined method id' for nil:NilClass" and for "show" option "undefined methodname' for nil:NilC"
but if i add new record then every thing works fine i dont know what is the error
this is delete file
<%= link_to("<< Back to List", {:action => 'index'}, :class => 'back-link') %>
<div>
<h2>Delete vendor</h2>
<%= form_for(:vendor, url: {action:'destroy', id: #vendor.id}) do |f| %>
<p>Are you sure you want to permanently delete this vendor?</p>
<p><%= #vendor.name %></p>
<div>
<%= submit_tag("Delete vendor") %>
</div>
<% end %>
</div>
and this is controller
class VendorController < ApplicationController
def index
#vendors=Vendor.all
end
def new
#initiate new vendor which hits back to create
#vendor=Vendor.new
end
def create
#vendor=Vendor.new(vendor_params)
if#vendor.save
flash[:notice]="vendor ceated"
redirect_to(action: 'index')
else
flash[:notice]="there is error"
render('new')
end
def show
#vendor=Vendor.find(params[:id])
end
def edit
#vendor=Vendor.find(params[:id])
end
def update
#vendor=Vendor.find(params[:id])
if #vendor.update_attributes(vendor_params)
flash[:notice]='record updated'
redirect_to(action:'index')
else
flash[:notice]='there is some error'
render('edit')
end
end
def delete
#vendor=Vendor.find(params[:id])
end
def destroy
#vendor=Vendor.find(params[:id]).destroy
if #vendor.destroy
redirect_to(action: 'index')
else
render('delete')
end
end
end
private
def vendor_params
params.require(:vendor).permit(:name ,:image_url)
end
end
this is edit
<%= link_to("<< Back to List", {:action => 'index'}) %>
<div>
<h2>update Vendor</h2>
<%= form_for(:vendor, :url => {:action => 'update', id: #vendor.id }) do |f| %>
<%= render(partial:"form" , locals: {f: f}) %>
<div class="form-buttons">
<%= submit_tag("Update vendor") %>
</div>
<% end %>
</div>
this is show
<%= link_to("<<BAck to main",{action:"index"}) %>
<div>
<h1>showing <%=#vendor.name %></h1>
<p><%=#vendor.image_url %></p>
</div>
Good to see you fixed the problem.
--
For the benefit of future visitors, the error was caused by your controller name being singular, rather than plural.
All controllers should be named in the plural; all models in the singular:
#app/models/vendor.rb
class Vendor < ActiveRecord::Base
end
#app/controllers/vendors_controller.rb
class VendorsController < ApplicationController
end
As an added recommendation, your forms could be tidied up considerably by using the #vendor variable:
#app/views/vendors/edit.html.erb
<%= form_for #vendor do |f| %>
This will auto-fill the path and method, depending on the construct of the object you've passed.
Related
I have a rails question. I'm building a site where posts have likes, both posts and likes are their own model. A user can only like a post once, and once they like it the like button becomes an "unlike" button, that deletes the like.
I'm trying to create an experience in which the user can like, or unlike a post - and will not be redirected, but the like will update. With my limited rails knowledge, this isn't an easy task. Can anyone point me in the right direction?
Here is my /likes/_likes.html.erb template partial with the like/unlike button:
<% liked = #post.likes.find { |like| like.user_id == current_user.id} %>
<div class="likes">
<% if liked %>
<%= button_to 'Unlike', post_like_path(#post, liked), method: :delete %>
<% else %>
<%= button_to 'Like', post_likes_path(#post), method: :post %>
<% end %>
<%= #post.likes.count %><%= (#post.likes.count) == 1 ? 'Like' : 'Likes'%>
</div>
Here is my Like controller:
class LikesController < ApplicationController
before_action :find_post
before_action :find_like, only: [:destroy]
def create
if (!already_liked?)
#post.likes.create(user_id: current_user.id)
end
end
def destroy
if (already_liked?)
#like.destroy
end
end
private
def already_liked?
Like.where(user_id: current_user.id, post_id:
params[:post_id]).exists?
end
def find_post
#post = Post.find(params[:post_id])
end
def find_like
#like = #post.likes.find(params[:id])
end
end
Here is one of the views in which the _likes partial shows up (although the issue persists everywhere it appears):
<div class="post-display">
<% if #post.title %>
<h1><%= #post.title %></h1>
<% end %>
<% if #post.user %>
Post by <%= #post.user.email %>
<% end %>
<% if #post.price %>
<p>$<%= sprintf "%.2f", #post.price %></p>
<% end %>
<% if #post.description %>
<p><%= #post.description %></p>
<% end %>
<% if #post.image.present? %>
<%= image_tag #post.image.variant(:small) %>
<% end %>
<%= render 'likes/likes' %>
</div>
<% if current_user == #post.user %>
<%= link_to "Edit", edit_post_path(#post) %>
<%= button_to "Delete", #post, method: :delete %>
<% end %>
<% if #post.comments.count > 0 %>
<div class="post-comments">
<h2 class="post-comments-headline">Comments</h2>
<%= render #post.comments %>
</div>
<% end %>
<h2>Add a comment:</h2>
<%= render 'comments/form' %>
If you don't have an answer to my question, but have an idea on how to improve my code - let me know either way! I'm trying to learn here...
Thank you,
Jill
Since you're using rails 7, rendering turbo_stream in response to "like" and "unlike" buttons will update the page without refreshing.
# config/routes.rb
resources :posts do
# NOTE: i've used singular `resource`, since there is no need to have `id`
# for the like.
resource :like, only: [:destroy, :create]
end
https://api.rubyonrails.org/classes/ActionDispatch/Routing/Mapper/Resources.html#method-i-resource
# app/models/post.rb
class Post < ApplicationRecord
has_many :likes
def liked_by? user
likes.where(user: user).exists?
end
end
# app/models/like.rb
class Like < ApplicationRecord
belongs_to :post
belongs_to :user
# NOTE: avoid double likes.
validates_uniqueness_of :user, scope: :post, message: "already liked this post"
# TODO: create a unique index migration, to really make sure no double likes.
# `add_index :likes, [:post_id, :user_id], unique: true`
end
I've simplified LikesController a bit. No need for before_action filters:
# app/controllers/likes_controller.rb
class LikesController < ApplicationController
# POST /posts/:post_id/like
def create
# NOTE: uniqueness validation in `Like` model will prevent creating dup likes.
post.likes.create(user: current_user)
# you can access `like` error if you want to show it:
# like = post.likes.create(user: current_user)
# like.errors.full_messages
# NOTE: that's it, now we render `likes` partial inside a `turbo_stream`
render turbo_stream: turbo_stream.replace(
helpers.dom_id(post, :like), # this is the target div `id` that will be replaced
partial: "posts/likes", # with `likes` partial.
locals: { post: post }
)
end
# DELETE /posts/:post_id/like
def destroy
# NOTE: this will work regardless if there are any likes or not.
post.likes.where(user: current_user).destroy_all
# NOTE: alternatively, we can render the same `turbo_stream` as above
# in a template `likes/likes.turbo_stream.erb`:
render :likes
end
private
def post
#post ||= Post.find params[:post_id]
end
end
<!-- app/views/posts/_likes.html.erb -->
<!-- `dom_id` helps us generate a uniq id: "like_post_1" -->
<div id="<%= dom_id(post, :like) %>">
<!-- yes, there is a rails helper for this -->
<%= pluralize post.likes.count, "like" %>
<% if post.liked_by? current_user %>
<%= button_to "Unlike", post_like_path(post), method: :delete %>
<% else %>
<%= button_to "Like", post_like_path(post) %>
<% end %>
</div>
This turbo_stream is the same as in create action:
<!-- app/views/likes/likes.turbo_stream.erb -->
<%= turbo_stream.replace dom_id(#post, :like) do %>
<%= render "posts/likes", post: #post %>
<% end %>
https://turbo.hotwired.dev/handbook/streams
Try this
views file where likes partial render
<div id='post_likes'>
<%= render 'likes/likes' %>
</div>
/likes/_likes.html.erb
<div class="likes">
<% if liked %>
<%= button_to 'Unlike', post_like_path(#post, liked), method: :delete, remote: true %>
<% else %>
<%= button_to 'Like', post_likes_path(#post), method: :post, remote: true %>
<% end %>
<%= #post.likes.count %><%= pluralize(#post.likes.count, 'Like') %>
</div>
views/likes/create.js.erb
$('#post_likes').html('<%= render 'likes/likes' %>');
views/likes/destroy.js.erb
$('#post_likes').html('<%= render 'likes/likes' %>');
I have a custom object in Salesforce called Sales__c. I am trying to create a record from a RoR app using the databasedotcom-rails gem.
Controller
Class SalesController < ApplicationController
include Databasedotcom::Rails::Controller
def new
#sale = Sale__c.new
end
def create
sale = Sale__c.new(params[:sale])
sale.Item_on_Machine__c = "a0Co0000001TszL"
sale.OwnerId = "005o0000000HLctAAG"
if #sale.save
redirect_to(sale, :notice => 'Sale was successfully created.')
end
end
end
View
<h1>New Sale</h1>
<div class="actions">
<%= f.submit %>
</div>
#Note there is no field on the form, just the submit button.
Routes
VendingForce2::Application.routes.draw do
resources :sales
end
////////////////////////
ERROR
When I go to
http://0.0.0.0:3000/sales/new
I get the following error
NoMethodError in Sales#new
undefined method `sale__cs_path' for #<#<Class:0x007f85cc7c79d8>:0x007f85cc7c4dc8>
app/views/sales/new.html.erb:4:in `_app_views_sales_new_html_erb___1324507284181611345_70106319051940'
Any idea?
Try having the <%= f.submit %> under a for tag:
<% #sale.each do |f| %>
<%= f.submit %>
<% end %>
Or
try this
<%= form_for #sale do |f| %>
<%= f.submit %>
<% end %>
I'm using rails 4.0.8. I added a comment section to a model called 'Things', but I keep getting the same error "param is missing or the value is empty: thing" when I press the submit comment button. It says the error is in the Things#Controller. What am I doing wrong?
UPDATE: I removed the url path from the form, but a new error returns "Couldn't find Thing without an ID". The error is in Comments#Controller.
VIEW FOR THING/SHOW
<div id= "thing">
<h1>
<%= #thing.name %>
</h1>
<br>
<div id= "commentsection">
Comments
<div id= "comments">
<br>
<% #thing.comments.each do |c| %>
<%= c.username %>
<br>
<%= c.text %>
<% end %>
<%= form_for #comment, :url => thing_path do |f| %>
<%= f.label :username %>
<%= f.text_field :username %>
<%= f.label :comment %>
<%= f.text_field :text %>
<%= f.submit "Enter", class: "btn btn-small btn-primary" %>
<% end %>
</div>
</div>
</div>
THINGS CONTROLLER
class ThingsController < ApplicationController
def show
#thing = Thing.find(params[:id])
#thing.comments.build
#comment = Comment.new
end
def index
end
def new
#thing = Thing.new
#things = Thing.all
end
def create
#thing = Thing.new(thing_params)
if #thing.save
redirect_to #thing
else
render 'new'
end
end
private
def thing_params
params.require(:thing).permit(:name, :avatar)
end
end
COMMENTS CONTROLLER (I put asterisks around the line where the error is)
class CommentsController < ApplicationController
def show
#comment = Comment.find(params[:id])
end
def new
#comment = Comment.new
#comments = Comment.all
end
def create
****#thing = Thing.find(params[:thing_id])****
#comment = #thing.comments.create(comment_params)
redirect_to thing_path(#thing)
end
end
private
def comment_params
params.require(:comment).permit(:user, :text, :upvotes, :downvotes, :thing_id)
end
end
ROUTES
Website::Application.routes.draw do
get "comments/new"
get "comments/show"
get "things/new"
root 'home_page#home'
get "all/things/new" => 'things#new'
get "all/allthings"
resources :things
resources :good_comments
get "things/show"
get "things/results"
end
You are posting the #comment form to post '/things' path.
<%= form_for #comment, :url => thing_path do |f| %>
It should just be <%= form_for #comment do %> (Rails is smart enough to plug in the comments_path) or if you feel like being more explicit (even though it's not necessary)
<%= form_for #comment, url: :comments_path do %>
Another note though, if you want that Comment to be tied to that specific Thing then in your models it should be
Class Thing
has_many :comments
end
Class Comment
belongs_to :thing
end
Then make sure in your database comment has a thing_id foreign_key field and then your form for comment should actually look like
<%= form_for #thing, #comment do %>
<% end %>
I'm doing a Rails tutorial, and trying to figure out why this is happening.
I'm making a to-do list, and everytime I try and insert a record into my Todo model, I get the following:
Here is the new.html.erb view that this is from:
<h1>Add new item to your todo listM</h1>
<%= form_for #todo, :url=>todo_path(#todo) do |f| %>
<%= f.label :name %> <%= f.text_field :name%>
<%= f.hidden_field :done, :value => false %>
<%= f.submit "Add to todo list" %>
<% end %>
Here is index.html.erb from where the user is linked to new.html.erb
<h1>TASKS</h1>
<h3> TO DO </h3>
<ul>
<% #todos.each do |t| %>
<li>
<strong><%= t.name %></strong>
<small><%= link_to "Mark as Done", todo_path(t), :method => :put %></small>
</li>
<% end %>
</ul>
<h3> DONE </h3>
<ul>
<% #todones.each do |t| %>
<li>
<strong><%= t.name %></strong>
<small><%= link_to "Remove", t, :confirm => "You sure?", :method => :delete %></small>
</li>
<% end %>
</ul>
<%= link_to "Add new task", new_todo_path %>
Here is the TodoController I have managing these actions:
class TodoController < ApplicationController
def index
#todos = Todo.where(done:false)
#todones = Todo.where(done:true)
end
def new
#todo = Todo.new
end
def todo_params
params.require(:todo).permit(:name, :done)
end
def create
#todo = Todo.new(todo_params)
if #todo.save
redirect_to todo_index_path, :notice => "Your todo item was created!"
else
render "new"
end
end
def update
#todo = Todo.find(params[:id])
if #todo.update_attribute(:done, true)
redirect_to todo_index_path, :notice => "Your todo item was marked done!"
else
redirect_to todo_index_path, :notice => "Couldn't update your task"
end
end
def destroy
#todo = Todo.find(params[:id])
#todo.destroy
redirect_to todo_index_path, :notice => "Your todo item was deleted"
end
end
And finally the routes.rb
Oneday::Application.routes.draw do
devise_for :users
root 'home#index'
resources :todo
end
Any input as to why this is happening and how to rectify it would be great.
You do not comply with the rails convention. Use plural form for resources. Then, your action is correct.
TodosController, todos_controller.rb, resources :todos
( Rails use singular/plural format to support RESTful links and to recognize named actions )
This
<%= form_for #todo, :url=>todo_path(#todo) do |f| %>
will set (or leave) the form http method to get. You could change it to:
<%= form_for #todo, :url=>todo_path(#todo), method: :post do |f| %>
or even shorter, leave it to Rails to find out what method is needed:
<%= form_for #todo do |f| %>
I found a fix to this exact issue if anyone is still curious, i know its an old issue and an easy one at that, but still figured id solve it. the original route todo_path leads to todo#show. todo_index however is assigned to todo#index and todo#create so its what we want. the line should look like this:
<%= form_for #todo, :url => todo_index_url(#todo), method: :post do |f| %>
I encountered a similar issue with one of my applications and stumbled across this post as a fix. None of the suggestions worked for me, but i was able to fix it with a little tinkering on the routes.
I have an object "device" that contains a good deal of attributes. When editing one of the devices, I want to to have a copy button that when pressed will create a new device and load its "edit page" with the old devices parameters filled into the text_field. But I am very confused on how to called action control methods with buttons and I am currently getting an error saying
"No route matches {:action=>"clone", :controller=>"device", :id=>"1"}"
If I could just get the call to the "clone" method to work then I think I would be in good shape. Any help would be greatly appreciated! My current code is as followed:
edit.html.erb
<div class="row">
<%= form_for(#device) do |f| %>
<div class="span3 offset0">
<%= f.label "Unit Name" %>
<%= f.text_field :unitName %>
.
.
.
<%= f.label "Router Terminal Server IP" %>
<%= f.text_field :routerTerminalServerIp %>
</div>
<div class="span3 offset0">
<%= f.label "N2x Server" %>
<%= f.text_field :n2xServer %>
.
.
.
<%= f.label "Last Changed On" %>
<%= f.text_field :updated_at %>
<%= f.label "Update, Copy, or Delete Device" %>
<%= f.submit "Update", class: "btn btn-medium btn-info" %>
<%= link_to "Clone", :controller => "device", :action => "clone" %>
<%= link_to "Delete", device_path, class: "btn btn-medium btn-danger" %>
</div>
<% end %>
</div>
routes.rb
App::Application.routes.draw do
resources :devices
root 'static_pages#home'
match 'devices/clone', to: 'devices#clone', via: 'get'
end
devices_controller.rb
class DevicesController < ApplicationController
def new
#device = Device.new
end
def clone
oldDevice = Device.find(params[:id])
#device = Device.new
#device = #oldDevice.dup
#device.save
redirect_to edit_device_path(#device.id)
end
def create
#device = Device.new(device_params)
#device.lastChangedBy = request.remote_ip
if #device.save
redirect_to edit_device_path(#device.id)
else
render 'new'
end
end
def show
#device = Device.find(params[:id])
end
def edit
#device = Device.find(params[:id])
end
def update
#device = Device.find(params[:id])
#device.lastChangedBy = request.remote_ip
if #device.update_attributes(device_params)
redirect_to edit_device_path(#device)
else
render "edit"
end
def destroy
Device.find(params[:id]).destroy
redirect_to root_url
end
end
private
def device_params
params.require(:device).permit(:unitName, ...., :owner)
end
end
You should specify the route like this:
resources :devices do
member do
get :clone
end
end
Then you can use the clone_device_path(id) helper.
Also, you should consider using post method for this action and use button instead of link.
You're going to have no end of obscure problems if you name your controller method 'clone', as that is already defined in ruby on Object: http://ruby-doc.org/core-2.0.0/Object.html#method-i-clone
It used to be overridden in ActiveRecord, but lately ActiveRecord uses #dup for this functionality. You can still use 'clone' in your route and in the link label, but use something else for the actual method in your controller; you might want to use an ' :as => 'my_clone' ' option in your routes, if you call your method 'my_clone" for instance.