Packages
This package provides cache manifest file with digest and file or inline assets helper for .eex templates.
Current section
Files
Jump to
Current section
Files
lib/plug/plug_offline.ex
defmodule Plug.PlugOffline do
import Plug.Conn
@spec init(map) :: map
def init(options) do
options
end
@spec call(map, map) :: map
def call(conn, options) do
if "/#{conn.path_info}" == options[:at] do
conn
|> put_resp_content_type("text/cache-manifest")
|> send_resp(200, cache_content(options))
|> halt
else
conn
end
end
@spec cache_content(map) :: String.t
def cache_content(options) do
["CACHE MANIFEST", cache_key(options[:cache], options[:base_path])]
|> cache(options)
|> network(options)
|> fallback(options)
|> Enum.join("\n")
end
# When inline option present do not generate cache manifest entry for the assets file, though
# the digest is still based on the content of all assets. Which make update possible when
# assets changes
defp cache(body, %{offline_asset: true, inline: true} = _opts) do
body
end
defp cache(body, opts) do
body ++ opts[:cache]
end
# https://bordeltabernacle.github.io/2016/01/04/notes-on-elixir-pattern-matching-maps.html
defp network(body, %{network: _network} = opts) do
body ++ ["NETWORK:" | opts[:network]]
end
defp network(body, _opts) do
body
end
defp fallback(body, %{fallback: _fallback} = opts) do
body ++ ["FALLBACK:" | opts[:fallback]]
end
defp fallback(body, _opts) do
body
end
@spec cache_key(nil, String.t) :: String.t
defp cache_key(nil, _path) do
''
end
@spec cache_key(list(String.t), String.t) :: String.t
defp cache_key(keys, base_path) do
keys
|> Enum.sort
|> Enum.map(&digest_file(&1, base_path))
|> Enum.join
|> magic_comment
end
@spec digest_file(String.t, nil) :: String.t
defp digest_file(file_name, nil) do
read_file(file_name)
end
@spec digest_file(String.t, String.t) :: String.t
defp digest_file(file_name, base_path) do
read_file(Path.join([base_path, file_name]))
end
@spec read_file(String.t) :: String.t
defp read_file(file) do
case File.read(file) do
{ :ok, body } ->
:crypto.hash(:sha256, body) |> Base.encode16
{ :error, _reason } -> ''
end
end
@spec magic_comment(String.t) :: String.t
defp magic_comment(text) do
"# #{:crypto.hash(:sha256, text) |> Base.encode16}"
end
end