Today I Learned

hashrocket A Hashrocket project

270 posts about #elixir surprise

Elixir Data Type Comparison

Elixir has an interesting defined sorting order for its data types. The order is as follows:

number < atom < reference < function < port < pid < tuple < map < list < bitstring

For example:

random_data = [["Hello World"], {:hello, :world}, 16, %{key: "value"}, :atom]
Enum.sort(random_data)
#=> [16, :atom, {:hello, :world}, %{key: "value"}, ["Hello World"]]

Or even

4 < :atom
#=> true

Unix Timestamps in Elixir

Unix timestamps are a very simple way to compare times in an integer format. They are the total seconds elapsed since January 1st, 1970 UTC (The Unix Epoch).

In elixir, if you want to do some operations on a DateTime struct, and want to keep things simple, you can convert a DateTime struct to an integer with to_unix/2. By default, it will use seconds as the unit.

How to get javascript params in LiveView?

I found out that Phoenix LiveView has a function get_connect_params to be used when you want to get some value sent at the connection time from the javascript.

This way I can send params like:

// app.js
const params = {
  _csrf_token: csrfToken,
  timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
};

const liveSocket = new LiveSocket('/live', Socket, { params });

And get those like on my LiveView:

# my_live_view.ex
@impl true
def mount(_params, _session, socket) do
  tz = get_connect_params(socket)["timezone"] || "America/New_York"
  {:ok, assign(socket, :timezone, tz)}
end

Skip hidden fields on Phoenix inputs_for

I just found out that Phoenix Component inputs_for accepts a skip_hidden attr which was very useful today. We have a has_many relationship that we are using <.inputs_for> to decompose the nested fields and as it was generating some hidden fields the style was breaking.

We are actually using a Tailwind divide on the parent tag and those hidden fields were adding extra separation borders. A way to solve that was calling <.inputs_for twice as this:

<div>

  <.inputs_for field={@form[:posts]} />

  <div class="divide-y">
    <.inputs_for :let={form} field={@form[:posts]} skip_hidden>
      <.input type="text" field={form[:title]} label="Title" />
    </.inputs_for>
  </div>
</div>

Macro Guard Clauses in Elixir

Have you ever wanted to pass your own custom function into a guard clause?

Lets start by looking at a super basic guard clause here:

def greater_than_10(num) when num > 10, do: true
def greater_than_10(_), do: false

Let's say we want to get more specific with the operation inside of the when guard. This is overkill for our situation, but lets say we want the when to use a custom function of ours instead of a basic operation, but when guards don't allow you to directly pass functions to them.

Elixir has a fancy macro called defguard that allows you to define custom guards like functions. Let's change our function up and define a guard that checks if our argument is both a integer and even.

defmodule Guards do
  defguard is_even_num(num) when is_integer(value) and rem(value, 2) == 0
end

Let's use that guard in our function

import Guards

def even_num_greater_than_10(num) when is_even_num(num) do
  case num > 10 do
    true -> true
    _ -> false
  end
end

Even though this example is a bit overkill, the option to use custom guards is a cool elixir feature.

Casting Associations in Phoenix

If you want to cast changes on a struct's associations, you can use the changeset function cast_assoc/3. This allows you to make changes to a parent struct and its associations at the same time.

Example

Lets say you have a struct called Artist and an associated schema called Album. The schema for the Album looks something like this:

def module MyApp.Music.Album do
 use Ecto.Schema
 alias MyApp.Music.Artist

  schema "albums" do
    field :title, :string
    
    belongs_to :artist, Artist
  end

  def changeset(artist, params \\ %{}) do
    artist
    |> cast(params, [:title, :artist_id])
    |> validate_required([:title])
  end
end

and the schema for artist looks like this:

def module MyApp.Music.Artist do
 use Ecto.Schema
 alias MyApp.Music.Album

  schema "albums" do
    field :name, :string
    
    has_many :albums, Album
  end
end

Now, if you wanted to build a changeset for the Artist where you can change the artist's name and the title field on an associated album:

def changeset(artist, params \\ %{}) do
    artist
    |> cast(params, [:name])
    |> cast_assoc(:albums, with: &MyApp.Album.changeset/2)
    |> validate_required([:name])
end

Convert Changeset Into Schema With `apply_changes`

Often times, it's necessary to perform some intermediate operations on a Ecto changeset before saving it to the database. It can be easier to deal with the underlying schema. And you might need the entire schema rather than only the changes in the current changeset.

You can use the apply_changes/1 function to apply that changeset and receive schema with which you can perform operations. It's worth noting that the data is not validated when changes are applied, so care needs to be taken to ensure validity before an attempt to save that record.

iex> lead_schema = Lead.changeset(%{name: "Andrew", email: "Andrew.Vogel@hashrocket.com"})
                   |> apply_changes() 
%Lead{name: "Andrew", email: "Andrew.Vogel@hashrocket.com"}

iex> lead_schema |> downcase_email() |> do_some_other_processing()

https://hexdocs.pm/ecto/Ecto.Changeset.html#apply_changes/1

Phoenix LiveView slot attributes

Today I learned how to define an attribute on a Phoenix LiveView slot using a do block:

slot :column
  attr :field, :atom
  attr :sortable, :boolean, default: false
end

def table(assigns) do
  ~H"""
  <table>
    <tr>
      <th :for={col <- @column}>
        <%= col.label %>
        <.sort_by :if={col.sortable} field={col.field}/>
      </th>
    </tr>
  </table>
  """
end

This way we can use our slots with attributes:

def render(assigns) do
  ~H"""
    <.table>
      <:colum field={:name} sortable>Name</:colum>
      <:colum field={:price}>Price</:colum>
      <:colum>Actions</:colum>
      ...
    </.table>
  """
end

Check this out

Pattern Matching Args with Exact Values

In elixir there is a handy trick for pattern matching for exact values. Let's say we have a function head_match that takes two arguments, a string and a list, that checks if the string argument is the same as the head of the list. We could use pattern matching like this:

def head_match(head, [head, _tail]) do
  IO.puts("Thats the head of the List!")
end
#this will match if we called it like head_match("first_word", ["first_word", "second_word"])

See how we named the first argument head and used that same name as the list head? This means that the function will only match if those two values are the exact same rather than just matching the structure of the arguments.

Shorthand Elixir Anonymous Functions

Elixir has a really cool syntax for writing anonymous functions (unnamed functions). It goes like this:

# The '&' operator is used to define the function and its arguments
putter = &(IO.puts &1)
putter.("Today I learned")
#...> Today I learned
#...> :ok

&1 refers to the functions first argument; in this example, we used "Today I learned". You can use more arguments with &2, &3, etc... For example:

combiner = &(&1 <> &2)
combiner.("Foo", "Bar")
#...> "FooBar"

Also notice that we are not calling either of these functions like function(arg1, arg2), instead we are calling them like function.(arg1, arg2). We have to use . because we are not actually naming these functions, we are only assigning them a reference, hence "anonymous" functions.

Pattern Match Keyword List in Function Def

TIL that you can pattern match a keyword list in a function definition.

Sometimes you'll receive the last argument as an empty keyword list; in this case, we're calling it opts. You can pattern match key-values by defining the shape in a function.

  defmodule Example do
    def hello(opts \\ [])
 
   # will match when message is the only keyword included in opts
    def hello([message: message]) do
      IO.puts("Hello #{message}")
    end

    # will match when there are multiple keywords but message is the first
    def hello([{:message, message} | _rest] = opts) do
      IO.puts("Hello #{message}")
    end
  end

> Example.hello(message: "World")
Hello World
:ok

# could also call in the more verbose way
> Example.hello([message: "World"])
Hello World
:ok

# :message but with other args after
> Example.hello(message: "World", something_else: "hi")
Hello World
:ok

Note: As a TIL reader pointed out, pattern matching Keywords will make your function args order dependent. The following would not work:

> Example.hello(foo: "bar", message: "World")
** (FunctionClauseError) no function clause matching in Example.hello/1 

If you need them to be order independent, use a map or just match on single argument, then check for each option appropriately with the Keyword module.

https://elixir-lang.org/getting-started/keywords-and-maps.html

Composite Primary Keys using Elixir Ecto

Ecto allows us to map a table with composite primary keys into our schemas. The way to do that is by using the primary_key: true option for the Ecto.Schema.field/3 instead of dealing with the special module attr @primary_key. Check this out:

defmodule MyApp.OrderItem do
  use Ecto.Schema

  @primary_key false
  schema "order_items" do
    field :product_id, :integer, primary_key: true
    field :order_id, :integer, primary_key: true
    field :quantity, :integer
  end
end

Identity Primary Keys on Ecto

Ecto, allow the type :identity to be used since 3.5 which is cool:

create table(:user, primary_key: false) do
  add :id, :identity, primary_key: true
  add :name, :string, null: false

  timestamps()
end

That generates this SQL:

CREATE TABLE public.users (
    id bigint NOT NULL,
    name character varying(255) NOT NULL,
    inserted_at timestamp(0) without time zone NOT NULL,
    updated_at timestamp(0) without time zone NOT NULL
);

ALTER TABLE public.users ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY (
    SEQUENCE NAME public.users_id_seq
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1
);

The only issue is that there's no option to change from BY DEFAULT to ALWAYS 😕

Phoenix component attr definition

The new Phoenix.Component attr/3 function is awesome. It does compile time validations to all the places that are calling the component and also it comes with nice and useful options. For instance the default option:

attr :name, :string, default: "there"

def greet(assigns) do
  ~H"""
  <h1>Hey <%= @name %>!</h1>
  """
end

That's very useful. That would totally replace most usages of assign_new like I used to do:

def greet(assigns) do
  assigns = assign_new(assigns, :name, fn -> "there" end)

  ~H"""
  <h1>Hey <%= @name %>!</h1>
  """
end

This way we can call:

<.greet />

And have this html generated:

<h1>Hey there!</h1>

Migrating Data in Ecto

I usually have created a mix task for data migrations to avoid putting the app down, but today I learned that ecto migrations accept an arg --migrations-path to their commands which allow us to have 2 separate folders for migrations.

With that we can easily use the default priv/repo/migrations folder for automatic migrations (for running on deployments) and a separate folder, let's say priv/repo/data_migrations that we can run when it's more convenient.

So in prod we run migrations on deploy and data_migrations on a quite time for the app to avoid downtime. And in dev and test env we just run them all as we usually have smaller dataset, so not a big deal.

Here's a good post about this strategy.

Use created_at in Ecto

You can use created_at in Ecto/phoenix app with timestamps/1. When migrating data from a rails application to a phoenix application you will have many tables with a created_at column.

defmodule Phoenix.Accounts.User do
  use Ecto.Schema
  import Ecto.Changeset

  schema "users" do
    field :email, :string
    field :password, :string, virtual: true, redact: true
    field :hashed_password, :string, redact: true
    field :confirmed_at, :utc_datetime

    timestamps(inserted_at: :created_at, type: :utc_datetime)
  end

end

Elixir Compilation Cycles with `--fail-above`

Elixir 1.13.0 introduced a --fail-above flag for the mix xref task which will fail that task execution under a certain criteria.

With that we can verify, for example, that our code is ok to have 1 cycle that goes over length of 4, but not 2 cycles. Let's see how it works:

$ mix xref graph --format cycles --min-cycle-size 4 --fail-above 1
2 cycles found. Showing them in decreasing size:

Cycle of length 6:

    lib/my_app_web/endpoint.ex
    lib/my_app_web/router.ex
    lib/my_app_web/views/layout_view.ex
    lib/my_app_web/live/components/sorting_link.ex
    lib/my_app_web/live/components/icon.ex
    lib/my_app_web/endpoint.ex

Cycle of length 5:

    lib/my_app_web/live/components/sorting_link.ex
    lib/my_app_web/live/components/icon.ex
    lib/my_app_web/router.ex
    lib/my_app_web/views/layout_view.ex
    lib/my_app_web/live/components/sorting_link.ex

** (Mix) Too many cycles (found: 2, permitted: 1)

In this case xref found 2 cycles with a length greater than 4, and as I allowed only 1 then we can see the error.

Elixir __struct__/0

When we define a struct via defstruct macro we end up getting a __struct__/0 function on both struct module definition and on each struct map. The intriguing part is that the implementation of the module and the map are different, check this out:

iex(1)> defmodule Book do
...(1)>   defstruct title: nil, pages_count: 0
...(1)> end

iex(2)> Book.__struct__()
%Book{pages_count: 0, title: nil}

iex(3)> Book.__struct__().__struct__()
Book

As we can see Book.__struct__() returns a new %Book{} struct with its defaults, meanwhile %Book{}.__struct() returns the Book module.

Elixir IEX multi-line command

A change on Elixir 1.12.0 made possible to pipe |> multi-line commands in iex where the |> operator is in the beginning of new lines.

That means that we can:

iex(1)> :foo
:foo
iex(2)>       |> to_string()
"foo"
iex(3)>       |> String.upcase()
"FOO"

The docs also mention that all other binary operators works the same way, except +/2 and -/2, so that's also valid:

iex(1)> [:foo]
[:foo]
iex(2)> ++ [:bar]
[:foo, :bar]
iex(3)> |> Enum.join(" ")
"foo bar"
iex(4)> |> String.upcase()
"FOO BAR"

Each line will run at a new command, so if you assign a variable in the first line you may not have what you expect, so watch out.

Phoenix Live View enable Profiling

Phoenix LiveView has a way to enable Profiling in the client side by just adding this into the app.js file:

// app.js
liveSocket.enableProfiling();

That will enable a log into your browser console such as:

toString diff (update): 1.224853515625 ms
premorph container prep: 0.006103515625 ms
morphdom: 397.676025390625 ms
full patch complete: 411.117919921875 ms

In this case we can see that the morphdom library is taking a considerable time to apply my DOM patches as my page has a huge html table full of data.

BTW, this function adds to 2 other very useful ones for debugging the client:

  • enableDebug ()
  • enableLatencySim(ms)

Get the Values for a Ecto Schema Enum Column

I recently started learning Elixir and had a model with an enum column with the following attributes:

 schema "keyboards" do
   field :nickname, :string
   field :form_factor, Ecto.Enum, values: [:macro, :num, :custom, :split, :forty, :sixty, :sixty_five, :seventy_five, :tkl, :full]

   timestamps()
 end

In my view, I had a form object where I wanted to have a select input with the values from my enum column, form_factor. Luckily, the Ecto.Enum module has a few functions that can help with this - mappings/2, values/2, and dump_values/2.

I ended up using the following in my form:

  <%= label f, :form_factor %>
  <%= select f, :form_factor, Ecto.Enum.mappings(Keyboard, :form_factor)  %>
  <%= error_tag f, :form_factor %>

https://hexdocs.pm/ecto/Ecto.Enum.html

Get Elixir GenServer current state

Today I learned that we can use :sys.get_state/1 to get the current state of a GenServer.

Check this out:

iex(1)> pid = Process.whereis(MyApp.Repo)
iex(2)> :sys.get_state(pid)

{:state, {:local, MyApp.Repo}, :one_for_one,
 {[DBConnection.ConnectionPool],
  %{
    DBConnection.ConnectionPool => {:child, #PID<0.421.0>,
     DBConnection.ConnectionPool,
     {Ecto.Repo.Supervisor, :start_child,
      [
        {DBConnection.ConnectionPool, :start_link,
         [
           {Postgrex.Protocol,
            [
              types: Postgrex.DefaultTypes,
              repo: MyApp.Repo,
              telemetry_prefix: [:my_app, :repo],
              otp_app: :my_app,
              timeout: 15000,
              database: "my_app_dev",
              hostname: "localhost",
              port: 5432,
              show_sensitive_data_on_connection_error: true,
              pool_size: 10,
              pool: DBConnection.ConnectionPool
            ]}
         ]},
        MyApp.Repo,
        Ecto.Adapters.Postgres,
        %{
          cache: #Reference<0.1645792067.2416050178.91334>,
          opts: [
            timeout: 15000,
            pool_size: 10,
            pool: DBConnection.ConnectionPool
          ],
          repo: MyApp.Repo,
          sql: Ecto.Adapters.Postgres.Connection,
          telemetry: {MyApp.Repo, :debug, [:my_app, :repo, :query]}
        }
      ]}, :permanent, false, 5000, :worker, [Ecto.Repo.Supervisor]}
  }}, :undefined, 0, 5, [], 0, :never, Ecto.Repo.Supervisor,
 {MyApp.Repo, MyApp.Repo, :my_app, Ecto.Adapters.Postgres, []}}

Split a string in elixir

You can split a string in elixir with String.split

defmodule Phoenix.Repo.Migrations.CreateUsers do
  use Ecto.Migration

  def up do
    execute_sql("""
      create extension citext;

      create table users (
        id bigint generated by default as identity primary key,
        email citext unique not null,
        inserted_at timestamptz not null default now(),
        updated_at timestamptz not null default now()
      );
    """)
  end

  def down do
    execute_sql("""
      drop table users;
      drop extension citext;
    """)
  end
   

  def execute_sql(sql_statements) do
     sql_statements
     |> String.split(";")
     |> Enum.filter(fn s -> String.trim(s) != "" end)
     |> Enum.each(&execute/1)
  end
end

Elixir: Sandboxing an `iex` session

We can change the default .iex.exs file for another to be pre-loaded by iex using --dot-iex:

MIX_ENV=test iex --dot-iex .iex-sandbox.exs -S mix

This way we could create a rudimentary way to "protect" our existing database of changes on the iex session. Check this out:

# .iex-sandbox.exs
:ok = Ecto.Adapters.SQL.Sandbox.checkout(MyApp.Repo, ownership_timeout: 300_000)

This approach works for existing databases, so if the app we are working has a very difficult data setup then maybe this is the way to go. Although, making the db to hold data changes for as long as iex sessions are running it can be a bit too much in terms of db memory. Row & Table locks can definately be a problem as well.

Another important note is that we may have to call Sandbox.allow/3 if we need to call a GenServer or another process that touches the db.

The way to go

If we have a simpler data, with a nice seeds file, we'd be much better this way:

(export DB_NAME=my_app_sandbox && mix ecto.reset && iex -S mix)

Don't forget to change our ecto config to use that ENV var.

Hibernating a GenServer

I was reading a great blog post from @cloud8421 this morning and I learned that Elixir GenServer has the capability to hibernate itself, which it means that it halts the continuous looping if there's no incoming message and it runs a garbage collection to release some memory generated by that process.

Use that with moderation, and it's always nice to evaluate if we really need to put a GenServer to hibernate, but in some cases it's very handy.

How to force reload associations in Ecto

Ecto by default doesn't load associations and you have to explicitly call preload:

id = 1
user = Repo.get(User, id) |> Repo.preload(:posts)

If you call preload again on the same user, Ecto won't make the SQL query again because the association is already loaded:

user = user |> Repo.preload(:posts)

But in some cases you do want to reload a preloaded association then you can use force: true:

user = user |> Repo.preload(:posts, force: true)

Disable capture in Elixir Regex

Today I came across a regex that had to use the ( parenthesis to match a sequence of chars but I wanted just to match them, so I'd love if I could ignore that piece from the "capture" part.

So I learned that we could use ?: in the beginning of the ( and that would turn the capture off for that piece. Check this out;

iex> Regex.scan(~r/aa(bb)(?:cc|CC)/, "aabbccdd aabbCCdd")
[["aabbcc", "bb"], ["aabbCC", "bb"]]

In this case I am matching and capturing the bb sequence and I am matching but not capturing the cc|CC sequence options.

Preloading data for Phoenix LiveView components

One of the most common problems in web development is N+1. Graphql has taught me the DataLoader pattern and since then I never had to worry about N+1 as long as I kept using that pattern.

With Phoenix LiveView you don't need to think about APIs anymore but I'm glad that it has support for preloading data kind of the same way as if it was a DataLoader. You can define a preload function that will receive a list of all the assigns so you can preload all the data needed for multiple components in one single batch before they are mounted. Here is the sequence of a component lifecycle:

preload(list_of_assigns) -> mount(socket) -> update(assigns, socket) -> render(assigns)

On this example only one SQL query is made to load all products by id:

@impl true
def preload(list_of_assigns) do
  product_ids = Enum.map(list_of_assigns, & &1.product_id)

  products =
    from(p in Product, where: p.id in ^product_ids, select: {p.id, p})
    |> Repo.all()
    |> Map.new()

  Enum.map(list_of_assigns, fn assigns ->
    Map.put(assigns, :product, products[assigns.product_id])
  end)
end

Elixir Supervisor default child_spec

When using Supervisor elixir already declares a child_spec/1 for us with the right type: :supervisor. The generated code will be something like:

defmodule MyApp.MySupervisor do
  use Supervisor, opts
  
  ...

  def child_spec(init_arg) do
    default = %{
      id: __MODULE__,
      start: {__MODULE__, :start_link, [init_arg]},
      type: :supervisor
    }

    Supervisor.child_spec(default, unquote(Macro.escape(opts)))
  end
end

Keep in mind that's a pseudo-code as I left the Macro.escape for better understanding of the "expanded" code that happens on the __using__/1 function.

I tried to get this info from the docs, which I could not find that easily so I dig into the code, and as a good surprise this was so easy to find. Check this out.

By the way, the generated code is very similar to the GenServer one for the same function, the only difference is that on the GenServer there's no :type key, and it works fine because the default value for the :type key is :worker.

Using `@describetag` in Elixir Tests

Wow! ExUnit have some special tags for @moduletag and @describetag which are self explanatory if you know what tags are used for in ExUnit. They are so useful in case you want to, for example, only run that section of tests:

defmodule MyApp.AccountsTest do
  use MyApp.DataCase

  @moduletag :module_tag

  describe "search" do
    @describetag :describe_tag

    @tag :test_tag
    test "return all users" do
      user_1 = insert!(User)
      user_2 = insert!(User)

      assert Accounts.search(User) == [user_1, user_2]
    end
    ...
  end
  ...
end

In case you want to run only tests that match with a module (file) level @moduletag:

my_app git:(main) ✗ mix test --only my_module_tag
Excluding tags: [:test]
Including tags: [:my_module_tag]
............
Finished in 0.2 seconds
22 tests, 0 failures, 10 excluded

Or if we want to run all tests that match with a describe block tagged with @describetag:

my_app git:(main) ✗ mix test --only my_describe_tag
Excluding tags: [:test]
Including tags: [:my_describe_tag]
....
Finished in 0.2 seconds
22 tests, 0 failures, 18 excluded

We can see by the number of excluded tests that this works great as expected.

Phoenix LiveView with some JSX flavor

Today I learned from Vinny about this Elixir library called Surface that allows you to write LiveView components that look a lot like JSX from React.

You first create a module that uses Surface.Component and define some props:

defmodule MyApp.WelcomeMessage do
  use Surface.Component

  prop name, :string, required: true

  def render(assigns) do
    ~F"""
    <div class="title">Hello, <strong>{@name}!</strong><div>
    """
  end
end

Then your LiveView module needs to use Surface.LiveView and you are ready to render that new "JSX" component passing some props:

defmodule MyApp.ExampleLive do
  use Surface.LiveView
  alias MyApp.WelcomeMessage

  def render(assigns) do
    ~F"""
    <div class="container">
      <WelcomeMessage name="Vinny" />
    </div>
    """
  end
end

Surface also handles state and has a way to handle portals, which are called slots. I might play with this library in one of my side-projects.

Nested attributes in Phoenix (Live) Views:

To use nested attributes in a form you have to use the inputs_for helper:

<%= f = form_for @changeset, Routes.user_path(@socket, :create), phx_submit: :submit, as: :user %>
  <%= input f, :name %>
  <%= inputs_for f, :address, fn a -> %>
    <%= input a, :city %>
  	<%= input a, :zipcode %>
	<% end %><
  <%= submit "Create" %>
</form>

And then you can use cast_assoc and specify the changeset for the nested schema you want to validate:

defmodule MyApp.User do
  def registration_changeset(user, attrs, opts \\ []) do
    user
      |> cast(attrs, [:name])
      |> cast_assoc(:address, with: &MyApp.Address.changeset/2)
      |> validate_required([:name, :address])
  end
end

defmodule MyApp.Address do
  def changeset(address, attrs, opts \\ []) do
    user
      |> cast(attrs, [:city, :zipcode])
      |> validate_required([:city, :zipcode])
  end
end

Session cookie with Phoenix LiveView

You can use phx-trigger-action to make sure a form submit goes through http instead through the socket.

On the example below, when @trigger_submit is true then the form will be sumitted to the path Routes.user_session_path(@socket, :create), which will hit the controller and set the cookie:

<%= f = form_for @changeset, Routes.user_session_path(@socket, :create), phx_trigger_action: @trigger_submit, phx_change: :validate, phx_submit: :submit, as: :user %>
  <%= input f, :email %>
  <%= input f, :password %>
  <%= submit "Sign In", disabled: !@changeset.valid? %>
</form>

Count Occurrences in Elixir

Elixir recently introduced an useful couple of functions to count how many times a value appears in an Enumerable. It comes in two formats: frequencies/1 and frequencies_by/2. Here's an example:

iex> [
...>   %{name: "Falcon", power: "Flight"},
...>   %{name: "Titan Spirit", power: "Flight"},
...>   %{name: "Atom Claw", power: "Strength"},
...>   %{name: "Electro", power: "Electricity Control"},
...>   %{name: "Loki Brain", power: "Telekinesis"},
...> ] |> Enum.frequencies_by(& &1.power)
%{
  "Electricity Control" => 1,
  "Flight" => 2,
  "Strength" => 1,
  "Telekinesis" => 1
}

Elixir Date and Time conversion into US standards

Today I learned how to manually convert a Date and Time into US standards: mm/dd/YYYY and hh:mm am|pm. Here's my code snippet:

defmodule Utils.Converter do
  def to_usa_date(%Date{day: day, month: month, year: year}) do
    "~2..0B/~2..0B/~4..0B"
    |> :io_lib.format([month, day, year])
    |> to_string()
  end

  def to_usa_time(%Time{} = time) do
    period = (time.hour < 12 && "am") || "pm"
    hour = time.hour |> rem(12)

    "~2..0B:~2..0B ~2..0s"
    |> :io_lib.format([hour, time.minute, period])
    |> to_string()
  end
end

This way I can convert dates like ~D[2020-05-29] into 05/29/2020" and times like ~T[11:00:07.001] into 11:00 am" and ~T[23:00:07.001] into 11:00 pm".

Here's the erlang :io_lib documentation