From 695b2a8c854defc8ad222ed3a798415d1a7e144a Mon Sep 17 00:00:00 2001 From: Tiago Moraes Date: Sun, 9 Jan 2022 12:04:22 -0300 Subject: [PATCH 1/6] require elixir 1.5 --- mix.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mix.exs b/mix.exs index 3e76001..98159c7 100644 --- a/mix.exs +++ b/mix.exs @@ -4,7 +4,7 @@ defmodule ExSamples.Mixfile do def project do [app: :exsamples, version: "0.1.0", - elixir: "~> 1.1", + elixir: "~> 1.5", description: description(), package: package(), deps: deps()] From 28664482d259e216d3ce45ad40b1756e9b7296d3 Mon Sep 17 00:00:00 2001 From: Tiago Moraes Date: Sun, 9 Jan 2022 12:05:01 -0300 Subject: [PATCH 2/6] add github actions CI --- .github/workflows/ci.yml | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..21ff2bc --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,23 @@ +name: CI +on: [push, pull_request] +jobs: + build: + strategy: + matrix: + # https://hexdocs.pm/elixir/compatibility-and-deprecations.html + # https://github.com/erlef/setup-beam#compatibility-between-operating-system-and-erlangotp + elixir: ["1.5"] + otp: ["20"] + os: ["ubuntu-20.04"] + include: + - { elixir: "1.5", otp: "19", os: "ubuntu-18.04" } + - { elixir: "1.5", otp: "18", os: "ubuntu-18.04" } + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v2 + - uses: erlef/setup-beam@v1.9.0 + with: + otp-version: ${{ matrix.otp }} + elixir-version: ${{ matrix.elixir }} + - run: mix compile --warnings-as-errors + - run: mix test From c48de12cef05104f80f789a2adec6b0e9940a34d Mon Sep 17 00:00:00 2001 From: Tiago Moraes Date: Sun, 9 Jan 2022 12:08:21 -0300 Subject: [PATCH 3/6] add elixir 1.6 to CI --- .github/workflows/ci.yml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 21ff2bc..9ff4b1a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,12 +6,14 @@ jobs: matrix: # https://hexdocs.pm/elixir/compatibility-and-deprecations.html # https://github.com/erlef/setup-beam#compatibility-between-operating-system-and-erlangotp - elixir: ["1.5"] - otp: ["20"] + elixir: ["1.6"] + otp: ["21", "20", "19"] os: ["ubuntu-20.04"] include: - - { elixir: "1.5", otp: "19", os: "ubuntu-18.04" } - - { elixir: "1.5", otp: "18", os: "ubuntu-18.04" } + - { elixir: "1.6", otp: "19", os: "ubuntu-18.04" } + - { elixir: "1.5", otp: "20", os: "ubuntu-20.04" } + exclude: + - { otp: "19", os: "ubuntu-20.04" } runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v2 From 993423c88c09b07e43af6dd84d8ea52055e3f56e Mon Sep 17 00:00:00 2001 From: Tiago Moraes Date: Sun, 9 Jan 2022 12:14:02 -0300 Subject: [PATCH 4/6] fix for elixir 1.6 --- lib/samples.ex | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/lib/samples.ex b/lib/samples.ex index f976153..e80e1e0 100644 --- a/lib/samples.ex +++ b/lib/samples.ex @@ -33,7 +33,7 @@ defmodule Samples do end defp slice_table(table) do - [header|rows] = table + [header|rows] = extract_header_rows(table) {type, fields} = extract_type_and_fields(header) {vars, fields_values} = extract_vars_and_fields_values(type, rows) @@ -61,10 +61,17 @@ defmodule Samples do end end + defp extract_header_rows([]), do: [[nil]] + defp extract_header_rows(table), do: table + def extract_type_and_fields([type = {atom, _, []}|fields]) when atom == :%{} do {type, fields} end + def extract_type_and_fields([{:__aliases__, _, [_]} = type | fields]) do + {type, fields} + end + def extract_type_and_fields(fields = [{field, [_], _}|_]) when is_atom(field) do {nil, Enum.map(fields, fn {field, [_], _} -> field end)} end @@ -98,6 +105,10 @@ defmodule Samples do {:%, [], [{:__aliases__, [], [module]}, {:%{}, [], value}]} end + defp replace_value({:__aliases__, [line: _], [module]}, value) do + {:%, [], [{:__aliases__, [], [module]}, {:%{}, [], value}]} + end + # As structs defp replace_value({:%, meta, [lhs, {:%{}, _, _value}]}, value) do {:%, meta, [lhs, {:%{}, [], value}]} From d162b9b5f27c69149a8b471f66aab811f7796e69 Mon Sep 17 00:00:00 2001 From: Tiago Moraes Date: Sun, 9 Jan 2022 12:16:01 -0300 Subject: [PATCH 5/6] add elixir 1.7-1.13 to CI --- .github/workflows/ci.yml | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9ff4b1a..7bbad67 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,14 +6,18 @@ jobs: matrix: # https://hexdocs.pm/elixir/compatibility-and-deprecations.html # https://github.com/erlef/setup-beam#compatibility-between-operating-system-and-erlangotp - elixir: ["1.6"] - otp: ["21", "20", "19"] + elixir: ["1.13"] + otp: ["24", "23", "22"] os: ["ubuntu-20.04"] include: - - { elixir: "1.6", otp: "19", os: "ubuntu-18.04" } + - { elixir: "1.12", otp: "24", os: "ubuntu-20.04" } + - { elixir: "1.11", otp: "24", os: "ubuntu-20.04" } + - { elixir: "1.10", otp: "23", os: "ubuntu-20.04" } + - { elixir: "1.9", otp: "22", os: "ubuntu-20.04" } + - { elixir: "1.8", otp: "22", os: "ubuntu-20.04" } + - { elixir: "1.7", otp: "22", os: "ubuntu-20.04" } + - { elixir: "1.6", otp: "21", os: "ubuntu-20.04" } - { elixir: "1.5", otp: "20", os: "ubuntu-20.04" } - exclude: - - { otp: "19", os: "ubuntu-20.04" } runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v2 From ea8c4891a994ceee9e394e95fddcd5af93b9394e Mon Sep 17 00:00:00 2001 From: Tiago Moraes Date: Sun, 9 Jan 2022 14:37:31 -0300 Subject: [PATCH 6/6] add README Livebook --- README.livemd | 257 ++++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 4 + 2 files changed, 261 insertions(+) create mode 100644 README.livemd diff --git a/README.livemd b/README.livemd new file mode 100644 index 0000000..4cf2d9d --- /dev/null +++ b/README.livemd @@ -0,0 +1,257 @@ + + + + +# ExSamples Guide + +## Setup + +```elixir +Mix.install([ + :exsamples +]) +``` + +```output +:ok +``` + +## Usage + +Initializes lists of maps, structs or keyword lists using tabular data in Elixir. + +ExSamples helps you to describe data of the same type in a more **compact** and **readable** way. Specially useful when defining sample data (e.g. for tests). Here is an example: + + + +```elixir +import ExSamples + +countries = samples do + :id | :name | :currency | :language | :population + 1 | "Brazil" | "Real (BRL)" | "Portuguese" | 204_451_000 + 2 | "United States" | "United States Dollar (USD)" | "English" | 321_605_012 + 3 | "Austria" | "Euro (EUR)" | "German" | 8_623_073 + 4 | "Sweden" | "Swedish krona (SEK)" | "Swedish" | 9_801_616 +end + +countries |> Enum.at(1) +``` + +```output +%{ + currency: "United States Dollar (USD)", + id: 2, + language: "English", + name: "United States", + population: 321605012 +} +``` + + + +```elixir +import ExSamples + +users = + samples do + :name | :country | :city | :admin + "Christian" | "United States" | "New York City" | false + "Peter" | "Germany" | "Berlin" | true + "José" | "Brazil" | "São Paulo" | false + "Ingrid" | "Austria" | "Salzburg" | false + "Lucas" | "Brazil" | "Fortaleza" | true + end +``` + +```output +[ + %{admin: false, city: "New York City", country: "United States", name: "Christian"}, + %{admin: true, city: "Berlin", country: "Germany", name: "Peter"}, + %{admin: false, city: "São Paulo", country: "Brazil", name: "José"}, + %{admin: false, city: "Salzburg", country: "Austria", name: "Ingrid"}, + %{admin: true, city: "Fortaleza", country: "Brazil", name: "Lucas"} +] +``` + +As you can see, after macro expansion you get a regular list. + +You can use `for` comprehensions for mapping and filtering your data just like with any other Enumerable. + +```elixir +for %{name: name, country: country, city: city} <- users, country == "Brazil" do + {name, city} +end +``` + +```output +[{"José", "São Paulo"}, {"Lucas", "Fortaleza"}] +``` + +## Data Types + +By default `samples` initializes a list of maps. But you can also define structs and keyword lists. + +### Initializing structs + + + +```elixir +import ExSamples + +defmodule Country do + defstruct [:id, :name, :currency, :language, :population] +end +``` + +```output +{:module, Country, <<70, 79, 82, 49, 0, 0, 7, ...>>, + %Country{currency: nil, id: nil, language: nil, name: nil, population: nil}} +``` + +```elixir +samples as: Country do + :id | :name | :currency | :language | :population + 1 | "Brazil" | "Real (BRL)" | "Portuguese" | 204_451_000 + 2 | "United States" | "United States Dollar (USD)" | "English" | 321_605_012 +end +``` + +```output +[ + %Country{ + currency: "Real (BRL)", + id: 1, + language: "Portuguese", + name: "Brazil", + population: 204451000 + }, + %Country{ + currency: "United States Dollar (USD)", + id: 2, + language: "English", + name: "United States", + population: 321605012 + } +] +``` + +### Initializing keyword lists + + + +```elixir +import ExSamples + +samples as: [] do + :id | :name | :currency | :language | :population + 3 | "Austria" | "Euro (EUR)" | "German" | 8_623_073 + 4 | "Sweden" | "Swedish krona (SEK)" | "Swedish" | 9_801_616 +end +``` + +```output +[ + [id: 3, name: "Austria", currency: "Euro (EUR)", language: "German", population: 8623073], + [id: 4, name: "Sweden", currency: "Swedish krona (SEK)", language: "Swedish", population: 9801616] +] +``` + +### Assigning variables as structs + + + +```elixir +import ExSamples + +defmodule Country do + defstruct [:name, :currency, :language] +end + +defmodule User do + defstruct [:id, :name, :country, :admin, :last_login] +end +``` + +```output +{:module, User, <<70, 79, 82, 49, 0, 0, 7, ...>>, + %User{admin: nil, country: nil, id: nil, last_login: nil, name: nil}} +``` + +```elixir +samples do + Country | :name | :currency | :language + country1 | "Brazil" | "Real (BRL)" | "Portuguese" + country2 | "United States" | "United States Dollar (USD)" | "English" + country3 | "Austria" | "Euro (EUR)" | "German" +end + +samples do + User | :id | :name | :country | :admin | :last_login + user1 | 16 | "Lucas" | country1 | false | {2015, 10, 08} + user2 | 327 | "Ingrid" | country3 | true | {2014, 09, 12} + user3 | 34 | "Christian" | country2 | false | {2015, 01, 24} +end + +user1 +``` + +```output +%User{ + admin: false, + country: %Country{currency: "Real (BRL)", language: "Portuguese", name: "Brazil"}, + id: 16, + last_login: {2015, 10, 8}, + name: "Lucas" +} +``` + +```elixir +IO.puts("Name: #{user1.name}, Country: #{user1.country.name}") +``` + +```output +Name: Lucas, Country: Brazil +``` + +```output +:ok +``` + +### Assigning variables as maps + + + +```elixir +import ExSamples + +samples do + %{} | :name | :country | :city + user1 | "Christian" | "United States" | "New York City" + user2 | "Ingrid" | "Austria" | "Salzburg" +end + +user1 +``` + +```output +%{city: "New York City", country: "United States", name: "Christian"} +``` + +### Assigning variables as keyword lists + + + +```elixir +samples do + [] | :name | :country | :city + user1 | "Christian" | "United States" | "New York City" + user2 | "Ingrid" | "Austria" | "Salzburg" +end + +user1 +``` + +```output +[name: "Christian", country: "United States", city: "New York City"] +``` diff --git a/README.md b/README.md index 88bdcd1..72c2ce8 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,10 @@ Run `mix deps.get`. ## Usage +You can see it in action with [livebook](https://livebook.dev/) with [README.livemd](README.livemd) + +[![Run in Livebook](https://livebook.dev/badge/v1/blue.svg)](https://livebook.dev/run?url=https%3A%2F%2Fgithub.com%2Fmsaraiva%2Fexsamples%2Fblob%2Fmaster%2FREADME.livemd) + ```Elixir import ExSamples