isolate_namespace
is that one feature which Rails boasts about to use while
creating gems, but doesn't work when one wants to extend models, controllers and
views which are provided by that gem. We have used it while developing one of our
gems and found that its really hard to extend models, controllers and views.
For instance, if you copy views from gem to main application in order to customize
them, and try to use routes defined in the main application, you will be slapped
with an error saying those routes are not defined! It takes some time to understand
how it works under the hood, and all it boils down to is: isolate_namespace
is
nothing but a bunch of hacks. This blog post will do a code walk-through and will
try to explain how it works:
The isolate_namespace
method
This method can be found in railties-engine file. Its reads as:
mod
is module, which is has to be isolated. In case of blorgh gem, its Blorgh
itself. Hence forth, we will use blorgh
gem as an example given in rails
guides
-
routes.default_scope
: It defines default scope for routes. This scope will be used while generating routes for Rails engine. It says module to be used isblorgh
, and all the controllers will be searched under gem. This can be easily understood. Just put abinding.pry
inside routes.rb of gem, and you can see this:It says default module that should be used is
blorgh
. All the controllers will be prepended byblorgh/
-
If module doesn't respond to
railtie_namespace
(generally modules dont!), it goes ahead and adds bunch of methods to module (i.eBlorgh
for example), and not to engine. Thats why its a hack! There is nothing done on engine! Everything is added toBlorgh
module. So, what it adds exactly? -
table_name_prefix
: This can be easily guessed. It will be used by active record. Now searching through activerecord source, we find this:It looks tricky, but this is how it works. It searches through all the parents of the AR class, and checks whether any of them responds to
table_name_prefix
and returns it. (Default value is empty string). Well, parents are not the parents of the class, but the hierarchy of the modules. activesupport defines this method:Now, here comes the funny part: Create a folder called
blorgh
in your main application underapp/models
folder, and create a model calledTest
Now, fire up console, and execute the following:
See, it automatically prepends
tests
table name withblorgh_
. One will be wondering how did that happen? Well it happened because of the module calledBlorgh
. So anything one puts underBlorgh
module will get special treatment. Do it with any other namespace, (i.e module), it will just returntests
. The only way you can get rid of this behavior is to specify the table name explicity on model usingself.table_name = "tests"
. If someone in some gem magically says to isolate a namespace which you are using in your application, hell breaks loose! You will be wondering why your application code is behaving strangely.You can find other hacks by searching through the Rails code. We will cover another hack here:
-
railtie_routes_url_helpers
: This method is used to define route helpers which can be accessible to generate paths. Digging through the code, you can find it in actionpack.This
inherited
method will be called in the context of your controllers. Again if your controller is underBlorgh
module, it magically includes only the routes defined by gem, otherwise it includes the application route helpers. Thats why even though you copy views from gem to your main app, they still cannot access helpers defined by main application. Generally we all know how to fix this: Call the url helpers by prepending with eithermain_app
or gem mount point, i.eblorgh
here. This way all the route helpers will be available in all the views. -
Other issues include extending models and controllers. Rails guides gives two options here. One to use
class_eval
, and other to use concerns introduced in Rails 4. Both are kind of hacky. Hope there is a better solution.