From 0a4e125a24c787e6d7f168c78960e692a06b6ac5 Mon Sep 17 00:00:00 2001 From: Marcin Gornik Date: Sat, 10 Nov 2018 16:54:18 +0100 Subject: [PATCH 01/18] Refactor tests to support multiple struct examples * added a standard struct name (target for renaming examples) * modified the standardise function to support passing the name of the example and always rename to the standard name --- test/typed_struct_test.exs | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/test/typed_struct_test.exs b/test/typed_struct_test.exs index 9281f33..3e0df01 100644 --- a/test/typed_struct_test.exs +++ b/test/typed_struct_test.exs @@ -30,6 +30,8 @@ defmodule TypedStructTest do end @bytecode bytecode + # standard struct name used when comparing generated types + @standard_struct_name TypedStructTest.TestStruct ## Standard cases @@ -79,7 +81,11 @@ defmodule TypedStructTest do # Get both types and standardise them (remove line numbers and rename # the second struct with the name of the first one). type1 = @bytecode |> extract_first_type() |> standardise() - type2 = bytecode2 |> extract_first_type() |> standardise() + + type2 = + bytecode2 + |> extract_first_type() + |> standardise(TypedStructTest.TestStruct2) assert type1 == type2 end @@ -170,24 +176,27 @@ defmodule TypedStructTest do end end - # Standardises a type (removes line numbers and renames the struct). - defp standardise({name, type, params}) when is_tuple(type), - do: {name, standardise(type), params} + # Standardises a type (removes line numbers and renames the struct + # to the standard struct name). + defp standardise(type_info, struct \\ @standard_struct_name) + + defp standardise({name, type, params}, struct) when is_tuple(type), + do: {name, standardise(type, struct), params} - defp standardise({:type, _, type, params}), - do: {:type, :line, type, standardise(params)} + defp standardise({:type, _, type, params}, struct), + do: {:type, :line, type, standardise(params, struct)} - defp standardise({:remote_type, _, params}), - do: {:remote_type, :line, standardise(params)} + defp standardise({:remote_type, _, params}, struct), + do: {:remote_type, :line, standardise(params, struct)} - defp standardise({:atom, _, TypedStructTest.TestStruct2}), - do: {:atom, :line, TypedStructTest.TestStruct} + defp standardise({:atom, _, struct}, struct), + do: {:atom, :line, @standard_struct_name} - defp standardise({type, _, litteral}), + defp standardise({type, _, litteral}, _struct), do: {type, :line, litteral} - defp standardise(list) when is_list(list), - do: Enum.map(list, &standardise/1) + defp standardise(list, struct) when is_list(list), + do: Enum.map(list, &standardise(&1, struct)) # Deletes the context from a quoted expression. defp delete_context(list) when is_list(list), From 452925786714f202e65d665740d11c59be412223 Mon Sep 17 00:00:00 2001 From: Marcin Gornik Date: Sat, 10 Nov 2018 18:02:35 +0100 Subject: [PATCH 02/18] Add support for opaque: true option in typedstruct --- lib/typed_struct.ex | 44 ++++++++++++++++++++++++++++++++++---- test/typed_struct_test.exs | 44 ++++++++++++++++++++++++++++++++++---- 2 files changed, 80 insertions(+), 8 deletions(-) diff --git a/lib/typed_struct.ex b/lib/typed_struct.ex index fd254b9..6264739 100644 --- a/lib/typed_struct.ex +++ b/lib/typed_struct.ex @@ -131,6 +131,18 @@ defmodule TypedStruct do end end + You can also generate an opaque type for the struct by specyfing + `opaque: true`: + + defmodule MyOpaqueStruct do + use TypedStruct + + # Generate an opaque type for the struct + typedstruct opaque: true do + field :name, String.t + end + end + ### Documentation To add a `@typedoc` to the struct type, just add the attribute above the @@ -249,6 +261,23 @@ defmodule TypedStruct do @type t() :: %__MODULE__{ name: String.t() # Not nullable } + + Passing `opaque: true` replaces `@type` with `@opaque` in the struct + type specification: + + defmodule Example do + use TypedStruct + + typedstruct opaque: true do + field :name, String.t() + end + end + + # Becomes + + @opaque t() :: %__MODULE__{ + name: String.t() + } """ @doc false @@ -269,6 +298,7 @@ defmodule TypedStruct do * `enforce` - if set to true, sets `enforce: true` to all fields by default. This can be overridden by setting `enforce: false` or a default value on individual fields. + * `opaque` - if set to true, creates an opaque type for the struct ## Examples @@ -309,7 +339,7 @@ defmodule TypedStruct do @enforce_keys @keys_to_enforce defstruct @fields - TypedStruct.__type__(@types) + TypedStruct.__type__(@types, unquote(opts)) def __keys__, do: @fields |> Keyword.keys() |> Enum.reverse() def __defaults__, do: Enum.reverse(@fields) @@ -371,9 +401,15 @@ defmodule TypedStruct do end @doc false - defmacro __type__(types) do - quote bind_quoted: [types: types] do - @type t() :: %__MODULE__{unquote_splicing(types)} + defmacro __type__(types, opts) do + if Keyword.get(opts, :opaque, false) do + quote bind_quoted: [types: types] do + @opaque t() :: %__MODULE__{unquote_splicing(types)} + end + else + quote bind_quoted: [types: types] do + @type t() :: %__MODULE__{unquote_splicing(types)} + end end end diff --git a/test/typed_struct_test.exs b/test/typed_struct_test.exs index 3e0df01..67d9f49 100644 --- a/test/typed_struct_test.exs +++ b/test/typed_struct_test.exs @@ -16,6 +16,15 @@ defmodule TypedStructTest do def enforce_keys, do: @enforce_keys end + {:module, _name, bytecode_opaque, _exports} = + defmodule OpaqueTestStruct do + use TypedStruct + + typedstruct opaque: true do + field :int, integer() + end + end + defmodule EnforcedTypedStruct do use TypedStruct @@ -30,6 +39,7 @@ defmodule TypedStructTest do end @bytecode bytecode + @bytecode_opaque bytecode_opaque # standard struct name used when comparing generated types @standard_struct_name TypedStructTest.TestStruct @@ -90,6 +100,32 @@ defmodule TypedStructTest do assert type1 == type2 end + test "generates an type for the struct if `opaque: true` is set" do + # Define a second struct with the type expected for TestStruct. + {:module, _name, bytecode_expected, _exports} = + defmodule TestStruct3 do + defstruct [:int] + + @opaque t() :: %__MODULE__{ + int: integer() | nil + } + end + + # Get both types and standardise them (remove line numbers and rename + # the second struct with the name of the first one). + type1 = + @bytecode_opaque + |> extract_first_type(:opaque) + |> standardise(TypedStructTest.OpaqueTestStruct) + + type2 = + bytecode_expected + |> extract_first_type(:opaque) + |> standardise(TypedStructTest.TestStruct3) + + assert type1 == type2 + end + test "generates a function to get the struct keys" do assert TestStruct.__keys__() == [ :int, @@ -161,16 +197,16 @@ defmodule TypedStructTest do # avoid compiler warnings. if Version.compare(@elixir_version, @min_version) == :lt do # API for Elixir 1.6 (TODO: Remove when 1.6 compatibility is dropped.) - defp extract_first_type(bytecode) do + defp extract_first_type(bytecode, type_keyword \\ :type) do bytecode |> Kernel.Typespec.beam_types() - |> Keyword.get(:type) + |> Keyword.get(type_keyword) end else # API for Elixir 1.7 - defp extract_first_type(bytecode) do + defp extract_first_type(bytecode, type_keyword \\ :type) do case Code.Typespec.fetch_types(bytecode) do - {:ok, types} -> Keyword.get(types, :type) + {:ok, types} -> Keyword.get(types, type_keyword) _ -> nil end end From 058b806586b9994c0940de7185520df4cf767656 Mon Sep 17 00:00:00 2001 From: Marcin Gornik Date: Sat, 10 Nov 2018 19:08:29 +0100 Subject: [PATCH 03/18] Document opaque: true in README.md --- README.md | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/README.md b/README.md index 1040eb1..c73c64e 100644 --- a/README.md +++ b/README.md @@ -144,6 +144,20 @@ defmodule MyStruct do end ``` +You can also generate an opaque type for the struct by specyfing +`opaque: true`: + +```elixir +defmodule MyOpaqueStruct do + use TypedStruct + + # Generate an opaque type for the struct + typedstruct opaque: true do + field :name, String.t + end +end +``` + ### Documentation To add a `@typedoc` to the struct type, just add the attribute above the @@ -281,6 +295,26 @@ case it is enforced. Both options would generate the following type: } ``` +Passing `opaque: true` replaces `@type` with `@opaque` in the struct +type specification: + +```elixir +defmodule Example do + use TypedStruct + + typedstruct opaque: true do + field :name, String.t() + end +end + +# Becomes + +@opaque t() :: %__MODULE__{ + name: String.t() + } +``` + + ## [Contributing](CONTRIBUTING.md) Before contributing to this project, please read the From 551766702dc1b98dac14d7c9d7b1d4f0e7c8804f Mon Sep 17 00:00:00 2001 From: Jean-Philippe Cugnet Date: Mon, 12 Nov 2018 20:25:06 +0100 Subject: [PATCH 04/18] Update the environment --- .envrc | 2 +- shell.nix | 11 ++++------- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/.envrc b/.envrc index e2e9592..6d28500 100644 --- a/.envrc +++ b/.envrc @@ -67,7 +67,7 @@ use_nix() { if [[ ! -f "${dump}" ]] || [[ "${XDG_CONFIG_DIR}/direnv/direnvrc" -nt "${dump}" ]]; then log_status "use nix: updating cache" - old=`find ${dir} -name 'dump-*'` + old=`find "${dir}" -name 'dump-*'` nix-shell "${drv}" --show-trace "$@" --run 'direnv dump' > "${dump}" rm -f ${old} fi diff --git a/shell.nix b/shell.nix index ac33355..f63279d 100644 --- a/shell.nix +++ b/shell.nix @@ -5,17 +5,14 @@ with pkgs; let inherit (lib) optional optionals; - erlang = beam.interpreters.erlangR21.override { - # Temporary fix to enable use on OS X El Capitan. - enableKernelPoll = if stdenv.isDarwin then false else true; - }; - - elixir = (beam.packages.erlangR21.override { inherit erlang; }).elixir_1_7; + elixir = beam.packages.erlangR21.elixir_1_7; + gitflow = gitAndTools.gitflow; in mkShell { - buildInputs = [ elixir git ] + buildInputs = [ elixir git gitflow ] ++ optional stdenv.isLinux libnotify # For ExUnit Notifier on Linux. + ++ optional stdenv.isLinux inotify-tools # For file_system on Linux. ++ optional stdenv.isDarwin terminal-notifier # For ExUnit Notifier on macOS. ++ optionals stdenv.isDarwin (with darwin.apple_sdk.frameworks; [ # For file_system on macOS. From 0ca0ebcaba29f470b28209019048268fc755352c Mon Sep 17 00:00:00 2001 From: Jean-Philippe Cugnet Date: Mon, 12 Nov 2018 20:27:24 +0100 Subject: [PATCH 05/18] Bump the version to 0.1.4-dev --- mix.exs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/mix.exs b/mix.exs index 357e2b3..2a4e75a 100644 --- a/mix.exs +++ b/mix.exs @@ -1,13 +1,13 @@ defmodule TypedStruct.MixProject do use Mix.Project - @version "0.1.3" + @version "0.1.4" @repo_url "https://github.com/ejpcmac/typed_struct" def project do [ app: :typed_struct, - version: @version, + version: @version <> dev(), elixir: "~> 1.6", elixirc_paths: elixirc_paths(Mix.env()), start_permanent: Mix.env() == :prod, @@ -86,4 +86,17 @@ defmodule TypedStruct.MixProject do links: %{"GitHub" => @repo_url} ] end + + # Helper to add a development revision to the version. Do NOT make a call to + # Git this way in a production release!! + def dev do + with {rev, 0} <- + System.cmd("git", ["rev-parse", "--short", "HEAD"], + stderr_to_stdout: true + ) do + "-dev+" <> String.trim(rev) + else + _ -> "-dev" + end + end end From 49bc57cd632ae09c769beca01ffcc38f8aeb5d89 Mon Sep 17 00:00:00 2001 From: Jean-Philippe Cugnet Date: Mon, 12 Nov 2018 20:56:33 +0100 Subject: [PATCH 06/18] [CI] Add a Travis CI configuration --- .travis.yml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..e3e1535 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,16 @@ +sudo: false +language: elixir +elixir: + - 1.6.6 + - 1.7.4 +otp_release: + - 19.3 + - 20.3 + - 21.1 +script: + - mix deps.compile + - mix compile --warnings-as-errors + - mix test --trace + - mix dialyzer --halt-exit-status + - mix credo + - mix format --check-formatted From bcaae143cf9a0b64b973bbbcb7b5d799ec26e359 Mon Sep 17 00:00:00 2001 From: Jean-Philippe Cugnet Date: Mon, 12 Nov 2018 22:09:50 +0100 Subject: [PATCH 07/18] [CI] Cache Dialyzer PLTs --- .travis.yml | 10 +++++++++- mix.exs | 10 ++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index e3e1535..5ab33c0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,10 +7,18 @@ otp_release: - 19.3 - 20.3 - 21.1 -script: +env: + - PLT_DIR=$HOME/.plt +before_script: + - mkdir -p $PLT_DIR - mix deps.compile + - mix dialyzer --plt +script: - mix compile --warnings-as-errors - mix test --trace - mix dialyzer --halt-exit-status - mix credo - mix format --check-formatted +cache: + directories: + - $PLT_DIR diff --git a/mix.exs b/mix.exs index 2a4e75a..4febbc3 100644 --- a/mix.exs +++ b/mix.exs @@ -61,6 +61,9 @@ defmodule TypedStruct.MixProject do # Dialyzer configuration defp dialyzer do [ + # Use a custom PLT directory for continuous integration caching. + plt_core_path: System.get_env("PLT_DIR"), + plt_file: plt_file(), plt_add_deps: :transitive, flags: [ :unmatched_returns, @@ -71,6 +74,13 @@ defmodule TypedStruct.MixProject do ] end + defp plt_file do + case System.get_env("PLT_DIR") do + nil -> nil + plt_dir -> {:no_warn, Path.join(plt_dir, "typed_struct.plt")} + end + end + defp cli_env do [ # Always run coveralls mix tasks in `:test` env. From 6bb5f8883a189ce72ff02f60d09f39d56bf33ac8 Mon Sep 17 00:00:00 2001 From: Jean-Philippe Cugnet Date: Mon, 12 Nov 2018 22:33:14 +0100 Subject: [PATCH 08/18] [CI] Make Travis wait for the PLT to build --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 5ab33c0..ac54f59 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,7 +12,7 @@ env: before_script: - mkdir -p $PLT_DIR - mix deps.compile - - mix dialyzer --plt + - travis_wait mix dialyzer --plt script: - mix compile --warnings-as-errors - mix test --trace From 0b19194b9e23be6561be8d0e15ab28d72508d73b Mon Sep 17 00:00:00 2001 From: Jean-Philippe Cugnet Date: Mon, 12 Nov 2018 23:47:57 +0100 Subject: [PATCH 09/18] [CI] Check formatting only for the latest Elixir version --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index ac54f59..b18f1e0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,7 +18,7 @@ script: - mix test --trace - mix dialyzer --halt-exit-status - mix credo - - mix format --check-formatted + - if [[ "$TRAVIS_ELIXIR_VERSION" == "1.7.4" ]]; then mix format --check-formatted; fi cache: directories: - $PLT_DIR From eeab6543a649c07583e6ad2329706a0c58884ae4 Mon Sep 17 00:00:00 2001 From: Jean-Philippe Cugnet Date: Mon, 12 Nov 2018 23:59:24 +0100 Subject: [PATCH 10/18] [CI] Update (yet again) the configuration --- .travis.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index b18f1e0..6878321 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,11 +12,12 @@ env: before_script: - mkdir -p $PLT_DIR - mix deps.compile + - MIX_ENV=test mix deps.compile - travis_wait mix dialyzer --plt script: - - mix compile --warnings-as-errors + - mix compile --force --verbose --warnings-as-errors - mix test --trace - - mix dialyzer --halt-exit-status + - mix dialyzer --no-compile --no-check --halt-exit-status - mix credo - if [[ "$TRAVIS_ELIXIR_VERSION" == "1.7.4" ]]; then mix format --check-formatted; fi cache: From beac630f034bd0ae817a40bf9788dd7d8be11b64 Mon Sep 17 00:00:00 2001 From: Jean-Philippe Cugnet Date: Tue, 13 Nov 2018 00:09:38 +0100 Subject: [PATCH 11/18] Use custom envs for docs and test.watch --- mix.exs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/mix.exs b/mix.exs index 4febbc3..8904c6b 100644 --- a/mix.exs +++ b/mix.exs @@ -48,13 +48,13 @@ defmodule TypedStruct.MixProject do {:credo, "~> 0.10.0", only: :dev, runtime: false}, {:dialyxir, ">= 0.0.0", only: :dev, runtime: false}, {:excoveralls, ">= 0.0.0", only: :test, runtime: false}, - {:mix_test_watch, ">= 0.0.0", only: :dev, runtime: false}, + {:mix_test_watch, ">= 0.0.0", only: :test, runtime: false}, {:ex_unit_notifier, ">= 0.0.0", only: :test, runtime: false}, # Project dependencies # Documentation dependencies - {:ex_doc, "~> 0.19", only: :dev, runtime: false} + {:ex_doc, "~> 0.19", only: :docs, runtime: false} ] end @@ -83,10 +83,16 @@ defmodule TypedStruct.MixProject do defp cli_env do [ - # Always run coveralls mix tasks in `:test` env. + # Run mix test.watch in `:test` env. + "test.watch": :test, + + # Always run Coveralls Mix tasks in `:test` env. coveralls: :test, "coveralls.detail": :test, - "coveralls.html": :test + "coveralls.html": :test, + + # Use a custom env for docs. + docs: :docs ] end From b654213375d45a8adcbaf09e7cae95e2fad53202 Mon Sep 17 00:00:00 2001 From: Jean-Philippe Cugnet Date: Tue, 13 Nov 2018 00:15:44 +0100 Subject: [PATCH 12/18] Cleanup the mix.exs --- mix.exs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/mix.exs b/mix.exs index 8904c6b..a1e0ad5 100644 --- a/mix.exs +++ b/mix.exs @@ -9,7 +9,6 @@ defmodule TypedStruct.MixProject do app: :typed_struct, version: @version <> dev(), elixir: "~> 1.6", - elixirc_paths: elixirc_paths(Mix.env()), start_permanent: Mix.env() == :prod, deps: deps(), @@ -34,14 +33,6 @@ defmodule TypedStruct.MixProject do ] end - def application do - [extra_applications: []] - end - - # Specifies which paths to compile per environment. - defp elixirc_paths(:test), do: ["lib", "test/support"] - defp elixirc_paths(_), do: ["lib"] - defp deps do [ # Development and test dependencies From 97919c2aab9a77f4fb413c9f9cc39540b6fa0938 Mon Sep 17 00:00:00 2001 From: Jean-Philippe Cugnet Date: Tue, 13 Nov 2018 00:27:18 +0100 Subject: [PATCH 13/18] Update the dependencies --- mix.exs | 2 +- mix.lock | 29 +++++++++++++++-------------- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/mix.exs b/mix.exs index a1e0ad5..53095bb 100644 --- a/mix.exs +++ b/mix.exs @@ -37,7 +37,7 @@ defmodule TypedStruct.MixProject do [ # Development and test dependencies {:credo, "~> 0.10.0", only: :dev, runtime: false}, - {:dialyxir, ">= 0.0.0", only: :dev, runtime: false}, + {:dialyxir, "~> 1.0-rc", only: :dev, runtime: false}, {:excoveralls, ">= 0.0.0", only: :test, runtime: false}, {:mix_test_watch, ">= 0.0.0", only: :test, runtime: false}, {:ex_unit_notifier, ">= 0.0.0", only: :test, runtime: false}, diff --git a/mix.lock b/mix.lock index 03f7b4a..adcac23 100644 --- a/mix.lock +++ b/mix.lock @@ -1,23 +1,24 @@ %{ "bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm"}, - "certifi": {:hex, :certifi, "2.3.1", "d0f424232390bf47d82da8478022301c561cf6445b5b5fb6a84d49a9e76d2639", [:rebar3], [{:parse_trans, "3.2.0", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm"}, - "credo": {:hex, :credo, "0.10.0", "66234a95effaf9067edb19fc5d0cd5c6b461ad841baac42467afed96c78e5e9e", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm"}, - "dialyxir": {:hex, :dialyxir, "0.5.1", "b331b091720fd93e878137add264bac4f644e1ddae07a70bf7062c7862c4b952", [:mix], [], "hexpm"}, + "certifi": {:hex, :certifi, "2.4.2", "75424ff0f3baaccfd34b1214184b6ef616d89e420b258bb0a5ea7d7bc628f7f0", [: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": {:hex, :dialyxir, "1.0.0-rc.4", "71b42f5ee1b7628f3e3a6565f4617dfb02d127a0499ab3e72750455e986df001", [:mix], [{:erlex, "~> 0.1", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm"}, "earmark": {:hex, :earmark, "1.2.6", "b6da42b3831458d3ecc57314dff3051b080b9b2be88c2e5aa41cd642a5b044ed", [:mix], [], "hexpm"}, + "erlex": {:hex, :erlex, "0.1.6", "c01c889363168d3fdd23f4211647d8a34c0f9a21ec726762312e08e083f3d47e", [:mix], [], "hexpm"}, "ex_doc": {:hex, :ex_doc, "0.19.1", "519bb9c19526ca51d326c060cb1778d4a9056b190086a8c6c115828eaccea6cf", [:mix], [{:earmark, "~> 1.1", [hex: :earmark, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.7", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm"}, "ex_unit_notifier": {:hex, :ex_unit_notifier, "0.1.4", "36a2dcab829f506e01bf17816590680dd1474407926d43e64c1263e627c364b8", [:mix], [], "hexpm"}, - "excoveralls": {:hex, :excoveralls, "0.10.0", "a4508bdd408829f38e7b2519f234b7fd5c83846099cda348efcb5291b081200c", [:mix], [{:hackney, "~> 1.13", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm"}, + "excoveralls": {:hex, :excoveralls, "0.10.2", "fb4abd5b8a1b9d52d35e1162e7e2ea8bfb84b47ae07c38d39aa8ce64be0b0794", [:mix], [{:hackney, "~> 1.13", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm"}, "file_system": {:hex, :file_system, "0.2.6", "fd4dc3af89b9ab1dc8ccbcc214a0e60c41f34be251d9307920748a14bf41f1d3", [:mix], [], "hexpm"}, - "hackney": {:hex, :hackney, "1.13.0", "24edc8cd2b28e1c652593833862435c80661834f6c9344e84b6a2255e7aeef03", [:rebar3], [{:certifi, "2.3.1", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "5.1.2", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "1.0.2", [hex: :mimerl, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.1", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm"}, - "idna": {:hex, :idna, "5.1.2", "e21cb58a09f0228a9e0b95eaa1217f1bcfc31a1aaa6e1fdf2f53a33f7dbd9494", [:rebar3], [{:unicode_util_compat, "0.3.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm"}, - "jason": {:hex, :jason, "1.1.1", "d3ccb840dfb06f2f90a6d335b536dd074db748b3e7f5b11ab61d239506585eb2", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm"}, - "makeup": {:hex, :makeup, "0.5.1", "966c5c2296da272d42f1de178c1d135e432662eca795d6dc12e5e8787514edf7", [:mix], [{:nimble_parsec, "~> 0.2.2", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm"}, - "makeup_elixir": {:hex, :makeup_elixir, "0.8.0", "1204a2f5b4f181775a0e456154830524cf2207cf4f9112215c05e0b76e4eca8b", [:mix], [{:makeup, "~> 0.5.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 0.2.2", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm"}, + "hackney": {:hex, :hackney, "1.14.3", "b5f6f5dcc4f1fba340762738759209e21914516df6be440d85772542d4a5e412", [:rebar3], [{:certifi, "2.4.2", [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.0.2", [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"}, + "jason": {:hex, :jason, "1.1.2", "b03dedea67a99223a2eaf9f1264ce37154564de899fd3d8b9a21b1a6fd64afe7", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm"}, + "makeup": {:hex, :makeup, "0.5.5", "9e08dfc45280c5684d771ad58159f718a7b5788596099bdfb0284597d368a882", [:mix], [{:nimble_parsec, "~> 0.4", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm"}, + "makeup_elixir": {:hex, :makeup_elixir, "0.10.0", "0f09c2ddf352887a956d84f8f7e702111122ca32fbbc84c2f0569b8b65cbf7fa", [:mix], [{:makeup, "~> 0.5.5", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm"}, "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm"}, "mimerl": {:hex, :mimerl, "1.0.2", "993f9b0e084083405ed8252b99460c4f0563e41729ab42d9074fd5e52439be88", [:rebar3], [], "hexpm"}, - "mix_test_watch": {:hex, :mix_test_watch, "0.8.0", "acf97da2abc66532e7dc1aa66a5d6c9fc4442d7992d5d7eb4faeaeb964c2580e", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}], "hexpm"}, - "nimble_parsec": {:hex, :nimble_parsec, "0.2.2", "d526b23bdceb04c7ad15b33c57c4526bf5f50aaa70c7c141b4b4624555c68259", [:mix], [], "hexpm"}, - "parse_trans": {:hex, :parse_trans, "3.2.0", "2adfa4daf80c14dc36f522cf190eb5c4ee3e28008fc6394397c16f62a26258c2", [:rebar3], [], "hexpm"}, - "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.1", "28a4d65b7f59893bc2c7de786dec1e1555bd742d336043fe644ae956c3497fbe", [:make, :rebar], [], "hexpm"}, - "unicode_util_compat": {:hex, :unicode_util_compat, "0.3.1", "a1f612a7b512638634a603c8f401892afbf99b8ce93a45041f8aaca99cadb85e", [:rebar3], [], "hexpm"}, + "mix_test_watch": {:hex, :mix_test_watch, "0.9.0", "c72132a6071261893518fa08e121e911c9358713f62794a90c95db59042af375", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}], "hexpm"}, + "nimble_parsec": {:hex, :nimble_parsec, "0.4.0", "ee261bb53214943679422be70f1658fff573c5d0b0a1ecd0f18738944f818efe", [:mix], [], "hexpm"}, + "parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "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"}, } From 9e16b804b436bcbdb864d4413f7d7430674f47bd Mon Sep 17 00:00:00 2001 From: Jean-Philippe Cugnet Date: Tue, 13 Nov 2018 00:27:32 +0100 Subject: [PATCH 14/18] Add the build status --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 1040eb1..f9302f2 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ # TypedStruct +[![Build Status](https://travis-ci.com/ejpcmac/typed_struct.svg?branch=develop)](https://travis-ci.com/ejpcmac/typed_struct) [![hex.pm version](http://img.shields.io/hexpm/v/typed_struct.svg?style=flat)](https://hex.pm/packages/typed_struct) TypedStruct is a library for defining structs with a type without writing From e71aa1ce409ff638edf842d5f429c11e6fb6bd41 Mon Sep 17 00:00:00 2001 From: Jean-Philippe Cugnet Date: Tue, 13 Nov 2018 00:57:16 +0100 Subject: [PATCH 15/18] Fix a few typos, format and cleanup the docs --- README.md | 19 +++++++------------ lib/typed_struct.ex | 19 +++++++------------ test/typed_struct_test.exs | 9 +++++---- 3 files changed, 19 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index e7831f2..86dd66d 100644 --- a/README.md +++ b/README.md @@ -145,16 +145,15 @@ defmodule MyStruct do end ``` -You can also generate an opaque type for the struct by specyfing -`opaque: true`: +You can also generate an opaque type for the struct: ```elixir defmodule MyOpaqueStruct do use TypedStruct - # Generate an opaque type for the struct + # Generate an opaque type for the struct. typedstruct opaque: true do - field :name, String.t + field :name, String.t() end end ``` @@ -296,16 +295,12 @@ case it is enforced. Both options would generate the following type: } ``` -Passing `opaque: true` replaces `@type` with `@opaque` in the struct -type specification: +Passing `opaque: true` replaces `@type` with `@opaque` in the struct type +specification: ```elixir -defmodule Example do - use TypedStruct - - typedstruct opaque: true do - field :name, String.t() - end +typedstruct opaque: true do + field :name, String.t() end # Becomes diff --git a/lib/typed_struct.ex b/lib/typed_struct.ex index 6264739..01c59fc 100644 --- a/lib/typed_struct.ex +++ b/lib/typed_struct.ex @@ -131,15 +131,14 @@ defmodule TypedStruct do end end - You can also generate an opaque type for the struct by specyfing - `opaque: true`: + You can also generate an opaque type for the struct: defmodule MyOpaqueStruct do use TypedStruct - # Generate an opaque type for the struct + # Generate an opaque type for the struct. typedstruct opaque: true do - field :name, String.t + field :name, String.t() end end @@ -262,15 +261,11 @@ defmodule TypedStruct do name: String.t() # Not nullable } - Passing `opaque: true` replaces `@type` with `@opaque` in the struct - type specification: - - defmodule Example do - use TypedStruct + Passing `opaque: true` replaces `@type` with `@opaque` in the struct type + specification: - typedstruct opaque: true do - field :name, String.t() - end + typedstruct opaque: true do + field :name, String.t() end # Becomes diff --git a/test/typed_struct_test.exs b/test/typed_struct_test.exs index 67d9f49..fa18011 100644 --- a/test/typed_struct_test.exs +++ b/test/typed_struct_test.exs @@ -40,7 +40,8 @@ defmodule TypedStructTest do @bytecode bytecode @bytecode_opaque bytecode_opaque - # standard struct name used when comparing generated types + + # Standard struct name used when comparing generated types. @standard_struct_name TypedStructTest.TestStruct ## Standard cases @@ -100,7 +101,7 @@ defmodule TypedStructTest do assert type1 == type2 end - test "generates an type for the struct if `opaque: true` is set" do + test "generates an opaque type if `opaque: true` is set" do # Define a second struct with the type expected for TestStruct. {:module, _name, bytecode_expected, _exports} = defmodule TestStruct3 do @@ -212,8 +213,8 @@ defmodule TypedStructTest do end end - # Standardises a type (removes line numbers and renames the struct - # to the standard struct name). + # Standardises a type (removes line numbers and renames the struct to the + # standard struct name). defp standardise(type_info, struct \\ @standard_struct_name) defp standardise({name, type, params}, struct) when is_tuple(type), From c3513e1ce6d9b8d5ae0b49fa4f7f0a78c47c40ea Mon Sep 17 00:00:00 2001 From: Jean-Philippe Cugnet Date: Tue, 13 Nov 2018 08:42:00 +0100 Subject: [PATCH 16/18] Fix mix test.watch config --- config/config.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/config.exs b/config/config.exs index 320735f..8559ae2 100644 --- a/config/config.exs +++ b/config/config.exs @@ -6,7 +6,7 @@ use Mix.Config # values for your application for 3rd-party users, it should be done in your # "mix.exs" file. -if Mix.env() == :dev do +if Mix.env() == :test do # Clear the console before each test run config :mix_test_watch, clear: true end From de781cded8164cdeba49c0fb9a545442302708a0 Mon Sep 17 00:00:00 2001 From: Jean-Philippe Cugnet Date: Tue, 13 Nov 2018 08:43:33 +0100 Subject: [PATCH 17/18] Bump the version to 0.1.4 --- README.md | 2 +- mix.exs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 86dd66d..1a8904d 100644 --- a/README.md +++ b/README.md @@ -82,7 +82,7 @@ Thanks to TypedStruct, this is now possible :) To use TypedStruct in your project, add this to your Mix dependencies: ```elixir -{:typed_struct, "~> 0.1.3"} +{:typed_struct, "~> 0.1.4"} ``` If you do not plan to compile modules using TypedStruct at runtime, you can add diff --git a/mix.exs b/mix.exs index 53095bb..055f303 100644 --- a/mix.exs +++ b/mix.exs @@ -7,7 +7,7 @@ defmodule TypedStruct.MixProject do def project do [ app: :typed_struct, - version: @version <> dev(), + version: @version, elixir: "~> 1.6", start_permanent: Mix.env() == :prod, deps: deps(), From e6a8572b017d668194e1de7548bbf61af21d6dbc Mon Sep 17 00:00:00 2001 From: Jean-Philippe Cugnet Date: Tue, 13 Nov 2018 08:45:08 +0100 Subject: [PATCH 18/18] Update the changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9bbc459..15d778b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## v0.1.4 + +* Add the ability to generate an opaque type (#10) + ## v0.1.3 * Fix a bug where fields with `default: false` where still enforced when setting