Rails route helpers for map.connect - ruby-on-rails

in my current rails application I have a bunch of named routes defined to deal with the static content like this:
map.with_options :controller => 'static_content' do |static|
static.imprint 'imprint', :action => 'imprint'
static.menu1 'menu1', :action => 'menu1'
static.menu1_sub1 'menu1/sub1', :action => 'menu1_sub1'
static.menu1_sub2 'menu1/sub2', :action => 'menu1_sub2'
static.menu2 'menu2', :action => 'menu2'
...
end
Now I'd like to refactor this quite disgusting piece of routing to have something like this:
map.connect 'menu1/:action', :controller => 'static/menu1'
map.connect 'menu2/:action', :controller => 'static/menu2'
...
I created the controller namespace static and map the actions of all those controllers in the namespace. But now - of course - all those helpful route helpers like menu1_sub2_path stop working and I'll have to change them.
Uff! Refactor all usages of path helpers to ugly :controller-:action-style?
So my question is if anybody sees a good way to surround this. Is there a way to define those path helpers - or the way they are created? Or even a smarter way to do those nasty mappings?
Thanks for your help,
Joe

map.with_options :controller => 'static_content' do |static|
static.page ':action'
end
then call it:
page_path(:imprint)

Related

How to pass a default query param with Rails 2.3.x routing

I'm trying to do something trivial. I have a bunch of URLs that I need to map like the following:
http://example.com/foo
http://example.com/foo/something
Both need to go to the same controller/action. The problem I'm having is when http://example.com/foo is invoked, I need to specify a default query parameter. I thought that's what the :defaults hash does in routes.rb, but unfortunately the following doesn't work:
map.connect 'foo', :controller => 'something', :action => 'anaction',
:defaults => { :myparam => 'foobar' }
This should route http://example.com/foo to the something controller, anaction action, and make params[:myparam] point to the string "foobar".
I'm assuming for the second example http://example.com/foo/something, I'll need an additional route.
What's the best way to tackle this?
I wouldn't complicate things by adding such logic to my routes file, I'd just do it in my action:
params[:my_param] ||= 'foobar'
Untested, but:
map.connect 'foo', :controller => 'something', :action => 'anaction', :myparam => 'foobar'
It looks like the :controller and :action arguments in there are not in any way special, but just end up feeding into params. The 2.3.8 documentation seems to confirm this.
More formally, you can include
arbitrary parameters in the route,
thus:
map.connect ':controller/:action/:id', :action => 'show', :page => 'Dashboard'
This will
pass the :page parameter to all
incoming requests that match this
route.

Get SEO friendly URLS with Rails without method_missing?

Currently we are using method_missing to catch for calls to SEO friendly actions in our controllers rather than creating actions for every conceivable value for a variable. What we want are URLS like this:
/students/BobSmith
and NOT /students/show/342
IS there a cleaner solution than method_missing?
Thank you!
You can define a route for that particular format fairly easily.
map.connect "/students/:name", :controller => :students, :action => :show, :requirements => {:name => /[A-Z][A-Z]+/}
Then in your show action you can find by name using params[:name].
You can create a catch-all route. Put this at the bottom of config/routes.rb with whatever controller and action you want:
map.connect '*path', :controller => '...', :action => '...'
The segments of the route will be available to your controller in the params[:path] array.

Rails routes matching query parameters

Rails routes are great for matching RESTful style '/' separated bits of a URL, but can I match query parameters in a map.connect config. I want different controllers/actions to be invoked depending on the presence of a parameter after the '?'.
I was trying something like this...
map.connect "api/my/path?apple=:applecode", :controller => 'apples_controller', :action => 'my_action'
map.connect "api/my/path?banana=:bananacode", :controller => 'bananas_controller', :action => 'my_action'
For routing purposes I don't care about the value of the parameter, as long as it is available to the controller in the params hash
The following solution is based on the "Advanced Constraints" section of the "Rails Routing from the Outside In" rails guide (http://guides.rubyonrails.org/routing.html).
In your config/routes.rb file, include a recognizer class have a matches? method, e.g.:
class FruitRecognizer
def initialize(fruit_type)
#fruit_type = fruit_type.to_sym
end
def matches?(request)
request.params.has_key?(#fruit_type)
end
end
Then use objects from the class as routing constraints, as in:
map.connect "api/my/path", :contraints => FruitRecognizer.new(:apple), :controller => 'apples_controller', :action => 'my_action'
Unless there is a concrete reason why you can't change this, why not just make it restful?
map.connect "api/my/path/bananas/:id, :controller => "bananas_controller", :action => "my_action"
If you have many parameters, why not use a POST or a PUT so that your parameters don't need to be exposed by the url?

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