Rails 3 routes with non-numeric id's - ruby-on-rails

I have an object that by necessity has an id that looks something like "1.3.6.1.4.1.25623.1.0.14377". When I try to create a link that uses the id (such as link_to my_object(object.id)), Rails 3 barfs and says that no route exists.
I have verified that I have a valid route in place by trying link_to my_object(1). Rails doesn't have a problem with creating this link.
Any ideas?
Thanks in advance!

I've figured it out... By default, Rails freaks when a period (.) is found in a parameter field (in this case id). To change this behavior, simply add a :constraints directive to your route statement and tell it explicitly what the parameter should look like. In my case it turned out that the following fixed the problem:
resources :nvts, :constraints => { :id => /[0-9\.]+/ }
Thanks to Avdi Grimm for his most helpful blog post.

Maybe you should override in your model the method to_param, Rails uses it to generate id for routing.
Usually it looks like this:
def to_param
object.parameterize
end
It gets the model's name and use as id in route.

Related

Rails Routing: How to route to a specific controller/action/id?

I think this should be a simple question. I would like to route a URL of the form:
http://mywebsite.com/my_special_tag
to a specific controller/action pair with a specific id, e.g.
http://mywebsite.com/user/post/13
(where 13 is the id I want to pass into the controller/action).
Note also that I don't want to redirect, just want to render.
Is there an easy way to do this in Rails 3?
Thanks!
Assuming your know specifically which URL you want to have rendered to, you can explicitly match to the target in your routes:
# config/routes.rb
match 'my_special_tag' => 'user#post', :defaults => {:id => 13}
Note that this necessarily requires that you have a post action within a UserController (note that the names are singular, as they are in your desired path).
You simply need a table like this:
tags table:
id | post_id | tag
-------------------
1 | 13 | my_special_tag
And then you need a before_filter, that takes that tag and checks the database for the post_id.
You'd also need a wildcard route that looks something like this: Rails 3.1 - in routes.rb, using the wildcard, match '*' is not catching any routes
Start with that and then let us know when you have more specific questions and we can help you better.
If you check your routes you should get the syntax for a user post route, if you have defined the nested route correctly.
To alias the route, you can define a specific id for a particular match like so:
match '/special_tag' => 'posts#index', :defaults => { :id => '5' }
Update
To clarify, the above should only be used for a couple of specific pages you'd like to alias a route for. If you want to provide a more user friendly link to each user post page, I'd recommend something like https://github.com/norman/friendly_id which allows you to have routes like
http://example.com/states/washington
instead of:
http://example.com/states/4323454

Adding a record's attribute to the URL before the ID

I'm trying to add a specific attribute of a record in Rails to the URL from something like:
domain.com/share/5
(where 5 is the record ID) to something like:
domain.com/share/johnsmith/5
where johnsmith is stored in record 5. I'm alternating between these two routes to no success:
get "/share/:name/:id" => "share#show"
resources :share, :only => [:show]
And between these two methods:
share_path(doc.user.name, doc)
share_path(doc)
The show method in the ShareController is pretty standard.
The problem:
Using share_path(doc.user.name, doc) in the view generates a link to /share/johnsmith.5, instead of /share/johnsmith/5.
get "/share/:name/:id" => "share#show" should do the job. But you may have to look at the order of routes in routes.rb, maybe Rails took the wrong route?
Best tip to look at what's happening:
Call the URL in your browser (or using curl or whatever) and then look into your console where your started rails s (or rails server).
There you should see something like this:
Processing by ShareController#show
Parameters: {"id"=>"5", "name"=>"johnsmith"}
Concerning the path methods:
Simply use rake routes, it will tell you which path methods are available.
No idea what happened but it resolved itself with this:
get "/share/:name/:id" => "share#show", :as => :share
share_path(doc.user.name, doc)
I do not get the . and / issue at all. I restarted everything and it was gone.

How do I get the format of my URLs to be username/controller/:id in Rails 3.1?

I want it similar to the way Twitter handles the URLs for its tweets.
For instance, right now my URL looks like this: mydomain.com/feedbacks/1/, where feedbacks is the name of the controller.
I want it to look like: mydomain.com/username/feedbacks/1/ which is similar to Twitter's: twitter.com/username/status/:id/.
My routes.rb looks like this:
resources :users do
resources :feedbacks
end
When I have it like this, it gives me the URLs as mydomain.com/users/1/feedbacks, but I want the actual username in the URL.
How do I get that?
Thanks.
Edit 1: If you are adding another answer to this question, please make sure it addresses my comments/questions to the answer already given. Otherwise it will be redundant.
scope ":username" do
resources :feedbacks
end
From the docs:
This will provide you with URLs such as /bob/posts/1 and will allow
you to reference the username part of the path as params[:username] in
controllers, helpers and views.
UPDATE:
I have tested and confirmed the accuracy of paozac's answer. I'll clarify it a bit.
Suppose you had a #feedback object with an id of 12, and the associated user had a username of foouser. If you wanted to generate a URL to the edit page for that #feedback object, you could do the following:
edit_feedback_url(:username => #feedback.user.username, :id => #feedback)
The output would be "/foouser/feedbacks/12/edit".
# A URL to the show action could be generated like so:
feedback_url(:username => feedback.user.username, :id => feedback)
#=> "/foouser/feedbacks/12"
# New
new_feedback_url(:username => current_user.username)
#=> "/foouser/feedbacks/new"
Additionally, as noted by nathanvda in the comments, you can pass ordered arguments which will be matched with the corresponding dynamic segment. In this case, the username must be passed first, and the feedback id should be passed second, i.e.:
edit_feedback_url(#feedback.user.username, #feedback)
Also, if you need help handling the params from the controller, I suggest creating a new question specific to that.
Once you have defined the scope like dwhalen says you can generate the url like this:
feedbacks_url(:username => 'foo')
and get
http://www.example.com/foo/feedbacks
or
edit_feedback_url(:username => 'foo', :id => 1)
and get
http://www.example.com/foo/feedbacks/1/edit

Path helpers generate paths with dots instead of slashes

In my routes.rb I have the following:
resources :message_threads
When I call:
message_threads_path(1)
I get:
/message_threads.1
Why is this? My other resources work fine. Am I not pluralizing this correctly or something?
Yes, this is a pluralization error.
By passing the ID 1, I assume that you wish to display a single record.
So you need to use the singular 'message_thread':
message_thread_path(1)
Which will yield:
http://localhost:3000/message_threads/1
Sometimes this also is when you don't provide an :as parameter in your route:
delete "delete/:id" => "home#delete"
Changed to:
delete "delete/:id" => "home#delete", as: :delete
(ignore the odd example, just happened to be something we just ran into for an internal app we're building)
Other folks that land here might be in this situation:
If you have a singular resource declared in your routes.rb:
resource :map
You don't need to pass an object to map_path. Attempting to call map_path(map) will result in similar behavior (i.e. a URL like map.12).

renaming routes (map, link_to, to_param) in rails

I'm having a little issue...I setup a rails application that is to serve a german website. To make use of Rails' internal pluralization features, I kept all my models in english (e.g. the model "JobDescription").
Now, if I call "http://mysite.com/job_descriptions/", I get all my job_descriptions....so far, so good. Because I didn't want the english term "job_descriptions" in my url, I put the following into my routes.rb
map.german_term '/german_term', :controller => 'job_descriptions', :action => 'index'
map.german_term '/german_term/:id', :controller => 'job_descriptions', :action => 'show'
If I call "http://mysite.com/german_term/" or "http://mysite.com/german_term/283" I get all my job_descriptions, which is fine.
However, to make the URL more SEO friendly, I'd like to exchange the id for a more userfriendly slug in the URL. Thus, I put the following in my job_description.rb:
def to_param
"#{id}-#{name.gsub(/[^a-z0-9]+/i, '-')}"
end
which, whenever I use "job_description_path" in any link_to method, renders my URLs out to something like "http://mysite/job_descriptions/13-my-job-description-title".
However, and this is where I'm stuck, I'd like to get "http://mysite/german_term/13-my-job-description-title". I already tried to exchange the "job_description_path" with "german_term_path" in the link_to code, but that only generates "http://mysite/german_term/13". Obviously, to_param isn't called.
One workaround I found is to build the link with:
<%= link_to job_description.name, german_term_path(job_description.to_param) %>
But that's rather tedious to change all the link_to calls in my code. What I want is to replace "job_description" by "german_term" whenever it occurs in a URL.
Any thoughts?!?
Regards,
Sebastian
I think you're going to need to use the restful route helpers to get what you want.
In that case, it wouldn't take much re-factoring (assuming you've mapped JobDescriptions as a resource). Leave your to_param as is and change your JobDescriptions route to something like the following:
map.resources :job_descriptions, :as => 'german_term'
Hope this helps!
Rails only utilizes the
def to_params
end
URL builder when you are using a restful route/link helper. The only way I am aware of is to do it similar to how you did, unless you are willing to just scrap your english language links and do it all in German. In that case, just get rid of the named route lines and change the to_params to use the correct name field from the database. At that point, the REST routes should behave correctly.

Resources