Skip to content

Instantly share code, notes, and snippets.

View TylerPachal's full-sized avatar

Tyler Pachal TylerPachal

View GitHub Profile
@doc """
Asserts that the given changeset is invalid, and that when the
assertion_expression is applied to the error_message it results in a truthy
value.
"""
defmacro assert_invalid(changeset, field, assertion_expression) when is_atom(field) do
expr = Macro.to_string(assertion_expression)
quote do
c = unquote(changeset)
@TylerPachal
TylerPachal / custom-compile-time-warnings-in-elixir.md
Last active September 26, 2023 15:14
A short example of emitting custom compile-time warnings in Elixir using IO.warn/1

Custom Compile-Time Warnings in Elixir Using IO.warn/1

When you are writing macros which run at compile-time, it can be useful to emit custom compile-time warnings.

Skip down to The Solution

Example Scenario

Say you are using a text file to dynamically define function clauses. Our function will be called official_language/1; its only argument is the name of a country, and it returns an :ok tuple with a list of the countries official languages.

def join_game(user_id, game_id) do
with {:ok, user} <- Users.get(user_id),
{:ok, game} <- Games.get(game_id),
false <- Game.is_full?(game),
false <- Game.is_started?(game),
true <- User.has_permission?(user, game)
do
Game.add_user(game, user)
else
# Don't care what specific thing failed
# You don't always need pattern matching
is_admin = Map.get(user, "is_admin") || false
@doc """
Assert that a Kafka contains a message on a given topic.
Example usage:
assert_kafka_contains "my_topic", fn message_body ->
Jason.decode!(message_body)["name"] == "tyler"
end
"""
defmacro assert_kafka_contains(topic, opts \\ [], assert_body_fn) do
@doc """
Asserts that the given changeset is invalid, and that the given field is
invalid and contains an error message that contains the asserted_message
substring.
Example usage:
assert_invalid changeset, :people, error_message =~ "tyler is not allowed"
"""
defmacro assert_invalid(changeset, field, assertion_expression) when is_atom(field) do
# Without specifying a child_spec, you get the default one:
# default = %{
# id: __MODULE__,
# start: {__MODULE__, :start_link, [init_arg]}
# }
# A: calls start_link/1 with empty list of args
# children = [
# DevWorker
# ]
# An experiment to render a flat-list of nested errors.
#
# The default translate_errors() function would render (nested) errors like this:
# %{
# "pets" => [
# %{},
# %{"type" => ["is invalid"]},
# %{},
# %{"type" => ["is invalid"]}
# ]
@TylerPachal
TylerPachal / ecto_vs_phoenix_validations.exs
Created July 23, 2021 17:26
Comparing the Ecto return value to what Phoenix renders
# When using Ecto Schemas for validating, I wanted to see what error information was present in the
# Ecto.Changeset, and then compare that to what Phoenix would render by default.
#
# Q: Can you get the index of nested objects' errors?
# A: Yes, for nested lists anything valid will have an empty entry in the list, while invalid
# objects will have some error information. Either way you could use Enum.with_index() to figure
# out a specific index.
## lib/phoenix_playground/accounts/user.ex
defmodule PhoenixPlayground.Accounts.User do
def init(:ok) do
children =
[
kafka_producer_spec(),
kafka_consumer_spec(),
other_thing_spec(),
Api # Start the Api from the child_spec() function defined in that module
]
end