Rails: link_to calls custom method in controller - ruby-on-rails

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').

Related

link_to, view, controller

I implemented this link:
View:
<li><%= link_to "Trainer-Sportler", :controller => "trainerones", :action => "trspmatch" %></li>
controller:
def trspmatch
render :trspmatch
end
and one view trspmatch.html.erb
Rails says:
Unknown action
The action 'show' could not be found for TraineronesController
When i implemented def show end and create a show.html.erb. Rails opens the show.html.erb not the trspmatch.html.erb?
You probably want to define your trainerones resource like this:
resources :trainerones do
collection do
get 'trspmatch'
end
end
that will expose a url /trainerones/trspmatch that maps to TraineronesController#trspmatch. It seems that a look at this link will help you understand routes better.
In any case, you will benefit a lot from running rake routes in your console, which will display all your routes and how they map to your controllers methods. Try before and after rewriting your trainerones resource as I explained above, and you'll see the difference. Good luck!

How to make a custom route in Rails? By custom I mean one that reacts to params

So essentially I've setup a route to match "products/:product", which seems to respond to a page like baseurl/products/toaster and displays the toaster product. My problem is I can't seem to use link_to to generate this path, and by that I mean I don't know how. Any help on this?
There are several solutions on this one :
<%= link_to 'Toaster', { :controller => 'products', :action => 'whatever', :product => 'toaster' } %>
But it's not really Rails Way, for that you need to add :as => :product at the end of your route. This will create the product_path helper that can be used this way :
<%= link_to 'Toaster', product_path(:product => 'toaster') %>
Within your routes file you can do something like:
match "products/:product" => "products#show", :as => :product
Where the controller is ProductsController and the view is show
within the Products controller your have
def show
#product = Hub.find_by_name(params[:product])
respond_to do |format|
format.html # show.html.erb
end
end
Where whatever is in the products/:product section will be available via params.
Then, since we used :as in your routes you can do this with link_to:
<%= link_to product(#product) %>
Where #product is an instance of a product or a string. This is just an example and the param can be anything you want, the same goes for controller/action. For more info you should check out this.
Hope this helps!

Rails - Add action to controller created by scaffold

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%>

Ruby on Rails: Need help updating db attribute in a nested route

Let me preface this by saying, i'm pretty new to rails and programming.
I'm trying to make some links to toggle a boolean attribute on and off. I've essentially succeeded in doing it on a non-nested resource by doing the following:
Route:
resources :my_resource do
get 'toggle_attribute', :on => :member
end
Controller:
def toggle_attribute
#resource = Resource.find(params[:id])
#resource.toggle!(:attribute)
end
View:
<%= link_to "Toggle Resource", toggle_attribute_resource_path(#resource), :remote => true %>
First, like I said above, this works on my non-nested route, however no matter what solution I try to add to the controller I can't get my link to flash a message or re-direct to anything when clicked, you click the button and nothing happens, you have to manually refresh to see the change.
Second, I can't figure out how to get this same sort of thing to work on a route that is nested like so:
Route:
resources :resource_1 do
resources :resource_2
end
Can anyone give me some tips?
Thanks a ton in advance. This stuff has been driving me batty.
By using remote => true, you are telling it to make an ajax call. This means that you need to also add a toggle_attribute.js.erb file in your views folder and in that file use javascript to replace the link element or text with what you want.
Also make sure to respond to js by setting respond_to :html, :js at the top of your controller.
repond_to :html, :js
def toggle_attribute
#resource = Resource.find(params[:id])
#resource.toggle!(:attribute)
end
toggle_attribute.js.erb :
$('#toggler').html("my new html here");
in view:
<%= link_to "Toggle Resource", toggle_attribute_resource_path(#resource), :remote => true, :id => "toggler"%>
Update:
For your nested route try this:
resources :resource_1 do
resources :resource_2 do
member do
get :toggle_attribute
end
end
end
your path would be something like:
toggle_attribute_resource_1_resource_2_path(#resource, #resource2)

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