Rails - Add action to controller created by scaffold - ruby-on-rails

I'm trying to add an action called rollback to controller.
As I've seen, the only things I should do is writting the new action:
def rollback
puts "ROLLBACK!"
respond_to do |format|
format.html # index.html.erb
format.json { render json: #components }
end
Modify the routes.rb file:
resources :components do
collection do
post :rollback, :as => 'rollback'
end
end
And calling the action from some view:
<%= link_to 'Rollback', rollback_components_path %>
But I get the following error:
Couldn't find Component with id=rollback
app/controllers/components_controller.rb:18:in `show'
That's because instead of going to rollback action, the controller thinks that we are trying to 'show' to component with id 'rollback'.
Something that it seems weird for me is that calling 'new' action rails uses new_component_path (without s, in singular), but if I write rollback_component_path it throws me an error and I cant see the view.

In your routes you require a POST, just clicking a link is by default a GET, so either write
resources :components do
collection do
get :rollback
end
end
and then the link_to will work as expected.
I am assuming the rollback operation is not idempotent, so a POST is semantically better in that case.
If you write your link as follows, then rails will create an inline form for you:
link_to 'Rollback', rollback_components_path, :method => 'post'
Hope this helps.

This will work
routes.rb
resources :components
match "components/rollback" => "components#rollback", :as => :rollback
In views
<%=link_to 'Rollback', rollback_path%>

Related

How to add routes for a new template?

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.

Rails Routing Incorrectly links to show.html

I'm learning Rails and have encountered some behavior I don't understand. I'm trying to create a simple CRUD app. On the 'new' view after data entry fields and submit button I am trying to add a link that will go to another page (I'll call it 'fnord').
Instead of linking to fnord, it's going to show.html. Given that rails is convention based I wonder if this is a default behavior of some kind, but I haven't been able to figure out what triggers it or the proper way of routing to fnord.
This is Rails 3.2.21, Ruby 1.9.3. I originally generated the model, views and controller using scaffold and then started tweaking things.
Here's my controller (minus unrelated actions - index, destroy, etc):
class EmployeesController < ApplicationController
def show
#employee = Employee.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #employee }
end
end
def new
#employee = Employee.new
respond_to do |format|
format.html # new.html.erb
format.json { render json: #employee }
end
end
def fnord
#nothing yet - just trying to get the page to appear right now
end
Here's the relevant code from new.html.erb:
<h2>New Employee</h2>
<%= render 'form' %>
<!-- content, data entry fields, submit button, etc -->
<div>
<%= link_to 'Fnord', employees_fnord_path %>
<div/>
Here's the view, fnord.hmtl.erb:
<h2>Fnord</h2>
<br/>
<p>this is fnord.html</p>
Here's my routes.rb:
MyApp::Application.routes.draw do
resources :employees
get 'employees/fnord'
root :to => 'employees#new'
end
I ran 'rake routes' and get the following output:
employees GET /employees(.:format) employees#index
POST /employees(.:format) employees#create
new_employee GET /employees/new(.:format) employees#new
edit_employee GET /employees/:id/edit(.:format) employees#edit
employee GET /employees/:id(.:format) employees#show
PUT /employees/:id(.:format) employees#update
DELETE /employees/:id(.:format) employees#destroy
employees_fnord GET /employees/fnord(.:format) employees#fnord
root / employees#new
As far as I can see this all looks right, but when I click on the link in new.html it returns show.html, not fnord.html.
The url that shows up in the browser is even locahost:3000/employees/fnord, but the content is from show.html.
I read the routing documentation (http://guides.rubyonrails.org/routing.html) but didn't see any explanations.
I've tried restarting rails (many times) and clearing my browser cache as well and those steps didn't help.
Can anyone enlighten me as to what I'm missing? Thank you very much.
The route for /employees/fnord matches the show route because the show route is looking for URL's in the format: /employees/:id
When you go to /employees/fnord it interprets fnord as the ID of some employee, and it routes it to the show method.
One way to fix it is to declare the /employees/fnord route before the resources routes, so that it matches first:
MyApp::Application.routes.draw do
get 'employees/fnord'
resources :employees
root :to => 'employees#new'
end
Another way is to simply change the name of the URL so that it doesn't match the pattern of /employees/:id:
MyApp::Application.routes.draw do
resources :employees
get 'employees1/fnord', to: 'employees#fnord'
# or whatever
get 'foo', to: 'employees#fnord'
# etc...
get 'employees/foo/fnord', to: 'employees#fnord'
root :to => 'employees#new'
end

Rails: link_to calls custom method in controller

I am looking to use link_to to call a method in my controller. However, for some odd reason the route looks for the show method.
In my view:
<% #beverages.each do |beverage| %>
..
<%= link_to 'Archive', beverages_archive_path(:id => beverage.id) %>
..
<% end %>
In my config/routes.rb
match 'beverages/archive' => 'beverages#archive'
In my beverages_controller.rb
def archive
beverage = Beverage.find(params[:id])
respond_to do |format|
# format.html # show.html.erb
format.json { render json: beverage }
end
# beverage.update_attribute('archive', true)
end
When I click on the archive link in the view, the URL does change to: http://localhost:3000/beverages/archive?id=11, however I get the following error.
The error I get:
ActiveRecord::RecordNotFound (Couldn't find Beverage with id=archive):
app/controllers/beverages_controller.rb:46:in `show'
Any idea on what I am doing wrong? Your help is much appreciated!
PS. I also looked at Rails 3 link_to delete destory method calls show method?
but nothing seemed to work.
Have you tried this in your routes?
match 'beverages/:id/archive' => 'beverages#archive', as: :beverages_archive
This will create the beverages_archive_path method for you. Also, as you are looking for a specific beverage, use :id inside the route so that it knows where to take the id parameter from.
Apart from that, you can always tell a link specifically which controller and action to link to by doing:
link_to "Label", :controller => :my_controller, :action => :index
Taken from here: Ruby on rails 3 link_to controller and action
Use the other notation (not match) instead.
resources :beverages do
collection do
get :archive
end
end
Try this one out and let me know if something went wrong.
There's not quite enough information here to know why beverages_archive_path works in your app -- one problem is that your routes file does not define a name for your beverages#archive route. What you want is something like:
match 'beverages/archive' => 'beverages#archive', :as => :beverages_archive
or better yet use resourceful routing conventions like so:
resources :beverages do
collection do
get :archive
end
end
What's happening is that you have a beverages#show route that matches /beverages/:id, and this is what /beverages/archive matches (with :id => 'archive').

Unknown action error

I have created a custom action req inside my membership controller and defined routes for it.But when i give path to that custom action on link_to ,it gives me error saying that unknown action ,The action 'show' could not be found for MembershipsController.I dny understand why it goes to find show action when i am giving path to my custom action.
Following is my custom action inside membership controller
def req
#user =User.find_by_email(params[:email])
#group =params[:group_id]
unless #group.nil?
if Membership.request(#user.id, #group)
redirect_to :back, :notice => 'Joined successfully.'
else
redirect_to :back, :notice => 'couldnot Joine.'
end
end
end
There is no show action inside my memberships controller.
Following is the route for my custom action :
resources :memberships do
collection do
post 'req'
end
end
Following is the code inside my groups/show.html.haml where i am giving path of custom action..
- if #collegemates.empty?
%p.info You have no common collegemates ...
- else
%table.datatable
%thead
%tr
...
%tbody
- #collegemates.each do |c|
- unless Membership.group_member(c.id, #group)
%tr
%td= c.full_name
%td= c.email
%td= link_to "request to join",{:controller => :memberships, :action => "req",:email =>c.email,:group_id => #group.id},:method => "post"
When i click on request to join I am getting that unknown action error for show action. What am i doing wrong here?
To make a link do a POST request, you need javascript. Rails takes care of this for you, but you do have to include the necessary files. Adding <%= javascript_include_tag :defaults %> to your layouts file will solve your problem. If you don't do this a GET request will be performed instead of a POST request.
You may want to assign your action either to collection or to member.
resources :memberships do
post 'req', :on => :collection # or :member
end
See http://guides.rubyonrails.org/routing.html#adding-more-restful-actions.
:on => :collection creates a route like memberships/req where
:on => :member gives you a route for an object like memberships/:id/req.
As default rails gives the CRUD actions.
new,create,edit,update,delete
the code
collection do
post 'req'
end
will create the route as below.
/memberships/req
So rails misunderstands that you are requesting /memberships/:id
and tries to match the show action.as the there is no show action it raises unknown action.
So the solution is you need to inform that you are not using CRUD actions
It can be done as below
resources :memberships, :only => [] do
collection do
post 'req'
end
end

How do you use a Rails 3 gem method to update a database model?

I am using the Thumb_Up gem for ruby on rails.
https://github.com/brady8/thumbs_up
I want users to be able to vote on posts.
However, I am unable to figure out how I can allow a user to click a button next to each post and add a vote to the database.
I can get this to happen in the rails console through doing the following:
u=User.first
m=Micropost.first
u.vote_for(m)
However, how can I get this to happen when a button is clicked in view. I am assuming I would have to use ajax, but how would I know the url I need to post to to make this action occur?
Any help would be greatly appreciated.
Update:
Thanks so much for the help! I am still having a problem with the code below.
Here is my routes.rb
resources :microposts do
post :vote, :on => :member
end
View:
<%= link_to('vote for this post!', vote_micropost_path(#micropost), :method => :post) %>
Controller:
def vote
#micropost = Micropost.find(params[:id])
current_user.vote_for #micropost
# This assumes you'll only call it via AJAX.
# If your ajax call doesn't return "ok", then you know something went wrong
render :text => 'ok', :layout => false
end
However, I'm still getting this error:
No route matches {:controller=>"microposts", :id=>#, :action=>"vote"}
Would anyone know why the routes aren't matching correctly?
I am assuming Rails 3. Rails 2's routes would look a little different.
First you would need to define a route in your config/routes.rb file. You could do this many ways. If you already have a route for microposts, you could simply add a "vote" action:
resources :microposts do
post :vote, :on => :member
end
(For clarity, the "post" above refers to the HTTP POST method and has nothing to do with your Micropost class.) If you use that route, you would then need to create a "vote" method in your Microposts controller to catch it. Something like
def vote
#post = Micropost.find(params[:id])
current_user.vote_for #post
# This assumes you'll only call it via AJAX.
# If your ajax call doesn't return "ok", then you know something went wrong
render :text => 'ok', :layout => false
end
Then in your view's AJAX POST call (assuming the example route I gave), you would get the url with:
vote_micropost_path(#micropost)
It would look like /microposts/56/vote

Resources