diff --git a/.credo.exs b/.credo.exs index 31e5089..089d353 100644 --- a/.credo.exs +++ b/.credo.exs @@ -20,7 +20,7 @@ # # You can give explicit globs or simply directories. # In the latter case `**/*.{ex,exs}` will be used. - # + # included: ["lib/", "src/", "web/", "apps/"], excluded: [~r"/_build/", ~r"/deps/"] }, @@ -72,10 +72,8 @@ # set the `excluded_macros` parameter to `[:schema, :setup, :test]`. # {Credo.Check.Design.DuplicatedCode, excluded_macros: []}, - {Credo.Check.Design.TagTODO, exit_status: 2}, {Credo.Check.Design.TagFIXME}, - {Credo.Check.Readability.FunctionNames}, {Credo.Check.Readability.LargeNumbers}, {Credo.Check.Readability.MaxLineLength, false}, @@ -93,7 +91,6 @@ {Credo.Check.Readability.VariableNames}, {Credo.Check.Readability.Semicolons}, {Credo.Check.Readability.SpaceAfterCommas}, - {Credo.Check.Refactor.DoubleBooleanNegation}, {Credo.Check.Refactor.CondStatements}, {Credo.Check.Refactor.CyclomaticComplexity}, @@ -105,11 +102,9 @@ {Credo.Check.Refactor.Nesting}, {Credo.Check.Refactor.PipeChainStart}, {Credo.Check.Refactor.UnlessWithElse}, - {Credo.Check.Warning.BoolOperationOnSameValues}, {Credo.Check.Warning.IExPry}, {Credo.Check.Warning.IoInspect}, - {Credo.Check.Warning.LazyLogging}, {Credo.Check.Warning.OperationOnSameValues}, {Credo.Check.Warning.OperationWithConstantResult}, {Credo.Check.Warning.UnusedEnumOperation}, @@ -132,11 +127,7 @@ # Deprecated checks (these will be deleted after a grace period) # - {Credo.Check.Readability.Specs, false}, - {Credo.Check.Warning.NameRedeclarationByAssignment, false}, - {Credo.Check.Warning.NameRedeclarationByCase, false}, - {Credo.Check.Warning.NameRedeclarationByDef, false}, - {Credo.Check.Warning.NameRedeclarationByFn, false}, + {Credo.Check.Readability.Specs, false} # Custom checks can be created using `mix credo.gen.check`. # diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..5593c97 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,34 @@ +name: CI +on: + push: + branches: + - master + pull_request: + +permissions: + contents: read + +jobs: + elixir: + runs-on: ubuntu-latest + strategy: + matrix: + elixir: ["1.17", "1.18"] + otp: ["26", "27"] + env: + MIX_ENV: test + steps: + - uses: actions/checkout@v4 + + - uses: erlef/setup-beam@v1 + with: + elixir-version: ${{ matrix.elixir }} + otp-version: ${{ matrix.otp }} + + - run: mix deps.get + - run: mix compile --warnings-as-errors + - run: mix format --check-formatted + - run: mix credo --strict + - run: mix dialyzer --format github + - run: mix test --warnings-as-errors + - run: mix docs --warnings-as-errors diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..3857990 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,35 @@ +on: + push: + branches: + - master + +permissions: + contents: write + pull-requests: write + +name: Release + +jobs: + release-please: + runs-on: ubuntu-latest + env: + MIX_ENV: prod + steps: + - uses: googleapis/release-please-action@v4 + id: release + with: + release-type: elixir + + - uses: actions/checkout@v4 + if: ${{ steps.release.outputs.release_created }} + + - uses: erlef/setup-beam@v1 + if: ${{ steps.release.outputs.release_created }} + with: + elixir-version: "1.18" + otp-version: "27" + + - run: mix hex.publish --yes + if: ${{ steps.release.outputs.release_created }} + env: + HEX_API_KEY: ${{ secrets.HEX_API_KEY }} diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 38e3020..0000000 --- a/.travis.yml +++ /dev/null @@ -1,15 +0,0 @@ -language: elixir -elixir: - - 1.6.5 -addons: - apt: - update: true - -script: - - mix compile --warnings-as-errors - - mix credo --strict - - mix coveralls.travis - -after_script: - - mix deps.get --only docs - - MIX_ENV=docs mix inch.report diff --git a/README.md b/README.md index e35e8b1..9bd0f86 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,5 @@ # Optimal -[![Build Status](https://travis-ci.com/albert-io/optimal.svg?branch=master)](https://travis-ci.com/albert-io/optimal) [![Ebert](https://ebertapp.io/github/albert-io/optimal.svg)](https://ebertapp.io/github/albert-io/optimal) [![Coverage Status](https://coveralls.io/repos/github/albert-io/optimal/badge.svg?branch=master)](https://coveralls.io/github/albert-io/optimal?branch=master) [![Inline docs](http://inch-ci.org/github/albert-io/optimal.svg)](http://inch-ci.org/github/albert-io/optimal) - Optimal is a schema based `opt` validator. It is verbose, but I've tried many other data validation libraries, and their succinctness came with a cost when it came to features. There are still a lot of optimizations and improvements that can be made, so contributions are very welcome. This `opt` validator has a bit of a niche. It fits in just fine with validating any keyword list, but its especially useful for validating compile-time options, like ones provided to functions in a DSL. diff --git a/config/config.exs b/config/config.exs index 30b0848..8695a3f 100644 --- a/config/config.exs +++ b/config/config.exs @@ -1,6 +1,6 @@ # This file is responsible for configuring your application # and its dependencies with the aid of the Mix.Config module. -use Mix.Config +import Config # This configuration is loaded before any dependency and is restricted # to this project. If another project depends on this project, this diff --git a/lib/optimal.ex b/lib/optimal.ex index 23f6770..dcb3f07 100644 --- a/lib/optimal.ex +++ b/lib/optimal.ex @@ -261,8 +261,7 @@ defmodule Optimal do defp message(errors) do short_messages = errors - |> Enum.map(&short_message/1) - |> Enum.join(", ") + |> Enum.map_join(", ", &short_message/1) "Opt Validation Error: " <> "#{short_messages}" end @@ -271,14 +270,13 @@ defmodule Optimal do path = path |> Enum.with_index() - |> Enum.map(fn {elem, i} -> + |> Enum.map_join(fn {elem, i} -> if i == 0 do elem else "[:#{elem}]" end end) - |> Enum.join() "#{path} - #{message}" end diff --git a/lib/optimal/doc.ex b/lib/optimal/doc.ex index ef9afc7..fbfd5d1 100644 --- a/lib/optimal/doc.ex +++ b/lib/optimal/doc.ex @@ -82,7 +82,7 @@ defmodule Optimal.Doc do defp do_document_opts(opts, schema) do opts |> Enum.sort_by(fn opt -> - not (opt in schema.required) + opt not in schema.required end) |> Enum.map_join("\n", fn opt -> string_opt = "`" <> Atom.to_string(opt) <> "`" diff --git a/lib/optimal/type.ex b/lib/optimal/type.ex index 6f19bd0..b075c77 100644 --- a/lib/optimal/type.ex +++ b/lib/optimal/type.ex @@ -110,7 +110,7 @@ defmodule Optimal.Type do def matches_type?(%_{}, _), do: false def matches_type?({:enum, list}, value), do: value in list # Below this line is only scalar types. Do not move things below/above this line. - def matches_type?(type, _) when not (type in @scalar_types), + def matches_type?(type, _) when type not in @scalar_types, do: raise("Unreachable: no type #{inspect(type)}") def matches_type?(:int, value) when is_integer(value), do: true diff --git a/mix.exs b/mix.exs index 88d0f8c..2845fa8 100644 --- a/mix.exs +++ b/mix.exs @@ -12,11 +12,7 @@ defmodule Optimal.MixProject do deps: deps(), description: "A schema based `opt` validator", docs: docs(), - package: package(), - test_coverage: [tool: ExCoveralls], - preferred_cli_env: [ - "coveralls.travis": :test - ] + package: package() ] end @@ -34,7 +30,7 @@ defmodule Optimal.MixProject do def package do [ - maintainers: ["Zach Daniel"], + maintainers: ["Alec Hartung"], licenses: ["MIT"], links: %{"GitHub" => "https://github.com/albert-io/optimal"}, source_url: "https://github.com/albert-io/optimal", @@ -52,15 +48,9 @@ defmodule Optimal.MixProject do # Run "mix help deps" to learn about dependencies. defp deps do [ - {:ex_doc, ">= 0.0.0", only: :dev}, - {:credo, "~> 0.8", only: [:dev, :test], runtime: false}, - {:excoveralls, "~> 0.6", only: :test}, - {:inch_ex, "~> 0.5", only: [:dev, :test]}, - {:dialyxir, - github: "jeremyjh/dialyxir", - ref: "00c1e32153b54e4b54f0d33f999d642c00dcd72b", - only: [:dev], - runtime: false} + {:ex_doc, "~> 0.34", only: [:dev, :test], runtime: false, warn_if_outdated: true}, + {:credo, "~> 1.7", only: [:dev, :test], runtime: false}, + {:dialyxir, "~> 1.4", only: [:dev, :test], runtime: false} ] end end diff --git a/mix.lock b/mix.lock index 4c3638c..9a4afd9 100644 --- a/mix.lock +++ b/mix.lock @@ -1,24 +1,14 @@ %{ - "bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm"}, - "certifi": {:hex, :certifi, "2.5.1", "867ce347f7c7d78563450a18a6a28a8090331e77fa02380b4a21962a65d36ee5", [:rebar3], [{:parse_trans, "~>3.3", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm"}, - "credo": {:hex, :credo, "0.10.2", "03ad3a1eff79a16664ed42fc2975b5e5d0ce243d69318060c626c34720a49512", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm"}, - "dialyxir": {:git, "https://github.com/jeremyjh/dialyxir.git", "00c1e32153b54e4b54f0d33f999d642c00dcd72b", [ref: "00c1e32153b54e4b54f0d33f999d642c00dcd72b"]}, - "earmark": {:hex, :earmark, "1.3.2", "b840562ea3d67795ffbb5bd88940b1bed0ed9fa32834915125ea7d02e35888a5", [:mix], [], "hexpm"}, - "ex_doc": {:hex, :ex_doc, "0.20.1", "88eaa16e67c505664fd6a66f42ddb962d424ad68df586b214b71443c69887123", [:mix], [{:earmark, "~> 1.3", [hex: :earmark, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.10", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm"}, - "excoveralls": {:hex, :excoveralls, "0.10.6", "e2b9718c9d8e3ef90bc22278c3f76c850a9f9116faf4ebe9678063310742edc2", [:mix], [{:hackney, "~> 1.13", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm"}, - "exjsx": {:hex, :exjsx, "4.0.0", "60548841e0212df401e38e63c0078ec57b33e7ea49b032c796ccad8cde794b5c", [:mix], [{:jsx, "~> 2.8.0", [hex: :jsx, repo: "hexpm", optional: false]}], "hexpm"}, - "hackney": {:hex, :hackney, "1.15.1", "9f8f471c844b8ce395f7b6d8398139e26ddca9ebc171a8b91342ee15a19963f4", [:rebar3], [{:certifi, "2.5.1", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.4", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm"}, - "idna": {:hex, :idna, "6.0.0", "689c46cbcdf3524c44d5f3dde8001f364cd7608a99556d8fbd8239a5798d4c10", [:rebar3], [{:unicode_util_compat, "0.4.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm"}, - "inch_ex": {:hex, :inch_ex, "0.5.6", "418357418a553baa6d04eccd1b44171936817db61f4c0840112b420b8e378e67", [:mix], [{:poison, "~> 1.5 or ~> 2.0 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm"}, - "jason": {:hex, :jason, "1.1.2", "b03dedea67a99223a2eaf9f1264ce37154564de899fd3d8b9a21b1a6fd64afe7", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm"}, - "jsx": {:hex, :jsx, "2.8.3", "a05252d381885240744d955fbe3cf810504eb2567164824e19303ea59eef62cf", [:mix, :rebar3], [], "hexpm"}, - "makeup": {:hex, :makeup, "0.8.0", "9cf32aea71c7fe0a4b2e9246c2c4978f9070257e5c9ce6d4a28ec450a839b55f", [:mix], [{:nimble_parsec, "~> 0.5.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm"}, - "makeup_elixir": {:hex, :makeup_elixir, "0.13.0", "be7a477997dcac2e48a9d695ec730b2d22418292675c75aa2d34ba0909dcdeda", [:mix], [{:makeup, "~> 0.8", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm"}, - "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm"}, - "mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm"}, - "nimble_parsec": {:hex, :nimble_parsec, "0.5.0", "90e2eca3d0266e5c53f8fbe0079694740b9c91b6747f2b7e3c5d21966bba8300", [:mix], [], "hexpm"}, - "parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm"}, - "poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], [], "hexpm"}, - "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.4", "f0eafff810d2041e93f915ef59899c923f4568f4585904d010387ed74988e77b", [:make, :mix, :rebar3], [], "hexpm"}, - "unicode_util_compat": {:hex, :unicode_util_compat, "0.4.1", "d869e4c68901dd9531385bb0c8c40444ebf624e60b6962d95952775cac5e90cd", [:rebar3], [], "hexpm"}, + "bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"}, + "credo": {:hex, :credo, "1.7.12", "9e3c20463de4b5f3f23721527fcaf16722ec815e70ff6c60b86412c695d426c1", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "8493d45c656c5427d9c729235b99d498bd133421f3e0a683e5c1b561471291e5"}, + "dialyxir": {:hex, :dialyxir, "1.4.5", "ca1571ac18e0f88d4ab245f0b60fa31ff1b12cbae2b11bd25d207f865e8ae78a", [:mix], [{:erlex, ">= 0.2.7", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "b0fb08bb8107c750db5c0b324fa2df5ceaa0f9307690ee3c1f6ba5b9eb5d35c3"}, + "earmark_parser": {:hex, :earmark_parser, "1.4.44", "f20830dd6b5c77afe2b063777ddbbff09f9759396500cdbe7523efd58d7a339c", [:mix], [], "hexpm", "4778ac752b4701a5599215f7030989c989ffdc4f6df457c5f36938cc2d2a2750"}, + "erlex": {:hex, :erlex, "0.2.7", "810e8725f96ab74d17aac676e748627a07bc87eb950d2b83acd29dc047a30595", [:mix], [], "hexpm", "3ed95f79d1a844c3f6bf0cea61e0d5612a42ce56da9c03f01df538685365efb0"}, + "ex_doc": {:hex, :ex_doc, "0.38.2", "504d25eef296b4dec3b8e33e810bc8b5344d565998cd83914ffe1b8503737c02", [:mix], [{:earmark_parser, "~> 1.4.44", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "732f2d972e42c116a70802f9898c51b54916e542cc50968ac6980512ec90f42b"}, + "file_system": {:hex, :file_system, "1.1.0", "08d232062284546c6c34426997dd7ef6ec9f8bbd090eb91780283c9016840e8f", [:mix], [], "hexpm", "bfcf81244f416871f2a2e15c1b515287faa5db9c6bcf290222206d120b3d43f6"}, + "jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"}, + "makeup": {:hex, :makeup, "1.2.1", "e90ac1c65589ef354378def3ba19d401e739ee7ee06fb47f94c687016e3713d1", [:mix], [{:nimble_parsec, "~> 1.4", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "d36484867b0bae0fea568d10131197a4c2e47056a6fbe84922bf6ba71c8d17ce"}, + "makeup_elixir": {:hex, :makeup_elixir, "1.0.1", "e928a4f984e795e41e3abd27bfc09f51db16ab8ba1aebdba2b3a575437efafc2", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "7284900d412a3e5cfd97fdaed4f5ed389b8f2b4cb49efc0eb3bd10e2febf9507"}, + "makeup_erlang": {:hex, :makeup_erlang, "1.0.2", "03e1804074b3aa64d5fad7aa64601ed0fb395337b982d9bcf04029d68d51b6a7", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "af33ff7ef368d5893e4a267933e7744e46ce3cf1f61e2dccf53a111ed3aa3727"}, + "nimble_parsec": {:hex, :nimble_parsec, "1.4.2", "8efba0122db06df95bfaa78f791344a89352ba04baedd3849593bfce4d0dc1c6", [:mix], [], "hexpm", "4b21398942dda052b403bbe1da991ccd03a053668d147d53fb8c4e0efe09c973"}, }