sprockets
sprockets copied to clipboard
//= depend_on not working when making small change in file
First thought this was an issue with Capistrano-Rails: https://github.com/capistrano/rails/issues/208
Expected behavior
Want to force a rebuild of assets when /config/app_settings.yml changes.
Created symbolic link making /config also available under /app/assets/stylesheets (because Sprockets is limited to //= depend_on files under /app/assets ).
cd app/assets/stylesheets
ln -s ../../../config/ config
application.scss.erb can now depend on config/app_settings.yml soft linked into stylesheets/config
//= depend_on 'config/app_settings.yml'
<%= case ZS::SETTINGS[:theme]
when "default"
"@import 'default_theme/';"
when "blue"
"@import 'blue';"
else
"@import 'default_theme/';"
end %>
This is what /config/app_settings.yml looks like:
default: &default
theme: "default"
production:
<<: *default
server_public_name: "MyServer"
development:
<<: *default
server_public_name: "MyServer"
Tell us what should happen
Production mode: when a change is made in /config/app_settings.yml asset cache should be marked dirty and dumped during subsequent compile_assets task.
Actual behavior
Tell us what happens instead
- Change name of theme from
defaulttobluein/config/app_settings.yml
- Asset cache is not rebuilt.
touch /config/app_settings.yml
- Asset cache is rebuilt
- Change name of theme from
bluetodefaultin/config/app_settings.yml
- Asset cache is not rebuilt.
Add five empty newlines to /config/app_settings.yml
- Asset cache is rebuilt
System configuration
$ ruby -v
ruby 2.4.1p111 (2017-03-22 revision 58053) [x86_64-darwin16]
$ bundle list
Gems included by the bundle:
...
* rails (5.1.2)
* sass (3.4.24)
* sass-rails (5.0.6)
* sprockets (3.7.1)
* sprockets-rails (3.2.0)
Example App
Will put this together in the next couple of days.
I think all you have to do is create a new Directive to Sprockets, as I wrote below.
# https://gist.github.com/dylanjha/11233766
# hints from https://github.com/rails/sprockets/issues/366
Sprockets::DirectiveProcessor.class_eval do
def process_depend_on_config_directive(file)
path = File.expand_path(file, "#{Rails.root}/config")
context.depend_on(path)
end
end
Within the file that you want, application.scss.erb, you should just add at the top:
//= depend_on_config 'app_settings.yml.
I am unsure if just using depend_on at the top of your application.scss.erb works as expected. In theory, I understand what you want it to do therefore rendering my suggestion pointless since you pass in the path of the file within depend_on. I believe when I was first trying to do what you were doing, I couldn't get it to work so I had to create a new directive.
However, if we can determine why you cannot just place depend_on at the top of your asset, that would be fantastic.
@reidcooper I tried that, at least in Sprockets 3.7.1 it fails because context is nil when the process_depend_on_config_directive method is called. What version of Sprockets are you using?
I wonder what happens if you clear your rails cache and delete the /tmp directory. Make sure you don't have anything. Maybe try that.
Sprockets 2.12.4
Try perhaps debugging at that point and see if you can resolve that file.
In Sprockets 3.7.1 DirectiveProcessor#resolve(path) doesn't accept absolute paths or relative paths outside: /app/assets or /vendor/assets.
For what it's worth, I'm having a similar problem with a js.erb file & a file located in config. I'm contemplating just putting a hash of my config file into an otherwise unused file within the assets directory (although this feels wayyy more convoluted than it needs to).
@stepheneb's note about paths outside of app/assets and vendor/assets made me think about symlinks.
I've been able to work around this by creating relative path symlinks in app/assets/depend_on_symlinks and then using, e.g., //= depend_on depend_on_symlinks/some_ruby_file
I haven't tried it with a .yml file but it might be worth a try.
This is admittedly a gross hack but it seems to get the job done until paths outside app/assets and vendor/assets are accepted.
I think the real issue here is that depend_on lets you depend on a file outside of the load path without any kind of warnings or errors.
It sounds like symlinking is valid. The only other solution I could think of is if you added config/ to your load path, but that's not a great idea. You don't want to make a mistake and accidentally render all of your config in your public folder.
Perhaps in sprockets we could relax the depend_on behavior to work with files outside of the load path, however I think that assumption is pretty well baked into the depths of sprockets and would be hard to change.
instead of symlink you can create some dummy file and calculate checksum e.g. app/assets/stlesheets/config_checksum.erb
<%= Digest::SHA2.hexdigest(Rails.root.join('config', 'initializers', 'settings.rb')) %>
and use regular depend_on instead of process_depend_on_config_directive this should work for sprockets 3.7.2
I was just bit by this one as well. As I have JS.erb code that pulls in constant data from Rails models and I would like the assets rebuilt when the model changes.
Here is a hacky workaround to add in a new direct that works on sprockets 3.7.x
Sprockets::DirectiveProcessor.class_eval do
def process_depend_on_app_file_directive(file)
path = Rails.root.join('app', file).to_s
if File.exists?(path)
deps = Set.new
deps << @environment.build_file_digest_uri(path)
@dependencies.merge(deps)
end
end
end
Simply call it like this.
//= depend_on_app_file models/user.rb