rack-spa 0.10.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f987e16a684d7095a44f36c3aa3c829d16f13a6d6eff194b3275a2a546941afc
4
- data.tar.gz: dc4df61474e03ef8c60d80f72a3dace7c32b17105a3466dbaede1e95d9bfc62b
3
+ metadata.gz: df310728de6bc267ad3d202364d7500429e18cdc2d9e14f96de887b6bbeae47d
4
+ data.tar.gz: 8347024dc1cf3a84b1b880687680e14f1299915a0ec60c83af466e1c7f2fc86c
5
5
  SHA512:
6
- metadata.gz: 8a31ce6bd3909f435824c945dce2f99037fa9acac5aca3dbd916bcbe61999c6bf93d2b0e47833217d37015980d50990cd88039ca85d0bd15074e535d1a1ea358
7
- data.tar.gz: e62de5586131f91df9f0d5f52bcea43cc1cf525597448a8dd3f4904f342e82cddb3001094ddc13dea481022f89928f42fbb59ff6103cc34492bc4de45d6b3516
6
+ metadata.gz: 1393398e04629cd0f7af9d17c2b351340f223312c75dc6fb73286e1a91c21ab83dac8ed986a8f41a3f0513caa664fa511186c5b1cb17992969fa55b995b7a7f5
7
+ data.tar.gz: 78d5aeb76e1d48a8ab1d1ad0b136c38a0c3da61fe1865a2ff8e6eb47d0751b4610609a1983a0fb0e431ef997f7a09eff5fd91bfdc9c33f6aac4ebf109d21be9b
@@ -30,27 +30,42 @@ module Rack
30
30
  global_assign: GLOBAL_ASSIGN,
31
31
  backup_suffix: BACKUP_SUFFIX
32
32
  )
33
- @index_html_path = index_html_path
33
+ @index_html_path = index_html_path.to_s
34
34
  @global_assign = global_assign
35
- @index_html_backup = index_html_path + backup_suffix
35
+ @index_html_backup = @index_html_path + backup_suffix
36
36
  end
37
37
 
38
+ # Copy +index_html_path+ to index.html.original (see +BACKUP_SUFFIX+),
39
+ # and add the dynamic config to +index_html_path+.
40
+ # Use +as_string+ to avoid file writes.
38
41
  def emplace(keys_and_values)
39
- self.prepare
40
- json = JSON.generate(keys_and_values)
41
- script = "#{@global_assign}=#{json}"
42
+ self.ensure_unmodified_html_backup
42
43
  ::File.open(@index_html_backup) do |f|
43
- doc = Nokogiri::HTML5(f)
44
- doc.at("head").prepend_child("<script>#{script}</script>")
45
- ::File.write(@index_html_path, doc.serialize)
44
+ new_html = self.serialize(f, keys_and_values)
45
+ ::File.write(@index_html_path, new_html)
46
46
  end
47
47
  end
48
48
 
49
- protected def prepare
49
+ protected def ensure_unmodified_html_backup
50
50
  return if ::File.exist?(@index_html_backup)
51
51
  ::FileUtils.move(@index_html_path, @index_html_backup)
52
52
  end
53
53
 
54
+ # Return the new index.html with dynamic config as a string.
55
+ def as_string(keys_and_values)
56
+ ::File.open(@index_html_path) do |f|
57
+ return self.serialize(f, keys_and_values)
58
+ end
59
+ end
60
+
61
+ protected def serialize(f, keys_and_values)
62
+ json = JSON.generate(keys_and_values)
63
+ script = "#{@global_assign}=#{json}"
64
+ doc = Nokogiri::HTML5(f)
65
+ doc.at("head").prepend_child("<script>#{script}</script>")
66
+ return doc.serialize
67
+ end
68
+
54
69
  def self.pick_env(regex_or_prefix)
55
70
  return ENV.to_a.select { |(k, _v)| k.start_with?(regex_or_prefix) }.to_h if regex_or_prefix.is_a?(String)
56
71
  return ENV.to_a.select { |(k, _v)| regex_or_prefix.match?(k) }.to_h
data/lib/rack/spa_app.rb CHANGED
@@ -7,16 +7,31 @@ require "rack/spa_rewrite"
7
7
 
8
8
  module Rack
9
9
  class SpaApp
10
- def self.dependencies(build_folder, immutable: true, enforce_ssl: true, service_worker_allowed: nil)
10
+ # Return Rack middleware dependencies for the SPA app.
11
+ # @param build_folder [String,Pathname] Path to the build folder.
12
+ # @param immutable [true,false] True to use +Rack::Immutable+ middleware,
13
+ # so that files that look fingerprinted (<name>.<git sha>.ext) get immutable cache headers.
14
+ # @param enforce_ssl [true,false] True to use +Rack::SsslEnforcer+. Requires `rack-ssl-enforcer` gem.
15
+ # @param service_worker_allowed [String,nil] Scope of the server worker for the Service-Worker-Allowed header.
16
+ # @param index_bytes [String,nil] Bytes of the app's index.html file. If not passed,
17
+ # use the contents of `<build_folder>/index.html`.
18
+ def self.dependencies(
19
+ build_folder,
20
+ immutable: true,
21
+ enforce_ssl: true,
22
+ service_worker_allowed: nil,
23
+ index_bytes: nil
24
+ )
25
+ index_bytes ||= File.read("#{build_folder}/index.html")
11
26
  result = []
12
27
  result << [Rack::SslEnforcer, {redirect_html: false}] if enforce_ssl
13
28
  result << [Rack::ConditionalGet, {}]
14
29
  result << [Rack::ETag, {}]
15
30
  result << [Rack::Immutable, {match: immutable.is_a?(TrueClass) ? nil : immutable}] if immutable
16
- result << [Rack::SpaRewrite, {index_path: "#{build_folder}/index.html", html_only: true}]
31
+ result << [Rack::SpaRewrite, {index_bytes:, html_only: true}]
17
32
  result << [Rack::ServiceWorkerAllowed, {scope: service_worker_allowed}] if service_worker_allowed
18
33
  result << [Rack::Static, {urls: [""], root: build_folder.to_s, cascade: true}]
19
- result << [Rack::SpaRewrite, {index_path: "#{build_folder}/index.html", html_only: false}]
34
+ result << [Rack::SpaRewrite, {index_bytes:, html_only: false}]
20
35
  return result
21
36
  end
22
37
 
@@ -28,8 +43,8 @@ module Rack
28
43
  builder.run Rack::LambdaApp.new(->(_) { raise "Should not see SpaApp fallback" })
29
44
  end
30
45
 
31
- def self.run_spa_app(builder, build_folder, enforce_ssl: true, immutable: true, **kw)
32
- deps = self.dependencies(build_folder, enforce_ssl:, immutable:, **kw)
46
+ def self.run_spa_app(builder, build_folder, enforce_ssl: true, immutable: true, index_bytes: nil, **kw)
47
+ deps = self.dependencies(build_folder, enforce_ssl:, immutable:, index_bytes:, **kw)
33
48
  self.install(builder, deps)
34
49
  self.run(builder)
35
50
  end
@@ -3,21 +3,24 @@
3
3
  require "rack"
4
4
 
5
5
  module Rack
6
+ # Intercept HTTP requests and serve index.html.
7
+ # This middleware caches the index_html file (or the bytes can be passed in).
8
+ # If the file contents change, new content is NOT served.
6
9
  class SpaRewrite
7
10
  ALLOWED_VERBS = ["GET", "HEAD", "OPTIONS"].freeze
8
11
  ALLOW_HEADER = ALLOWED_VERBS.join(", ")
9
12
 
10
- def initialize(app, index_path:, html_only:)
13
+ # @param app The Rack app.
14
+ # @param index_bytes [String] The index.html contents to serve.
15
+ # @param html_only [true,false] True to only intercept html requests,
16
+ # false to intercept all requests. Usually you want to setup SpaRewrite
17
+ # first using html:false, then html:true as the final Rack app/middleware.
18
+ def initialize(app, index_bytes:, html_only:)
11
19
  @app = app
12
- @index_path = index_path
20
+ @index_bytes = index_bytes
13
21
  @html_only = html_only
14
- begin
15
- @index_mtime = ::File.mtime(@index_path).httpdate
16
- rescue Errno::ENOENT
17
- @index_mtime = Time.at(0)
18
- end
19
- @index_bytes = nil
20
22
  @head = Rack::Head.new(->(env) { get env })
23
+ @started_at = Time.now.httpdate
21
24
  end
22
25
 
23
26
  def call(env)
@@ -37,15 +40,12 @@ module Rack
37
40
  return [200, {"Allow" => ALLOW_HEADER, Rack::CONTENT_LENGTH => "0"}, []] if
38
41
  request.options?
39
42
 
40
- lastmod = ::File.mtime(@index_path)
41
- lastmodhttp = lastmod.httpdate
42
- return [304, {}, []] if request.get_header("HTTP_IF_MODIFIED_SINCE") == lastmodhttp
43
+ return [304, {}, []] if request.get_header("HTTP_IF_MODIFIED_SINCE") == @started_at
43
44
 
44
- @index_bytes = ::File.read(@index_path) if @index_bytes.nil? || @index_mtime < lastmodhttp
45
45
  headers = {
46
46
  "content-length" => @index_bytes.bytesize.to_s,
47
47
  "content-type" => "text/html",
48
- "last-modified" => lastmodhttp,
48
+ "last-modified" => @started_at,
49
49
  }
50
50
  return [200, headers, [@index_bytes]]
51
51
  end
data/lib/rack_spa.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RackSpa
4
- VERSION = "0.10.0"
4
+ VERSION = "0.11.0"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rack-spa
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.10.0
4
+ version: 0.11.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lithic Tech
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-03-03 00:00:00.000000000 Z
11
+ date: 2024-03-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: nokogiri