Zhu Wu's Blog

The world is a fine place and worth fighting for.

Sprocket: Difference Between "Require" and "Require Tree"

As a Rails developer, you should have worked with Sprocket to deal with front-end resources. This article highlights a key difference between require and require_tree.

When require is used, Sprocket searches the file from search paths. You can find out the search paths by inspecting Rails.application.config.assets.paths in Rails console. The search starts from the first path in the list to the last one until the required file is found. In contrast, when require_tree is used, Sprocket gets all the files from the relative directory specified in the argument, and these files are required in alphabetical order.

As a result, we can see that Sprocket loads file in different ways between require and require_tree. This can cause some chaotic behavior. I encountered such a case in my project. There is a JavaScript file called foo.js in one of the gem, and this gem loads JavaScripts in the following maner:

//= require foo
//= require_tree .

The gem require foo.js first because it defines a function bar, and this function is called by the rest of the JavaScript files. However, the function bar did not meet my requirements, so I decided to overwrite it. I wrote another foo.js with a new implementation of function bar, and put the new foo.js under app/assets/javascripts. Since //= require foo loads foo.js from search paths, and the path app/assets/javascripts has a higher priority than the path inside the gem, I expected that the overwritten foo.js would be loaded instead of the original one, and the new function bar should take effect.

However, I noticed that the new function bar did not take effect. Moreover, I found that both original and new foo.js were loaded. I spent a lot of time to troubleshoot, and finally I realized that //= require foo loaded the new foo.js from app/assets/javascripts, while //= require_tree . loaded the original one from the specified directory. The new foo.js was loaded before the original one, so the bar function in the original foo.js took effect.

Finally, I solved it by renamed the new foo.js to foo2.js, and added a //= require foo2 at the bottom of application.js in the application. It is working because the bar function in foo2.js is loaded later than the original bar function, though this solution is not clean. Anyway, this is a good lesson to understand the difference between require and require_tree in Sprocket and be aware of it in future.