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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e15c642851a30d7cf973bc826cdd953de5248df2
4
- data.tar.gz: ad30587132acd6fbf1052370d5dafa12f5d24edb
3
+ metadata.gz: 6c1a7fe0cb0777d62d399186270fd11a4965ed77
4
+ data.tar.gz: 24f12fd53135a6dc7a5ebbfaa8b73a39af9b5e1d
5
5
  SHA512:
6
- metadata.gz: c695e3493724c49d99b7f8b72174057abd43ca4b73e6782706c80843efeca64c54512a535490f31a171b2f4e19175233b9a81f413fe5158d92b0e0ddaa3ef911
7
- data.tar.gz: 2e4a8f6c092de36eb47fcaab1d0fb30bd21fa1244e8aa9b14dacf4757bcc87e5f0d7a9778c14d9af722b2002e1732188c5b5108a6f245fb2b208beb0d37c0048
6
+ metadata.gz: a38f837ac9dab05e34edbee772b98ca12959bed7851ad0ca7f8537eabc768cc5f5cac5a053c00605a3d72ace70c7520bd5bc0b1cda7a5dcd765f16193c5e65d6
7
+ data.tar.gz: 5db0e5eaf38682d447786a4950cc1e65b893052e5fe52b76016f4a5dc464ea82e059896df07f2144e79dbd13c023e2c13ccf4ce63bd1bb0254869e9ccfa2539a
data/.travis.yml CHANGED
@@ -1,4 +1,6 @@
1
1
  language: ruby
2
2
  rvm:
3
+ - 2.1.8
4
+ - 2.2.5
3
5
  - 2.3.0
4
6
  before_install: gem install bundler -v 1.11.2
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
- gem "pry-byebug", platforms: [:ruby_23]
7
- gem "guard"
8
- gem "guard-minitest"
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
- Integrates ServiceWorker scripts with the Rails asset pipeline.
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
- * Leverages Rails asset pipeline to compile service worker scripts
8
- * Adds appropriate response headers to service worker scripts
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
- get "/basic-serviceworker.js"
40
+ match "/basic-serviceworker.js"
39
41
 
40
- get "/proxied-serviceworker.js"
41
- asset: "nested/asset/serviceworker.js"
42
+ match "/proxied-serviceworker.js" => "nested/asset/serviceworker.js"
42
43
 
43
- get "/nested/serviceworker.js",
44
- asset: "another/serviceworker.js"
44
+ match "/nested/serviceworker.js" => "another/serviceworker.js"
45
45
 
46
- get "/header-serviceworker.js",
47
- asset: "another/serviceworker.js",
46
+ match "/header-serviceworker.js" => "another/serviceworker.js",
48
47
  headers: { "X-Resource-Header" => "A resource" }
49
48
 
50
- get "/*/serviceworker.js",
51
- asset: "serviceworker.js"
49
+ match "/*/serviceworker.js" => "serviceworker.js"
52
50
  end
53
51
  ```
54
52
 
55
- `Serviceworker-Rails` with insert a `Cache-Control` header to instruct browsers
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["REQUEST_METHOD"]
12
- when "GET", "HEAD"
13
- path = env["PATH_INFO"].chomp("/")
14
- info("responding to #{path}")
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 respond_to_route(route, env)
31
- status, headers, body = process_handler(route, env)
33
+ def respond_to_match(route_match, env)
34
+ env = env.merge("serviceworker.asset_name" => route_match.asset_name)
32
35
 
33
- [status, headers.merge(@headers).merge(route.headers), body]
34
- end
36
+ status, headers, body = handler.call(env)
35
37
 
36
- def process_handler(route, env)
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.root.join("public"))
23
+ @file_server ||= ::Rack::File.new(::Rails.public_path)
22
24
  end
23
25
 
24
26
  def config
@@ -1,5 +1,5 @@
1
1
  module ServiceWorker
2
2
  module Rails
3
- VERSION = "0.2.0"
3
+ VERSION = "0.3.0"
4
4
  end
5
5
  end
@@ -1,22 +1,32 @@
1
1
  module ServiceWorker
2
2
  class Route
3
- attr_reader :path, :options
3
+ attr_reader :path_pattern, :asset_pattern, :options
4
4
 
5
- def initialize(path, options = {})
6
- @path = path
7
- @options = options
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 match?(path)
13
- @pattern =~ path
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 asset_name
17
- @options.fetch(:asset, @path.gsub(%r{^/}, ""))
18
- rescue NoMethodError
19
- raise RouteError, "Cannot determine asset name from #{path.inspect}. Please specify the :asset option for this path."
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 compile(path)
29
- if path.respond_to?(:to_str)
30
- special_chars = %w[. + ( )]
31
- pattern = path.to_str.gsub(/([\*#{special_chars.join}])/) do |match|
32
- case match
33
- when "*"
34
- "(.*?)"
35
- when *special_chars
36
- Regexp.escape(match)
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
@@ -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 get(path, options = {})
32
- Route.new(path, options).tap do |route|
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(path)
42
- @routes.detect { |r| r.match?(path) }
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.2.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-04-25 00:00:00.000000000 Z
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.5.1
148
+ rubygems_version: 2.2.3
149
149
  signing_key:
150
150
  specification_version: 4
151
151
  summary: ServiceWorker for Rails 3+