Custom Nested Routing in Rails 2.3 - ruby-on-rails

I'm trying to change some URLs in this project I'm working on, but I can't find any examples of what I'm looking for in any documentation and all my attempts seem to end up not working.
Here's what I'm trying to do. I have a post that has several images, and an individual image would be accessed with an URL like this:
/posts/aliased_title/images/2
However, I want to change it so I can remove the "posts" and "images" part of the URL, change it to something like /aliased_title/2.
I know how to do this for a single controller, but how can I accomplish this with the images controller as well? Can I do this in a way that will generate the "paths" (like post_image_path), or do I need to hard code every single action into the routing?
Thank you in advance.

Depending what your routes and aliased titles look like, you can use something like the following:
resources :images, :path => '/:title/:id', :constrain => { :title => /[a-z_-]+/ }
# in your view
image_path(image.title, image)
Keep in mind that routes match in the order they are defined, so you will want to put this line down at the bottom of your routes file so that it doesn't interfere with routes like /users/2.
All this said, you should strongly consider whether your use case can live with an anchor like /image. It'll prevent a lot of confusing problems with routing that will very likely bite you in the ass down the line. It's not infeasible to do without the anchor, but it does create additional work.

Related

Self nested infinite routes

Is there a way to self nest a resource in the routes infinetelly?
Suppose the following scenario: I want to create several pages that may be self nested one to another, like a products page has many product pages and each product page has several sub-pages etc.
The resource would be a page in a tree structure , like with the awesome nested set gem.
In case an admin can create pages at unknown depth level how would I make the routes?
The above example has to produce a url like /:friendly_id_of_level_1/:friendly_id_of_level_2/.../:friendly_id_of_level_n
I've tried going with dynamic routes but it has many drawbacks this way.
Any suggestions?
I do a similar thing to this in one of my apps with this route:
map.connect "/c/*modules", :controller => "content", :action => "show"
(Note this is using rails2 routing syntax, you might need to update it).
This will resolve this url
/c/123-foo/456-bar/789-baz/653-qux
to the content#show action, with params set like
params = {"modules"=>[123-foo", "456-bar", "789-baz", "653-qux"]}
The modules are in a tree structure, so i can use the sequence of module ids in params[:modules] to make a breadcrumb chain and any other heirarchical data, and i use the last one in the array as the "current" one to actually show to the user.
Note: I put the "/c/" at the start of the url to separate these nested routes out from all my other routes: otherwise it's very greedy and will match pretty much any url on your site. That's not a problem if you want to always have it as your "catchall" route down at the bottom of your routes file, but if not then you'll need to add something to make it distinct. Obviously this doesn't need to be "/c/", you could have anything which will stop it colliding with your other routes.

Intricate Rails 3 Routing Configuration - Can routes.rb Access Initializers' Code?

I'm converting a bunch of landing pages written in php in order to add them to my RoR-based site (that's been live for more than 2 years now). These landing pages are divided into several versions, but unfortunately there is no consistency as far as URL names go. My problem is that the php pages I'm converting already have a high page rank, therefore I'd like to keep their URLs exactly the way it was.
I'm not sure how to set my routes.rb so that example.com/* will always go to my homepage; however, when (* == 'name-of-one-of-the-landing-pages') Rails will route to a separate controller, where a specific action will determine which page to render, based on an Initializer and the params hash, all this while the URL is, as mentioned, identical to what it was prior to the php-to-RoR conversion, namely www.example.com/name_of_landing_page, rather than www.example.com/*controller_name*/name_of_landing_page.
I know of the :path property that enables one to exclude the controller name from the path if passed an empty string (i.e. resources :examples, :path => ''), but that doesn't quite solve the entire problem.
I was thinking about writing an initializer that would hold a hash of all relevant landing pages, and using constraints in routes.rb to check against it, but I'm not sure if this kind of implementation is possible and how to go about it. A code example would be much appreciated.
Is there some kind of syntax for routes.rb that would enable me to do so, or perhaps a better solution?
To answer the first question: in routes.rb, inside the do/end block you will actually be in the context of ActionDispatch::Routing::Mapper, so no you won't. But, right after that block, you are back to the top level of your application and will have access to whatever variables you initialized inside your initializers, however, that code might be better suited to go in application.rb.
The only thing you should be doing in routes.rb is defining routes.
You could also handle the request for the legacy pages in rack
def call(env)
request = Rack::Request.new(env)
return [200, {"Location" => request.url("http://www.example.com")} if request.host == "www.oldpage.com"
end
More info here: http://railscasts.com/episodes/222-rack-in-rails-3
I don't understand why complex things involved. Setting such should be very simple in route.
Suppose your have a controller to handle static pages named "PagesController"
get 'name-of-one-of-the-landing-pages-a', to: 'pages#a'
get 'name-of-one-of-the-landing-pages-b', to: 'pages#b'
There is no need to add controller names in the path. You can control all of them.

Basic Rails 3 Routing Question

I am trying to make some clean URLs in a Rails3 application I am working on... but I am having a hard time understanding how to (or if I even should) customise my routes to make this work.
Here is the example:
I have a list of Stores. Each store is in a category (health, sports etc). Each store has a location.
I have 2 ways I'd like to present the data. One display is a list of all the stores in a directory type structure, the other is on a map.
Ideally I'd like my URLs to work something like this:
/stores/health/map (or /stores/map/health) to show just the health stores on a map (where essentially the map parameter is effecting which view is displayed, but still using the Index controller... which using a collection in my route doesn't seem to suit)
The other URL I'd like is /stores/sports/ to show just the sports stores in a directory view (the default) for example...
I am not entirely clear how I can manipulate the routes to handle this...
Here is my current Route which isn't really doing it for me:
resources :stores do
collection do
get 'map'
end
end
On top of that, I'd like to be able to add filters without using ?query=params... so:
/stores/sports/hockey , would essentially filter out only hockey stores...
I have no issues doing this with ?query, it's just putting my params into a nicer URL that I'm trying to achieve.
The documentation does not seem to outline what I am trying to do, so Im assuming what Im trying to do is wrong.
Is this breaking REST? Am I looking at it all backwards?
Thanks for your help, JD
You might be overthinking this. :-)
If you want to route HTTP Get of 'stores/health/map' to the StoresController with an action name of, say, health_map, what you need to do is:
get 'stores/health/map' => 'stores#health_map'
Anything that is a clean URL and doesn't modify data and uses HTTP GET is RESTful. (And that is coming from a co-author of a book on REST). It is when you wish to modify data that you need to be more careful on how you use methods.
To do filtering, try something like:
get '/stores/sports/:filter' => 'stores#sports'
The value of the filter will come into your method as params[:filter]

URL rewriting in ROR

I am Amit. i am new to Rails. Please forgive me if ask any stupid
questions.
I have gone through this article. I am also suffering with the same
problem.
my website URL like this: locahost:3000/users/edit/30
I don't want to show the controller:users and action: edit.
I want to Re-Write (rewrite) the URL or i want to maintain the URL as
http://127.0.0.0:3000/30/ only.(30 is a user id)
I am not interested to show the controller(user) and action (edit)
I total intention is to Hiding (HIDING) and rewriting (REWRING) the URL
and mainly i want to hide the URL Extensions with controller and actions
mainly..
It's an odd requirement to want to use a route this way as it will make it difficult for you to expand this scheme to support other actions in your application. One of the advantages of Rails conventions done as they are is that you usually don't have to worry about (often trivial) application details or have to have strong opinions about them.
But it you really, really want to, you can add this to your config/routes.rb
ActionController::Routing::Routes.draw do |map|
map.connect "/:id", :controller => "users", :action => "edit"
end
Remember that this limited scheme will mean you can only route to the edit action. Not terribly useful I would suggest.
The rails routes.rb file is your answer:
Lots of information here on how to do exactly what you need:
http://guides.rails.info/routing.html#customizing-resourceful-routes
For most application I have been using the standard:
/script.cgi?arg1=foo&arg2=bar
Now obviously /user/edit/eric looks much better for a URL that involves
editing the "Eric" user. But how does this work out when you have
multiple items. For example if I want to delete a group of users. I
might have something like this in the old way:
/user/delete.cgi?id=3&id=5&id=7
or for a more readable format we could have:
/user/delete.cgi?username=eric&username=john&username=paul
This of course assumes that usernames are unique. :) So it seems that I
would want something like:
/user/delete/eric/john/paul
This makes it obvious what is happening from the url. But how do I
generate this? And if a action receives this request how does this get
parsed so I have something like:
#delete_usernames ===>>> [ 'eric', 'john', 'paul' ]
This way I can then load up those users and delete them. I know that
ActionController provides some methods for dealing with URL rewriting
but I am unsure how to best use them. Any help or pointers are greatly
appreciated.

Rails: resources for unorthodox urls?

I'd like to have a URL like this:
/payroll/region/1
and I'd like it to map to the Tasks Controller's payroll_list function. I'd also like to use REST. What's the best way to do this?
Many thanks!
Well I'd suggest you better go with the convention how Rails handles this. If you still insist on using such "strange" URLs and want to ignore the problems/headaches this can create during further development, then try to use Refraction.
I don't want to be rude but currently it seems to me that you did not understand why restful URLs are the way they are. Please do understand the design behind this first, then rethink your application/controller and routing design. I bet you will be enlighted.
In this example, your URL should probably be /regions/1/payrolls with map.resources :regions, :has_many => :payrolls. Then your payroll list would be rendered by the PayrollsController having a params[:region_id] - and that actually makes sense (and probably what you tried to achieve with your URL layout). Code snippet:
def index
if params[:region_id]
#region = Region.find(params[:region_id])
#payrolls = #region.payrolls
else
#payrolls = Payroll.all
end
end
If you still want to have a resource under a different named URL, use the following:
map.resources :regions do |regions|
regions.resources :tasks, :as => :payrolls
end
This will map the nested resources to the tasks controller using the named URL part "payrolls." But this probably does not work as you might expect because restful logic means you should handle the payroll model in the PayrollsController. Otherwise you might run into strange looking code. Maybe your design of the TasksController is just wrong? Rails will probably expect tasks to be handled over to your tasks controller although you name it payrolls. This can be confusing at least (however, it does not actually expect these being task models, so it will probably work).
BTW - Keep in mind: "restful" also means your application should answer to standard verbs on a resource, not just using "resourceful" routes. It's also about the GET, PUT, DELETE and POST http verbs, and of course the "edit", "new" etc default actions. Do not try to make your controllers big and complicated. Follow the motto "skinny controllers - fat models".
OK, so a better question, then might be this:
How can I get it so that I use your suggestion:
/regions/1/payroll
and have that map RESTfully to:
Tasks controller with index, new, etc that are prefixed by "payroll_"?
Like this: TasksController#payroll_index or TasksController#payroll_new

Resources