Initial commit
A toolkit for handling conditional HTTP requests in Plug-based services.
Conditional HTTP requests are closely related to HTTP caching and are useful in situations where one wants to:
Prevent sending a response payload if the client has a cached representation that's still the same as on the server.
Prevent an update if the client has an older version than the server.
There are two modes of operation - automatic and manual.
The automatic mode can be activated by simply placing a plug in the pipeline.
plug Frugality.ConditionalGET
The plug generates ETag and last-modified headers if needed and evaluates the request preconditions against the headers.
As you can see the automatic mode is pretty easy to use, easier than the manual one, but the latter is more flexible.
In manual mode, one is expected to define at least one additional module - a metadata generator.
Let's say you have an endpoint serving individual orders and you want to support conditional requests.
defmodule OrderMetadata do
@behaviour Frugality.Generator
@impl true
def etag(%{order: order}) do
# For the purpose of the example, `order` is an Ecto schema struct.
{:source, ["order", to_string(order.id), DateTime.to_iso8601(order.updated_at)]}
end
@impl true
def last_modified(%{order: order}),
do: order.updated_at
end
After we've defined a metadata generator, we can evaluate the request preconditions in the controller.
import Frugality,
only: [put_generator: 2, evaluate_preconditions: 2]
plug :put_generator, OrderMetadata
def show(conn, %{"id" => order_id}) do
order = Orders.get_order!(order_id)
assigns = [order: order]
with {:ok, conn} <- evaluate_preconditions(conn, assigns) do
render(conn, :show, assigns)
end
end
So far we've seen only Frugality.evaluate_preconditions/2
being used
but there's another function which is applicable in many cases -
Frugality.short_circuit/3
. Let's rewrite the previous example using
it.
import Frugality,
only: [put_generator: 2, short_circuit: 2]
plug :put_generator, OrderMetadata
def show(conn, %{"id" => order_id}) do
order = Orders.get_order!(order_id)
assigns = [order: order]
short_circuit(conn, assigns, fn conn ->
render(conn, :show, assigns)
end)
end
If available in Hex, the package can be installed
by adding frugality
to your list of dependencies in mix.exs
:
def deps do
[
{:frugality, "~> 0.1.0"}
]
end
Documentation can be generated with ExDoc and published on HexDocs. Once published, the docs can be found at https://hexdocs.pm/frugality.