问题
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.
回答1:
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.
回答2:
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
来源:https://stackoverflow.com/questions/16435428/how-to-override-single-files-from-gem-assets-for-assetsprecompile