5

I'm trying to use a service worker in my Ruby on Rails application.

I need to use some erb features in my app/javascripts/service-worker.js.erb file. The service worker registration script looks like this:

var registerServiceWorker = function() {
  navigator.serviceWorker.register(
    '<%= asset_path('service-worker.js') %>',
    { scope: '/assets/' }
  )
  .then(function() {
    console.info('Service worker successfully registered');
  })
  .catch(function(error) {
    console.warn('Cannot register sercie worker. Error = ', error);
  });
}

This does not work; I never get promise here:

navigator.serviceWorker.ready.then

I also tried ./ and / scopes but I got this error:

DOMException: Failed to register a ServiceWorker: The path of the provided scope ('/') is not under the max scope allowed ('/assets/'). Adjust the scope, move the Service Worker script, or use the Service-Worker-Allowed HTTP header to allow the scope.

If I move my service-worker.js to the public folder, remove the .erb extension and change scope to ./, everything works great, but I have no template engine there.

Any suggestions?

Jordan Running
  • 102,619
  • 17
  • 182
  • 182
user525717
  • 1,612
  • 5
  • 28
  • 40

6 Answers6

4

There's now a gem, serviceworker-rails that will allow you to proxy requests for serviceworker scripts in the asset pipeline. Because of the way browsers register serviceworker scripts, it's best to avoid caching them.

Sprockets will fingerprint assets and Rails (or your webserver) typically serves the compiled files with aggressive caching headers from the /assets directory.

What we want is the ability to customize service worker paths and response headers and still get Sprockets preprocessing, i.e. CoffeeScript/ES2015 to JavaScript transpilation.

The serviceworker-rails gem inserts middleware to your Rails stack that will proxy a request for /serviceworker.js (for example) to the corresponding fingerprinted, compiled asset in production. In development, you get the auto-reloading behavior you would expect. You can set up multiple serviceworkers at different scopes as well.

https://github.com/rossta/serviceworker-rails

rossta
  • 11,394
  • 1
  • 43
  • 47
2

Because of the security purpose, you can't register ServiceWorker in higher scope than from where it was executed.

If you really need template engine, you may try to dynamically load JS file from file in your /public folder (How do I include a JavaScript file in another JavaScript file?). Currently Service-Worker-Allowed HTTP header is not implemented yet in Firefox (https://bugzilla.mozilla.org/show_bug.cgi?id=1130101)

Community
  • 1
  • 1
Karol Klepacki
  • 2,070
  • 1
  • 18
  • 30
2

I'm running into this problem now as well. Based on my research, I think the proper way for a Rails application to handle service workers is to serve the service worker javascript file normally using the Asset pipeline, scope the service worker using the scope option (like you have done), and then use the Service-Worker-Allowed HTTP header to explicitly allow the new scope.

Unfortunately, at the moment there are several barriers to implementing this method:

  1. Rails 4 (apparently) does not allow adding HTTP headers to Assets it serves, other than the cache-control header. To work around this you could add a Rack plugin as detailed in this S.O. answer, or in production if you are using an Asset server such as Nginx, you can get around this by having Nginx add the HTTP header. Though this doesn't help during development.
  2. Rails 5 does allow adding HTTP headers to Assets however, as detailed in this blog post.
  3. Browser support for service workers and the Service-Worker-Allowed HTTP header is currently poor.
Community
  • 1
  • 1
John
  • 9,249
  • 5
  • 44
  • 76
  • 2
    Does the serviceworker-rails gem mentioned in a later answer (http://stackoverflow.com/a/36849798/1299792) resolve the barriers you mentioned above? – Marklar Aug 12 '16 at 06:26
  • @Marklar I haven't used that gem before, but, looking at it on Github, it appears to resolve all of the barriers except the fact that browser support for service workers is still poor. If I was building a new rails app using service workers I'd definitely check it out. – John Sep 11 '16 at 06:21
1

Just do it in the root directory. For example, I put a route

get 'service-worker(.format)', to: 'core#service_worker'

Then just put in your controller (core_controller.rb in my example)

def service_worker
end

Then create app/views/core/service_worker.js.erb and put your JS there (including any <%= stuff %>).

Jason
  • 519
  • 5
  • 14
0

I recently wrote an article about how to do that entirely with Webpacker. Please find my guide to a Progressive Web App with Rails. In short:

add the webpacker-pwa npm package with yarn add webpacker-pwa and edit environment.js with:

const { resolve } = require('path');
const { config, environment, Environment } = require('@rails/webpacker');
const WebpackerPwa = require('webpacker-pwa');
new WebpackerPwa(config, environment);
module.exports = environment;

This will allow you to use service workers. Please check the README of the project as well: https://github.com/coorasse/webpacker-pwa/tree/master/npm_package

coorasse
  • 5,278
  • 1
  • 34
  • 45
-1

Leave the service worker file in whatever directory imposed by your project structure and set the scope option to / and add the Service-Worker-Allowed HTTP header to the response of your service worker file.

In ASP.Net, I added the following to web.config file:

<location path="assets/serviceWorker.js">
    <system.webServer>
        <httpProtocol>
            <customHeaders>
                <add name="Service-Worker-Allowed" value="/" />
            </customHeaders>
        </httpProtocol>
    </system.webServer>
</location>

And register the worker by:

navigator.serviceWorker.register('/assets/serviceWorker.js', { scope: '/' })
        .then(function (registration)
        {
          console.log('Service worker registered successfully');
        }).catch(function (e)
        {
          console.error('Error during service worker registration:', e);
        });

Tested on Firefox and Chrome.

Refer to the examples in Service worker specification

Ashraf Sabry
  • 3,081
  • 3
  • 32
  • 29