Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Livebook README #2

Merged
merged 6 commits into from
Jan 17, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
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.13"]
otp: ["24", "23", "22"]
os: ["ubuntu-20.04"]
include:
- { 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" }
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
257 changes: 257 additions & 0 deletions README.livemd
Original file line number Diff line number Diff line change
@@ -0,0 +1,257 @@
<!-- vim: syntax=markdown -->

<!-- livebook:{"persist_outputs":true} -->

# 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:

<!-- livebook:{"disable_formatting":true} -->

```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
}
```

<!-- livebook:{"disable_formatting":true} -->

```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

<!-- livebook:{"disable_formatting":true} -->

```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

<!-- livebook:{"disable_formatting":true} -->

```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

<!-- livebook:{"disable_formatting":true} -->

```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

<!-- livebook:{"disable_formatting":true} -->

```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

<!-- livebook:{"disable_formatting":true} -->

```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"]
```
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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%2Fgit.luolix.top%2Fmsaraiva%2Fexsamples%2Fblob%2Fmaster%2FREADME.livemd)

```Elixir
import ExSamples

Expand Down
13 changes: 12 additions & 1 deletion lib/samples.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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}]}
Expand Down
2 changes: 1 addition & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -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()]
Expand Down