Error pages are really just static assets. Why not make them part of the asset pipeline?

https://github.com/rails/sprockets-rails/issues/49 (which is locked so no further discussion is possible there) debates whether asset precompile should create a non-digest version of the assets.

There are a few valid use cases for this, and I do not have solutions for all of them, but I just thought of a solution to one–static error pages.

@pelargir, in some of the first comments on the issue (https://github.com/rails/sprockets-rails/issues/49#issuecomment-20529994, https://github.com/rails/sprockets-rails/issues/49#issuecomment-20531267), stated the issue:

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.

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?

My idea is to make the static error pages part of the asset pipeline. Instead of generating public/500.html, public/404.html, etc, a new rails app should generate app/assets/html/500.en.html.erb, app/assets/html/404.en.html.erb, etc. “app/assets/html/*” should by default be included in config.assets.precompile (which I believe it is anyway since it’s in app/assets and the extension is neither .css or .js).

There are 2 places where the behavior would be significantly different than of all other assets, so things would get a little tricky here:

  1. Unlike other assets, these would need to be precompiled in the context of a layout. This could be either the same application layout as the rest of the application (app/views/layouts/application.html.erb ), or the “rails new” generator could create a separate app/assets/html/error_layout.en.html.erb (which itself should be excluded from precompiled). Telling sprockets when to compile in the context of a layout could be tricky, but an alternative is just to code that into the view. Below is an example from an existing project of mine. It’s in HAML, not ERB, but still illustrates the point:

    app/assets/html/500.en.html

    • layout = Rails.root.join(“app”, “assets”, “html”, “error_layout.en.haml”)

    = Haml::Engine.new(File.read layout).render do

    %h5 We’re sorry, but something went wrong.

    %p Our developers are working on it.

  2. Error pages currently live in public, but with this approach, they would end up in public/assets, and would have digests in their file names. So we would need to change https://github.com/rails/rails/blob/v4.2.3/actionpack/lib/action_dispatch/middleware/public_exceptions.rb#L44 to look in “public/assets” and use the appropriate digests, and add a Depracation warning (that shows up in development environment) if the error pages are found in “public”.

What does it mean when nobody replies after almost a week? Does that mean everyone thinks it’s a good idea or everyone thinks it’s a bad idea? Should I just take my chances and submit a PR?

I think it means people don’t feel strongly about it one way or the other. It’s good because no one hates the idea.

I personally would love to have fully dynamic error pages that could use my _header.html.erb and _footer.html.erb so when I make a change to one I don’t have to copy it over to the error page, the problem is if you have an error in your error page then everything breaks.

To help mitigate this I wanted to introduce the concepts of multiple error fallback pages, but it went nowhere https://github.com/rails/rails/pull/8425 I closed it due to lack of interest.

Some changes are better debated as a PR. It sounds like this one might depend heavily on the implementation. Go ahead and give it a shot. Even if it isn’t merged, you’ll learn plenty about Rails internals :slight_smile:

I can’t speak for others, but for me it meant that I had no interest in the idea.

The suggestions in that PR of putting non-fingerprinted files in public/assets reveals a misunderstanding of how you’re meant to configure that directory for long cache expiry (and hence, a misunderstanding of the entire point of the asset pipeline and fingerprinting files).

Your suggestion is at least feasible, although I think (and in my experience) the use case you explain is much more easily resolved by adding some ERB processing into your deploy scripts. It’s basically (as far as I can tell) your same idea, but just done in your own rake task as part of your deploy instead of added to the existing precompile task.

Jason, thank you for your honesty!

I think where you and I disagree is that you call it a “use case”, as though it’s part of one applications particular business logic, whereas IMO it’s a part of the framework that should “just work”, because who wants to maintain 2 separate stylesheets–one for dynamic pages and one for static ones–or to have to write their own deployment scripts to tell some files where to find the assets, when Rails already has that feature.

At this point, I’m leaning more towards just creating a new gem to handle this, instead of submitting a PR to rails. We’ll see if I get some time to do that in the next few weeks.

Definitely pushing it out as a gem is a great way to start. See what the traction is and see if it proves useful to lots of people.