I have something like this in view:
<% if actions.include? "delete" %>
<%= link_to 'UsuĊ', asset_path(asset), :method => :delete, :data => { :confirm => "Want to delete it?" }, :role => "link" %>
<% end %>
and this in assetcontroller:
def destroy
#asset = current_user.assets.find(params[:id])
#asset.destroy
redirect_to assets_path
end
the question is, why it "use" destroy method when action in view is "delete" ?
delete is method of HTTP protocol. destroy is the action of your controller.
Route with delete HTTP method leads to destroy action.
To edit this routes and make delete HTTP method lead to delete action (for example), you should edit config/routes.rb file.
This is because in your routes.rb file you have defined the model as a resource (or a generator like scaffold did). This means that the default CRUD routes are generated. If you want to do it another way, use your own routes instead of generating them.
http://guides.rubyonrails.org/routing.html#crud-verbs-and-actions
Related
I have an Approved column in a database which is false by default and might become true on "Approve" button click.
That's what this button look like at the moment:
<%= link_to('Approve It', #comment_path, method: :update) %>
But it raises an exception:
No route matches [POST] "/books/4/comments/6
# app/controllers/comments_controller.rb
def update
#comment = Comment.find(params[:id])
#comment.approve = true
redirect_to '/dashboard'
end
# config/routes.rb
resources :books do
resources :comments
end
How can I fix it?
link_to has to point to an existing route/action, with a proper method name. There is no :update HTTP method.
FYI: Approve action doesn't seem like it belongs to the #update method/action. You might want to extract it to a separate route like so:
resources :books do
resources :comments do
post :approve, on: :member
end
end
this is more idiomatic/common approach in Ruby because #update is usually preserved for more general object updates.
For this you will need to change :method argument value to :post and update your route/#comment_path.
Rails-ujs event handlers - this link might be useful for understanding how it works behind the scenes.
Controller Namespaces and Routing
Post / Update actions require forms
You're using a link_to. This is good for GET requests, but is no good for POST/PATCH/UPDATE requests. For that you'll have to use a form in HTML. Luckily Rails offers some short cut. You can use something like button_to:
<%= button_to "Approve", { controller: "comments", action: "update" }, remote: false, form: { "id" => #comment.id, "approved" => true } %>
This creates a form for you. Which will come with CSRF protection automatically. You can style the button however you like.
or you could use a link to:
<%= link_to comment_approved_path(#comment), method: :put %>
but then you would need to create a separate "approved" action in your controller, and a separate route to reach it.
(The above code has not been tested).
#html
<%= link_to "Approve It", book_comment_path(#comment), method: 'put' %>
# app/controllers/comments_controller.rb
def update
#comment = Comment.find(params[:id])
#comment.approve = true
#comment.save
redirect_to '/dashboard'
end
I am trying to link to a custom controller route action and I'm doing something wrong. I have a Document model that handles uploading documents to my CRUD app. I want users to be able to 'delete' something, but not actually delete it from the system, but rather update the column 'active' to false. Then if someone with admin privileges can go ahead an complete the deletion. This process is needed because the app gets audited and we do not want to accidentally delete uploaded files.
I can't get the custom update action (remove) to work. When I rake routes I see:
remove_documents PUT /documents/remove(.:format) document#remove
In my routes file (I'll a couple similar routes I'll want to add later so I used collection it up this way):
resources :documents do
collection do
put "remove", to: "document#remove", as: :remove
end
end
In the Documents index view:
<%= link_to remove_documents_url(document), :method => :put do %>
<span class="fa fa-trash text-danger"></span>
<% end %>
My Controller:
def remove
#document = Document.find(params[:id])
#document.active = false
#document.save
html { redirect_to(:back, :notice => 'Document was successfully removed.')}
end
The link works, but then I get the following error:
NameError at /documents/remove.75 uninitialized constant DocumentController
raise unless e.missing_name? qualified_name_for(parent, const_name)
end
end
name_error = NameError.new("uninitialized constant #{qualified_name}", const_name)
name_error.set_backtrace(caller.reject {|l| l.starts_with? __FILE__ })
raise name_error
end
# Remove the constants that have been autoloaded, and those that have been
# marked for unloading. Before each constant is removed a callback is sent
If you want the remove action on a specific Document, change the routes to :
resources :documents do
member do
put "remove", to: "documents#remove", as: :remove
end
end
which gives you : remove_document PUT /documents/:id/remove(.:format)
and use it like :
<%= link_to remove_document_path(document), :method => :put do %>
<span class="fa fa-trash text-danger"></span>
<% end %>
I am working with rails I have a controller name books and has a user defined method in it .I need to call this method so that i can see the output on console.And I dont want to call this method in helpers.
def approve
#user=current_user.users.find params[:id]
puts '#{#usery}'
end
Also I Have a link
<%= link_to 'approve',users_path,data: { :confirm => 'Are you sure to delete the folder and all of its contents?'} %>
.When i click on this link I want to call the above method on it .
You'll just need to define a route and call it through that:
#config/routes.rb
resources :users do
get :approve, on: :member
end
<%= link_to "Approve", users_approve_path(#user) %>
As #Rich suggested that, you can achieve it by member. Please note that when you'll create a member route in member block
resources :users do
member do
get 'approve'
end
end
then you'll get the params[:id]. Like
def approve
#user = User.find params[:id]
puts '#{#user}'
end
and when create a member route using :on then you'll get params[:user_id]. Like
def approve
#user = User.find params[:user_id]
puts '#{#user}'
end
Path will be same in both cases that is
<%= link_to "Approve", users_approve_path(#user) %>
Source Rails - Adding More RESTful Actions
Happy coding !!!
I am new in Ruby and Rails and little bit confused about rendering and adding routes for a new template.
I have following link_to tag
<td colspan="3">
<%= link_to 'Show Current State', simulation, :action => :current_state, :class => 'btn btn-primary'%>
</td>
Where simulation is the name of controller and action is name of the method in SimulationController.
I added this in my routes.rb
resources :simulations, except: [:edit]
resources :simulations do
collection do
get 'current_state'
post 'current_state'
end
end
In my SimulationController class I added a new method i.e.
def current_state
byebug
end
My problem? routes is not re-directing to current_state method. Instead, it is redirecting to http://localhost:3000/simulations/{someID}
This redirection is calling show action.
def show
...
end
How can I make this work out and make <%= #simulation.dat %> line accessible in new.html.erb. Location of new.html.erb is in following path
views/simulations/index.html.js
views/similations/show.html.js
views/simulations/new.html.erb
This could be a basic question but I am new to rails 4. Thanks in advance.
Edit-1
Def of get_state method in controller
def get_state
#simulation = current_user.simulations.find(params[:id])
return not_found if #simulation.nil?
.....
/// How to send `#simulation` into `state.html.erb` formally as `new.html.erb`
end
You have too many misses in your code.
First, You don't need 2 resources :simulations, just merge them into one:
resources :simulations, except: :edit do
member do
get 'current_state', action: 'get_state'
post 'current_state', action: 'change_state'
end
end
Note that the original collection block is changed to a member block.
The difference between a collection block and a member block is that you need to provide an resource id for each routes in the member block, while no resource id is required for those in the collection block.
Also note that I added action: 'xxx' in each route, so you have to add these 2 actions in your SimulationsController, one for GET requests, and the other for POST requests.
UPDATE
In both of these actions, add render 'new' at the end.
END OF UPDATE
Run rake routes in your console (or bundle exec rake routes if you have multiple versions of rails installed), and you will see all the routes along with there url helper methods listed, like this:
Prefix Verb URI Pattern Controller#Action
current_state_simulations GET /simulations/:id/current_state simulations#get_state
current_state_simulations POST /simulations/:id/current_state simulations#change_state
...
According to the Prefix column, the link in the view should be
<%= link_to 'Show Current State', current_state_simulations_path(simulation), :class => 'btn btn-primary'%>
Or in short
<%= link_to 'Show Current State', [:current_state, simulation], :class => 'btn btn-primary'%>
UPDATE FOR Edit-1
Don't return in actions, because return doesn't stop rendering.
Instead, use raise ActionController::RoutingError.new('Not Found') to redirect users to the 404 page.
You can define an instance method in ApplicationController:
class ApplicationController < ActionController::Base
private
def not_found!
raise ActionController::RoutingError.new('Not Found')
end
end
And modify your SimulationsController:
def get_state
#simulation = current_user.simulations.find(params[:id])
not_found! unless #simulation
# ...
render 'new'
end
Best Practice
For dynamic page web applications, don't render views for non-GET requests!
Why? Because if a user POSTs some data to your web app, and then refreshes his/her browser, that request gets POSTed again, and your database got tainted. Same for PATCH, PUT and DELETE requests.
You can redirect the user to a GET path if the non-GET request succeeds, or to a 400 page if the non-GET request fails.
So I have a simple list of messages that users can submit. I'm trying to put a delete button (that works) for each message. But as you can see here you get an error about an entirely different action if you click the button. I'm not totally sure about where I went wrong. My guess is that I'm out of my depth in the controller area. Here are all the applicable files.
Routes.rb:
Rails.application.routes.draw do
root 'messages#index', as: :home
get '/new' => 'messages#new', as: :edit
resources :messages
post '/new' => 'messages#create', as: :create
delete 'messages/:id' => 'messages#destroy', as: :delete
The relevant controller:
class MessagesController < ApplicationController
def index
#messages=Message.all
end
def new
#messages=Message.new
end
def destroy
#messages=Message.find(params[:id])
#messages.destroy
end
def create
#messages = Message.new(message_params)
if #messages.save
redirect_to '/'
else
render 'new'
end
end
private
def message_params
params.require(:message).permit(:content, :subject)
end
end
The Relevant View:
<div class="main">
<div="messages">
<%#messages.each do |t|%>
<h2 class="subject"><%=t.subject%></h2>
<p class="content"><%=t.content%></p>
<%=link_to "Delete Message", delete_path(t)%>
<% end %>
<%=link_to "Create Message", edit_path%>
</div>
</div>
You need to pass the method DELETE as well, otherwise it will perform the simply GET request. Here's how:
<%=link_to "Delete Message", delete_path(t), :method => 'delete' %>
Remember if you do not mention any method in link_to, the default will be taken as GET. So you have to be explicit about other HTTP methods.
Edit:
Either use resources :messages, or use the routes that you wrote yourself. Using resources :messages is a bit easier, and it is the preferred way.
Using resources :messages, you'd have to write:
<%= link_to "Delete Message", t, :method => 'delete' %>
Edit 2:
You are getting the error Template is missing, because in your destroy method, neither you are rendering anything, nor you are redirect_toing anything. After you destroy the object, you will have to tell where should it go. Like if you want the user to go all messages page after he/she destroys the record, you need to add the following line to the end of the method:
redirect_to messages_path