serviceworker-rails 0.3.1 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +87 -0
- data/.travis.yml +0 -1
- data/Gemfile +5 -2
- data/Guardfile +1 -1
- data/README.md +175 -21
- data/Rakefile +4 -3
- data/bin/{rspec → rubocop} +2 -2
- data/lib/generators/serviceworker/install_generator.rb +73 -0
- data/lib/generators/serviceworker/templates/manifest.json +66 -0
- data/lib/generators/serviceworker/templates/serviceworker-companion.js +6 -0
- data/lib/generators/serviceworker/templates/serviceworker.js +14 -0
- data/lib/generators/serviceworker/templates/serviceworker.rb +26 -0
- data/lib/service_worker.rb +1 -0
- data/lib/serviceworker-rails.rb +1 -0
- data/lib/serviceworker/handler.rb +21 -0
- data/lib/serviceworker/middleware.rb +18 -9
- data/lib/serviceworker/rails/handler.rb +1 -1
- data/lib/serviceworker/rails/version.rb +1 -1
- data/lib/serviceworker/route.rb +18 -25
- data/lib/serviceworker/router.rb +2 -4
- data/serviceworker-rails.gemspec +5 -4
- metadata +26 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3752a3b8e5aab69a95949bf282791c5871a1297d
|
4
|
+
data.tar.gz: 22de311f4ed1728871bb3032673f03e3bac57e4e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e14a4a092b558e85a68d3dbef7f61d2fdf185d3510d9def1480a45d8abd7a96202f00da5ce2debe3a05fe2bd408d7a8ec26a801f19427fb6a8bd25aaae4c6c70
|
7
|
+
data.tar.gz: b439353ce2aa25da2df120f902b59ed431b873cf077cb48e3d3a612c71f6ae474f12cf57b5e4900ad70f66d220c82297b4e71dac0885178eda6aeb7bbbc920a8
|
data/.rubocop.yml
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
AllCops:
|
2
|
+
Exclude:
|
3
|
+
- "test/tmp/**/*"
|
4
|
+
|
5
|
+
Metrics/LineLength:
|
6
|
+
Max: 120
|
7
|
+
|
8
|
+
Metrics/ClassLength:
|
9
|
+
Max: 300
|
10
|
+
|
11
|
+
Metrics/ModuleLength:
|
12
|
+
Max: 300
|
13
|
+
|
14
|
+
Metrics/MethodLength:
|
15
|
+
Max: 100
|
16
|
+
|
17
|
+
Metrics/ParameterLists:
|
18
|
+
Max: 8
|
19
|
+
|
20
|
+
Metrics/AbcSize:
|
21
|
+
Enabled: false
|
22
|
+
|
23
|
+
Metrics/CyclomaticComplexity:
|
24
|
+
Enabled: false
|
25
|
+
|
26
|
+
Metrics/PerceivedComplexity:
|
27
|
+
Enabled: false
|
28
|
+
|
29
|
+
Style/AlignParameters:
|
30
|
+
EnforcedStyle: with_fixed_indentation
|
31
|
+
|
32
|
+
Style/StringLiterals:
|
33
|
+
EnforcedStyle: double_quotes
|
34
|
+
|
35
|
+
Style/StringLiteralsInInterpolation:
|
36
|
+
EnforcedStyle: double_quotes
|
37
|
+
|
38
|
+
Style/ClosingParenthesisIndentation:
|
39
|
+
Enabled: false
|
40
|
+
|
41
|
+
Style/OneLineConditional:
|
42
|
+
Enabled: false
|
43
|
+
|
44
|
+
Style/AndOr:
|
45
|
+
Enabled: false
|
46
|
+
|
47
|
+
Style/Not:
|
48
|
+
Enabled: false
|
49
|
+
|
50
|
+
Documentation:
|
51
|
+
Enabled: false # TODO: Enable again once we have more docs
|
52
|
+
|
53
|
+
Style/CaseIndentation:
|
54
|
+
IndentWhenRelativeTo: case
|
55
|
+
SupportedStyles:
|
56
|
+
- case
|
57
|
+
- end
|
58
|
+
IndentOneStep: true
|
59
|
+
|
60
|
+
Style/PercentLiteralDelimiters:
|
61
|
+
PreferredDelimiters:
|
62
|
+
'%w': "[]"
|
63
|
+
'%W': "[]"
|
64
|
+
|
65
|
+
Style/AccessModifierIndentation:
|
66
|
+
Enabled: false
|
67
|
+
|
68
|
+
Style/SignalException:
|
69
|
+
Enabled: false
|
70
|
+
|
71
|
+
Style/IndentationWidth:
|
72
|
+
Enabled: false
|
73
|
+
|
74
|
+
Style/TrivialAccessors:
|
75
|
+
ExactNameMatch: true
|
76
|
+
|
77
|
+
Lint/DefEndAlignment:
|
78
|
+
Enabled: false
|
79
|
+
|
80
|
+
Lint/HandleExceptions:
|
81
|
+
Enabled: false
|
82
|
+
|
83
|
+
Style/SpecialGlobalVars:
|
84
|
+
Enabled: false
|
85
|
+
|
86
|
+
Style/TrivialAccessors:
|
87
|
+
Enabled: false
|
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
@@ -1,11 +1,14 @@
|
|
1
|
-
source
|
1
|
+
source "https://rubygems.org"
|
2
2
|
|
3
3
|
# Specify your gem's dependencies in serviceworker-rails.gemspec
|
4
4
|
gemspec
|
5
5
|
|
6
6
|
group :development, :test do
|
7
|
+
gem "rubocop", "0.39.0"
|
8
|
+
|
7
9
|
unless ENV["TRAVIS"]
|
8
|
-
gem "pry
|
10
|
+
gem "pry"
|
11
|
+
gem "pry-byebug", platforms: [:mri]
|
9
12
|
gem "guard"
|
10
13
|
gem "guard-minitest"
|
11
14
|
end
|
data/Guardfile
CHANGED
@@ -19,7 +19,7 @@ guard :minitest do
|
|
19
19
|
# with Minitest::Unit
|
20
20
|
watch(%r{^test/(.*)\/?(.*)_test\.rb$})
|
21
21
|
watch(%r{^lib/(.*/)?([^/]+)\.rb$}) { |m| "test/#{m[1]}#{m[2]}_test.rb" }
|
22
|
-
watch(%r{^test/test_helper\.rb$}) {
|
22
|
+
watch(%r{^test/test_helper\.rb$}) { "test" }
|
23
23
|
|
24
24
|
# with Minitest::Spec
|
25
25
|
# watch(%r{^spec/(.*)_spec\.rb$})
|
data/README.md
CHANGED
@@ -2,7 +2,29 @@
|
|
2
2
|
|
3
3
|
[![Build Status](https://travis-ci.org/rossta/serviceworker-rails.svg?branch=master)](https://travis-ci.org/rossta/serviceworker-rails)
|
4
4
|
|
5
|
-
Use [Service Worker](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API) with the Rails asset pipeline.
|
5
|
+
Turn your Rails app into a Progressive Web App. Use [Service Worker](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API) with the Rails asset pipeline.
|
6
|
+
|
7
|
+
## Why?
|
8
|
+
|
9
|
+
The Rails asset pipeline makes a number of assumptions about what's best for deploying JavaScript, including asset digest fingerprints and long-lived cache headers - mostly to increase "cacheability". Rails also assumes a single parent directory, `/public/assets`, to make it easier to look up the file path for a given asset.
|
10
|
+
|
11
|
+
Service worker assets must play by different rules. Consider these behaviors:
|
12
|
+
|
13
|
+
* Service workers may only be active from within the scope from which they are
|
14
|
+
served. So if you try to register a service worker from a Rails asset pipeline
|
15
|
+
path, like `/assets/serviceworker-abcd1234.js`, it will only be able to interact
|
16
|
+
with requests and responses within `/assets/`<em>**</em>. This is not what we want.
|
17
|
+
|
18
|
+
* [MDN states](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API#Download_install_and_activate) browsers check for updated service worker scripts in the background every 24 hours (possibly less). Rails developers wouldn't be able to take advantage of this feature since the fingerprint strategy means assets at a given url are immutable. Beside fingerprintings, the `Cache-Control` headers used for static files served from Rails also work against browser's treatment of service workers.
|
19
|
+
|
20
|
+
We want Sprockets to compile service worker JavaScript from ES6/7, CoffeeScript, ERB, etc. but must remove the caching and scoping mechanisms offered by Rails asset pipeline defaults. This is where `serviceworker-rails` comes in.
|
21
|
+
|
22
|
+
*Check out the [blog post](https://rossta.net/blog/service-worker-on-rails.html)
|
23
|
+
for more background.*
|
24
|
+
|
25
|
+
### Demo
|
26
|
+
|
27
|
+
See various examples of using Service Workers in the demo Rails app, [Service Worker Rails Sandbox](https://serviceworker-rails.herokuapp.com/). The [source code](https://github.com/rossta/serviceworker-rails-sandbox) is also on GitHub.
|
6
28
|
|
7
29
|
## Features
|
8
30
|
|
@@ -26,31 +48,169 @@ Or install it yourself as:
|
|
26
48
|
|
27
49
|
$ gem install serviceworker-rails
|
28
50
|
|
29
|
-
|
51
|
+
To set up your Rails project for use with a Service Worker, you either use the
|
52
|
+
Rails generator and edit the generated files as needed, or you can follow the
|
53
|
+
manual installation steps.
|
54
|
+
|
55
|
+
### Automated setup
|
56
|
+
|
57
|
+
After bundling the gem in your Rails project, run the generator from the root of
|
58
|
+
your Rails project.
|
59
|
+
|
60
|
+
```
|
61
|
+
$ rails g serviceworker:install
|
62
|
+
```
|
63
|
+
|
64
|
+
### Manual setup
|
65
|
+
|
66
|
+
Let's add a `ServiceWorker` to cache some of your JavaScript and CSS assets. We'll assume you already have a Rails application using the asset pipeline built on Sprockets.
|
67
|
+
|
68
|
+
#### Add a service worker script
|
69
|
+
|
70
|
+
Create a JavaScript file called `app/assets/javascripts/serviceworker.js.erb`:
|
71
|
+
|
72
|
+
```javascript
|
73
|
+
// app/assets/javascripts/serviceworker.js.erb
|
74
|
+
console.log('[Service Worker] Hello world!');
|
75
|
+
|
76
|
+
function onInstall(event) {
|
77
|
+
event.waitUntil(
|
78
|
+
caches.open('cached-assets').then(function prefill(cache) {
|
79
|
+
return cache.addAll([
|
80
|
+
'<%= asset_path "application.js" %>',
|
81
|
+
'<%= asset_path "application.css" %>',
|
82
|
+
'<%= asset_path "admin.css" %>',
|
83
|
+
// you get the idea ...
|
84
|
+
]);
|
85
|
+
})
|
86
|
+
);
|
87
|
+
}
|
88
|
+
|
89
|
+
self.addEventListener('install', onInstall)
|
90
|
+
```
|
91
|
+
|
92
|
+
For use in production, instruct Sprockets to precompile service worker scripts separately from `application.js`, as in the following example:
|
93
|
+
|
94
|
+
#### Precompile the asset
|
95
|
+
|
96
|
+
```ruby
|
97
|
+
# config/initializers/assets.rb
|
98
|
+
|
99
|
+
Rails.application.configure do
|
100
|
+
config.assets.precompile += %w[
|
101
|
+
serviceworker.js
|
102
|
+
]
|
103
|
+
end
|
104
|
+
```
|
105
|
+
|
106
|
+
#### Register the service worker
|
107
|
+
|
108
|
+
You'll need to register the service worker with a companion script in your main page JavaScript, like `application.js`. You can use the following:
|
109
|
+
|
110
|
+
```javascript
|
111
|
+
// app/assets/javascripts/serviceworker-companion.js
|
30
112
|
|
31
|
-
|
32
|
-
|
113
|
+
if (navigator.serviceWorker) {
|
114
|
+
navigator.serviceWorker.register('/serviceworker.js', { scope: './' })
|
115
|
+
.then(function(reg) {
|
116
|
+
console.log('[Page] Service worker registered!');
|
117
|
+
});
|
118
|
+
}
|
119
|
+
|
120
|
+
// app/assets/javascripts/application.js
|
121
|
+
|
122
|
+
// ...
|
123
|
+
//= require serviceworker-companion
|
124
|
+
```
|
125
|
+
|
126
|
+
#### Add a manifest
|
127
|
+
|
128
|
+
You may also want to create a `manifest.json` file to make your web app installable.
|
129
|
+
|
130
|
+
```
|
131
|
+
// manifest.json
|
132
|
+
{
|
133
|
+
"name": "My Rails App"
|
134
|
+
"name": "My Progressive Rails App",
|
135
|
+
"short_name": "Progressive",
|
136
|
+
"start_url": "/"
|
137
|
+
}
|
138
|
+
```
|
139
|
+
|
140
|
+
You'd then link to your manifest from the application layout:
|
141
|
+
|
142
|
+
```html
|
143
|
+
<link rel="manifest" href="/manifest.json" />
|
144
|
+
```
|
145
|
+
|
146
|
+
#### Configure the middleware
|
147
|
+
|
148
|
+
Next, add a new initializer as show below to instruct the `serviceworker-rails`
|
149
|
+
middleware how to route requests for assets by canonical url.
|
150
|
+
|
151
|
+
```ruby
|
152
|
+
# config/initializers/serviceworker.rb
|
153
|
+
|
154
|
+
Rails.application.configure do
|
155
|
+
config.serviceworker.routes.draw do
|
156
|
+
match "/serviceworker.js"
|
157
|
+
match "/manifest.json"
|
158
|
+
end
|
159
|
+
end
|
160
|
+
```
|
161
|
+
|
162
|
+
#### Test the setup
|
163
|
+
|
164
|
+
At this point, restart your Rails app and reload a page in your app in Chrome or Firefox. Using dev tools, you should be able to determine.
|
165
|
+
|
166
|
+
1. The page requests a service worker at `/serviceworker.js`
|
167
|
+
2. The Rails app responds to the request by compiling and rendering the file in `app/assets/javascripts/serviceworker.js.erb`.
|
168
|
+
3. The console displays messages from the page and the service worker
|
169
|
+
4. The application JavaScript and CSS assets are added to the browser's request/response [Cache](https://developer.mozilla.org/en-US/docs/Web/API/Cache).
|
170
|
+
|
171
|
+
#### Using the cache
|
172
|
+
|
173
|
+
So far so good? At this point, all we've done is pre-fetched assets and added them to the cache, but we're not doing anything with them yet.
|
174
|
+
|
175
|
+
Now, we can use the service worker to intercept requests and either serve them from the cache if they exist there or fallback to the network response otherwise. In most cases, we can expect responses coming from the local cache to be much faster than those coming from the network.
|
176
|
+
|
177
|
+
(...more coming soon, WIP)
|
178
|
+
|
179
|
+
## Configuration
|
180
|
+
|
181
|
+
When `serviceworker-rails` is required in your Gemfile, it will insert a middleware into the Rails
|
33
182
|
middleware stack. You'll want to configure it by mapping serviceworker routes to
|
34
|
-
Sprockets JavaScript assets, like the example below
|
183
|
+
Sprockets JavaScript assets in an initializer, like the example below.
|
35
184
|
|
36
185
|
```ruby
|
37
|
-
#
|
186
|
+
# config/initializers/serviceworker.rb
|
187
|
+
|
188
|
+
Rails.application.configure do
|
189
|
+
config.serviceworker.routes.draw do
|
190
|
+
# maps to asset named 'serviceworker.js' implicitly
|
191
|
+
match "/serviceworker.js"
|
38
192
|
|
39
|
-
|
40
|
-
|
193
|
+
# map to a named asset explicitly
|
194
|
+
match "/proxied-serviceworker.js" => "nested/asset/serviceworker.js"
|
195
|
+
match "/nested/serviceworker.js" => "another/serviceworker.js"
|
41
196
|
|
42
|
-
|
197
|
+
# capture named path segments and interpolate to asset name
|
198
|
+
match "/captures/*segments/serviceworker.js" => "%{segments}/serviceworker.js"
|
43
199
|
|
44
|
-
|
200
|
+
# capture named parameter and interpolate to asset name
|
201
|
+
match "/parameter/:id/serviceworker.js" => "project/%{id}/serviceworker.js"
|
45
202
|
|
46
|
-
|
47
|
-
|
203
|
+
# insert custom headers
|
204
|
+
match "/header-serviceworker.js" => "another/serviceworker.js",
|
205
|
+
headers: { "X-Resource-Header" => "A resource" }
|
48
206
|
|
49
|
-
|
207
|
+
# anonymous glob exposes `paths` variable for interpolation
|
208
|
+
match "/*/serviceworker.js" => "%{paths}/serviceworker.js"
|
209
|
+
end
|
50
210
|
end
|
51
211
|
```
|
52
212
|
|
53
|
-
`Serviceworker::Rails`
|
213
|
+
`Serviceworker::Rails` will insert a `Cache-Control` header to instruct browsers
|
54
214
|
not to cache your serviceworkers by default. You can customize the headers for all service worker routes if you'd like,
|
55
215
|
such as adding the experimental [`Service-Worker-Allowed`](https://slightlyoff.github.io/ServiceWorker/spec/service_worker/#service-worker-allowed) header to set the allowed scope.
|
56
216
|
|
@@ -59,15 +219,9 @@ config.serviceworker.headers["Service-Worker-Allowed"] = "/"
|
|
59
219
|
config.serviceworker.headers["X-Custom-Header"] = "foobar"
|
60
220
|
```
|
61
221
|
|
62
|
-
### Demo
|
63
|
-
|
64
|
-
Check out the demo application, [Service Worker on Rails](https://serviceworker-rails.herokuapp.com/), to see various examples of using Service Workers in a Rails app.
|
65
|
-
|
66
222
|
## Development
|
67
223
|
|
68
|
-
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake
|
69
|
-
|
70
|
-
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
224
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/rake` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
71
225
|
|
72
226
|
## Contributing
|
73
227
|
|
data/Rakefile
CHANGED
@@ -1,15 +1,16 @@
|
|
1
1
|
require "bundler/gem_tasks"
|
2
2
|
require "rake/testtask"
|
3
|
+
require "rubocop/rake_task"
|
3
4
|
|
4
5
|
APP_RAKEFILE = File.expand_path("../test/sample/Rakefile", __FILE__)
|
5
6
|
load "rails/tasks/engine.rake"
|
6
7
|
|
7
|
-
|
8
|
+
RuboCop::RakeTask.new
|
8
9
|
|
9
10
|
Rake::TestTask.new(:test) do |t|
|
10
11
|
t.libs << "test"
|
11
12
|
t.libs << "lib"
|
12
|
-
t.test_files = FileList[
|
13
|
+
t.test_files = FileList["test/**/*_test.rb"]
|
13
14
|
end
|
14
15
|
|
15
|
-
task :
|
16
|
+
task default: [:test, :rubocop]
|
data/bin/{rspec → rubocop}
RENAMED
@@ -2,7 +2,7 @@
|
|
2
2
|
#
|
3
3
|
# This file was generated by Bundler.
|
4
4
|
#
|
5
|
-
# The application '
|
5
|
+
# The application 'rubocop' is installed as part of a gem, and
|
6
6
|
# this file is here to facilitate running it.
|
7
7
|
#
|
8
8
|
|
@@ -13,4 +13,4 @@ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
|
13
13
|
require "rubygems"
|
14
14
|
require "bundler/setup"
|
15
15
|
|
16
|
-
load Gem.bin_path("
|
16
|
+
load Gem.bin_path("rubocop", "rubocop")
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require "rails/generators"
|
2
|
+
|
3
|
+
module Serviceworker
|
4
|
+
module Generators
|
5
|
+
class InstallGenerator < ::Rails::Generators::Base
|
6
|
+
desc "Make your Rails app a progressive web app"
|
7
|
+
source_root File.join(File.dirname(__FILE__), "templates")
|
8
|
+
|
9
|
+
def create_assets
|
10
|
+
template "serviceworker.js",
|
11
|
+
File.join(javascripts_base_dir, "serviceworker.js")
|
12
|
+
template "serviceworker-companion.js",
|
13
|
+
File.join(javascripts_base_dir, "serviceworker-companion.js")
|
14
|
+
template "manifest.json",
|
15
|
+
File.join(javascripts_base_dir, "manifest.json")
|
16
|
+
end
|
17
|
+
|
18
|
+
def create_initializer
|
19
|
+
template "serviceworker.rb",
|
20
|
+
File.join(initializers_dir, "serviceworker.rb")
|
21
|
+
end
|
22
|
+
|
23
|
+
def update_application_js
|
24
|
+
ext, directive = detect_js_format
|
25
|
+
append_to_file application_js_path(ext), "#{directive} require serviceworker-companion\n"
|
26
|
+
end
|
27
|
+
|
28
|
+
def update_precompiled_assets
|
29
|
+
append_to_file File.join(initializers_dir, "assets.rb"),
|
30
|
+
"Rails.configuration.assets.precompile += %w[serviceworker.js]\n"
|
31
|
+
end
|
32
|
+
|
33
|
+
def update_application_layout
|
34
|
+
insert_into_file detect_layout,
|
35
|
+
%(<link rel="manifest" href="/manifest.json" />),
|
36
|
+
before: "</head>\n"
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def application_js_path(ext)
|
42
|
+
File.join(javascripts_base_dir, "application#{ext}")
|
43
|
+
end
|
44
|
+
|
45
|
+
def detect_js_format
|
46
|
+
%w[.coffee .coffee.erb .js.coffee .js.coffee.erb .js .js.erb].each do |ext|
|
47
|
+
next unless File.exist?(File.join(javascripts_base_dir, "application#{ext}"))
|
48
|
+
return [ext, "#="] if ext.include?(".coffee")
|
49
|
+
return [ext, "//="]
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def detect_layout
|
54
|
+
layouts = %w[.html.erb .html.haml .html.slim].map do |ext|
|
55
|
+
File.join(layouts_base_dir, "application#{ext}")
|
56
|
+
end
|
57
|
+
layouts.find { |layout| File.exist?(layout) }
|
58
|
+
end
|
59
|
+
|
60
|
+
def javascripts_base_dir
|
61
|
+
File.join("app", "assets", "javascripts")
|
62
|
+
end
|
63
|
+
|
64
|
+
def initializers_dir
|
65
|
+
File.join("config", "initializers")
|
66
|
+
end
|
67
|
+
|
68
|
+
def layouts_base_dir
|
69
|
+
File.join("app", "views", "layouts")
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
{
|
2
|
+
"name": "My Progressive Rails App",
|
3
|
+
"short_name": "Progressive",
|
4
|
+
"start_url": "/",
|
5
|
+
"icons": [
|
6
|
+
{
|
7
|
+
"src": "/icons/icon36.png",
|
8
|
+
"sizes": "36x36",
|
9
|
+
"type": "image/png"
|
10
|
+
},
|
11
|
+
{
|
12
|
+
"src": "/icons/icon48.png",
|
13
|
+
"sizes": "48x48",
|
14
|
+
"type": "image/png"
|
15
|
+
},
|
16
|
+
{
|
17
|
+
"src": "/icons/icon60.png",
|
18
|
+
"sizes": "60x60",
|
19
|
+
"type": "image/png"
|
20
|
+
},
|
21
|
+
{
|
22
|
+
"src": "/icons/icon72.png",
|
23
|
+
"sizes": "72x72",
|
24
|
+
"type": "image/png"
|
25
|
+
},
|
26
|
+
{
|
27
|
+
"src": "/icons/icon76.png",
|
28
|
+
"sizes": "76x76",
|
29
|
+
"type": "image/png"
|
30
|
+
},
|
31
|
+
{
|
32
|
+
"src": "/icons/icon96.png",
|
33
|
+
"sizes": "96x96",
|
34
|
+
"type": "image/png"
|
35
|
+
},
|
36
|
+
{
|
37
|
+
"src": "/icons/icon120.png",
|
38
|
+
"sizes": "120x120",
|
39
|
+
"type": "image/png"
|
40
|
+
},
|
41
|
+
{
|
42
|
+
"src": "/icons/icon152.png",
|
43
|
+
"sizes": "152x152",
|
44
|
+
"type": "image/png"
|
45
|
+
},
|
46
|
+
{
|
47
|
+
"src": "/icons/icon180.png",
|
48
|
+
"sizes": "180x180",
|
49
|
+
"type": "image/png"
|
50
|
+
},
|
51
|
+
{
|
52
|
+
"src": "/icons/icon192.png",
|
53
|
+
"sizes": "192x192",
|
54
|
+
"type": "image/png"
|
55
|
+
},
|
56
|
+
{
|
57
|
+
"src": "/icons/icon512.png",
|
58
|
+
"sizes": "512x512",
|
59
|
+
"type": "image/png"
|
60
|
+
}
|
61
|
+
],
|
62
|
+
"theme_color": "#000000",
|
63
|
+
"background_color": "#FFFFFF",
|
64
|
+
"display": "fullscreen",
|
65
|
+
"orientation": "portrait"
|
66
|
+
}
|
@@ -0,0 +1,14 @@
|
|
1
|
+
function onInstall() {
|
2
|
+
console.log('[Serviceworker]', "Installing!");
|
3
|
+
}
|
4
|
+
|
5
|
+
function onActivate() {
|
6
|
+
console.log('[Serviceworker]', "Activating!");
|
7
|
+
}
|
8
|
+
|
9
|
+
function onFetch() {
|
10
|
+
}
|
11
|
+
|
12
|
+
self.addEventListener('install', onInstall);
|
13
|
+
self.addEventListener('activate', onActivate);
|
14
|
+
self.addEventListener('fetch', onFetch);
|
@@ -0,0 +1,26 @@
|
|
1
|
+
Rails.application.configure do
|
2
|
+
config.serviceworker.routes.draw do
|
3
|
+
# map to assets implicitly
|
4
|
+
match "/serviceworker.js"
|
5
|
+
match "/manifest.json"
|
6
|
+
|
7
|
+
# Examples
|
8
|
+
#
|
9
|
+
# map to a named asset explicitly
|
10
|
+
# match "/proxied-serviceworker.js" => "nested/asset/serviceworker.js"
|
11
|
+
# match "/nested/serviceworker.js" => "another/serviceworker.js"
|
12
|
+
#
|
13
|
+
# capture named path segments and interpolate to asset name
|
14
|
+
# match "/captures/*segments/serviceworker.js" => "%{segments}/serviceworker.js"
|
15
|
+
#
|
16
|
+
# capture named parameter and interpolate to asset name
|
17
|
+
# match "/parameter/:id/serviceworker.js" => "project/%{id}/serviceworker.js"
|
18
|
+
#
|
19
|
+
# insert custom headers
|
20
|
+
# match "/header-serviceworker.js" => "another/serviceworker.js",
|
21
|
+
# headers: { "X-Resource-Header" => "A resource" }
|
22
|
+
#
|
23
|
+
# anonymous glob exposes `paths` variable for interpolation
|
24
|
+
# match "/*/serviceworker.js" => "%{paths}/serviceworker.js"
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require "serviceworker"
|
data/lib/serviceworker-rails.rb
CHANGED
@@ -0,0 +1,21 @@
|
|
1
|
+
module ServiceWorker
|
2
|
+
class Handler
|
3
|
+
def initialize(root = Dir.getwd)
|
4
|
+
@root = root
|
5
|
+
end
|
6
|
+
|
7
|
+
def call(env)
|
8
|
+
path_info = env.fetch("serviceworker.asset_name")
|
9
|
+
|
10
|
+
file_server.call(env.merge("PATH_INFO" => path_info))
|
11
|
+
end
|
12
|
+
|
13
|
+
def file_path(path_info)
|
14
|
+
@root.join(path_info)
|
15
|
+
end
|
16
|
+
|
17
|
+
def file_server
|
18
|
+
@file_server ||= ::Rack::File.new(@root)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -5,16 +5,26 @@ module ServiceWorker
|
|
5
5
|
GET = "GET".freeze
|
6
6
|
HEAD = "HEAD".freeze
|
7
7
|
|
8
|
+
# Initialize the Rack middleware for responding to serviceworker asset
|
9
|
+
# requests
|
10
|
+
#
|
11
|
+
# @app [#call] middleware stack
|
12
|
+
# @opts [Hash] options to inject
|
13
|
+
# @param opts [#match_route] :routes matches routes on PATH_INFO
|
14
|
+
# @param opts [Hash] :headers default headers to use for matched routes
|
15
|
+
# @param opts [#call] :handler resolves response from matched asset name
|
16
|
+
# @param opts [#info] :logger logs requests
|
8
17
|
def initialize(app, opts = {})
|
9
18
|
@app = app
|
10
19
|
@opts = opts
|
11
20
|
@headers = opts.fetch(:headers, {}).merge(default_headers)
|
12
21
|
@router = opts.fetch(:routes, ServiceWorker::Router.new)
|
22
|
+
@handler = @opts.fetch(:handler, default_handler)
|
13
23
|
end
|
14
24
|
|
15
25
|
def call(env)
|
16
26
|
case env[REQUEST_METHOD]
|
17
|
-
|
27
|
+
when GET, HEAD
|
18
28
|
route_match = @router.match_route(env)
|
19
29
|
return respond_to_match(route_match, env) if route_match
|
20
30
|
end
|
@@ -22,7 +32,7 @@ module ServiceWorker
|
|
22
32
|
@app.call(env)
|
23
33
|
end
|
24
34
|
|
25
|
-
|
35
|
+
private
|
26
36
|
|
27
37
|
def default_headers
|
28
38
|
{
|
@@ -33,7 +43,7 @@ module ServiceWorker
|
|
33
43
|
def respond_to_match(route_match, env)
|
34
44
|
env = env.merge("serviceworker.asset_name" => route_match.asset_name)
|
35
45
|
|
36
|
-
status, headers, body = handler.call(env)
|
46
|
+
status, headers, body = @handler.call(env)
|
37
47
|
|
38
48
|
[status, headers.merge(@headers).merge(route_match.headers), body]
|
39
49
|
end
|
@@ -42,14 +52,13 @@ module ServiceWorker
|
|
42
52
|
logger.info "[#{self.class}] - #{msg}"
|
43
53
|
end
|
44
54
|
|
45
|
-
# TODO
|
46
|
-
# provide non-rails handler as default
|
47
|
-
def handler
|
48
|
-
@handler ||= @opts.fetch(:handler)
|
49
|
-
end
|
50
|
-
|
51
55
|
def logger
|
52
56
|
@logger ||= @opts.fetch(:logger, Logger.new(STDOUT))
|
53
57
|
end
|
58
|
+
|
59
|
+
def default_handler
|
60
|
+
require "serviceworker/handler"
|
61
|
+
ServiceWorker::Handler.new
|
62
|
+
end
|
54
63
|
end
|
55
64
|
end
|
data/lib/serviceworker/route.rb
CHANGED
@@ -20,9 +20,7 @@ module ServiceWorker
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def match(path)
|
23
|
-
if path.to_s.strip.empty?
|
24
|
-
raise ArgumentError.new("path is required")
|
25
|
-
end
|
23
|
+
raise ArgumentError, "path is required" if path.to_s.strip.empty?
|
26
24
|
|
27
25
|
asset = resolver.call(path) or return nil
|
28
26
|
|
@@ -40,11 +38,11 @@ module ServiceWorker
|
|
40
38
|
end
|
41
39
|
|
42
40
|
class AssetResolver
|
43
|
-
PATH_INFO =
|
41
|
+
PATH_INFO = "PATH_INFO".freeze
|
44
42
|
DEFAULT_WILDCARD_NAME = :paths
|
45
|
-
WILDCARD_PATTERN =
|
46
|
-
NAMED_SEGMENTS_PATTERN =
|
47
|
-
LEADING_SLASH_PATTERN =
|
43
|
+
WILDCARD_PATTERN = %r{\/\*([^\/]*)}
|
44
|
+
NAMED_SEGMENTS_PATTERN = %r{\/([^\/]*):([^:$\/]+)}
|
45
|
+
LEADING_SLASH_PATTERN = %r{^\/}
|
48
46
|
INTERPOLATION_PATTERN = Regexp.union(
|
49
47
|
/%%/,
|
50
48
|
/%\{(\w+)\}/, # matches placeholders like "%{foo}"
|
@@ -58,9 +56,7 @@ module ServiceWorker
|
|
58
56
|
end
|
59
57
|
|
60
58
|
def call(path)
|
61
|
-
if path.to_s.strip.empty?
|
62
|
-
raise ArgumentError.new("path is required")
|
63
|
-
end
|
59
|
+
raise ArgumentError, "path is required" if path.to_s.strip.empty?
|
64
60
|
|
65
61
|
captures = path_captures(regexp, path) or return nil
|
66
62
|
|
@@ -78,15 +74,16 @@ module ServiceWorker
|
|
78
74
|
end
|
79
75
|
|
80
76
|
def compiled_source(pattern)
|
81
|
-
|
77
|
+
pattern_match = pattern.match(WILDCARD_PATTERN)
|
78
|
+
if pattern_match
|
82
79
|
@wildcard_name = if pattern_match[1].to_s.strip.empty?
|
83
80
|
DEFAULT_WILDCARD_NAME
|
84
81
|
else
|
85
82
|
pattern_match[1].to_sym
|
86
83
|
end
|
87
|
-
pattern.gsub(WILDCARD_PATTERN,
|
84
|
+
pattern.gsub(WILDCARD_PATTERN, "(?:/(.*)|)")
|
88
85
|
else
|
89
|
-
p = if
|
86
|
+
p = if pattern.match(NAMED_SEGMENTS_PATTERN)
|
90
87
|
pattern.gsub(NAMED_SEGMENTS_PATTERN, '/\1(?<\2>[^.$/]+)')
|
91
88
|
else
|
92
89
|
pattern
|
@@ -96,29 +93,25 @@ module ServiceWorker
|
|
96
93
|
end
|
97
94
|
|
98
95
|
def path_captures(regexp, path)
|
99
|
-
|
96
|
+
path_match = path.match(regexp) or return nil
|
100
97
|
params = if @wildcard_name
|
101
|
-
{ @wildcard_name => path_match[1].to_s.split(
|
98
|
+
{ @wildcard_name => path_match[1].to_s.split("/") }
|
102
99
|
else
|
103
100
|
Hash[path_match.names.map(&:to_sym).zip(path_match.captures)]
|
104
101
|
end
|
105
|
-
params.delete(:format) if params.
|
102
|
+
params.delete(:format) if params.key?(:format) && params[:format].nil?
|
106
103
|
params
|
107
104
|
end
|
108
105
|
|
109
106
|
def interpolate_captures(string, captures)
|
110
107
|
string.gsub(INTERPOLATION_PATTERN) do |match|
|
111
|
-
if match ==
|
112
|
-
|
108
|
+
if match == "%%"
|
109
|
+
"%"
|
113
110
|
else
|
114
|
-
key = (
|
115
|
-
value =
|
116
|
-
Array(captures[key]).join("/")
|
117
|
-
else
|
118
|
-
raise "Interpolation error: #{key} not captured in #{captures.inspect}"
|
119
|
-
end
|
111
|
+
key = (Regexp.last_match(1) || Regexp.last_match(2)).to_sym
|
112
|
+
value = captures.key?(key) ? Array(captures[key]).join("/") : key
|
120
113
|
value = value.call(captures) if value.respond_to?(:call)
|
121
|
-
|
114
|
+
Regexp.last_match(3) ? format("%#{Regexp.last_match(3)}", value) : value
|
122
115
|
end
|
123
116
|
end.gsub(LEADING_SLASH_PATTERN, "")
|
124
117
|
end
|
data/lib/serviceworker/router.rb
CHANGED
@@ -19,7 +19,7 @@ module ServiceWorker
|
|
19
19
|
return self unless block_given?
|
20
20
|
|
21
21
|
if block.arity == 1
|
22
|
-
|
22
|
+
yield(self)
|
23
23
|
else
|
24
24
|
instance_eval(&block)
|
25
25
|
end
|
@@ -51,9 +51,7 @@ module ServiceWorker
|
|
51
51
|
def match_route(env)
|
52
52
|
path = env[PATH_INFO]
|
53
53
|
@routes.each do |route|
|
54
|
-
|
55
|
-
return match
|
56
|
-
end
|
54
|
+
match = route.match(path) and return match
|
57
55
|
end
|
58
56
|
nil
|
59
57
|
end
|
data/serviceworker-rails.gemspec
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# coding: utf-8
|
2
|
-
lib = File.expand_path(
|
2
|
+
lib = File.expand_path("../lib", __FILE__)
|
3
3
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
-
require
|
4
|
+
require "serviceworker/rails/version"
|
5
5
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
7
|
spec.name = "serviceworker-rails"
|
@@ -9,8 +9,8 @@ Gem::Specification.new do |spec|
|
|
9
9
|
spec.authors = ["Ross Kaffenberger"]
|
10
10
|
spec.email = ["rosskaff@gmail.com"]
|
11
11
|
|
12
|
-
spec.summary =
|
13
|
-
spec.description =
|
12
|
+
spec.summary = "ServiceWorker for Rails 3+"
|
13
|
+
spec.description = "Integrates ServiceWorker into the Rails asset pipeline."
|
14
14
|
spec.homepage = "https://github.com/rossta/serviceworker-rails"
|
15
15
|
spec.license = "MIT"
|
16
16
|
|
@@ -26,4 +26,5 @@ Gem::Specification.new do |spec|
|
|
26
26
|
spec.add_development_dependency "rake", "~> 10.0"
|
27
27
|
spec.add_development_dependency "minitest", "~> 5.0"
|
28
28
|
spec.add_development_dependency "rack-test"
|
29
|
+
spec.add_development_dependency "rails"
|
29
30
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: serviceworker-rails
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ross Kaffenberger
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-11-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: railties
|
@@ -94,6 +94,20 @@ dependencies:
|
|
94
94
|
- - ">="
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rails
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
97
111
|
description: Integrates ServiceWorker into the Rails asset pipeline.
|
98
112
|
email:
|
99
113
|
- rosskaff@gmail.com
|
@@ -102,6 +116,7 @@ extensions: []
|
|
102
116
|
extra_rdoc_files: []
|
103
117
|
files:
|
104
118
|
- ".gitignore"
|
119
|
+
- ".rubocop.yml"
|
105
120
|
- ".travis.yml"
|
106
121
|
- CHANGELOG.md
|
107
122
|
- CODE_OF_CONDUCT.md
|
@@ -114,10 +129,17 @@ files:
|
|
114
129
|
- bin/console
|
115
130
|
- bin/guard
|
116
131
|
- bin/rake
|
117
|
-
- bin/
|
132
|
+
- bin/rubocop
|
118
133
|
- bin/setup
|
134
|
+
- lib/generators/serviceworker/install_generator.rb
|
135
|
+
- lib/generators/serviceworker/templates/manifest.json
|
136
|
+
- lib/generators/serviceworker/templates/serviceworker-companion.js
|
137
|
+
- lib/generators/serviceworker/templates/serviceworker.js
|
138
|
+
- lib/generators/serviceworker/templates/serviceworker.rb
|
139
|
+
- lib/service_worker.rb
|
119
140
|
- lib/serviceworker-rails.rb
|
120
141
|
- lib/serviceworker.rb
|
142
|
+
- lib/serviceworker/handler.rb
|
121
143
|
- lib/serviceworker/middleware.rb
|
122
144
|
- lib/serviceworker/rails.rb
|
123
145
|
- lib/serviceworker/rails/handler.rb
|
@@ -146,7 +168,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
146
168
|
version: '0'
|
147
169
|
requirements: []
|
148
170
|
rubyforge_project:
|
149
|
-
rubygems_version: 2.5.1
|
171
|
+
rubygems_version: 2.4.5.1
|
150
172
|
signing_key:
|
151
173
|
specification_version: 4
|
152
174
|
summary: ServiceWorker for Rails 3+
|