serviceworker-rails 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +2 -0
- data/Gemfile +7 -3
- data/README.md +11 -13
- data/lib/serviceworker/middleware.rb +13 -12
- data/lib/serviceworker/rails/handler.rb +3 -1
- data/lib/serviceworker/rails/version.rb +1 -1
- data/lib/serviceworker/route.rb +106 -28
- data/lib/serviceworker/router.rb +20 -5
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6c1a7fe0cb0777d62d399186270fd11a4965ed77
|
4
|
+
data.tar.gz: 24f12fd53135a6dc7a5ebbfaa8b73a39af9b5e1d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a38f837ac9dab05e34edbee772b98ca12959bed7851ad0ca7f8537eabc768cc5f5cac5a053c00605a3d72ace70c7520bd5bc0b1cda7a5dcd765f16193c5e65d6
|
7
|
+
data.tar.gz: 5db0e5eaf38682d447786a4950cc1e65b893052e5fe52b76016f4a5dc464ea82e059896df07f2144e79dbd13c023e2c13ccf4ce63bd1bb0254869e9ccfa2539a
|
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
@@ -3,6 +3,10 @@ source 'https://rubygems.org'
|
|
3
3
|
# Specify your gem's dependencies in serviceworker-rails.gemspec
|
4
4
|
gemspec
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
gem "
|
6
|
+
group :development, :test do
|
7
|
+
unless ENV["TRAVIS"]
|
8
|
+
gem "pry-byebug", platforms: [:ruby_23]
|
9
|
+
gem "guard"
|
10
|
+
gem "guard-minitest"
|
11
|
+
end
|
12
|
+
end
|
data/README.md
CHANGED
@@ -1,11 +1,13 @@
|
|
1
1
|
# ServiceWorker::Rails
|
2
2
|
|
3
|
-
|
3
|
+
[![Build Status](https://travis-ci.org/rossta/serviceworker-rails.svg?branch=master)](https://travis-ci.org/rossta/serviceworker-rails)
|
4
|
+
|
5
|
+
Use [Service Worker](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API) with the Rails asset pipeline.
|
4
6
|
|
5
7
|
## Features
|
6
8
|
|
7
|
-
*
|
8
|
-
* Adds appropriate response headers to service
|
9
|
+
* Maps service worker endpoints to Rails assets
|
10
|
+
* Adds appropriate response headers to service workers
|
9
11
|
* Renders compiled source in production and development
|
10
12
|
|
11
13
|
## Installation
|
@@ -35,24 +37,20 @@ Sprockets JavaScript assets, like the example below, in `application.rb`.
|
|
35
37
|
# application.rb
|
36
38
|
|
37
39
|
config.serviceworker.routes.draw do
|
38
|
-
|
40
|
+
match "/basic-serviceworker.js"
|
39
41
|
|
40
|
-
|
41
|
-
asset: "nested/asset/serviceworker.js"
|
42
|
+
match "/proxied-serviceworker.js" => "nested/asset/serviceworker.js"
|
42
43
|
|
43
|
-
|
44
|
-
asset: "another/serviceworker.js"
|
44
|
+
match "/nested/serviceworker.js" => "another/serviceworker.js"
|
45
45
|
|
46
|
-
|
47
|
-
asset: "another/serviceworker.js",
|
46
|
+
match "/header-serviceworker.js" => "another/serviceworker.js",
|
48
47
|
headers: { "X-Resource-Header" => "A resource" }
|
49
48
|
|
50
|
-
|
51
|
-
asset: "serviceworker.js"
|
49
|
+
match "/*/serviceworker.js" => "serviceworker.js"
|
52
50
|
end
|
53
51
|
```
|
54
52
|
|
55
|
-
`Serviceworker
|
53
|
+
`Serviceworker::Rails` with insert a `Cache-Control` header to instruct browsers
|
56
54
|
not to cache your serviceworkers by default. You can customize the headers for all service worker routes if you'd like,
|
57
55
|
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.
|
58
56
|
|
@@ -1,5 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module ServiceWorker
|
2
3
|
class Middleware
|
4
|
+
REQUEST_METHOD = "REQUEST_METHOD".freeze
|
5
|
+
GET = "GET".freeze
|
6
|
+
HEAD = "HEAD".freeze
|
7
|
+
|
3
8
|
def initialize(app, opts = {})
|
4
9
|
@app = app
|
5
10
|
@opts = opts
|
@@ -8,12 +13,10 @@ module ServiceWorker
|
|
8
13
|
end
|
9
14
|
|
10
15
|
def call(env)
|
11
|
-
case env[
|
12
|
-
when
|
13
|
-
|
14
|
-
|
15
|
-
route = @router.match_route(path)
|
16
|
-
return respond_to_route(route, env) if route
|
16
|
+
case env[REQUEST_METHOD]
|
17
|
+
when GET, HEAD
|
18
|
+
route_match = @router.match_route(env)
|
19
|
+
return respond_to_match(route_match, env) if route_match
|
17
20
|
end
|
18
21
|
|
19
22
|
@app.call(env)
|
@@ -27,14 +30,12 @@ module ServiceWorker
|
|
27
30
|
}
|
28
31
|
end
|
29
32
|
|
30
|
-
def
|
31
|
-
|
33
|
+
def respond_to_match(route_match, env)
|
34
|
+
env = env.merge("serviceworker.asset_name" => route_match.asset_name)
|
32
35
|
|
33
|
-
|
34
|
-
end
|
36
|
+
status, headers, body = handler.call(env)
|
35
37
|
|
36
|
-
|
37
|
-
handler.call(env.merge("serviceworker.asset_name" => route.asset_name))
|
38
|
+
[status, headers.merge(@headers).merge(route_match.headers), body]
|
38
39
|
end
|
39
40
|
|
40
41
|
def info(msg)
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require "rack/file"
|
2
|
+
|
1
3
|
module ServiceWorker
|
2
4
|
module Rails
|
3
5
|
class Handler
|
@@ -18,7 +20,7 @@ module ServiceWorker
|
|
18
20
|
end
|
19
21
|
|
20
22
|
def file_server
|
21
|
-
@file_server ||= ::Rack::File.new(::Rails.
|
23
|
+
@file_server ||= ::Rack::File.new(::Rails.public_path)
|
22
24
|
end
|
23
25
|
|
24
26
|
def config
|
data/lib/serviceworker/route.rb
CHANGED
@@ -1,22 +1,32 @@
|
|
1
1
|
module ServiceWorker
|
2
2
|
class Route
|
3
|
-
attr_reader :
|
3
|
+
attr_reader :path_pattern, :asset_pattern, :options
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
@pattern = compile(path)
|
5
|
+
RouteMatch = Struct.new(:path, :asset_name, :headers) do
|
6
|
+
def to_s
|
7
|
+
asset_name
|
8
|
+
end
|
10
9
|
end
|
11
10
|
|
12
|
-
def
|
13
|
-
|
11
|
+
def initialize(path_pattern, asset_pattern = nil, options = {})
|
12
|
+
if asset_pattern.is_a?(Hash)
|
13
|
+
options = asset_pattern
|
14
|
+
asset_pattern = nil
|
15
|
+
end
|
16
|
+
|
17
|
+
@path_pattern = path_pattern
|
18
|
+
@asset_pattern = asset_pattern || options[:asset] || path_pattern
|
19
|
+
@options = options
|
14
20
|
end
|
15
21
|
|
16
|
-
def
|
17
|
-
|
18
|
-
|
19
|
-
|
22
|
+
def match(path)
|
23
|
+
if path.to_s.strip.empty?
|
24
|
+
raise ArgumentError.new("path is required")
|
25
|
+
end
|
26
|
+
|
27
|
+
asset = resolver.call(path) or return nil
|
28
|
+
|
29
|
+
RouteMatch.new(path, asset, headers)
|
20
30
|
end
|
21
31
|
|
22
32
|
def headers
|
@@ -25,24 +35,92 @@ module ServiceWorker
|
|
25
35
|
|
26
36
|
private
|
27
37
|
|
28
|
-
def
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
38
|
+
def resolver
|
39
|
+
@resolver ||= AssetResolver.new(path_pattern, asset_pattern)
|
40
|
+
end
|
41
|
+
|
42
|
+
class AssetResolver
|
43
|
+
PATH_INFO = 'PATH_INFO'.freeze
|
44
|
+
DEFAULT_WILDCARD_NAME = :paths
|
45
|
+
WILDCARD_PATTERN = /\/\*([^\/]*)/.freeze
|
46
|
+
NAMED_SEGMENTS_PATTERN = /\/([^\/]*):([^:$\/]+)/.freeze
|
47
|
+
LEADING_SLASH_PATTERN = /^\//
|
48
|
+
INTERPOLATION_PATTERN = Regexp.union(
|
49
|
+
/%%/,
|
50
|
+
/%\{(\w+)\}/, # matches placeholders like "%{foo}"
|
51
|
+
)
|
52
|
+
|
53
|
+
attr_reader :path_pattern, :asset_pattern
|
54
|
+
|
55
|
+
def initialize(path_pattern, asset_pattern)
|
56
|
+
@path_pattern = path_pattern
|
57
|
+
@asset_pattern = asset_pattern
|
58
|
+
end
|
59
|
+
|
60
|
+
def call(path)
|
61
|
+
if path.to_s.strip.empty?
|
62
|
+
raise ArgumentError.new("path is required")
|
63
|
+
end
|
64
|
+
|
65
|
+
captures = path_captures(regexp, path) or return nil
|
66
|
+
|
67
|
+
interpolate_captures(asset_pattern, captures)
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def regexp
|
73
|
+
@regexp ||= compile_regexp(path_pattern)
|
74
|
+
end
|
75
|
+
|
76
|
+
def compile_regexp(pattern)
|
77
|
+
Regexp.new("\\A#{compiled_source(pattern)}\\Z")
|
78
|
+
end
|
79
|
+
|
80
|
+
def compiled_source(pattern)
|
81
|
+
if pattern_match = pattern.match(WILDCARD_PATTERN)
|
82
|
+
@wildcard_name = if pattern_match[1].to_s.strip.empty?
|
83
|
+
DEFAULT_WILDCARD_NAME
|
84
|
+
else
|
85
|
+
pattern_match[1].to_sym
|
86
|
+
end
|
87
|
+
pattern.gsub(WILDCARD_PATTERN,'(?:/(.*)|)')
|
88
|
+
else
|
89
|
+
p = if pattern_match = pattern.match(NAMED_SEGMENTS_PATTERN)
|
90
|
+
pattern.gsub(NAMED_SEGMENTS_PATTERN, '/\1(?<\2>[^.$/]+)')
|
91
|
+
else
|
92
|
+
pattern
|
93
|
+
end
|
94
|
+
p + '(?:\.(?<format>.*))?'
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def path_captures(regexp, path)
|
99
|
+
return nil unless path_match = path.match(regexp)
|
100
|
+
params = if @wildcard_name
|
101
|
+
{ @wildcard_name => path_match[1].to_s.split('/') }
|
102
|
+
else
|
103
|
+
Hash[path_match.names.map(&:to_sym).zip(path_match.captures)]
|
104
|
+
end
|
105
|
+
params.delete(:format) if params.has_key?(:format) && params[:format].nil?
|
106
|
+
params
|
107
|
+
end
|
108
|
+
|
109
|
+
def interpolate_captures(string, captures)
|
110
|
+
string.gsub(INTERPOLATION_PATTERN) do |match|
|
111
|
+
if match == '%%'
|
112
|
+
'%'
|
37
113
|
else
|
38
|
-
|
114
|
+
key = ($1 || $2).to_sym
|
115
|
+
value = if captures.key?(key)
|
116
|
+
Array(captures[key]).join("/")
|
117
|
+
else
|
118
|
+
raise "Interpolation error: #{key} not captured in #{captures.inspect}"
|
119
|
+
end
|
120
|
+
value = value.call(captures) if value.respond_to?(:call)
|
121
|
+
$3 ? sprintf("%#{$3}", value) : value
|
39
122
|
end
|
40
|
-
end
|
41
|
-
/^#{pattern}$/
|
42
|
-
elsif path.respond_to?(:match)
|
43
|
-
path
|
44
|
-
else
|
45
|
-
raise TypeError, path
|
123
|
+
end.gsub(LEADING_SLASH_PATTERN, "")
|
46
124
|
end
|
47
125
|
end
|
48
126
|
end
|
data/lib/serviceworker/router.rb
CHANGED
@@ -1,5 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module ServiceWorker
|
2
3
|
class Router
|
4
|
+
PATH_INFO = "PATH_INFO".freeze
|
5
|
+
|
3
6
|
def self.default
|
4
7
|
new.draw_default
|
5
8
|
end
|
@@ -28,19 +31,31 @@ module ServiceWorker
|
|
28
31
|
draw { get "/serviceworker.js" }
|
29
32
|
end
|
30
33
|
|
31
|
-
def
|
32
|
-
|
34
|
+
def match(path, *args)
|
35
|
+
if path.is_a?(Hash)
|
36
|
+
opts = path.to_a
|
37
|
+
path, asset = opts.shift
|
38
|
+
args = [asset, opts.to_h]
|
39
|
+
end
|
40
|
+
|
41
|
+
Route.new(path, *args).tap do |route|
|
33
42
|
@routes << route
|
34
43
|
end
|
35
44
|
end
|
45
|
+
alias get match
|
36
46
|
|
37
47
|
def any?
|
38
48
|
@routes.any?
|
39
49
|
end
|
40
50
|
|
41
|
-
def match_route(
|
42
|
-
|
51
|
+
def match_route(env)
|
52
|
+
path = env[PATH_INFO]
|
53
|
+
@routes.each do |route|
|
54
|
+
if match = route.match(path)
|
55
|
+
return match
|
56
|
+
end
|
57
|
+
end
|
58
|
+
nil
|
43
59
|
end
|
44
60
|
end
|
45
|
-
|
46
61
|
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.3.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-05-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: railties
|
@@ -145,7 +145,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
145
145
|
version: '0'
|
146
146
|
requirements: []
|
147
147
|
rubyforge_project:
|
148
|
-
rubygems_version: 2.
|
148
|
+
rubygems_version: 2.2.3
|
149
149
|
signing_key:
|
150
150
|
specification_version: 4
|
151
151
|
summary: ServiceWorker for Rails 3+
|