Rails Routing Question: Mapping Slugs/Permalinks Directly under Root? - ruby-on-rails

Morning Everyone!..
General Routing Quesiton Here... I'm currently working to achieve a route similar to this for users in my application.
http://www.example.com/username
This then maps to the usersControllers#show, hence I have the following in my routes file.
map.connect '/:permalink', :controllers => "users", :action => "show"
I've then got the show action to find the user by the permalink in the param. So its works but....
The problem I'm running into is that all other UNDEFINED routes get sent to userController#show. i.e 404's & other un-named routes. So I dont think i'm going with the right convention for this. My solution is to just add other named routes above this, which solves the problem, but to me seems brittle. Am I thinking about this wrong?
Whats a better solution? I'm going to mine google for answers but I just thought i'd throw this up for discussion. Ideas?

You're doing it right. Rails routes go from high priority at the top to low priority at the bottom. Your users show action should go at the bottom. Just make sure that if the permalink does not correspond to a user a proper 404 is generated.

What if you get a user whose username is the same as other URLs on your site?
This seems like a trouble waiting to happen.
Just change it to http://www.example.com/user/username
This way you create a "user" namespace for all username based URLs.

Related

Rails route only if resource is present

I want to have a rails route that triggers if the given id is present, but falls back to further route matching if not.
Specifically, I want to have promo codes at the root level of my site. So, you can go to foo.com/save87 or foo.com/xmasspecial, and it'll treat it as a promo code, but if you go to a promo code that's not there (foo.com/badcode), that route will not match and rails will continue down the route list.
In an ideal world, I'd do something like this in my routes.rb file:
get '/:promo_id' => 'promos#show, constraints => lambda { Promo.exists?(promo_id) }
I know that the above code, without the constraints, would work as a catch-all for foo.com/*, and would sorta work if I put it as the last line in the routes file. Unfortunately, that would result in foo.com/badcode a 'promocode not found' error, rather than a normal 'route not found' error.
So, is there a way to accomplish what I'm trying to accomplish? This is in Rails 3, for reference.
Edit: To clarify a bit-
I want a wildcard url as described above so that our promocode urls are short and memorable (foo.com/save87 instead of foo.com/promo_codes/save87)
I'd prefer to have the option of having other routes after this one. I may, at some point, need another wildcard url at the root level- for example, if I want vanity urls for another resource in my system. For example, if I sell a dozen varieties of widgets, I might want foo.com/widget_deluxe, foo.com/widget_extreme, etc in addition to my promo code urls. I'd have to make sure that there's no collision between promo codes and widget varieties, but that's easily handled elsewhere.
In an ideal world, I'd do something like this in my routes.rb file:
No way. In Rails World, this functionality should go inside controller.
In controller you can do something like
def show
if Promo.exists?(promo_id)
#do something
else
raise ActionController::RoutingError.new('Not Found')
end
end
Update
With routes, you can do something like this
constraints(lambda { |req| Promo.exists?(req.params["promo_id"]) }) do
get '/:promo_id' => 'promos#show
end
Please keep in mind that this constraints will query the database for every request with a url matching the pattern /:promo_id (e.q. /users, /faq). To avoid unnecessary database queries that decrease your website performance, you should add this rule as far as possible to the end of your routes.rb.
Using this routing logic, every request to your application would do an extra search for a promo code before it moved on to the rest of the routes. I recommend looking at your business case and consider doing a Promo controller. If you must do routes, something like this would work but I would put it at the end so that it goes to your regular routes first.
get '*', to: 'promos#show'

Rails 4 routing: using namespaces with dynamic segments

When discussing Routing using Dynamic Segments, the Ruby on Rails Guides (v4.0.1) say "You can't use :namespace or :module with a :controller path segment." They go on to suggest: "if you need to do this then use a constraint on :controller that matches the namespace you require."
I've got a lot of controllers under quite a few namespaces. If I could get dynamic segments to work in my situation then I could handle all of that routing with one line in routes.rb. Perhaps something like this:
get '/:namespace/:controller/:action' #note: this isn't valid
But that won't work (see above). I'd rather keep the namespaces, so I could just write out a fresh line in my routes.rb file for each namespace and then add a constraint to check that the URL is asking for that namespace. That wouldn't be the end of the world. It just seems so un-DRY and a shame because all the routing information is neatly presented right there in the URL.
I'm just wondering if there are any sneaky ways round what seems (to me) like a bit of a gap in the capabilities of Dynamic Segment Routing. Can one get the Routing DSL to run a block that (unlike Constraints) does something more complex than a Boolean yes/no on whether the route matches? Or perhaps I'm missing something in the way the standard Rails routing capabilities work. Any suggestions appreciated.
Update: Just to spell it out a little bit more. My understanding of the Rails Guides is that I'll need to have lines like these:
post ':controller/:action' , controller: /user_details\/[^\/]+/
post ':controller/:action' , controller: /preferences\/[^\/]+/
...and so on. If I want 20 namespaces then I'll need 20 of these lines. I'm just wondering if there isn't a more concise way that approaches my 1-line ideal given above.
If what you want is a dynamic route that matches all routes like
post 'user_details/name/edit' => 'user_details/name#edit'
post 'user_details/address/edit' => 'user_details/address#edit'
post 'user_details/password/edit' => 'user_details/password#edit'
post 'preferences/privacy/edit' => 'preferences/privacy#edit'
post 'preferences/colors/edit' => 'preferences/colors#edit'
then you could just use
post ':controller/:action'
since the :controller part will match namespaced controllers, too.
Verified for Rails 4.0.1 and 4.2.0.

In rails how do I route domain.com/12345 to /fetch/12345 dynamically?

I'm having a little trouble understand how routes work in Ruby on Rails.
What I'm trying to achieve is have all ID's accessible directly after the domain name,
for instance
domain.com/<- ID goes here->
routes to
domain.com/fetch/<- entered ID ->
Any push in the right direction would be greatly appreciated.
Thanks a lot
This might be a bad idea; once you put in this general route then any unrecognized url with a single path component is going to end up being handled by your fetch method. Assuming you understand and are okay with that there are several ways that you could do this, the most straightforward:
I assume that you already have /fetch/:id in your routes, something like this to handle /fetch requests in ApplicationController#fetch:
namespace :fetch
get '/:id' => 'application#fetch'
end
Then you can add a rule at the bottom of your routes like this:
get '/:id' => 'application#fetch'
That should go at the very bottom because you don't want it to override any more specific single-path-component routes.

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 2 Trailing Route Parameter

I'm building an article/blog website where any article may show up within a series of URL paths.
/section/article
/section/page/article
/section/page/page2/article
This works, but what if I wanted to have the page number for that article (/page/123) be bound to those URLs
/section/article/page/123/
/section/page/article/page/123
/section/page/page2/article/page/123
This would mean that I would have to create a specific route for each different url?
/:section/:page/:sub_page/:article/page/:page
This would mean that I would create dozens of URL routing paramters.
Is there anyway in rails to say that all urls may have a /page/NUMBER suffix at the end of the URL and still route normally (that is assign the NUMBER to a parameter and continue to goto the page normally)?
Route globbing, described at http://guides.rubyonrails.org/routing.html#route-globbing, might work in this situation. For example, your route might read map.connect '/:section/*page_subpage_path/page/:number', :controller => 'articles', :action => 'show'
This exact code might not work as intended, but this method might be a good direction to try. Good luck :)
If you want to create routes that are as customized as that you normally need to create a large number of routes to accommodate them. The general format is /resource/:id when using map.resource, anything other than that is left to you to specify.
Given that the Rails routes.rb file is executable ruby you can often define your routes programmatically by repeating patterns or doing combinations on arrays if required.

Resources