One thing I love Rails is it provides really handy URL helpers to the developers. Today, I am going to present you guys how to use polymorphic_url like a ninja.
Say, I’ve got the following models:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
As you can see, we’ve have a STI table named vehicles
in the database, and it stores all the bicycles, motorcycles and the other new vehicles we may have in the future, like unicycle
.
We have a collection of vehicles, @vehicles
, it contains bicycles and motercycles. What we are going to do is iterating the @vehicles
, and display a link to the vehicle detail page. With polymorphic_url
, we can write down the following code:
1 2 3 |
|
instead of:
1 2 3 4 5 6 7 |
|
Once we add a new kind of vehicle, we don’t need to open the above view file and add another elsif
, it complies to the Open Closed Principle perfectly!
The request to bicycle detail will go to:
1 2 3 4 5 6 7 8 9 |
|
And the request to motorcycle detail will go to:
1 2 3 4 5 6 7 8 9 |
|
What if we want to have the same controller rendering the same view for all the vehicles? Say, we only want one VehiclesController#show
for all the vehicles’s detail, here we go:
1 2 3 4 5 6 7 8 9 |
|
You may say we can just use vehicle_url(v)
to generate the URL, I can not agree more, but we are exploring something deep inside of Ruby on Rails, so, bare with me, :)
To have the polymorphic_url
, generate the URL like vechicle_url
, we need to overwrite self.model_name
for the Vehicle
, here’s how I found it out:
- the
polymorphic_url
will callbuild_named_route_call
to generate the URL - the
build_named_route_call
will callRecordIdentifier.__send__("plural_class_name", record)
to find out reource name, say, a record ofBicycle
will generatebicycles
- the
plural_class_name
will callmodel_name_from_record_or_class
to determine the model name of the record model_name_from_record_or_class
will call the record’s classmodel_name
method to find out the model name of the class
We need to override the self.model_name
to let polymorphic_url
pick up the right model name:
1 2 3 4 5 6 7 8 |
|
If we want a custom URL and view template for specific vehicle, we can also write something like:
1 2 3 4 5 6 |
|
With the following customization, polymorphic_url(@unicycle)
will generate the URL unicycle_url(@unicycle)
.