ActionController::MethodNotAllowed - ruby-on-rails

I have a rails model called 'audioclip'. I orginally created a scaffold with a 'new' action, which I replaced with 'new_record' and 'new_upload', becasue there are two ways to attach audio to this model.
Going to /audioclips/new_record doesn't work because it takes 'new_record' as if it was an id. Instead of changing this, I was trying to just create '/record_clip' and '/upload_clip' paths.
So in my routes.db I have:
map.record_clip '/record_clip', :controller => 'audioclips', :action => 'new_record'
map.upload_clip '/upload_clip', :controller => 'audioclips', :action => 'new_upload'
When I navigate to /record_clip, I get
ActionController::MethodNotAllowed
Only get, head, post, put, and delete requests are allowed.
I'm not extremely familiar with the inner-workings of routing yet. What is the problem here? (If it helps, I have these two statements above map.resources => :audioclips

Yes, your two routes are conflicting with your mapped resource (the map.resources => :audioclips bit).
If you want to continue using resources, I suggest you change that line to:
map.resources => :audioclips,
:new => {
:new_record_clip => :post,
:new_upload_clip => :post }
If you want some more information, the Rails guide is incredibly helpful on this topic:
http://guides.rubyonrails.org/routing.html#adding-more-restful-actions

Related

Why to add a connection in routes file when using link_to in rails 2

I was trying to accomplish the following:
<%= link_to "Log out", { :controller
=> 'users', :action => 'logout' }, :class => 'menulink2' %>
But it didn't work, it always redirected me to a show view. I had to had the following to my routes.rb:
map.connect 'users/logout',
:controller => 'users', :action =>
'logout'
Why didn't rails recognize the action I was passing ('logout') ?
That logic has to be specified somewhere. There's got to be some mapping from the hash {:controller => 'users', :action => 'logout'} to a url, and the place that's done in rails is the routes.rb file. In older versions of rails many routes.rb came with a default at the end:
map.connect ':controller(/:action/(:id(.:format)))'
Which would make it so that most any :controller, :action hash could be specified and then routed to host.url/:controller/:action.
With the more modern versions resource-based routes are heavily favored, and controllers which don't follow rails' REST conventions (i.e. having only :index,:show,:create,:new,:edit,:update,:destroy methods) generally have to have their routes explicitly specified in some way.
(Either with map.resources :users, :collection => {:get => :logout} or with map.connect( 'some_url', :controller => 'users', :action => 'logout'}))
I'm guessing, but the reason they did that is probably that the actions of a controller are really just its public methods.
It's frequently nice to have public methods in your controllers that aren't url-end-points for testing purposes.
For instance, you could have before_filters as public methods that you might want to test without having to use #controller.send(:your_before_filter_method) in your test code.
So they whitelist the resource actions, and make the others unreachable by default. Let me look through the rails changelog and see if I'm right.

How to rename the default REST routes in rails?

Before all, this question is about Rails 2.x.
I live in a spanish language country and the URLs for my web apps should be in spanish. I always created spanish spelled actions for my controllers until now, but that just turn off many of the advantages for using REST, like the built-in PUT method => edit action stuff.
So, I wanna know how to modify the routes.rb file for redirect all the traffic for all my existing and future resources without losing the RESTful standars.
Is this possible?
Example:
POST /inmuebles
:controller => inmuebles, :action => create
GET /inmuebles
:controller => inmuebles, :action => index
GET /inmuebles/nuevo
:controller => inmuebles, :action => new
Piggy backing off of Andrew V's answer, but couldn't preview my comment...
Since all of your resources will likely have the same actions that need the same path names, you can use a with_options block to set these for all routes.
For example:
map.with_options :path_names => {:new => 'nuevo', :edit => 'editar'} do |rt|
rt.resources :ineubles
rt.resources :pollos
rt.resources :gatos
end
Use the :path_names option:
map.resources :inmeubles, :path_names => { :new => 'nuevo'}
You can also try this i18n_routing gem http://github.com/kwi/i18n_routing

Is it possible to create a route like '~:id' in Rails 2.x?

I have been using the following route successfully in my Rails 2.x application:
map.user ':id', :controller => 'users', :action => 'show'
This, as my lowest route, properly catches things like /tsmango and renders Users#show.
I'm now trying to add a second, similar route like:
map.post '~:id', :controller => 'posts', :action => 'show'
Because neither my users or my posts are allowed to contain ~ and because this route will appear above my map.user route, I assumed this would properly catch any call starting with /~ and render my Posts#show action. Unfortunately, I'm having trouble getting this one to work.
What's interesting is that this similar route works perfectly:
map.post ':id~', :controller => 'posts', :action => 'show'
Although, I'm certainly willing to go with ':id~' since it has the same result, at this point I'm really just frustrated and curious as to how you would build a route that matches '~:id'.
It's worth mentioning that I do not want to modify my to_param method or my actual user and post slugs to include the prepended ~. I just want that in a route to indicate which action should handle it. Unless I'm mistaken, this rules out the use of something like:
:requirements => {:id => /\~[a-zA-Z0-9]/}
Thanks, in advance, for any help you can provide!
Update: I'm aware of route priority and stated above that I am placing the '~:id' route above the ':id' route. I receive the following error while trying to generate the url like post_path(#post):
You have a nil object when you didn't expect it!
The error occurred while evaluating nil.to_sym
Routes are prioritized depending of the order in which they're declared.
When you define first the :id route, the second one is never executed.
In order for this to work, you just have to first define the ~:id route and then the :id one.
map.post '~:id', :controller => 'posts', :action => 'show'
map.post ':id', :controller => 'users', :action => 'show'

Can controller names in RESTful routes be optional?

With a standard map.resource routing mechanics and several nested resources the resultant routes are unnecessarily long. Consider the following route:
site.org/users/pavelshved/blogs/blogging-horror/posts/12345
It's easy to create in routes.rb, and I'm sure it follows some kind of beneficial routing logic. But it's way too long and also seems like it's not intended to be human-readable.
A nice improvement would be to drop controller names, so it looks like:
site.org/pavelshved/blogging-horror/12345
Clear, simple, short. It may become ambiguous, but in my case I'm not going to name any user "users", for instance.
I tried setting :as => '', but it yields routes like this: site.org//pavelshved//blogging-horror//12345 when generating them by standard helpers.
Is there a way to map resources in such a way, that controller names become optional?
You're looking for the :path_prefix option for resources.
map.resources :users do |user|
user.resources :blogs do |blog|
blog.resources :posts, :path_prefix => '/:user_login/:blog_title/:id'
end
end
Will produce restful routes for all blogs of this form: site.org/pavelshved/bogging-horror/posts/1234. You'll need to go to a little extra effort to use the url helpers but nothing a wrapper of your own couldn't quickly fix.
The only way to get rid of the posts part of the url is with named routes, but those require some duplication to make restful. And you'll run into the same problems when trying to use route helpers.
The simplest way to get what you want would be to create a route in addition to your RESTful routes that acts as a shorthand:
map.short_blog ':user_id/:blog_id/:id', :controller => 'posts', :action => 'show'
You'll have to change the URL bits to work with how you're filtering the name of the user and the name of their blog. But then when you want to use the shorter URL you can use all the short_blog_* magic.
Straight out of the default routes.rb:
map.connect 'products/:id', :controller => 'catalog', :action => 'view'
You could write:
map.connect ':user_id/:blog_id/:id', :controller => 'posts', :action => 'show'
But be sure to include that in the very end of the file, or it will try to match every three levels deep url to it.
Try this
map.pavelshved '/pavelshved/', :controller => :users, :action => view or
map.pavelshved '/:id', :controller => :users, :action => show do | blogs|
blogs.bloging '/:id', :controller => :blogs, :action => show do | post|
post.posting '/:id', :controller => :posts, :action => show
end
end
I hope it work :)
Google "rails shallow routes" for information about this.

Routing in Rails

I'm designing a minimalistic wiki in RoR. Basically a project have many pages. My routing file looks like this:
map.root :controller => "projects"
map.resources :projects, :has_many => :pages
map.connect ':id', :controller => 'projects', :action => 'show'
map.connect ':project_id/:id', :controller => 'pages', :action => 'show'
map.connect ':controller/:action/:id'
map.connect ':controller/:action/:id.:format'
This allow me to access, for example, the 'main' page of 'teaching' project like this:
http://localhost:3000/teaching/main
However, as soon as I click a link, it gets transformed to this:
http://localhost:3000/projects/teaching/pages/main
How can I make the helper methods that create URLs to stick to the scheme I want? I tried named routes, but I must be missing something out because it didn't worked...
I would ditch the map.connect stuff - it's not very RESTful, and can get you very confused.
At first glance I thought you could use the :member and :collection directives to add in what you wanted, but when I look more closely I realised it won't help.
I'm having a little difficulty understanding your data model - a page can have many pages? Or is teaching the project name and then it has pages?
If that is the case, then you probably need to look at a plugin like SubDomainFu and use subdomains based on project names, rather than hacking the routes file directly. We have used this successfully to give a scheme like you describe (the domain implies the project, bit of extra code required) and also things like teaching.yourdomain.com and learning.yourdomain.com (which can be fun if you want to use SSL, but that's a different story).
Rails is all about convention over configuration. You have to buy into the conventions if you want the convenience that Rails brings. I would strongly encourage sticking to the RESTful model and accept what rails is doing now.
With that said, you can probably hack something together. It won't be pretty and it'll be a pain every time you want to create a link.
So first get rid of
map.resources :projects, :has_many => :pages
map.connect ':controller/:action/:id'
map.connect ':controller/:action/:id.:format'
because the map.resources is thing that is sending you to url you don't want and the two map.connect's don't help you either. So now you should just have
map.connect ':project_id/:id', :controller => 'pages', :action => 'show'
map.connect ':id', :controller => 'projects', :action => 'show'
Then when ever you want to create a link you are going to have to make it yourself. You are going to want to use something like this:
<%= link_to 'Blah', :project_id => #project, :id => #project.pages.first %>
or something like that. I don't know if that is exactly how are going to specify the route, you are probably going to have to change the :project_id and :id symbols.
Like I said you don't want to take this route if you don't have to. Stick to the conventions as much as you can because it'll make your life much easier.

Resources