# `ImagePipe.URL.Helpers`
[🔗](https://github.com/hlindset/image_pipe/blob/v0.1.0/image_pipe_url/lib/image_pipe/url/helpers.ex#L1)

An `image_url/2` macro that builds image URLs in templates and checks their
options at compile time.

In an app that serves its own images with `image_pipe`, name the
`ImagePipe` instance:

    # lib/my_app_web.ex, in html_helpers
    use ImagePipe.URL.Helpers, instance: MyApp.Images

```heex
<img src={image_url("cat.jpg", group: [resize: [width: 400, height: 300, fit: :cover]])} />
<img src={image_url(@photo.path, group: [resize: [width: @width]], output: [format: :webp])} />
```

In an app that builds URLs for an image service running elsewhere, give a
function that returns the URL configuration. The helper calls this function
for every URL, so build the configuration once when the app starts and
return the stored copy:

    # lib/my_app/image_urls.ex
    defmodule MyApp.ImageURLs do
      def put_config do
        config =
          ImagePipe.URL.config(
            base_url: "https://images.example.com",
            keys: [System.fetch_env!("IMAGE_PIPE_SIGNING_KEY")]
          )

        :persistent_term.put(__MODULE__, config)
      end

      def config, do: :persistent_term.get(__MODULE__)
    end

    # lib/my_app/application.ex, in start/2, before the endpoint starts
    MyApp.ImageURLs.put_config()

    # lib/my_app_web.ex, in html_helpers
    use ImagePipe.URL.Helpers, config: {MyApp.ImageURLs, :config, []}

Calling `put_config/0` again replaces the stored configuration without a
restart. [Signing URLs and rotating keys](https://hexdocs.pm/image_pipe/signing-urls.html)
gives the order for rotating a key.

## Options

  * `:instance` - the `:name` of an `ImagePipe` instance. Each URL uses
    the instance's URL settings from `ImagePipe.url_config/2`: its base
    URL, signing keys, source encryption settings, and the presets, request
    defaults, and watermark settings it checks options against. Building a
    URL raises `ArgumentError` when the instance isn't running. Needs the
    `image_pipe` package.
  * `:mount` - with `:instance`, one of the instance's `:mounts`, whose URL
    options the URLs use instead of the instance's own.
  * `:config` - `{module, function, args}`, called for each URL, that
    returns an `ImagePipe.URL.Config`. Use it in place of `:instance`.
  * `:on_invalid` - `:warn` (the default) or `:raise`. See
    [Checks when the page renders](#image_url/2-checks-when-the-page-renders).

The configuration is read each time a URL is built, so signing keys stay
out of the compiled code. Without `:instance` or `:config`, URLs use
`ImagePipe.URL.config([])`: no base URL, signing, or encryption.

# `image_url`
*macro* 

Builds the URL for `source` with the options of `ImagePipe.URL`.

    image_url("cat.jpg",
      new: [filename: "cat"],
      group: [resize: [width: 400, height: 300, fit: :cover]],
      group: [blur: 2],
      output: [format: :webp]
    )
    # "/w=400/h=300/fit=cover/-/blur=2/format=webp/filename=cat/src/cat.jpg"

`new:` takes the request controls of `ImagePipe.URL.new/2`, each `group:`
the options of one `ImagePipe.URL.group/2` call, in order, and `output:`
the options of `ImagePipe.URL.output/2`. `new:` applies first wherever it
is in the call. The option names must be written out in the call, and a
list that isn't fails to compile. Use `ImagePipe.URL` for options built at
runtime. Values can be variables or assigns.

## Checks at compile time

Each option name and value written out in the call is checked when the
module compiles. A list value, such as `gradient:` or `jpeg_options:`, is
checked only when all of it is written out, except `resize:`, whose
options are checked one by one. A mistake is a compiler warning at the
call site:

```text
warning: image_url: invalid_value at {:group, 0, :fit}: invalid value for :fit option: expected one of [:contain, :cover, :stretch, :auto], got: :fill
```

With `mix compile --warnings-as-errors`, a mistake fails the build. Values
that are variables are checked when the page renders. How the options
combine depends on the server's presets and request defaults, so how the
options combine is checked at render, with `:instance` or a configuration
that has `:validate_against`.

## Checks when the page renders

The URL is built with `ImagePipe.URL.url_with_issues/3`, so an error in
the options or the source never raises. The URL includes it, the server
rejects the URL with `400`, and the browser shows a broken image. Each
error is logged at `:warning`, and each warning, such as a mistake the
builder repaired or an option that has no effect, at `:debug`. The log
names the call's file and line, the reason, and the option, not the source
or values:

```text
[warning] image_url (lib/my_app_web/controllers/page_html/home.html.heex:3): invalid_value at {:group, 0, :fit}
```

These messages come from the `ImagePipe.URL.Helpers` module, so Logger's
module levels control them. To turn them off in production, set
`Logger.put_module_level(ImagePipe.URL.Helpers, :none)` when the app
starts.

With `on_invalid: :raise`, an error raises `ArgumentError` instead. To
raise only in tests, read the setting from the app's configuration:

    # config/test.exs
    config :my_app, :image_url_on_invalid, :raise

    # lib/my_app_web.ex, in html_helpers
    use ImagePipe.URL.Helpers,
      instance: MyApp.Images,
      on_invalid: Application.compile_env(:my_app, :image_url_on_invalid, :warn)

---

*Consult [api-reference.md](api-reference.md) for complete listing*
