diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e064be8..4e69b6a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,13 +12,11 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v5 + - name: Use Node.js 24.x + uses: actions/setup-node@v6 with: - ref: ${{ github.head_ref }} - - name: Use Node.js 18.x - uses: actions/setup-node@v3 - with: - node-version: 18.x + node-version: 24.x - name: Install Prettier run: npm install --global prettier - name: Run Prettier @@ -27,14 +25,14 @@ jobs: name: Format/Credo runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v5 - name: Set up Elixir uses: erlef/setup-beam@v1 with: - elixir-version: "1.18.0" - otp-version: "27.0.1" + elixir-version: "1.20" + otp-version: "29" - name: Restore dependencies cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: deps key: ${{ runner.os }}-mix-${{ hashFiles('**/mix.lock') }} @@ -45,18 +43,55 @@ jobs: run: mix format --check-formatted - name: Run Credo run: mix credo + dialyzer: + name: Dialyzer + runs-on: ubuntu-latest + env: + ELIXIR_VERSION: "1.20" + OTP_VERSION: "29" + steps: + - uses: actions/checkout@v5 + - name: Set up Elixir + uses: erlef/setup-beam@v1 + with: + elixir-version: ${{ env.ELIXIR_VERSION }} + otp-version: ${{ env.OTP_VERSION }} + - name: Restore dependencies cache + uses: actions/cache@v4 + with: + path: deps + key: ${{ runner.os }}-mix-${{ hashFiles('**/mix.lock') }} + restore-keys: ${{ runner.os }}-mix- + - name: Restore PLT cache + uses: actions/cache@v4 + with: + path: priv/plts + key: ${{ runner.os }}-plt-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ hashFiles('**/mix.lock') }} + restore-keys: ${{ runner.os }}-plt-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}- + - name: Install dependencies + run: mix deps.get + - name: Run Dialyzer + run: mix dialyzer --format github test: - name: Test + name: Test (Elixir ${{ matrix.elixir }} / OTP ${{ matrix.otp }}) runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + include: + - elixir: "1.16" + otp: "26" + - elixir: "1.20" + otp: "29" steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v5 - name: Set up Elixir uses: erlef/setup-beam@v1 with: - elixir-version: "1.18.0" - otp-version: "27.0.1" + elixir-version: ${{ matrix.elixir }} + otp-version: ${{ matrix.otp }} - name: Restore dependencies cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: deps key: ${{ runner.os }}-mix-${{ hashFiles('**/mix.lock') }} @@ -64,6 +99,4 @@ jobs: - name: Install dependencies run: mix deps.get - name: Run tests - env: - TERM: xterm run: mix test diff --git a/.gitignore b/.gitignore index ecdce2e..cb2586c 100644 --- a/.gitignore +++ b/.gitignore @@ -19,5 +19,9 @@ erl_crash.dump # Ignore package tarball (built via "mix hex.build"). jsend-*.tar +# Dialyzer PLT files. +/priv/plts/*.plt +/priv/plts/*.plt.hash + # Temporary files, for example, from tests. /tmp/ diff --git a/.tool-versions b/.tool-versions index 031d43b..1e6eab3 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1,2 +1,2 @@ -elixir 1.18.0-otp-27 -erlang 27.2 +elixir 1.20.4-otp-29 +erlang 29.0.6 diff --git a/CHANGELOG.md b/CHANGELOG.md index ba2fb82..239f894 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,17 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## Unreleased + +**Fixed** + +- `JSON.Encoder` implementation crashed on Elixir 1.20. It called an internal + Elixir module that 1.20 removed. + +**Changed** + +- Minimum supported Elixir version is now 1.16. + ## v0.1.0 - 2024-12-26 Initial release. diff --git a/LICENSE.md b/LICENSE.md index faf5b70..459d4ff 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2024 Codedge LLC (https://www.codedge.io/) +Copyright (c) 2024-2026 Codedge LLC (https://www.codedge.io/) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index ac2cc4a..aca3a35 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,6 @@ [![Version](https://img.shields.io/hexpm/v/jsend.svg)](https://hex.pm/packages/jsend) [![Total Downloads](https://img.shields.io/hexpm/dt/jsend.svg)](https://hex.pm/packages/jsend) [![License](https://img.shields.io/hexpm/l/jsend.svg)](https://github.com/codedge-llc/jsend/blob/main/LICENSE.md) -[![Last Updated](https://img.shields.io/github/last-commit/codedge-llc/jsend.svg)](https://github.com/codedge-llc/jsend/commits/main) [![Documentation](https://img.shields.io/badge/documentation-gray)](https://hexdocs.pm/jsend/) ## Installation @@ -39,6 +38,6 @@ Git commit subjects use the [Karma style](http://karma-runner.github.io/5.0/dev/ ## License -Copyright (c) 2024 Codedge LLC (https://www.codedge.io/) +Copyright (c) 2024-2026 Codedge LLC (https://www.codedge.io/) This library is MIT licensed. See the [LICENSE](https://github.com/codedge-llc/jsend/blob/main/LICENSE.md) for details. diff --git a/lib/jsend.ex b/lib/jsend.ex index 230ef76..da313f4 100644 --- a/lib/jsend.ex +++ b/lib/jsend.ex @@ -90,7 +90,7 @@ case Code.ensure_compiled(JSON.Encoder) do |> Enum.reject(fn {_, v} -> v == nil end) |> Enum.into(%{}) - :elixir_json.encode_map(map, encoder) + encoder.(map, encoder) end end diff --git a/mix.exs b/mix.exs index fc138b3..896e5da 100644 --- a/mix.exs +++ b/mix.exs @@ -8,8 +8,9 @@ defmodule JSend.MixProject do [ app: :jsend, deps: deps(), + dialyzer: dialyzer(), docs: docs(), - elixir: "~> 1.14", + elixir: "~> 1.16", name: "JSend", package: package(), start_permanent: Mix.env() == :prod, @@ -33,6 +34,12 @@ defmodule JSend.MixProject do ] end + defp dialyzer do + [ + plt_file: {:no_warn, "priv/plts/dialyzer.plt"} + ] + end + defp docs do [ extras: [ diff --git a/mix.lock b/mix.lock index d725f9d..09f8d0c 100644 --- a/mix.lock +++ b/mix.lock @@ -1,14 +1,14 @@ %{ "bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"}, - "credo": {:hex, :credo, "1.7.10", "6e64fe59be8da5e30a1b96273b247b5cf1cc9e336b5fd66302a64b25749ad44d", [: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", "71fbc9a6b8be21d993deca85bf151df023a3097b01e09a2809d460348561d8cd"}, - "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.42", "f23d856f41919f17cd06a493923a722d87a2d684f143a1e663c04a2b93100682", [:mix], [], "hexpm", "6915b6ca369b5f7346636a2f41c6a6d78b5af419d61a611079189233358b8b8b"}, - "erlex": {:hex, :erlex, "0.2.7", "810e8725f96ab74d17aac676e748627a07bc87eb950d2b83acd29dc047a30595", [:mix], [], "hexpm", "3ed95f79d1a844c3f6bf0cea61e0d5612a42ce56da9c03f01df538685365efb0"}, - "ex_doc": {:hex, :ex_doc, "0.36.1", "4197d034f93e0b89ec79fac56e226107824adcce8d2dd0a26f5ed3a95efc36b1", [:mix], [{:earmark_parser, "~> 1.4.42", [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", "d7d26a7cf965dacadcd48f9fa7b5953d7d0cfa3b44fa7a65514427da44eafd89"}, - "file_system": {:hex, :file_system, "1.0.1", "79e8ceaddb0416f8b8cd02a0127bdbababe7bf4a23d2a395b983c1f8b3f73edd", [:mix], [], "hexpm", "4414d1f38863ddf9120720cd976fce5bdde8e91d8283353f0e31850fa89feb9e"}, - "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"}, + "credo": {:hex, :credo, "1.7.19", "cc52129665fc7c15143d47838fda0f9cd6dac9ceced7bf4da6f85fcbfe64b12a", [: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", "2d8bc95d5a7bb99dd2613621d4f08c6a3575c3fd4b62e6a2b48a100352a557b8"}, + "dialyxir": {:hex, :dialyxir, "1.4.8", "7ef671a8aff9948b091d8c30f09467fbb16e77305cda451bce48109a0f5e021c", [:mix], [{:erlex, ">= 0.2.8", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "cbd5a851571e5dfeb32aaf2e840bfa98b7864cb3071bf2ef5d95d1276b12e072"}, + "earmark_parser": {:hex, :earmark_parser, "1.4.46", "67607a0532e810c6f630a515c548d0b24949643f168cc556303bee4cf96105c7", [:mix], [], "hexpm", "9c44636e8a1c68c62f526b2dcd85d941dbbcee7ab82cf64ba06ce28bef8e89f5"}, + "erlex": {:hex, :erlex, "0.2.9", "7debbbaa9f4f368b8cd648983e0f1d7963028508e9c59e9d4ed504e94ef52a55", [:mix], [], "hexpm", "8cfffc0ec7159e6d73de2ab28a588064de80f88b2798d5cbe4482cbbc200178b"}, + "ex_doc": {:hex, :ex_doc, "0.40.4", "66f2e42bf588594d5a8aab31cad87f2ddad09d0da1b1a2f379340ec2c2e497cb", [:mix], [{:earmark_parser, "~> 1.4.46", [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", "6222b9e423d76584ee34df2c82a5ed72c2d53dc153f7f483ad28b378694186cc"}, + "file_system": {:hex, :file_system, "1.1.1", "31864f4685b0148f25bd3fbef2b1228457c0c89024ad67f7a81a3ffbc0bbad3a", [:mix], [], "hexpm", "7a15ff97dfe526aeefb090a7a9d3d03aa907e100e262a0f8f7746b78f8f87a5d"}, + "jason": {:hex, :jason, "1.4.5", "2e3a008590b0b8d7388c20293e9dcc9cf3e5d642fd2a114e4cbbb52e595d940a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0 or ~> 3.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "b0c823996102bcd0239b3c2444eb00409b72f6a140c1950bc8b457d836b30684"}, + "makeup": {:hex, :makeup, "1.2.2", "882d46dc0905e9ff7abf2aab61a7e6b3dcc555533977d8a23b06019e6c89ac94", [:mix], [{:nimble_parsec, "~> 1.4", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "9a1a24e5b343b8ae16abea0822c10a6f75da27af7fa802ada5251f7579bfccfa"}, "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.1", "c7f58c120b2b5aa5fd80d540a89fdf866ed42f1f3994e4fe189abebeab610839", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "8a89a1eeccc2d798d6ea15496a6e4870b75e014d1af514b1b71fa33134f57814"}, - "nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"}, + "makeup_erlang": {:hex, :makeup_erlang, "1.1.0", "835f7e60792e08824cda445639555d7bf1bbbddb1b60b306e33cb6f6db24dc74", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "1cd6780fb1dd1a03979abaed0fe82712b0625118fd5257d3ebbf73f960c73c3c"}, + "nimble_parsec": {:hex, :nimble_parsec, "1.4.2", "8efba0122db06df95bfaa78f791344a89352ba04baedd3849593bfce4d0dc1c6", [:mix], [], "hexpm", "4b21398942dda052b403bbe1da991ccd03a053668d147d53fb8c4e0efe09c973"}, } diff --git a/test/jsend_test.exs b/test/jsend_test.exs index 8a7349e..180264d 100644 --- a/test/jsend_test.exs +++ b/test/jsend_test.exs @@ -10,6 +10,7 @@ defmodule JSendTest do assert res == "{\"data\":{\"id\":1,\"name\":\"Some name\"},\"status\":\"success\"}" end + @tag :json test "encodes with JSON" do data = %{id: 1, name: "Some name"} res = data |> JSend.success() |> JSON.encode!() @@ -26,6 +27,7 @@ defmodule JSendTest do assert res == "{\"data\":{\"msg\":\"Something went wrong\"},\"status\":\"fail\"}" end + @tag :json test "encodes with JSON" do data = %{msg: "Something went wrong"} result = data |> JSend.fail() |> JSON.encode!() @@ -42,6 +44,7 @@ defmodule JSendTest do "{\"code\":500,\"data\":{\"msg\":\"There was a server error\"},\"message\":\"INTERNAL SERVER ERROR\",\"status\":\"error\"}" end + @tag :json test "encodes with JSON" do data = %{msg: "There was a server error"} result = "INTERNAL SERVER ERROR" |> JSend.error(500, data) |> JSON.encode!() diff --git a/test/test_helper.exs b/test/test_helper.exs index 869559e..d685be9 100644 --- a/test/test_helper.exs +++ b/test/test_helper.exs @@ -1 +1,4 @@ -ExUnit.start() +# The built-in JSON module arrived in Elixir 1.18. +exclude = if Code.ensure_loaded?(JSON), do: [], else: [:json] + +ExUnit.start(exclude: exclude)