Routes in Rails Engines
My team uses engines because they add functionality in a modular way, and help us avoid the issues that come with bloated models (too many associations or methods) or deeply nested namespaces. In this post, I discuss some tips around engine routes.
Suppose we have a number of engines and we want to provide links to each engine’s root (mounted) route from our home or index page. We can either hardcode each route, or we can ask the engines themselves for this information. The latter was easier with Rails 3 engines (simply call MyEngine::Engine.class.mounted_path
), but for Rails 4 engines, we need to add the following code to an initializer or to application.rb:
Once the mounted_path
method is available, we can now easily distinguish between our mountable engines and other engines such as Coffee and Turbolinks:
The output of the code above should match the mount path in routes.rb, such as mount myEngine::Engine, at: "/my_engine"
from the Rails guide.
As mentioned previously, we can simply hardcode these values in the views, but it is not very DRY. On the other hand, the code above unnecessarily calls all engines, mountable or not. One improvement is to create a model that holds information about our mounted engines. For example, we can have a model called Ngin that has attributes including name and mounted_path. Then, in an initializer or application.rb, we create (or find) records for our mountable engines:
Our views will use the Ngin model to output links and other information about a mountable engine:
The only loose end is the proper handling of engines that we have unmounted from the Rails app. We should remove the corresponding Ngin records, and one way to accomplish this is by using a boolean attribute, called ‘available’, that gets set to true if we have ‘seen’ the engine. We can set this attribute during our iteration of the Rails::Engine.subclasses objects, but we must set the ‘available’ attribute to false for all Ngin records beforehand:
Don’t forget to add error handling on both the code above and on the views that use the Ngin model.
Bonus tip If we have a record from a Rails::Engine and we want a link to the show
action of its controller, add the following code in a helper of the main app: