Skip to content

Modernize for 1.0.0 — first release in 11 years#1

Merged
polymetis merged 14 commits into
masterfrom
claude/tender-leakey-15a622
May 4, 2026
Merged

Modernize for 1.0.0 — first release in 11 years#1
polymetis merged 14 commits into
masterfrom
claude/tender-leakey-15a622

Conversation

@polymetis
Copy link
Copy Markdown
Owner

Summary

Bring ex_clacks up to current Elixir/Plug/HexDocs conventions after about a decade of dormancy, and cut 1.0.0. The plug itself does the same thing it always did; this is a polish + correctness pass.

Headline changes:

  • Bump pins: elixir: "~> 1.1""~> 1.14", {:plug, "~> 1.0"}{:plug, "~> 1.14"}.
  • Make ExClacks a real Plug: declare @behaviour Plug, drop the accidental _opts \\ [] default from call/2. This is the one breaking change — direct callers must update to ExClacks.call(conn, ExClacks.init([])). Pipeline users are unaffected.
  • mix.exs modernization: MixfileMixProject, paren fixes, package files whitelist, ex_doc + :docs config so the HexDocs landing page renders.
  • Tests: expanded from one smoke test to four (header preservation, init/1 identity, method coverage), grouped by describe, import Plug.Test (not use).
  • README: typos, fenced code blocks, drop mix new boilerplate.
  • LICENSE: copyright holder filled in.
  • CHANGELOG.md: added (Keep a Changelog format) with migration note.
  • mix.lock: now tracked for CI reproducibility.

Test plan

  • mix deps.get and confirm clean fetch
  • mix test — should report 4 tests, 0 failures
  • mix docs — should build cleanly with no warnings
  • Eyeball the breaking-change note in CHANGELOG.md for migration clarity
  • Confirm 1.0.0 vs 0.2.0 framing is what you want before tagging

Not done in this PR (separate decisions)

A follow-up reviewer pass surfaced these as worth considering before tagging — none are blockers, all can land in a follow-up if you want this PR merged first:

  • .formatter.exs (absent — mix format has no project config)
  • .github/workflows/ci.yml (CI matrix across Elixir/OTP)
  • @spec and @type annotations on init/1/call/2
  • Hex/HexDocs/CI badges in README
  • .tool-versions for asdf/mise users
  • Drop build_embedded/start_permanent (legacy 2015 boilerplate, no-op for a library)

🤖 Generated with Claude Code

polymetis and others added 14 commits May 3, 2026 18:40
After ~11 years on `elixir: "~> 1.1"` and `{:plug, "~> 1.0"}`, bump pins
to current stable (1.14) and clean up the project file:

- Rename `Mixfile` -> `MixProject` (post-1.0 convention)
- `Mix.env ==` and `deps:`/`package:` get their parens (warns on 1.15+)
- Drop `applications: [:logger]` — this lib doesn't log
- Add `:files` whitelist so package contents are explicit
- Add `ex_doc` dev dep and `:docs` config so HexDocs renders
- Pull `@version` and `@source_url` into module attrs for reuse

No behavior change to the plug itself yet; that lands next.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Pin transitive dep versions so CI builds are reproducible. Standard
practice for Hex libraries since ~2019.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Declare `@behaviour Plug` and drop the default arg from `call/2` so the
module formally conforms to the Plug contract. The default `_opts \\ []`
papered over a signature mismatch — Plug's `call/2` is required to be
arity 2 with no default, and the pipeline always passes `init/1`'s
return value.

Also:

- Move the prose docstring from `@doc` (which was attached to `init/1`)
  up to `@moduledoc`, where it actually belongs. This is what HexDocs
  renders on the package landing page.
- Fix the example block to be valid Markdown (proper heading + 4-space
  indent so ExDoc renders it as code).
- Update the test to call through `init/1` then `call/2`, matching how
  Plug invokes the pipeline.

This is the breaking change that justifies the 1.0 cut: anyone who was
somehow calling `ExClacks.call/1` directly (vanishingly unlikely) gets
a clean compile error instead of silent behavior.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Beyond the original "header gets set" smoke test, lock down:

- `init/1` is the identity (it's half the public surface)
- the plug doesn't clobber other response headers already on the conn
- the header is set regardless of HTTP method, not just GET/HEAD

Group by function with `describe` blocks so failures point at the
relevant callback.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Typos: "explantion" -> "explanation", "Prachett's" -> "Pratchett's"
- `####Example:` -> `## Example` (h4 was wrong level under h1, also
  needed a space after the hashes)
- Fenced code blocks throughout instead of mixing fenced + indented
- Drop the `mix new` boilerplate ("If available in Hex...") — the
  package has been on Hex for a decade
- Bump install snippet from `~> 0.1.0` to `~> 1.0`
- Add a license section pointing at LICENSE.md

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The MIT text was already in place, but the copyright line was just
"Copyright (c) 2015" with no name. Add the holder and extend the year
range to the present.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Eleven years after the last release, ship a proper 1.0:

- Add CHANGELOG.md (Keep a Changelog format) covering the upgrade
- Bump @Version "0.1.1" -> "1.0.0"
- Wire CHANGELOG.md and LICENSE.md into the docs extras and
  package files list
- Gitignore /doc (ex_doc output) so it stops following us around

The breaking change justifying the major version bump is `call/2`
losing its accidental default arg — see CHANGELOG for migration.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
`mix docs` (added in the 1.0.0 cut) generates HTML/EPUB output into
/doc. Don't let it follow us around in `git status`.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
asdf/mise users get a consistent toolchain. Versions match the
maintainer's other Elixir project (best_place); python/nodejs lines
trimmed since this is an Elixir-only library.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
`build_embedded` and `start_permanent` are 2015 mix-new boilerplate.
Both are no-ops for a library (they only matter for an actual OTP
release), so drop them.

Bumping the floor to `~> 1.15` lets us assume modern compiler features
in any future work and aligns with what the CI matrix will realistically
test. 1.14 is end-of-life as of 2026.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Project-level formatter config so `mix format` works out of the box and
contributors don't produce non-conforming diffs. Contents are the
standard Elixir library default. CI will enforce via
`mix format --check-formatted` once that lands.

(Existing source already happened to be conformant — no reformatting
needed.)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Both callbacks are part of the public surface — HexDocs renders specs
inline with the function signature, which is part of how readers learn
the API.

Use `Plug.opts()` (the canonical type from the Plug behaviour) rather
than redefining a local `@type opts`. The plug doesn't read from opts
itself, but the contract follows the behaviour.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Two jobs, on every PR and push to main:

- `test` matrix runs `mix compile --warnings-as-errors` and `mix test`
  on the floor (Elixir 1.15 / OTP 26) and the ceiling (1.19 / OTP 28),
  so the version constraint in mix.exs actually means something.
- `quality` runs once on the maintainer's pinned toolchain (from
  .tool-versions), enforcing `mix format --check-formatted` and a
  clean `mix docs` build.

Caches deps and _build keyed on the resolved Elixir/OTP versions so
re-runs don't recompile from scratch. Style follows the maintainer's
other Elixir project (best_place).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Standard signal-of-life row at the top of the readme. The CI badge
points at the workflow that just landed; the license badge derives its
text from the Hex.pm metadata, so as long as `licenses: ["MIT"]` stays
in mix.exs they stay in sync.

The license link assumes `main` as the default branch — if the rename
hasn't happened by publish time, GitHub auto-redirects.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@polymetis polymetis merged commit 0908be6 into master May 4, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant