Ruby on Rails 4.2+ only, please!
I've been looking all over for tips on how to make URLs pretty in Rails, and I'm struggling to see a solution I like.
What I want:
Hypothetical example: given Topic, Course, etc. models that have a bunch of fields (including URL-friendly slugs), I want to be able to
# ../routes.rb
# Match urls of the form /edu/material-engineering. These are read-only
# public URLs, not resources.
get 'edu/:slug', to: 'education#topic', as: :learn_topic
get 'edu/course/:id/slug', to: 'education#course', as: :learn_course
...
# I also have admin-only resource-oriented controllers for managing
# the content, but that's separate.
namespace :admin do
resource :topic
resource :course
...
end
# ../some_view.html.erb
# Generate URLS like this:
<%= link_to topic.name, learn_topic_path(topic) %>
<%= link_to course.name, learn_course_path(course) %>
What I don't want:
to_param
. That's a dirty hack and completely breaks separation of concerns.link_to 'text', course_path(id: course.id, slug: course.slug)
. This completely defeats the purpose of not requiring views to know what params are required to generate a URL for a course.There has to be a way to tell the named route helper topic_path(topic)
to take the required parameters in the route (e.g, :slug
, :id
, whatever else) from the topic model object.
Anybody know? Thanks!
The best I've been able to come up with: just override the *_path
helpers with my own implementation.
If you know a way to make the default helpers work, please chime in!
This problem boils down to one issue: the auto-generated *_path
and *_url
helpers don't give me the flexibility I want. What I want them to do is trivial, so without another option, I can just write my own:
module ApplicationHelper
def learn_topic_path(topic)
"/edu/#{topic.slug}"
end
...
end
Writing a few _path/_url helper overrides avoids all kinds of complication, and allows you to keep out of to_param, avoid including new plugins, etc.
One could probably go another step forward and generate the static components of the route from known routing rules, as well as infer what attributes one needed to extract from a model if the dynamic segment names line up to the model attribute names, but that starts to break down once you do more complicated things or add multiple models (e.g., 'edu/:topic_slug/:course_slug').
The big downside to doing this is that you now have to update routes in two places every time you change them: the route definition itself in routes.rb
as well as the corresponding route helper in application_helper.rb
. I can live with that for now.