sprockets icon indicating copy to clipboard operation
sprockets copied to clipboard

//= depend_on not working when making small change in file

Open stepheneb opened this issue 8 years ago • 10 comments
trafficstars

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

  1. Change name of theme from default to blue in /config/app_settings.yml
  • Asset cache is not rebuilt.

touch /config/app_settings.yml

  • Asset cache is rebuilt
  1. Change name of theme from blue to default in /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.

stepheneb avatar Sep 14 '17 16:09 stepheneb

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 avatar Sep 14 '17 17:09 reidcooper

@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?

stepheneb avatar Sep 14 '17 17:09 stepheneb

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

reidcooper avatar Sep 14 '17 17:09 reidcooper

Try perhaps debugging at that point and see if you can resolve that file.

reidcooper avatar Sep 14 '17 17:09 reidcooper

In Sprockets 3.7.1 DirectiveProcessor#resolve(path) doesn't accept absolute paths or relative paths outside: /app/assets or /vendor/assets.

stepheneb avatar Sep 14 '17 18:09 stepheneb

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).

Tuckie avatar Oct 05 '17 19:10 Tuckie

@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.

semanticart avatar Nov 13 '17 16:11 semanticart

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.

schneems avatar Nov 17 '17 17:11 schneems

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

pironim avatar Nov 27 '18 16:11 pironim

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

urkle avatar May 09 '19 19:05 urkle