Rails3 nested-routing issues - ruby-on-rails

I would like to create a mechanism for a User to keep track of other, favorite Users, similar to SO's favorite questions. I'm using the Rails 3.0 beta.
To do so, I have a User-Favorite HABTM relationship, which works as expected:
class User < ActiveRecord::Base
has_and_belongs_to_many :favorites, :class_name => "User", :join_table => "favorites", :association_foreign_key => "favorite_id", :foreign_key => "user_id"
end
The Favorites Controller only needs 3 of the 7 RESTful methods to manage a User's favorites:
class FavoritesController < ApplicationController
# GET /favorites
# GET /favorites.xml
def index
#user = User.find(params[:user_id])
#favorites = #user.favorites.joins(:profile).order("last_name,first_name")
...
end
def create
#favorite = User.find(params[:id])
current_user.favorites << #favorite
...
end
def destroy
#favorite = User.find(params[:id])
current_user.favorites.delete(#favorite)
...
end
end
The Routes.rb file contains the routing instruction:
resources :users, :except => :destroy do
resources :favorites, :only => [:index,:create,:destroy]
end
that generates these user-favorite routes:
GET /users/:user_id/favorites(.:format) {:controller=>"favorites", :action=>"index"}
user_favorites POST /users/:user_id/favorites(.:format) {:controller=>"favorites", :action=>"create"}
user_favorite DELETE /users/:user_id/favorites/:id(.:format) {:controller=>"favorites", :action=>"destroy"}
In the User's Show View, the User (#user) can be toggled as a favorite using image links, which works as expected:
<% if [test if user is a favorite] %>
# http://localhost:3000/favorites/destroy/:id?post=true
<%= link_to image_tag("favorite.png", :border => 0), :controller => :favorites, :action => :destroy, :post=>true, :id => #user %>
<% else %>
# http://localhost:3000/favorites/create/:id?post=true
<%= link_to image_tag("not-favorite.png", :border => 0), :controller => :favorites, :action => :create, :post=>true, :id => #user %>
<% end %>
However, in the current_user's favorite Index View, the link_to each favorite user:
# http://localhost:3010/users/4/favorites/3?post=true
<%= link_to image_tag("favorite.png", :border => 0), :controller => :favorites, :action => :destroy, :id => favorite, :post=>true %>
generates an error that reads:
No route matches "/users/4/favorites/3"
Questions:
Have I correctly specified my routing? Seem like the create and destroy routes would only need the id of the favorite, as the 'owner' of the favorite is always current_user.
If I'm simply referencing the Controller/Action in the Show view, do I even need the create/destroy routes?
Why doesn't the link_to in the Index View work correctly?
Are there any improvements that can be made to the over-all approach?

Your routing looks fine.
I think there is something wrong with your link_to, though. For one thing, the RESTful way is not to specify URLs with :controller and :action parameters. The correct way is using the generated URL methods, such as user_favorite_path. Also, you need to specify the :method parameter when targeting the destroy action. This is how I think the link_to should look like:
<%= link_to image_tag("favorite.png", :border => 0), user_favorite_path(#user, #favorite), :method => :delete %>
I believe the reason it says no route matches that URL is because you didn't specify the :method as :delete.

in your rake routes output, the paramater needed is :user_id not :id, so you need to send that in your link_to call.

Related

How to pass an ID/parameter to ROR controller

In view I have a link as follow in "teacher" controller. this link supposed to send an id to another controller "teacher_details" (1 to 1 relationship). This link open a webpage to add more details about teacher.
<%= link_to 'Add details', new_teacher_detail_path(#teacher), :id => "add_detail_link" %>
My controller code is
private
def set_teacher
#teachers = Teacher.find(params[:id])
end
When I run this code it shows me an error that "cannot find with out an ID". What am I doing wrong. The link does not passing the id parameter properly.
Route file is
root 'sessions#login'
get 'homes/home'
get '/login' => "sessions#login", :as => "login"
get '/logout' => "sessions#logout", :as => "logout"
get '/homes' => "homes#home"
resources :users
resources :sessions
resources :homes
resources :teachers
resources :teacher_details
resources :profiles
It seems like you are not nesting TeacherDetail with Teacher model in routing.
That's why it not getting any id parameter in new_teacher_detail_path. So you can't find params[:id] in below action.
private
def set_teacher
#teachers = Teacher.find(params[:id])
end)
Try change Your link_to as below :
<%= link_to 'Add details', new_teacher_detail_path(:id => #teacher.id), :id => "add_detail_link" %>
Now you will get ID parameter properly.
Routes file for teacher and details should be like this:
resources :teachers do
get 'details/new', on: :member
end
Then in view you can use link_to like this
<%= link_to 'Add details', details_new_teacher(#teacher) %>
No need to change in controller
In one-to-one relationship teacher_detail has foreign_key as teacher_id, So you can use like this
<%= link_to 'Add details', new_teacher_detail_path(id: #teacher.id), :id => "add_detail_link" %>

Couldn't find User with id=search

I want to a searching function for users. It does not work. So I simplified the method, I just want to refresh the index page when I hit the search button. But it still does not work, it said
ActiveRecord::RecordNotFound in UsersController#show, Couldn't find User with id=search.
please tell me Why
my controller
class UsersController < ApplicationController
load_and_authorize_resource :except => [:index]
def search
redirect_to users_path
end
end
My view
<%= form_tag users_search_path, :method => 'get' do %>
<td><%= text_field_tag :username, params[:username] %></td>
<%= submit_tag "Search", :class => "buttons buttons-rounded buttons-flat-action", :id => "button-new"%>
<br><br><br>
<% end %>
My Route
Procedures::Application.routes.draw do
devise_for :users
#### USER MANAGEMENT ####
resources :users do
resources :rateofpays # professional timesheet
resources :roles
resources :biographies
resources :qualifications do
collection do
put 'complete', :action => 'complete'
end
end
resources :supervisors
end
#### users search ####
get 'users/search' => "users#search", as: 'users_search'
The line
get 'users/search' => "users#search", as: 'users_search'
...is too far down in your routes. the resources :users appears first, and it has a match path that looks like users/:id and the users/search is incorrectly matching against that.
Just move the get 'users/search' to the top... or alternatively define it as a collection method under resources :users
resources :users do
collection do
get 'search'
end

Action method is :id with link_to?

When I try to subscribe to a product by clicking the link:
<%= link_to "Subscribe", :controller => "products", :action => "subscribe_product", :id => product.id, :method => :post %>
I get this error and notice the parameters are wrong.
ActiveRecord::RecordNotFound in ProductsController#show
Couldn't find Product with id=subscribe_product
{"id"=>"subscribe_product", "method"=>"post"}
My subscribe_product method in my ProductsController is:
def subscribe_product
#product = Product.find(params[:id])
#product.subscriptions.create(:subscriber_id => current_user.id)
end
My route:
resources :products do
post :subscribe_product, :on => :collection
end
These are the associations:
class User
has_many :products
has_many :subscriptions, :foreign_key => :subscriber_id
class Product
belongs_to :user
has_many :subscriptions, :as => :subscribable
class Subscriptions
belongs_to :subscriber, :class_name => "User"
belongs_to :subscribable, :polymorphic => true
Users subscribe in another controller:
PagesController
def index
#product_history = current_user.products
end
end
pages/index.html.erb
<% for product in #product_history %>
<%= product.name %>
<%= product.price %>
<%= link_to "Subscribe", :controller => "products", :action => "subscribe_product", :id => product.id, :method => :post %>
<% end %>
So why is my action method being seen as the ID instead?
Try :
resources :products do
post :subscribe_product, :on => :member
end
It will generate routes like :
subscribe_product_product POST /product/:id/subscribe_product(.:format) {:action=>"subscribe_product", :controller=>"products"}
and use path like in view :
subscribe_products_path(product.id)
Since you're passing an id, the subscribe_product route should be a member route. Try this, and let me know what you get:
resources :products do
member do
post 'subscribe_product'
end
end
In the controller (to get around non-mass-assignable attributes):
def subscribe_product
#product = Product.find(params[:id])
subscription = Subscription.new
subscription.subscriber_id = current_user.id
#product.subscriptions << subscription
end
Please try this. Change your route to :
resources :products do
post :subscribe
end
Then change your link like :
<%= link_to "Subscribe", subscribe_products_path(:id => product.id), :method => :post %>

Rails 3 : Can't get form_for to work as a 'delete' following the RESTful achitecture => always giving a ROUTING ERROR

I have a very simple render that goes as follow:
<%= form_for(:relationships, :url => relationships_path, :html => {:method => 'delete'}) do |f| %>
<div><%= f.hidden_field :user_id_to_unfollow, :value => #user.id %></div>
<div class="actions"><%= f.submit "Unfollow" %></div>
<% end %>
When I submit this form it will always give me a
Routing Error
No route matches "/relationships"
on my page.
In my relationships controller, I have created all the propers methods:
def create
...
end
def destroy
...
end
def update
...
end
def show
...
end
And in my routes config I have made sure to allow all routes for the relationships controller
resources :relationships
But I can't seem to get into the destroy method of the controller :(
However if I remove the
:html => {:method => 'delete'}
method parameter in the form_for then I get to the create method of the controller no pb.
I don't get it....
Alex
ps: this is the rake routes results for relationships:
relationships GET /relationships(.:format) {:action=>"index", :controller=>"relationships"}
POST /relationships(.:format) {:action=>"create", :controller=>"relationships"}
You should point the delete request to single resource url eg. relationships/4325. Run rake routes to view what url/verb combinations are valid.
--edit
Routes for relationship resources:
resources :relationships, :only => [:index, :create, :destroy]
Unfollow button (creates a form for itself):
= button_to "Unfollow", relationship_path(relationship), :method => 'delete'

Posting to controller via link_to

I am trying to send a :vote parameter of 'up' to my controller, so that it performs the voting function of current_user.vote_exclusively_for(#book). I am using the thumbs up gem.
I am trying to do this using link_to, and the correct parameters are showing up in my server output, but it is not working with the controller. I must be doing something wrong, but I am not sure what. Do i need to do something different with routes, other than books :resources?
This my vote action in books_controller
def vote
#book = Book.find(params[:id])
if params[:vote] == 'up'
current_user.vote_exclusively_for(#book)
end
redirect_to #book
end
And this is the link_to example in my view:
<%= link_to "Vote Up", :url => { :controller => "books", :action => "vote", :vote => "up"}, :method => :post %>
Any advice on where my attempts are breaking down would be greatly appreciated ( extra note: when i put the current_user.vote_exclusively_for(#book) function in my view it works) so I think this is a view/routes/link_to issue, not the function itself.
I don't understand your link_to. It seems to be missing the ID of the book it's voting on?
Make sure your routes.rb file looks like this:
resources :books do
post :vote, :on => :member
end
Then change your link_to function to this:
link_to "Vote Up", vote_book_path(#book, :vote => "up"), :method => :post
I just had a similar problem and solved it by using this style syntax:
<%= link_to "Vote Up", {:controller => "books", :action => :vote, :vote => "up" }, {:method => :post} %>
Also make sure your routes.rb has something similar to
resources books do
post :vote
end

Resources