Rails controller edit action - how to avoid the id to be displayed? - ruby-on-rails

I have a resource named offer. When I edit the offer the URL looks like “/offers/1/edit”. How is it possible to avoid the ID to be displayed in the URL completely?
I want to store the offer ID in the session instead as the offer actions should be public. I’m afraid if one would try to enter different IDs in the URL manually. Is that possible?
My desired URL would look like this: “/offers/edit”.
The create action responds this: render "edit". Here the ID is not displayed, the URL is just “/offers”, fine.
But this response (render "edit") leads to displaying the URL with the ID again, grrr.
Is there a solution to that?
Thanks for your help!
Marc

You can overwrite method to_param in your model
def to_param
your_item.name.parameterize
end
then router will produce something like this “/offers/name_of_your_item/edit”.

You could (although it is a kind of misuse) use a resource mapping instead of a resources.
You could add resource :offers to your routes.rb.
That will give you routes like this:
offers POST /offers(.:format) {:action=>"create", :controller=>"offers"}
new_offers GET /offers/new(.:format) {:action=>"new", :controller=>"offers"}
edit_offers GET /offers/edit(.:format) {:action=>"edit", :controller=>"offers"}
GET /offers(.:format) {:action=>"show", :controller=>"offers"}
PUT /offers(.:format) {:action=>"update", :controller=>"offers"}
DELETE /offers(.:format) {:action=>"destroy", :controller=>"offers"}
Alternatively I would simply not use the standard edit mapping and add my own:
resources :offers, :except => [:edit, :update] do
get :edit, :on => :collection
put :update, :on => :collection
end
If my memory serves me right then the following should work in Rails 2.3.x:
map.resources :offers, :except => [:edit, :update], :collection => { :edit => :get, :update => :put }
I don't have such environment handy so I cannot test it at the moment...

Related

Routes on Rails application are failing

I'm taking an MOOC and the goal of this exercise is to add a new functionality to typo, where i can merge two articles together.
When I add the route to my new function merge to the routes.rb I'm losing the functionality to delete articles. I think something clashes here, but I have no idea what.
The original routes.rb:
%w{advanced cache categories comments content profiles feedback general pages
resources sidebar textfilters themes trackbacks users settings tags redirects seo post_types }.each do |i|
match "/admin/#{i}", :to => "admin/#{i}#index", :format => false
match "/admin/#{i}(/:action(/:id))", :to => "admin/#{i}", :action => nil, :id => nil, :format => false
end
This method in articles.rb creates the correct url for deleting
def delete_url
blog.url_for(:controller => "/admin/content", :action =>"destroy",:id => id)
end
correct url:
http://example.com/admin/content/destroy/7
If i follow this link i can successfully delete an article.
However, if I add the following before that to my routes.rb:
namespace "admin" do
resources :content do
post :merge, on: :member, as: :merge
end
end
The new merging functionality and forms are working fine, but the method delete_url now produces something like this:
http://example.com/admin/content/7
and if I follow a link created by this method i get:
Unknown action
The action 'show' could not be found for Admin::ContentController
Maybe I'm overwriting something? I can't figure out what's happening here and why this affects the delete action / route.
Thanks in advance!
EDIT: rake routes | grep content:
with the original routes.rb gives me:
admin_content /admin/content {:controller=>"admin/content", :action=>"index"}
/admin/content(/:action(/:id)) {:action=>nil, :id=>nil, :controller=>"admin/content"}
whereas my modified routes.rb produces
merge_admin_content POST /admin/content/:id/merge(.:format) {:action=>"merge", :controller=>"admin/content"}
admin_content_index GET /admin/content(.:format) {:action=>"index", :controller=>"admin/content"}
POST /admin/content(.:format) {:action=>"create", :controller=>"admin/content"}
new_admin_content GET /admin/content/new(.:format) {:action=>"new", :controller=>"admin/content"}
edit_admin_content GET /admin/content/:id/edit(.:format) {:action=>"edit", :controller=>"admin/content"}
admin_content GET /admin/content/:id(.:format) {:action=>"show", :controller=>"admin/content"}
PUT /admin/content/:id(.:format) {:action=>"update", :controller=>"admin/content"}
DELETE /admin/content/:id(.:format) {:action=>"destroy", :controller=>"admin/content"}
/admin/content {:controller=>"admin/content", :action=>"index"}
/admin/content(/:action(/:id)) {:action=>nil, :id=>nil, :controller=>"admin/content"}
Okay, thanks to #guitarman i worked through my routes code and found out I can add the following except:
namespace "admin" do
resources :content, except: [:index, :show, :update, :destroy, :edit, :new, :create] do
post :merge, on: :member, as: :merge
end
end
after that, the rake routes just shows the additional merge route I wanted and my destroy action works fine again.
Check rake routes command. I think there is a route /admin/content/:id which will be created by resources :content in the namespace "admin".
Your request to http://example.com/admin/content/7 will be catched be the defined route but I gess you have no show action in the controller.
Better:
namespace "admin" do
post "/content/:id/merge", to: "admin/content#merge", as: :merge
end
For more information about recources and the CRUD operations please see the rails routing guide.

When should I create named routes in Rails?

I'm confused by Rails 3 resource routes. I have following line in my routes.rb
resources :dungeons, only: [ :index, :destroy, :create, :update, :show ]
When I inspect what named routes are create with rake routes, I get:
dungeons GET /dungeons(.:format) dungeons#index
POST /dungeons(.:format) dungeons#create
dungeon GET /dungeons/:id(.:format) dungeons#show
PUT /dungeons/:id(.:format) dungeons#update
DELETE /dungeons/:id(.:format) dungeons#destroy
Why are there only named routes for the routes with a http get method? If I want to create a link to the destroy action, I have to use something like { :action => 'destroy', :method => :delete, :id => dungeon.id } instead of simply destroy_dungeon_path( dungeon ). Is there something wrong with my routes.rb?
Nothing wrong with your routes file. This is the destroy route: dungeon_path(id)
You have to send a DELETE request to trigger it. The show, update and destroy got the same named_route, the only thing what is different is the type of Request (GET for show, PUT for update or DELETE for destroy)
Here everything you need to know routing in Rails3: http://guides.rubyonrails.org/routing.html

Rails routing. Singular resource

I've got Rails routing problem. I would like to use singular resource with user controller but it doesn't work as I expected. Here is the fragment of my routes.rb file:
scope :module => "frontend" do
root :to => "home#index"
resource :user, :controller => "user"
get "/sign_up" => "user#new"
get "/sign_in" => "user#sign_in"
get "/sign_out" => "user#sign_out"
post "/authenticate" => "user#authenticate"
resources :articles
resources :article_categories
end
I thought it will work when I'll use for example "/user" or "/user/new" URL but it didn't. I get a routing error:
No route matches {:controller=>"frontend/user"}
The 'rake routes' command output is:
user POST /user(.:format) frontend/user#create
new_user GET /user/new(.:format) frontend/user#new
edit_user GET /user/edit(.:format) frontend/user#edit
GET /user(.:format) frontend/user#show
PUT /user(.:format) frontend/user#update
DELETE /user(.:format) frontend/user#destroy
sign_up GET /sign_up(.:format) frontend/user#new
sign_in GET /sign_in(.:format) frontend/user#sign_in
sign_out GET /sign_out(.:format) frontend/user#sign_out
authenticate POST /authenticate(.:format) frontend/user#authenticate
What is interesting, when I add route for index action in user controller, like this:
scope :module => "frontend" do
root :to => "home#index"
resource :user, :controller => "user"
get "/user" => "user#index"
get "/sign_up" => "user#new"
get "/sign_in" => "user#sign_in"
get "/sign_out" => "user#sign_out"
post "/authenticate" => "user#authenticate"
resources :articles
resources :article_categories
end
...it works!
But index action is not defined in user controller!
'rake routes' command returns double line for GET /user
GET /user(.:format) frontend/user#show
GET /user(.:format) frontend/user#index
so I suppose that's not the solution. Other actions assigned to '/users' URL don't work.
Is it necessary to define the route for the index action like
get "/controller_name" => "controller_name#index"
What am I doing wrong?
Defining a singular resource in your routes will not generate a route to an index action by design. The singular resource implies you're always going to lookup this resource without specifying an ID and consequently a get to index for a singular resource just doesn't make logical sense. So, a GET to your url "/user" will route to a show action for that singular resource and not an index.
EDIT: Since your issue isn't obvious, I'd simplify your routes until you can at least hit the controller you'd expect and then build from there.
config/routes.rb
scope :module=>"frontend" do
resource :user
end
#ensure you don't have any other user routes listed before this that would match "/user".
app/controllers/frontend/users_controller.rb
module Frontend
class UsersController < ApplicationController
def show
raise "in frontend/show"
end
end
end
Thanks a lot for help! I found the bug.
The routing error was caused by the following line of the layout html file
<%= auto_discovery_link_tag(:rss, {:action => "index"}, {:title => "RSS"}) %>
I was looking for errors in the erb view files but I forgot about the layout.
I must remember to check the entire view layer in such situations.

How to write match in route.rb

In my AdminController, I have methods named as edit, update and update_admin. And in route.rb
resources :session, :only => [update]
match '/:controller(/:action(/:id))'
end
When I navigate the url '/users/edit/1' matches. From this page i want to call the action method in update_admin in AdminController. How to do this?
My edit.erb has
<%= render :partial => '/submit', :locals =
> {:button_html => f.submit('Update'), :validate_present => true}
%>
at first, to check your routes go to console and do
rake routes | grep session
there you will get list all routes of your rails application (matched to the grep)
at second i dont get your routes.rb
would you do
resources :session, :only => [update] do
match '/:controller(/:action(/:id))'
end
or
resources :session, :only => [update]
match '/:controller(/:action(/:id))'
end
these are 2 different things. i think you want the last one. but here we go, there is another problem
resources :session, :only=>[update]
this throws an error.(undefined local variable or method `update' for #)
if you want to specify actions, you need to do it as a key
resources :session, :only=>[:update]
but you also want the edit message, (edit is the form, update the action to save the changes) so you have to do
resources :users, :only=>[:edit, :update]
now check your rake routes and see voila!
edit_session GET /session/:id/edit(.:format) {:action=>"edit", :controller=>"session"}
session PUT /session/:id(.:format) {:action=>"update", :controller=>"session"}
//edit
if you want to do this in your admin_controller you should have a namespace
#i take example for a user
namespace :admin do
resources :user :only => [:edit, :update]
end
if you want now to link to it from a view the route is named
edit_admin_user_path
and in a form you need to bring the namespace also in the form_for like:
=form_for [:admin, #user] do |f|

using form_for with a singular model

I have a Wizard model that the client references w/o an ID (it's saved in the session), so I've created a singular resource for :show and :update. I want the admin to have access to all instances of that model via index so admin can delete strays, so I've added a plural resources for :index and :destroy. The index and destroy works, but I can't figure out the right arguments to pass to form_for in the update view.
The setup
# config/routes.rb
WTest::Application.routes.draw.do
resource :wizard, :only => [:show, :update]
resources :wizards, :only => [:index, :destroy]
...
end
resulting in
$ rake routes
wizards GET /wizards(.:format) {:action=>"index", :controller=>"wizards"}
wizard DELETE /wizards/:id(.:format) {:action=>"destroy", :controller=>"wizards"}
GET /wizard(.:format) {:action=>"show", :controller=>"wizards"}
PUT /wizard(.:format) {:action=>"update", :controller=>"wizards"}
This sets up routes the way I'd expect.
The question (revised since original post)
In the console:
>> app.wizard_path
raises the error ActionController::RoutingError: No route matches {:action=>"destroy", :controller=>"wizards"}
Why is this? Have I set up my routes incorrectly? I need to specify :url => wizard_path for form_for() in the wizards's update view.
The details
If I specify an explicit path in my call to form_for:
# app/view/wizards/update.html.erb
<%= form_for #wizard, :url => wizard_path do |f| %>
<%= f.submit %>
<% end %>
... then attempting to render this for gets an error on the form_for line:
No route matches {:action=>"destroy", :controller=>"wizards"}
I have no idea why it's trying to match the destroy action. How do I get the form to submit to the {action=>"update", :controller=>"wizards"} route?
(By the way, I looked at bug 267, and I don't think it is the same as what I'm observing. But if it is this bug, is there a workaround?)
Carrying on the long tradition of answering my own questions (meh!), I think I figured it out. If my analysis is wrong, I'd be happy to give someone else the checkmark...
The cause of the problem
Look at the output of rake routes
$ rake routes
wizards GET /wizards(.:format) {:action=>"index", :controller=>"wizards"}
wizard DELETE /wizards/:id(.:format) {:action=>"destroy", :controller=>"wizards"}
GET /wizard(.:format) {:action=>"show", :controller=>"wizards"}
PUT /wizard(.:format) {:action=>"update", :controller=>"wizards"}
The path method 'wizard_path' is ambiguous: it can either refer to the DELETE clause, in which case it needs an :id argument (wizard_path(22)), or it can refer to the GET and PUT clauses, in which case it doesn't take an ID argument.
The solution
So my solution was to create a route specifically for deletion. My revised routes.rb file now reads:
resources :wizards, :only => [:index]
resource :wizard, :only => [:show, :update]
match 'wizard/:id' => 'wizards#destroy', :via => :delete, :as => :delete_wizard
and rake routes now produces:
$ rake routes
wizards GET /wizards(.:format) {:action=>"index", :controller=>"wizards"}
wizard GET /wizard(.:format) {:action=>"show", :controller=>"wizards"}
PUT /wizard(.:format) {:action=>"update", :controller=>"wizards"}
delete_wizard DELETE /wizard/:id(.:format) {:controller=>"wizards", :action=>"destroy"}
I needed to make a one-line change to the delete link in wizards/index.html.erb to use the new delete_wizard_path, but everything works now.

Resources