pftg
11/18/2018 - 6:44 PM

Install serviceworker-rails for Rails 5 + Webpacker

Install serviceworker-rails for Rails 5 + Webpacker

diff --git a/Gemfile b/Gemfile
index 4983412..454bd32 100644
--- a/Gemfile
+++ b/Gemfile
@@ -11,6 +11,10 @@ gem 'pg', '>= 0.18', '< 2.0'
 gem 'puma', '~> 3.11'
 # Transpile app-like JavaScript. Read more: https://github.com/rails/webpacker
 gem 'webpacker', '>= 4.0.x'
+
+# Turn your Rails app into a Progressive Web App. https://github.com/rossta/serviceworker-rails
+gem 'serviceworker-rails', github: 'rossta/serviceworker-rails'
+
 # Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
 gem 'jbuilder', '~> 2.5'
 # Use ActiveModel has_secure_password
diff --git a/Gemfile.lock b/Gemfile.lock
index 9414509..6be13c9 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -1,3 +1,10 @@
+GIT
+  remote: https://github.com/rossta/serviceworker-rails.git
+  revision: 757db5354c9e47a144397c4655f3d1cab6046bc0
+  specs:
+    serviceworker-rails (0.5.5)
+      railties (>= 3.1)
+
 GEM
   remote: https://rubygems.org/
   specs:
@@ -185,6 +192,7 @@ DEPENDENCIES
   puma (~> 3.11)
   rails (~> 5.2.1)
   selenium-webdriver
+  serviceworker-rails!
   spring
   spring-watcher-listen (~> 2.0.0)
   tzinfo-data
diff --git a/app/javascript/packs/application.js b/app/javascript/packs/application.js
index 54b106e..76062e6 100644
--- a/app/javascript/packs/application.js
+++ b/app/javascript/packs/application.js
@@ -7,4 +7,4 @@
 // To reference this file, add <%= javascript_pack_tag 'application' %> to the appropriate
 // layout file, like app/views/layouts/application.html.erb
 
-console.log('Hello World from Webpacker')
+import 'serviceworker-companion';
diff --git a/app/javascript/packs/serviceworker.js b/app/javascript/packs/serviceworker.js
new file mode 100644
index 0000000..abf2e9f
--- /dev/null
+++ b/app/javascript/packs/serviceworker.js
@@ -0,0 +1,57 @@
+console.log('[Service Worker] Hello world!');
+
+var CACHE_NAME = 'v1-cached-assets';
+
+function onInstall(event) {
+  event.waitUntil(
+    caches.open(CACHE_NAME).then(function prefill(cache) {
+      return cache.addAll([
+        '/offline.html'
+      ]);
+    })
+  );
+}
+
+function onActivate(event) {
+  console.log('[Serviceworker]', "Activating!", event);
+  event.waitUntil(
+    caches.keys().then(function (cacheNames) {
+      return Promise.all(
+        cacheNames.filter(function (cacheName) {
+          // Return true if you want to remove this cache,
+          // but remember that caches are shared across
+          // the whole origin
+          return cacheName.indexOf('v1') !== 0;
+        }).map(function (cacheName) {
+          return caches.delete(cacheName);
+        })
+      );
+    })
+  );
+}
+
+self.addEventListener('install', onInstall)
+self.addEventListener('activate', onActivate)
+
+
+function onFetch(event) {
+  // Fetch from network, fallback to cached content, then offline.html for same-origin GET requests
+  var request = event.request;
+
+  if (!request.url.match(/localhost/)) {
+    return;
+  }
+  if (request.method !== 'GET') {
+    return;
+  }
+
+  event.respondWith(
+    fetch(request).catch(function fallback() {
+      return caches.match('/offline.html')
+    })
+  );
+
+  // See https://jakearchibald.com/2014/offline-cookbook/#on-network-response for more examples
+}
+
+self.addEventListener('fetch', onFetch);
diff --git a/app/javascript/serviceworker-companion.js b/app/javascript/serviceworker-companion.js
new file mode 100644
index 0000000..62f8b9e
--- /dev/null
+++ b/app/javascript/serviceworker-companion.js
@@ -0,0 +1,6 @@
+if ('serviceWorker' in navigator) {
+  // Use the window load event to keep the page load performant
+  window.addEventListener('load', () => {
+    navigator.serviceWorker.register('/serviceworker.js');
+  });
+}
diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb
index 6e7310e..1ce8ada 100644
--- a/app/views/layouts/application.html.erb
+++ b/app/views/layouts/application.html.erb
@@ -6,6 +6,7 @@
     <%= csp_meta_tag %>
 
     <%= stylesheet_pack_tag    'hello_vue', media: 'all' %>
+    <%= javascript_pack_tag 'application' %>
     <%= javascript_pack_tag 'hello_vue' %>
   </head>
 
diff --git a/config/application.rb b/config/application.rb
index 418d35e..9b8dbf4 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -10,7 +10,7 @@ require "action_controller/railtie"
 # require "action_mailer/railtie"
 require "action_view/railtie"
 # require "action_cable/engine"
-# require "sprockets/railtie"
+require "sprockets/railtie"
 require "rails/test_unit/railtie"
 
 # Require the gems listed in Gemfile, including any gems
diff --git a/config/initializers/serviceworker.rb b/config/initializers/serviceworker.rb
new file mode 100644
index 0000000..a1b18e7
--- /dev/null
+++ b/config/initializers/serviceworker.rb
@@ -0,0 +1,25 @@
+Rails.application.configure do
+  config.serviceworker.routes.draw do
+    # map to assets implicitly
+    match '/serviceworker.js', pack: 'serviceworker.js'
+
+    # Examples
+    #
+    # map to a named asset explicitly
+    # match "/proxied-serviceworker.js" => "nested/asset/serviceworker.js"
+    # match "/nested/serviceworker.js" => "another/serviceworker.js"
+    #
+    # capture named path segments and interpolate to asset name
+    # match "/captures/*segments/serviceworker.js" => "%{segments}/serviceworker.js"
+    #
+    # capture named parameter and interpolate to asset name
+    # match "/parameter/:id/serviceworker.js" => "project/%{id}/serviceworker.js"
+    #
+    # insert custom headers
+    # match "/header-serviceworker.js" => "another/serviceworker.js",
+    #   headers: { "X-Resource-Header" => "A resource" }
+    #
+    # anonymous glob exposes `paths` variable for interpolation
+    # match "/*/serviceworker.js" => "%{paths}/serviceworker.js"
+  end
+end
diff --git a/public/offline.html b/public/offline.html
new file mode 100644
index 0000000..4734435
--- /dev/null
+++ b/public/offline.html
@@ -0,0 +1 @@
+<h1>Offline!</h1>