Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to override single files from gem assets for assets:precompile?

Situation

I use a gem that brings its own JavaScript and stylesheet assets. This gem uses a standard application.js and application.css manifest to require all its assets:

[gem]/app/assets/javascripts/gem_name/application.js
  require_tree .

[gem]/app/assets/javascripts/gem_name/backoffice/menu.js
  … some JavaScript code …

[gem]/app/assets/javascripts/gem_name/backoffice/forms.js
  … some JavaScript code …

In development mode I can then override single asset files by placing them in [my_app]/app/assets folder, e.g.:

[my_app]/app/assets/javascripts/gem_name/backoffice/menu.js

The load paths configured in Rails then ensure that files in [my_app]/app/assets override files in [gem]/app/assets.

But when I precompile the assets using rake assets:precompile, the overriding file menu.js doesn't replace the original file from the gem. The compiled application.js contains the code from the original menu.js.

Demo

See the behavior in action with this test app: https://github.com/widescape/AssetCompileTest

Considerations

So far, I found out that require_tree looks up files only in the directory of the current file and does not look across multiple load paths (e.g. app/assets or vendor/assets). So if the current file is [gem]/app/assets/javascripts/gem_name/application.js, Sprockets#require_tree will only look in [gem]/app/assets/javascripts/gem_name/ for more files and will not use the other load paths like [my_app]/app/assets/….

I am reluctant to copy all asset files from the gem into my app/assets only to override one single file. And forking the gem to customize a single file doesn't have a great appeal, too.

Is there any other way?

Note: I am aware that I could create a custom JavaScript file menu_patched.js that overrides the objects and methods defined in the original file and place it so that its definitions will be applied before the original objects and methods are called (monkey patching JavaScript style). But I am looking for a cleaner solution here.

like image 833
Robert Avatar asked May 08 '13 07:05

Robert


2 Answers

In the demo code you posted at on GitHub, your application.js has:

//= require any_gem

That does not bring in a directory, it brings in any_gem.js, which is the Gem's manifest file that brings in all of the Gem's JS files. If you do not want all of the files, then you need to change that to bring in only the files you want by listing them explicitly:

//= require ../../../vendor/assets/javascripts/any_gem/do_not_override.js

Then you can add back in all of your own JS files, including your overrides, with:

//= require_tree .

BTW, great job on creating the demo app! You're a role model for the kids.

like image 120
Old Pro Avatar answered Sep 23 '22 18:09

Old Pro


In application.rb or production.rb, you can prepend or append more assets path.

config.assets.prepend_path 'vendor/assets/jquery'
config.assets.append_path 'vendor/assets/something'

References:

  • sprockets/railtie source code
  • Sprockets: Manipulating the Load Path
like image 29
catcyborg Avatar answered Sep 23 '22 18:09

catcyborg