Rails routing: cannot differentiate static and dynamic routes - ruby-on-rails

I'm trying to create custom URLs for my site's users:
www.mysite.com/user1
If I set the routing rule to be this:
match ':id' => "user#view", :as => :user
Then it works great! I can access www.mysite.com/user1 and it will pull up the page for user1.
However, that breaks my about page, which is at www.mysite.com/about . The error I get here is:
"Couldn't find User with id=about"
Any idea how I can fix this problem? Thanks! As an aside - I'm using friendlyid to generate the human readable slugs.
Ringo

Is the about page a static file located at public/about.html? If so, then Rails will match this first and fall back to the router if it can't find the file.
If it's a route instead, then your match :id route should be beneath the one for about, so that about is matched first.

Related

Ruby on Rails page control

I'm sorry, my English is bad. Question: I have model Pages with columns title, description etc. I can create, change, destroy these pages. I can see the contents of the link mydomain/pages/1. I need for each page has been template and route, so I can see the content on the link, for example maydomain/contacts. How to do it? Help me please.
One way to implement your own solution is to add this to your routes file:
get '/mydomain/:slug, to: 'pages#show'
This is a pretty general matcher, so add it to the bottom of your routes so it doesn't override others.
Then your controller show action will look something like:
def show
#page = Page.find_by_slug(params[:slug])
end
This of course assumes you have a slug column on your Pages table.
I'm assuming by "mydomain" you mean the root url of your site (e.g. myapp.example.com)
I'd suggest that you separate the problem into two parts:
Use an attribute other than id to identify an item in the url
Reduce the route so that the controller does not need to be specified.
For 1, have a look at this: Rails routes with :name instead of :id url parameters Note, that as #spickermann suggests friendly_id could be a good solution for you.
For 2, you will need to create a route without the controller name, and then specify the controller in the route definition. (See the Rails Routing Guide):
get ':param', to: :show, controller: 'pages'
For that to work, you will need to put it after (lower in routes.rb) so that it doesn't intefer with other routes. I'd also recommend adding a constraint to the route - to limit the wrong urls that could be routed to that rout.

Rails routing issue, can't figure out the link path

Let me fair from the outset, and tell you that I've 'solved' the problem I'm describing. But a solution that you don't understand is not really a solution, now is it?
I have a resource, Newsbites. I have an index page for Newsbites. All my CRUD actions work fine.
I created a separate index (frontindex.html.erb) that acts as the front page of my website to show the newest Newsbites. The formatting is different from my normal index so readers get a larger photo, more of the text of the article(more ads too:).
In my routing table, I have the following statements:
resources :newsbites
get 'newsbites/frontindex'
root 'newsbites#frontindex'
Rake routes show the following:
newsbites_frontindex GET /newsbites/frontindex(.:format) newsbites#frontindex
If I load my website from the root (localhost:3000), it works great. There is a separate menu page that is rendered at the top, and it loads fine. I can click on all links, except the 'Home' link, and they work fine.
The 'Home' link is:
<%= link_to 'Home', newsbites_frontindex_path %>
When I click on the linked, I get the following error:
Couldn't find Newsbite with 'id'=frontindex
The error points to the 'show' action of my Newbites controller. Here are the frontindex and show def from the controller. They appear exactly as I'm posting them:
def frontindex
#newsbites = Newsbite.all
end
def show
#newsbite = Newsbite.find(params[:id])
end
I don't get it. Why is the show action being called by newbites_frontindex_path when there is both a def and views that match? Now, I can get around this by simply pointing home to root_path. But that doesn't help me understand. What if this was not the root of the site?
Any help would be greatly appreciated.
Actually I'm very surprised your code worked at all. A route must define two things
Some sort of regex against which the URL of the user is matched (newsbites/frontindex is different than newsbites/backindex)
What do you want to do for a given URL ? You want to point to a controller action
Rails won't usually "guess" what that action is. Or maybe, he was still able to "guess" that you wanted to use the newsbites controller, but it didn't guess the action right this time :(.
You should declare the root like this, which is what you did
root 'controller#action'
For the rest, there are two ways you can declare it. I prefer the second one
resources :newsbites
get 'newsbites/frontindex', to: 'newsbites#frontindex'
resources :newsbites do
# stuff added here will have the context of the `newsbites` controller already
get 'frontindex', on: :collection # the name of the action is inferred to be `frontindex`
end
The on: :collection, means that 'frontindex' is an action that concerns ALL the newsbites, so the URL generated will be newsbites/frontindex.
On the other hand get 'custom_action', on: :member, means that the custom_action targets a specific item, the URL generated would look like newsbites/:id/custom_action
EDIT : Rails also generate path_helpers based on the route declaration
get 'test', to: 'newsbites#frontindex'
get 'test/something', to: 'newsbites#frontindex'
resources :newsbites do
get 'frontindex', on: :collection
get 'custom_action', on: :member
Will generate path helpers
test_path
test_something_path
# CRUD helpers : new/edit/show/delete, etc. helpers
frontindex_newsbites_path
custom_actions_newsbite_path(ID) # without s !
You can always override this by adding an as: option
get 'custom_action', on: :member, as: 'something_cool'
# => something_cool_newsbites_path
Rails routes thinks that frontindex is an id. That's what the error message says. So it goes to GET newsbite/:id which maps to show.
You need to find a way let Rails routes know that frontindex is not an id.
On a side note: The order in which you define routes matters. The first one matched will be used. If you have GET newsbite/:id and GET newsbite/frontindex then the one that appears first will be matched. In your case this is the first one.
Maybe try to change the order.

Remove controller_name from URL in Rails 3 and use custom string instead of id

Since the beginning I always hat this one problem with rails, short urls without the controller name.
For example, I have a blog and I don't want any dates or controller names in the url, I already have a Page model with a unique field url in my database. Rails works great with such urls:
jeena.net/pages/1
And when I modify the model I even can get it to use
jeena.net/pages/foo
But it seems not to matter what I do I can not get it to work with just:
jeena.net/foo
Of course I want the index page still to work with
jeena.net/pages
And I want creating new pages and updating old pages to work too in some was as well as the link_to()-method. All suggestions are appreciated.
To define that route, try adding the following to your routes.rb:
match '/:id' => 'your_controller#your_action'
This will pretty much match everything to the id of your model. And that's not very nice... You don't want to route youe_host/pages to the pages controller, with an id equal to 'pages'... To prevent that from happening, make sure to put that line on the end of the routes.rb file. The router uses the first route that matches the path received, so putting that line on the end of it will make sure that it will only match your route after it ran out of other meaningful options.
A better practice would be to pass regexp constraints to the router, so that it will only match ids with a specific format, like that:
match '/:id' => 'your_controller#your_action', :constraints => { :id => /your_regexp/ }
Refer to the guides if you have doubts about the rails rounting system. It is pretty well written and covers lots of important things.
Rails rounting - Official Guides
edit: to create a named route, one that you can call in your controllers and override the normal routes that you are probably creating with resource, you have to provide the :as => parameter in your routes.rb
match '/:id' => 'your_controller#your_action', :as => some_name
Then you'll be able to call it in your controller/views like this:
link_to some_name_path(#my_string_id)
Hope this helps. And take a time to read the guides, it has really lots of useful info, including more details about creating named routes.

Rails 3 routing : customize a resourceful route

I've got some Documents (and a DocumentsController), which are sorted using limited, fixed set of categories. I'd want my routes to take into account these categories, so my urls would look like :
/documents/:category/:id
/documents/:category/new
/documents/:category/:id/edit
...and so on, which should allow me to access params[:category] in order to filter the results. Is there a simple way to achieve this, that would still generate path helpers ? Or im i wrong to do this that way ?
You can provide a path to a resource (as you mentioned):
# config/routes.rb
resources :documents, :path => 'documents/:category'
This would give you the following routes:
/documents/:category
/documents/:category/new
/documents/:category/:id/edit
/documents/:category/:id
I am not sure in this case what purpose the category capturing will serve, since you can reference the document by its primary key. This key most likely does not repeat across categories.
It's not hard to customize paths in Rails 3.
match '/documents/:id', to: 'documents#show', as: :document would give you the path helper document_path(:id). This will work even for an ID that's a string rather than a number, so extending this pattern to /documents/:category/:id/edit should be no problem.

How to create referral link like launchrock in ruby on rails?

I want to create referral links like.
www.abc.com/1234
www.abc.com/4345
Where number are the referral codes which will be unique for every user. I am sure this can be done in ruby on rails with some routes configuration. Means where the request will be routed. Which controller? which action? How to get value of unique code.
ps: launchrock is using referral links like this.
You can use this structure with route matching but you would need to have the referral codes match a specific pattern. If, for example, they matched the format of 3 letters followed by three numbers, you could put the following your routes file:
match '/:referrer_id' => 'app#index', :constraints => {:referrer_id => /[a-zA-Z]{3}[0-9]{3}/}
The reference to app#index should be changed to the controller in which you handle referrals and you can access the referrer_id through params[:referrer_id].
Certainly have a look at the link referenced in Markus' answer for suggestions on how to generate the tokens.
I have a link in my bookmarks with regard to token generation: http://blog.logeek.fr/2009/7/2/creating-small-unique-tokens-in-ruby
In your application you will need to store the individual tokens in the user table. Controller and action are up to you and for the routes you could go with something like www.abc.com/referral?123456.
routes.rb
match "/referral/:ref" => "controller#action"
access in controller with:
params[:ref]

Resources