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_urlwill callbuild_named_route_callto generate the URL - the
build_named_route_callwill callRecordIdentifier.__send__("plural_class_name", record)to find out reource name, say, a record ofBicyclewill generatebicycles - the
plural_class_namewill callmodel_name_from_record_or_classto determine the model name of the record model_name_from_record_or_classwill call the record’s classmodel_namemethod 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).