rails/sprockets-rails

Inability to compile nondigest and digest assets breaks compatibility with bad gems

gwcoffey opened this issue · 159 comments

For instance, the jquery_mobile-rails gem does not use image-url in its CSS. This is a bug really, but it "worked fine" in Rails 3. I assume they will fix the gem eventually. But in the meantime, in Rails 4, it is broken and as far as I can tell there's no way to work around it. The simple short term fix is to compile both digest and nondigest assets for this gem, but I don't see any way to do that. I can apparently only turn digest off or on globally for the application.

A rake assets:precompile:nondigest task or something similar would provide a workaround. But it seems to be gone now.

Is there a good way to deal with this, or do I just have to drop the gem and manage jquery mobile manually outside the asset pipeline, which is a pain?

+1

Removing generation of non-digest files also makes is impossible to have static 404 error pages that use the same images, JS, and CSS as the remainder of the site (without making duplicate non-digest copies of those files in /public). But then I end up with two copies of the same file in my app.

It's really unfortunate that this feature would be yanked without an explanation of how to deal with these situations.

From the README: Only compiles digest filenames. Static non-digest assets should simply live in public/.

I saw that. So please explain how I'm supposed to reference my precompiled application.css file from my static 404 error page? Am I supposed to copy the precompiled file into /public and remove the fingerprint from the filename? So then, if I change the file in the future, I have to precompile, copy, and rename all over again? That's silly.

You can use a rake task like this for your case:

task non_digested: :environment do
  assets = Dir.glob(File.join(Rails.root, 'public/assets/**/*'))
  regex = /(-{1}[a-z0-9]{32}*\.{1}){1}/
  assets.each do |file|
    next if File.directory?(file) || file !~ regex

    source = file.split('/')
    source.push(source.pop.gsub(regex, '.'))

    non_digested = File.join(source)
    FileUtils.cp(file, non_digested)
  end
end

FWIW, I disagree with the resolution of this issue. I'm all for software being opinionated, but I shouldn't have to create a rake task like this in order to compile assets without the digest. It is my application after all ;) The bigger issue from my perspective is that this breaks applications on Heroku that are doing things like serving JS widgets that can't have digests in the name.

https://gist.github.com/ryana/6049833

A monkeypatch for current version of Sprockets cc: @pelargir

I also disagree with the resolution of this issue:
"So please explain how I'm supposed to reference my precompiled application.css file from my static 404 error page?" is more or less the use case I have - a centralized Rails single-sign-on utility provides precompiled CSS via absolute URLS to other client applications.

If you need to generate assets without digest so you have to do this on your own.

After talking this over, my big-chair developer decided to handle this by having Rails return a 301 redirect FROM the old, non-digested CSS location, TO whatever the latest digested CSS location is. That way, the client sites get to use a non-changing location and the rails app doesn't have to bake everything twice.

This was exactly because we remove the undigest version. The Rails app had to bake the compilation twice.

@tikaro like https://github.com/sikachu/sprockets-redirect (it's for Rails 3.x but should be easy to port to Rails 4)

@tikaro clever, but you'll want to 302 instead of 301. 301s are permanent and some browsers will cache them.

@ryana good point, re: 302 and 301; I think I over-described what we've done, and may have gotten it wrong. Cloudfront is involved, too. Perhaps I can coax him in here so he can describe what he did.

Hey guys,

Just wanted to say that I liked the middleware solution so I updated @sikachu's code for rails 4 : https://github.com/Intrepidd/sprockets-redirect

I'll issue a PR in no time.

Cheers

This seems like a real problem to me. It is now impossible to refer to rails-generated assets from outside the rails application. @Intrepidd's middleware is an elegant patch but sadly no use if you're delivering the assets through a CDN.

Would you accept a pull request that adds a config.assets.nondigest setting? There's no need to bake twice: we can just write duplicate files during Manifest#compile and remove them again during Manifest#remove. That's what I'm doing in a monkeypatch now and it seems to work well.

I concur, no having non-digest files without an alternatives is pretty annoying. Maybe I missed something, but what problem is it solving that non-digest files are no longer generated?

For those of you looking for a way to make it work with error pages, I adopted a post on how to make error pages come from asset pipeline (added the bit about non-digest assets) https://gist.github.com/ilyakatz/6346020

I also have a use case for the ability to precompile assets without digests - we need to reference a javascript file from a 3rd party app, and it would be nice to use the asset pipeline - just without the digests!

Agreed, @will-r it seems easy to not compile assets twice and still have this built in.

I know it's unproductive to pile on but I really agree with all the commenters here. A decision was made to remove this and by gosh we're gunna stick to that decision no matter what it seems. While the use case for non-digest assets is completely valid IMO:

  1. download almost any javascript library, like fancybox
  2. put jquery.fancybox.min.js in vendor/assets/javascripts
  3. put related images in vendor/assets/images
  4. include the main javascript in your application.js
  5. watch all your images not loading.

What are we supposed to do in this case? Put fancybox in public? So by-pass the asset pipeline altogether. This does not seem like an improvement to me, in fact it's a regression. We had something that worked just fine, now we are back to loading a bunch of javascripts separately. We can ditch the asset pipeline altogether?

If I am missing something, I sure would like to be educated. But putting potentially a dozen scripts in public is no kind of solution, frankly I am shocked it is even being suggested.

Yeah I haven't yet seen a single rails 4 project that doesn't need to work around this. This decision should be reconsidered IMO.

This was a bad decision and is negatively affecting multiple apps i'm working on for no reason. Rails should not be this opinionated.

@rafaelfranca you ever gonna come comment on this issue again? I've moved on by using the monkeypatch above, but I find it funny that I get emails from this thread every few days and you guys are just ignoring it. I understand you have jobs and probably busy, but you're stewards of this project. If you can't handle it, responding with a simple "ok cool submit a pull request and we'll merge it in" would suffice. I'm sure the community would pick up the slack.

This is what I'm using at the moment. It's a little crude and doesn't tackle the slightly harder problem of what files to remove, but as you can see the added computation is very slight. All we have to do is build another filename and write that file too.

module Sprockets
  class Manifest

    def compile(*args)
      unless environment
        raise Error, "manifest requires environment for compilation"
      end

      paths = environment.each_logical_path(*args).to_a +
        args.flatten.select { |fn| Pathname.new(fn).absolute? if fn.is_a?(String)}

      paths.each do |path|
        if asset = find_asset(path)
          files[asset.digest_path] = {
            'logical_path' => asset.logical_path,
            'mtime'        => asset.mtime.iso8601,
            'size'         => asset.bytesize,
            'digest'       => asset.digest
          }
          assets[asset.logical_path] = asset.digest_path

          target = File.join(dir, asset.digest_path)
          undigested_target = File.join(dir, asset.logical_path)

          if File.exist?(target)
            logger.debug "Skipping #{target}, already exists"
          else
            logger.info "Writing #{target}"
            asset.write_to target
            asset.write_to undigested_target
            asset.write_to "#{target}.gz" if asset.is_a?(BundledAsset)
            asset.write_to "#{undigested_target}.gz" if asset.is_a?(BundledAsset)
          end

          save
          asset
        end
      end

    end
  end
end

Hot from the mines, there's now a gem for this:

https://github.com/alexspeller/non-stupid-digest-assets

gem 'non-stupid-digest-assets'

Problem solved.

lol

We've had no trouble using digested assets throughout, and we've also updated our Rails 3 apps to do the same.

When an external app needs to reference a specific static asset, you can use a separate rake task that symlinks yourasset-<digest>.css to yourasset.css after precompile.

When you want to pull in an external lib that has its own scripts, images, and css, you can vendor the assets in public/somelib-v1/* an include the javascript in the asset pipeline. Then you're still free to put far-future expires on all static assets. When you upgrade to somelib v2, just add a public/somelib-v2/ directory.

Stable assets that can be publicly cached—and CDNed—forever. Now that's non-stupid 😁

I don't have a problem using digested assets, but I do have a problem with rails being so opinionated about it and removing the choice. The biggest problem being with 3rd party javascript libraries or using my own app as a javascript source. Not every app is the same and that's why these choices need to exist.

  • This seems like a really bad Rails 4 default ( Forced default && No way out of it )
    Please reconsider adding the old non digested versions back!

@turlockmike I have the exact same usecase you presented.

Hi @jeremy, I appreciate your explanation on how we are supposed to use external libs now. We've been forced to not use public for anything besides a favicon, so this is I think why you're seeing the pushback. Also, front ends web servers will not have been configured to serve from /public with optimal headers. And with the version numbers hardcoded in paths, some libs will require changes to the JS/CSS file every time you upgrade them. It's going to be a painful experience.

It would be incredibly helpful to have a writeup in the sprockets README or some kind of documentation how to use third-party libs. This issue is going to sting a lot more developers. It would be even better if we could come up with a formal, optimized approach on how to include the wide variety of libs we have in the wild.

Is anyone in this thread aware of a better solution? Personally I'm intrigued by the middleware that redirects to the correct asset. I just fear that doing this in rack middleware is a performance problem.

@alexspeller that works as long as you don't upgrade your lib or can live with possible breakage from cached assets.

Which is cool by me but arguably not the best solution we can think of.

If one would only need certain files to be non digested which seems the case many times ( you don't need all the files to be non digested and copied over an extra time ) how about an paramater added to image_tag like

= image_tag('/assets/logo2.png', :digested => false)

I tried this initialiser code hacked together below from several sources
but it fails on rails 4, this would be ideal solution for those of us who only want to make some files available

module ActionView
  module Helpers
    module AssetTagHelper

      def image_tag_with_digested(source, options={})
        digested = options.delete(:digested)
        digested = true if digested.nil?

        unless digested
          source = image_tag_without_digested(source, options)
          source.sub(/(\-[a-f0-9]+)\.(png|gif|jpg)"/, '.\2"').html_safe
        else
          image_tag_without_digested(source, options)
        end
      end
      alias_method_chain :image_tag, :digested
    end
  end
end

@alexspeller
Tried you gem for production but it failed to create any non digested files.

@rubytastic I have tested it and it does create non-digested files. Please file an issue on the project and give more details if you can't get this to work.

+1 I would also really like to see this reversed, as an option. for now using @alexspeller's gem, does the trick in a jiffy!

Confirm @alexspeller gem is working correctly. I had other local issues earlier on.

gem 'non-stupid-digest-assets' convert only one css file, js files are not.
rails 4 + ruby 2.cap deploy

@alexspeller
After investigation i found that its do create non digest files, but another gem (assets_sync) is not uploading them all.

thanks for nice gem

@rafaelfranca seems to me like there are sensible and logically sound arguments against the change to sprockets. I believe a lot of people here are upset because you haven't addressed any of the comments objectively, but rather with a heavy-handed "just do what we say".

To recap arguments against non-digest to be included:

  1. Static pages (404s) will require you to create duplicate images (e.g. logos) from assets/images and put them into public/assets - that's a headache to remember later on down the road if you want to update images.
  2. Many people don't have control over css since they're using jQuery libraries that know nothing about the asset pipeline. Going into a plugin's css and changing url(..) to image-url("..") each time a library gets updated goes against Rails' philosophy of "web development that doesn't hurt".
  3. Some js libraries (like ckeditor) do tricky stuff like inject assets on the client side at runtime (ugly, but it's what it does)
  4. There hasn't been an explanation of why something so seemingly benign has been removed. So... why was this change so strongly supported?

To address your points, @sarmiena:

  1. Reference static images from these pages. I do not know why you would precompiling images for these things. Stick them in public/images and be done with it.
  2. There are plenty of "bridging" gems out there for the major plugins and libraries. I would suggest you use those if you want to save yourself the hassle of find+replace operations.
  3. I don't know enough about how ckeditor works to fairly answer this point, but I think it's probably in a similar boat to point no. 2. There's probably a gem for it.
  4. That's one for the maintainers to answer.

@radar
I can understand where you're coming from, but:

  1. Having duplicate images in public/assets goes against DRY, which is at a higher level than rails opinions
  2. In the event that plugins don't have gems, people shouldn't feel compelled to commit to maintaining a gem. That development hurts.
  3. There are gems that do handle ckeditor. 2 in fact. However there is a huge community around it, and why should we mess with their tests to modify it for a gem?
  4. Ditto

@radar For a lot of applications, this change makes absolute sense. Referencing a file which is cached by the browser/http does cause issues where a user might have an expired version of the asset but the cache header was not set appropriately.

For other applications, rails serves as a host for a set of javascript files which are used by third parties. In this case, given the impossibility of constantly asking users to update their script tags to reflect the new file hash, the best solution has been to have two copies of the file after compilation. One with the md5 hash and one without. If sprockets intends to support these types of applications, then I believe there should be an option to precompile an undigested version of the assets if desired.

It really comes down to the scope of the library and defining what it is for. If this gem is not meant to support certain types of applications out of the box, then it should probably be documented and rails should probably consider a replacement since i'm fairly certain rails would like to support those types of applications.

As far as third party javascript files like ckeditor or other javascript libraries which expect relative files to exist, perhaps a solution which allows you to pick files to be undigested using a whitelist would be the best solution. It would make it difficult enough that you would only whitelist files that you explicitly need to whitelist and asset pipeline can generally assume everything else is digested.

@radar

  1. Reference static images from these pages. I do not know why you would precompiling images for these things. Stick them in public/images and be done with it.

Because static 404/500 pages almost always need to look the same as the rest of the site, i.e. sharing images, css and potentially javascript. Having to duplicate all of your sites assets for these pages is not a solution.

  1. There are plenty of "bridging" gems out there for the major plugins and libraries. I would suggest you use those if you want to save yourself the hassle of find+replace operations.

Forcing the use of bridging gems is a not a good long term solution to this issue. You should be able to use arbitrary javascript libraries with the rails asset pipeline.

  1. I don't know enough about how ckeditor works to fairly answer this point, but I think it's probably in a similar boat to point no. 2. There's probably a gem for it.

Same answer as (2)

The asset pipeline clearly works for some people and also clearly does not work for other people. You seem to be someone in the latter group. Don't use the asset pipeline if it doesn't work for you

It seems like it would be possible to greatly increase the number of people the asset pipeline works for by reverting this change, whilst at the same time not causing any negative consequences to others.

I know rubygems counts are not entirely accurate but my gem has received nearly 800 downloads in less than 3 weeks. It certainly seems like a lot of people have this problem.

@alexspeller I can't speak for the other 799, but you got one from me! :)

Yes please listen to us developer, stepping back a little bit from your ( well deserved celebrity superman status )

TL;DR yes, we choose what we think is best for the direction of the project. And the reasons is here in @radar's comment and @jeremy's comment.

We never said this gem have to solve every problem in the world, and we don't think it should. If you have a different problem that is not solved by this gem, it is your job to solve your problem. And we already give you a lot of suggestion in this thread about how to solve some problems you may have.

The majority of Rails applications don't need non-digest assets and we don't want to keep this code in the gem since it will not used by the majority of our users. It is all about maintenance cost.

TL;DR 2, this rake task would solve almost all your problems: #49 (comment)

Well that escalated quickly. Must have been one hell of an IRC exchange to get people this heated :) I would encourage everyone to set a calendar reminder for like a month from now and come back and re-read this whole thread noting the timestamps. There are probably some valuable lessons to be learned in how to better manage this from all sides in the future. I'll certainly be thinking about it.

Give that this is not going to make it's way in sprockets, a summary of available options:

Happy hacking ;)

TL;DR: Digests are used for cache busting. Remember that if you use the non-digest assets and serve them with far-future expires headers, you will cause problems with cached assets if the contents ever need to change.

Rails embraces good defaults and in my book that doesn't count as a good default.

Anyway Rails is not stopping you if you want to re-add it using third party gems (like the gems suggested in this thread) or add a custom rake task to your app (as the custom task suggested in this thread).

I have, what might be a related issue.

I have a Sass file which references som SVG-images. All the SVG images are getting a hash appended to the file (as expected) when precompiling. The issue arises, since the compiled CSS file is referencing the file without the hash.
Shouldn't the Sass compiler use the digest version of the images, which are referenced in the sass file, or should I e.g. use the gem https://github.com/alexspeller/non-stupid-digest-assets, to create two versions?

Jell commented

+1 on the issue, although I don't think the title is appropriate. I'm coming here from https://github.com/alexspeller/non-stupid-digest-assets

We're building an API and chose to prefix our assets with a version number (i.e v2/my_asset.js). This was working fine with Rails 3 but is broken in Rails 4 because non-digest versions are no longer generated.

I feel that generating both digest and non-digest versions of the assets should be straightforward and cost very little work or maintenance, if that is the case would you please reconsider your decision? I know too little about the codebase to do a PR myself.

Jean-Louis:

Are you using the asset-url helper in your CSS files? What you describe should work fine with digested assets, and is a good example of why this change is probably beneficial. If your code doesn't work with digested assets, then you have a problem in your code, and it no longer "seems to work."

Jell commented

@gwcoffey like I'm saying, we're building an API and what we compile is a javascript and CSS which is used OUTSIDE our application. We absolutely need non-digest assets, because the clients that use our javascript are not in our control.

The version you see in the link is the version of our API, not the version of the asset.

Jell commented

@gwcoffey now that I'm re-ready my comment, I see how my message might have been confusing :p sorry about that

Oh hey there, I'm a little late to the party here. It should probably be possible to have the option to expose non-digest assets as well as the digest versions via a config setting. I need to refer to assets externally, as I have several syndicated JavaScript widgets. I'd be happy to submit a patch if other people agree :)

@forwardadvance I think several dozen people agree, but unfortunately not the gatekeepers. Their advice is to fork sprockets and maintain your own distribution of it. There is a list of available options in this comment above: #49 (comment)

Have a good one!

Hi there Rails Core Team. This just bit me. You got this one wrong.
I find it hard to see how this wouldn't affect any decent sized project to be honest.

I use two different JQuery plugins. Both have CSS and use images.

In development mode, no problem. Images from vendor/assets/stylesheets/images work

In production mode, they get digested (once you learn the magic config.assets.precompile += %w[*.png *.jpg *.jpeg *.gif] incantation).

This places them in public/assets/images which is the right place but they are now fingerprinted so the CSS references break.

It is not an option to use image_tag because these are 3rd party libs and it's a royal pain.
It is not an option to copy them to public/images because that's not really where they should be living.

Either we need a way to
a) selectively copy over these types of assets without digests
or
b) ???

There's no way this issue should be closed.

The solution AFAIK has always been to modify the plugins to update the asset references. It's a bit annoying, but it works fine.

On Dec 17, 2013, at 6:31 AM, igravious notifications@github.com wrote:

It is not an option to use image_tag because these are 3rd party libs and it's a pain anyway.
It is not an option to copy them to public/images because that's not really where they should be living.

Why not? If app/assets is for the asset pipeline, and you don't want to update the jquery library to use the asset pipeline, then I think they DO belong in public. In many cases, putting even the CSS or JS into the pipeline will still require you make changes to the library because they use relative paths that depend on their own directory structure. So in general, either a library IS or IS NOT asset-pipeline ready.

There are really a few ways to solve your issue:

1: Put the requisite parts into public just like we always did before the asset pipeline.

2: Use a gem someone else maintains. There are gems for, ie, jquery-mobile and jquery-ui. They do run a bit behind so if having the latest release right away is important for you this may be a problem.

3: Update the library to use asset-url. I do this semi-regularly now and it is usually very easy. Just search the css for "url" and revise. It is quite rare to have any asset dependency in the javascript.

#3 seems totally stupid at first but then you do it a few times and realize it isn't that hard, it doesn't happen often, and once it is done, you're actually using the asset pipeline (with fingerprinting etc...) properly. So when you DO update the library, you're users will see the new assets right away without caching issues, which is one of the primary reasons the asset pipeline exists.

I opened this issue for exactly the reasons you describe, but I've been convinced that for the use case you're describing, I was wrong. For those running APIs, I think there's a legitimate concern. But for normal rails app development, it is better to fail fast when assets aren't being pipelined properly.

That said, I would very much prefer they didn't work in development either, to save the last-minute-oops-sorry-client hustle.

Geoff

On Dec 17, 2013, at 5:32 PM, Andrew Kaspick notifications@github.com wrote:

@gwcoffey Regarding #3: Can you clarify how you're using asset-url. I have plain css files in vendor/assets/stylesheets that reference images that are digested through the pipeline. Are you renaming all of your css files to be sass files instead in order to get asset-url working correctly with image references? This is what I'm hung up on currently.

I only do this maybe every six months, but if I recall the general steps are:

1: Put the CSS file(s) into /app/assets/stylesheets (possibly in a sub-directory) and rename them .css.scss

2: Put the images into /app/assets/images/{whatever}

3: Search the CSS files for "url" and change them to asset-url calls. Ie:

url(../images/foo.png) => asset-url("/{whatever}/foo.png")

I think in most cases I've dealt with, that's all there is to it. If you have @imports, you may want to change them too, so they get compiled into the application.css. Also, don't forget the gem route. For instance, https://github.com/joliss/jquery-ui-rails is well maintained, and they do all this work for you.

Geoff

I was under the assumption that all 3rd party libs should go into vendor/assets (that's been what we've been "told" to do since rails 3... I thought).

Since I'm trying to follow "conventions" (which are now different?), I've got everything working in vendor/assets with one extra addition... needing to do the following...

config.assets.precompile += %w(third_party.css {whatever}/*.png)

Is the recommended approach now to move 3rd party libs that we've made "pipeline ready" back into app/assets?

For what it's worth, I put them in /vendor/assets as well. I was remembering wrong.

On Dec 18, 2013, at 7:14 AM, Andrew Kaspick notifications@github.com wrote:

I was under the assumption that all 3rd party libs should go into vendor/assets (that's been what we've been "told" to do since rails 3... I thought).

Since I'm trying to follow "conventions" (which are now different?), I've got everything working in vendor/assets with one extra addition... needing to do the following...

config.assets.precompile += %w(third_party.css {whatever}/*.png)
Is the recommended approach now to move 3rd party libs that we've made "pipeline ready" back into app/assets?


Reply to this email directly or view it on GitHub.

+1 for this issue. Wanted to make my 404/500/422 pages look the same as in the rest of the app. We use custom fonts and our app uses haml/sass everywhere. Some issues are fixed by using the asset-path within our sass/haml which is fine. However, I still need to reference from my static page to the end result (with digest).

I have put the files now in my rails app and let exceptions route through it.

@stefanhendriks Put non-digested files in your public dir. If you need the special casing for your static pages, then that's where you should put them. Otherwise there are the other proposed gems/options provided in this discussion that should help.

@akaspick Actually I am using now dynamic pages and it works fine. The only remaining problem I have now is rspec complaining about missing templates once a 404 or a 500 error occurs. Even though I set config.consider_all_requests_local = false within test.rb.

Any idea? generating static versions of them (as Ryan does in his screencast) works (as the template exists again, and the test is passing), but the whole point is to do dynamic pages, so this solution is not sufficient.

Over the past year it seems that front-end libraries have been rapidly adopting the bower package manger. For may reasons bower is awesome. Mainly because it is similar to ruby's gem system. You can configure bower to install packages directly into the pipeline. Updating packages just takes altering a bower.json file and running bower install. Front end dependency life is just grand.

This change to sprockets-rails makes any images these bower installed packages use unfindable because they reply on non digested relative paths. Updating the packages directly just wrong and using a gem wrapper is, imo, a way of the past.

I will be using the rake task listed above but it would be nice if sprockets-rails could support this common scenario. As others have suggested having the ability to whitelist files or directories to keep non-digest assets, like a config.assets.nondigest option, would solve this.

Just some thoughts. Thanks to those who contribute to this project. I appreciate it.

Completely agree with @bennick and strongly recommend having a look at using bower for managing front-end assets. I wrote this blog post on using bower with rails that might be useful to some people interested in doing that.

gkop commented

+1 that this issue ought to be reopened.

@alexspeller what if I rename my rake task to assets:precompile?

namespace :assets do
  task :precompile => :environment do
    assets = Dir.glob(File.join(Rails.root, 'public/assets/**/*'))
    regex = /(-{1}[a-z0-9]{32}*\.{1}){1}/
    assets.each do |file|
      next if File.directory?(file) || file !~ regex

      source = file.split('/')
      source.push(source.pop.gsub(regex, '.'))

      non_digested = File.join(source)
      FileUtils.cp(file, non_digested)
    end
  end
end

Then your argument about not working with heroku/capistrano, etc aren't valid anymore

Thanks for the gem @alexspeller

+100500

I have the same use case others have mentioned with error pages that are broken with the removal of non digest assets.

Just started to convert my applications and stumbled on this problem too. So I thought I could put my 2 cents on problem.

We have this wonderfull mechanism of ruby gems, to include external ruby code in our projects and use them as engines in rails applications. But world is moving to more and more code being written in javascript and with this sprockets move it has become even harder to include third party javascript code transparently in our projects.

Moving static javascript to public folder, for using projects like ck_editor, is probaly OK if you use library only for your own, single project. But what if same code is beeing used by multiple projects. Than copying same code all over again becomes pain. That's why gems were invented for. Problem is that gems don't handle putting external javascript code into right destination directory. But sprockets know everything about it.

Funny thing is that code placed into assets folder works perfectly OK when in development, but in production and test nothing works at all, because the code doesn't get copied to destination folders.

I was already suggesting new directive eg. 'copy_directory' which would copy everything what is found in that directory to /assets/diretory when assets:precompile is run. This would help us a lot to gemify third party javascript libraries and include them into rails projects.

Or have I missed something that is already build into rails?

by
TheR

@TheR2 gems work fine with the asset pipeline. However rubygems are not the best solution for packaging javascript libraries. Have a look into bower and bower-rails for packaging javascript libraries, they work quite well together.

@guilleiguaran Thanks for the Rakefile solution, but unfortunately I found a problem with it after using it for a few days.

If you keep old copies of files in public/assets (the default behavior in Rails 4, even after an asset:clean) then you risk having old versions of your assets ending up in the non-digest files.

For example, imagine on Monday assets/javascripts/application.js compiles to public/assets/application-39f56e9c8b27cc62488e84f9f5cf7788.js, and after making changes on Tuesday it compiles to public/assets/application-1274e865b362e15aa2a29b1c9dbdf025.js. During precompile, both application-39f56e9c8b27cc62488e84f9f5cf7788.js and application-1274e865b362e15aa2a29b1c9dbdf025.js will get copied to application.js, with one clobbering the other. If you're lucky, the most recent file will be the one that wins. I wasn't so lucky today in a production environment. :-)

I switched to using https://github.com/alexspeller/non-stupid-digest-assets and that's working better for me.

@righi @guilleiguaran I faced with that problem too. non-stupid-digest-assets gem helps with it.

+1, I'm using the provided rake task to solve the problem, but it should be a Rails default, this is a pain specially for beginners where things should work smoothly by default, it's the Rails philosophy.

If you do not want non-digested files to be generated you should config not to generate them, instead of forcing the rest of us to do the opposite, since we are like 99% of the cases.

I think it's a good default not to generate non-digest because non-digest requires doubling the number of files written. However, there should be a config option to either generate all non-digest or s subset of non-digest files.

I think for a lot of people (myself included), there are a few files like generating error pages that need non-digest versions but all the rest get served via digest assets.

Everybody I know uses some files that references non-digested assets. I should not force the front-end developers of my project to always use rails helpers in their layouts to reference correctly digested assets, this violates Rails convention over configuration pattern.

There was no single project in rails 4( even my coming soon page) where this was not an issue.

+1 on Rails core getting this wrong and not providing an officially supported route (via config option or official rake task) for remediating the problems caused by their heavy-handedness.

+1. This needs to be a config option.

Another use case to consider is HTML emails. There may be many emails sitting in people's mail readers that were sent before this change was made. Those emails probably use non-digested asset references because they were the stable choice for long lived documents, if you're willing to commit to not deleting or changing the assets. It's impossible to go back and change these emails because they were already sent.

I appreciate the hard-core digest only default, but I'd like to be able to make a hand tuned exception for certain assets.

+1. Also bit by the use case @wlipa mentioned.

+1 Also here just to complain. Seems very short-sighted to assume all 3rd-party assets will use the Rails pipeline hooks like 'asset-url' when referencing images/fonts. Why not just provide both?

Please make a config option so 'non-stupid-digest-assets' is not basically a requirement for anyone building a Rails 4 app.

Question for the team: How can you build a JS API with Rails 4? It doesn't seem possible without generating a non-digested version of the file.

@celsodantas I think you are confusing the asset pipeline with json responses to controller actions. Nothing in this discussion is relevant to building APIs.

I'm not talking about controllers responding to JSON, I'm talking about writing JS code to publish to clients of my application to use (like a SDK). In these case we need to give the clients a fixed path.

Example: http://docs.shopify.com/embedded-app-sdk/initialization

@celsodantas oh, I see what you mean. The techniques to achieve this are discussed in great detail above.

@alexspeller, I saw the "solutions"/workaround. I just think it's not a wise idea to not add an option to add the non digested files back.

I find it "ironical" that this issue just bit me again 8 months after I first expressed my opinion that this should be changed.

I'm using one of Symbolset's awesome font icon sets: https://symbolset.com/icons/gizmo

I copied their webfonts and CSS into my assets directory. Here is a segment of the stylesheet that accompanies the font:

stylesheets/ss-social.css:15:  src: url('/assets/webfonts/ss-social-circle.eot');
stylesheets/ss-social.css:16:  src: url('/assets/webfonts/ss-social-circle.eot?#iefix') format('embedded-opentype'),
stylesheets/ss-social.css:17:       url('/assets/webfonts/ss-social-circle.woff') format('woff'),
stylesheets/ss-social.css:18:       url('/assets/webfonts/ss-social-circle.ttf') format('truetype'),
stylesheets/ss-social.css:19:       url('/assets/webfonts/ss-social-circle.svg#SSSocialCircle') format('svg');

ss-social.css is then imported into my application.css.scss. Now, it's a pretty safe bet these assets are never going to change. But sure enough, after upgrading to Rails4, all my social icons go bye bye because the ss-social-circle.(eot|woff|ttf|svg) files no longer exist in my asset directory.

I know how to fix this. I think we are saying that the best practice here is to go into ss-social.css and change the call from url() to asset-url(). Ok. That's fixes it. Ignoring the fact it's annoying to have to do that an already minified CSS file (which this isn't, but others are), it does solve the problem.

But that's not what concerns me here. What concerns me here is that we've now built something into Rails that by default works correctly in the development environment and breaks in the production environment, and there is literally no way you would know it until you run the app in the production environment.

This doesn't just happen on Heroku btw. It also happens on dokku/docker since the assets directory starts fresh every time a container is started. On deploys directly to a linux box (via Capistrano et al), these issues may be hidden if the assets directory is old and still contains non-digested assets from before this change was instituted.

What's maybe even worse is that this default also works in the test environment, so if you're going out of your way to generate screenshots in Poltergeist to ensure stuff looks good, you're still going to get bit once you go to production.

Opinions about caching best practices and performance aside, I think we can all agree that having a default that introduces an error when going from dev -> production is not something that can stand.

I think the cleanest way to address this is to take all the assets generated by the asset compilation (with their digests) and simply make copies of them without digests in the name. We don't need to recompile. The copied files will still contain references to digested assets, but that's ok I think. This should be the default behavior.

If you're a contributor on this project, please let me know if you'd accept a pull request of this nature.

Thanks for reading this whole thing!

@ryana I agree the default should be to have non digest assets with an option to exclude them. To me this change feels like a big case of premature optimization.

@ryana @bennick It doesn't bother me that the default is for digested assets. I think convention over configuration is perhaps Rails greatest contribution to the web development zeitgeist, and if most people need digests only, great, I have no problem making a slight modification to my environment settings upon upgrade.

What I find incredibly irksome, heavy-handed, and a bit arrogant is that the maintainers of the gem have determined that they know what's better for my use-case than I do, and that there is in fact no reason for me to have an officially supported method of generating non-digested assets. The hacks detailed in this thread are just that: hacks. If it works, and its apparently easy enough to do that nobody on the maintenance team feels the least bit troubled by requiring everyone in this thread (realizing that the commenters are likely the tip of the iceberg) to use them, then why such resistance to adding it as an officially supported option so that we have an interface we can rely on in future versions of the framework? This isn't merely a matter of a vocal minority clamoring for a feature no one needs. This is a feature which was official, and as such relied upon, by many people, and its summary removal (without even a deprecation process, AFAICT—correct me if i'm wrong) was absolutely the wrong move, and I'm flabbergasted at the resistance to adding it back when there's zero impact to anyone else.

I completely agree that it is bad and broken that things work in development and not in production. It makes sorting out asset problems a big pain and it needs to be fixed. Although I think the fix should be that they don't work in development, not that they do in production.

Bear in mind in your use case with a minified webfont CSS, you have another option you didn't mention: Put the assets in public.

By putting them into the asset pipeline, it is reasonable for you to assume that you are getting the actual primary benefit of the pipeline, which is fingerprinting to ensure proper assets are shown with each version of the site + excellent cache support. In the same way that it is wrong for things to work in development and not in production, it is wrong for them to appear to work in production when they really aren't working as expected.

If you want fingerprinted safe assets, put them in the asset pipeline and adjust the code as needed so that it works.

If you want to simply drop the assets into your project and not worry about versioning, then put them in public.

I suspect in rails 3 there are thousands of sites out there with willy-nilly asset handling that doesn't really do what the pipeline promises because people running the site don't even realize they aren't doing it right and it "seems fine" to them. It is better to fail fast in this case in my mind.

NB: None of this addressed the other use case people have mentioned where they want combined, minified JS and/or CSS for use in non-rails pages. I'm not sure how best to deal with that, and it would probably be good if Rails had a simple sanctioned way to do it.

Geoff

@gwcoffey yeah good points. I would support a solution that made this not work in development. Of course, that would probably break a lot more applications :)

Perhaps we make the asset pipeline throw an error saying "Hey man you need to use asset-url() here" when it encounters a "url()" call to a relative path in an asset directory, that might be the right way to handle this.

@ryana A problem with that approach, as discussed above, is that you should not be modifying 3rd party front end libraries. These libraries also conflict with @gwcoffey idea of putting assets into public unless you are will to store all of your 3rd part library code directly in public. This, however, defeats much of the purpose of using the asset pipeline.