Rails Polymorphic Routes

When turning a class into a reusable module, I needed to use some routing paths without knowing beforehand what the models were called. I wanted to write things like new_category_path without knowing about category; I needed a model-agnostic way to express routes. It turns out that Rails has this covered with Polymorphic Routes.

Take the route to create a new object, say a Category, which would normally be expressed as:

new_category_path #=> "/en-GB/categories/new"

Now, assuming that category is known as the variable model, you can do

new_polymorphic_path(model) #=> "en-GB/categories/new"

The model variable can contain the constant Category, the string category, or the symbol :category. It can also be a model instance, for example:

edit_category_path(model) #=> "/en-GB/categories/1/edit"

can be written as

edit_polymorphic_path(model) #=> "/en-GB/categories/1/edit"

You can also make the action polymorphic: the above example can also be written

polymorphic_path([:edit, model]) #=> "/en-GB/categories/1/edit"

Note, however, how the arguments are presented as an array. An array is also used for nested routes. Instead of

new_category_category_path(model) #=> "/en-GB/categories/1/categories/new"

you can write

new_polymorphic_path([model,model.class]) #=> "/en-GB/categories/1/categories/new"

However, for a custom route like

index_category_path(model) #=> "/en-GB/categories/1/index"

the index_polymorphic_path form doesn't work, but you can write

polymorphic_path([:index, model]) #=> "/en-GB/categories/1/index"

So it turns out to be very easy to write model-agnostic routes.

All of the above applies to URLs too: just replace _path with _url:

polymorphic_url([:edit, model]) #=> "http://localhost:3000/en-GB/categories/1/edit"

Happy times!