🎯 Exemplos recomendados
Balanced sample collections from various categories for you to explore
Elixir Language Samples
Essential Elixir programming examples for concurrent and distributed systems
💻 Elixir Hello World elixir
🟢 simple
⭐⭐
Basic Elixir Hello World program and fundamental functional programming concepts
⏱️ 20 min
🏷️ elixir, functional programming, beginner, erlang
Prerequisites:
Basic programming concepts, Understanding of functional programming
# Elixir Hello World Examples
# 1. Basic Hello World
IO.puts("Hello, World!")
# 2. Hello World with variable
message = "Hello, World!"
IO.puts(message)
# 3. Hello World with function
defmodule HelloWorld do
def say_hello do
"Hello, World!"
end
def print_hello do
IO.puts(say_hello())
end
end
HelloWorld.print_hello()
# 4. Hello World with function parameters
defmodule Greeting do
def greet(name) do
"Hello, #{name}!"
end
def print_greeting(name) do
IO.puts(greet(name))
end
end
Greeting.print_greeting("World")
Greeting.print_greeting("Elixir")
# 5. Hello World with pattern matching
defmodule PatternGreeting do
def greet({:formal, name}) do
"Good day, #{name}."
end
def greet({:informal, name}) do
"Hey #{name}!"
end
def greet({:professional, name, title}) do
"Hello #{title} #{name}."
end
def greet(name) when is_binary(name) do
"Hello, #{name}!"
end
end
IO.puts(PatternGreeting.greet("Alice"))
IO.puts(PatternGreeting.greet({:formal, "Bob"}))
IO.puts(PatternGreeting.greet({:informal, "Charlie"}))
IO.puts(PatternGreeting.greet({:professional, "Diana", "Dr."}))
# 6. Hello World multiple times
defmodule MultipleGreetings do
def repeat_greeting(greeting, times) do
1..times
|> Enum.map(fn i -> "#{greeting} #{i}" end)
|> Enum.each(&IO.puts/1)
end
end
MultipleGreetings.repeat_greeting("Hello, World!", 5)
# 7. Hello World with list
defmodule ListGreetings do
def greet_all(names) do
names
|> Enum.map(&"Hello, #{&1}!")
|> Enum.each(&IO.puts/1)
end
def greet_in_different_languages do
greetings = [
"Hello", "Bonjour", "Hola", "Ciao", "こんにちは",
"안녕하세요", "مرحبا", "Здравствуйте", "नमस्ते"
]
greetings
|> Enum.map(&"#{&1}, World!")
|> Enum.each(&IO.puts/1)
end
end
ListGreetings.greet_all(["Alice", "Bob", "Charlie"])
ListGreetings.greet_in_different_languages()
# 8. Hello World with map
defmodule MapGreetings do
def greet_by_language do
translations = %{
"en" => "Hello",
"es" => "Hola",
"fr" => "Bonjour",
"de" => "Hallo",
"ja" => "こんにちは",
"ko" => "안녕하세요"
}
translations
|> Enum.map(fn {lang, greeting} -> "#{greeting}, World! (#{lang})" end)
|> Enum.each(&IO.puts/1)
end
end
MapGreetings.greet_by_language()
# 9. Hello World with recursion
defmodule RecursiveGreetings do
def greet_countdown(0) do
IO.puts("Liftoff! 🚀")
end
def greet_countdown(n) when n > 0 do
IO.puts("Hello, World! #{n}")
greet_countdown(n - 1)
end
def greet_list([]) do
:ok
end
def greet_list([head | tail]) do
IO.puts("Hello, #{head}!")
greet_list(tail)
end
end
RecursiveGreetings.greet_countdown(5)
RecursiveGreetings.greet_list(["Alice", "Bob", "Charlie"])
# 10. Hello World with pipe operator
defmodule PipeGreeting do
def create_greeting(name, style) do
name
|> String.upcase()
|> add_style(style)
|> format_message()
end
defp add_style(name, :excited) do
name <> "!"
end
defp add_style(name, :formal) do
"Dear " <> name
end
defp add_style(name, :casual) do
"Hey " <> name
end
defp format_message(message) do
"Hello, #{message}"
end
end
IO.puts(PipeGreeting.create_greeting("World", :excited))
IO.puts(PipeGreeting.create_greeting("World", :formal))
IO.puts(PipeGreeting.create_greeting("World", :casual))
# Basic data types and operations
# Numbers
integer = 42
float = 3.14
# Atoms (constants that start with colon)
atom = :hello
boolean_true = true
boolean_false = false
nil_value = nil
# Strings
string = "Hello, Elixir!"
string_with_interpolation = "The answer is #{integer}"
# Lists (linked lists)
list = [1, 2, 3, 4, 5]
string_list = ["apple", "banana", "cherry"]
mixed_list = [1, "two", :three, 4.0]
# Tuples
tuple = {:ok, "success"}
point = {10, 20}
person = {"Alice", 30, "Engineer"}
# Maps (key-value collections)
map = %{
name: "Alice",
age: 30,
city: "New York"
}
map_with_string_keys = %{
"name" => "Bob",
"age" => 25,
"skills" => ["Elixir", "Phoenix", "Ecto"]
}
# Ranges
range = 1..10
char_range = ?a..?z
# Keyword lists (special list of key-value tuples)
keyword_list = [name: "Alice", age: 30, city: "NYC"]
IO.puts("\n=== Data Types ===")
IO.inspect(integer, label: "Integer")
IO.inspect(float, label: "Float")
IO.inspect(atom, label: "Atom")
IO.inspect(string, label: "String")
IO.inspect(list, label: "List")
IO.inspect(tuple, label: "Tuple")
IO.inspect(map, label: "Map")
IO.inspect(range, label: "Range")
IO.inspect(keyword_list, label: "Keyword List")
# Pattern matching examples
# Variable binding
{a, b, c} = {1, 2, 3}
IO.puts("Pattern matching: a=#{a}, b=#{b}, c=#{c}")
# List pattern matching
[head | tail] = [1, 2, 3, 4, 5]
IO.puts("List pattern: head=#{head}, tail=#{inspect(tail)}")
# Map pattern matching
%{name: name, age: age} = %{name: "Alice", age: 30, city: "NYC"}
IO.puts("Map pattern: name=#{name}, age=#{age}")
# Function pattern matching
defmodule MathFunctions do
def describe_number(0), do: "Zero"
def describe_number(n) when n > 0, do: "Positive: #{n}"
def describe_number(n) when n < 0, do: "Negative: #{n}"
def max(a, b) when a > b, do: a
def max(a, b), do: b
def factorial(0), do: 1
def factorial(n) when n > 0, do: n * factorial(n - 1)
end
IO.puts("\n=== Pattern Matching Functions ===")
IO.puts(MathFunctions.describe_number(5))
IO.puts(MathFunctions.describe_number(-3))
IO.puts(MathFunctions.describe_number(0))
IO.puts("Max of 5 and 3: #{MathFunctions.max(5, 3)}")
IO.puts("Factorial of 5: #{MathFunctions.factorial(5)}")
# Higher-order functions and Enum module
defmodule ListOperations do
def process_numbers do
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
squared = Enum.map(numbers, &(&1 * &1))
even_numbers = Enum.filter(numbers, &(rem(&1, 2) == 0))
sum = Enum.reduce(numbers, 0, &+/2)
IO.puts("Original: #{inspect(numbers)}")
IO.puts("Squared: #{inspect(squared)}")
IO.puts("Even: #{inspect(even_numbers)}")
IO.puts("Sum: #{sum}")
end
def string_operations do
words = ["hello", "world", "elixir", "functional", "programming"]
upper_words = Enum.map(words, &String.upcase/1)
long_words = Enum.filter(words, &(String.length(&1) > 5))
word_lengths = Enum.map(words, &{&1, String.length(&1)})
all_words = Enum.join(words, " ")
IO.puts("Words: #{inspect(words)}")
IO.puts("Upper: #{inspect(upper_words)}")
IO.puts("Long words: #{inspect(long_words)}")
IO.puts("Word lengths: #{inspect(word_lengths)}")
IO.puts("Joined: #{all_words}")
end
end
IO.puts("\n=== List Operations ===")
ListOperations.process_numbers()
IO.puts("\n=== String Operations ===")
ListOperations.string_operations()
# Control flow
defmodule ControlFlow do
def check_grade(score) do
cond do
score >= 90 -> "A"
score >= 80 -> "B"
score >= 70 -> "C"
score >= 60 -> "D"
true -> "F"
end
end
def case_example(value) do
case value do
:ok -> "Operation succeeded"
:error -> "Operation failed"
{:ok, result} -> "Operation succeeded with: #{result}"
{:error, reason} -> "Operation failed because: #{reason}"
_ -> "Unknown result"
end
end
def unless_example(condition) do
unless condition do
"Condition is false"
else
"Condition is true"
end
end
end
IO.puts("\n=== Control Flow ===")
IO.puts("Grade for 85: #{ControlFlow.check_grade(85)}")
IO.puts("Case example ok: #{ControlFlow.case_example(:ok)}")
IO.puts("Case example error: #{ControlFlow.case_example({:error, "timeout"})}")
IO.puts("Unless example: #{ControlFlow.unless_example(false)}")
# Structs
defmodule Person do
defstruct name: nil, age: nil, city: nil
def greet(%Person{name: name}) do
"Hello, #{name}!"
end
def birthday(%Person{age: age} = person) do
%{person | age: age + 1}
end
def is_adult?(%Person{age: age}) when age >= 18, do: true
def is_adult?(_), do: false
end
IO.puts("\n=== Structs ===")
person = %Person{name: "Alice", age: 30, city: "NYC"}
IO.inspect(person, label: "Person")
IO.puts(Person.greet(person))
older_person = Person.birthday(person)
IO.inspect(older_person, label: "After birthday")
IO.puts("Is adult?: #{Person.is_adult?(person)}")
# Protocols
defmodule Greeter do
@doc "Greets any type that implements this protocol"
@callback greet(any) :: String.t()
end
defmodule Human do
defstruct [:name]
defimpl Greeter do
def greet(%Human{name: name}), do: "Hello, #{name}!"
end
end
defmodule Robot do
defstruct [:id]
defimpl Greeter do
def greet(%Robot{id: id}), do: "BEEP BOOP. I am robot #{id}."
end
end
IO.puts("\n=== Protocols ===")
human = %Human{name: "Alice"}
robot = %Robot{id: 1234}
IO.puts(Greeter.greet(human))
IO.puts(Greeter.greet(robot))
# Module attributes and documentation
defmodule Calculator do
@moduledoc """
A simple calculator module that provides basic arithmetic operations.
"""
@pi 3.14159
@version "1.0.0"
@doc """
Adds two numbers together.
## Examples
iex> Calculator.add(2, 3)
5
iex> Calculator.add(-1, 1)
0
"""
def add(a, b), do: a + b
@doc "Multiplies two numbers together"
def multiply(a, b), do: a * b
@doc "Returns the value of pi"
def pi, do: @pi
@doc "Returns the version of this calculator"
def version, do: @version
end
IO.puts("\n=== Module with Documentation ===")
IO.puts("2 + 3 = #{Calculator.add(2, 3)}")
IO.puts("5 * 4 = #{Calculator.multiply(5, 4)}")
IO.puts("Pi: #{Calculator.pi()}")
IO.puts("Version: #{Calculator.version()}")
# Error handling
defmodule SafeOperations do
def safe_divide(_, 0), do: {:error, "Cannot divide by zero"}
def safe_divide(a, b), do: {:ok, a / b}
def process_result({:ok, result}), do: "Result: #{result}"
def process_result({:error, reason}), do: "Error: #{reason}"
def safe_divide_and_process(a, b) do
a
|> safe_divide(b)
|> process_result()
end
end
IO.puts("\n=== Error Handling ===")
IO.puts(SafeOperations.safe_divide_and_process(10, 2))
IO.puts(SafeOperations.safe_divide_and_process(10, 0))
# Comprehensions
defmodule Comprehensions do
def matrix do
for x <- 1..3, y <- 1..3 do
{x, y, x * y}
end
end
def pythagorean_triplets(limit) do
for a <- 1..limit,
b <- a..limit,
c <- b..limit,
a * a + b * b == c * c do
{a, b, c}
end
end
def word_lengths(words) do
for word <- words,
String.length(word) > 3,
into: %{} do
{word, String.length(word)}
end
end
end
IO.puts("\n=== Comprehensions ===")
IO.puts("Matrix products:")
Enum.each(Comprehensions.matrix(), &IO.puts/1)
IO.puts("\nPythagorean triplets up to 20:")
Enum.each(Comprehensions.pythagorean_triplets(20), &IO.inspect/1)
IO.puts("\nWord lengths:")
words = ["cat", "elephant", "dog", "programming", "hi"]
IO.inspect(Comprehensions.word_lengths(words))
# Working with files (basic examples)
defmodule FileOperations do
def write_hello(filename) do
content = "Hello from Elixir!\nThis is a test file.\nWritten at: #{DateTime.utc_now()}"
case File.write(filename, content) do
:ok -> IO.puts("Successfully wrote to #{filename}")
{:error, reason} -> IO.puts("Failed to write: #{reason}")
end
end
def read_hello(filename) do
case File.read(filename) do
{:ok, content} ->
IO.puts("File content:")
IO.puts(content)
{:error, reason} ->
IO.puts("Failed to read: #{reason}")
end
end
end
IO.puts("\n=== File Operations ===")
filename = "hello_elixir.txt"
FileOperations.write_hello(filename)
FileOperations.read_hello(filename)
File.rm(filename) # Clean up
IO.puts("\n=== All Elixir Hello World Examples Complete! ===")
💻 Elixir Concurrency and OTP elixir
🟡 intermediate
⭐⭐⭐⭐
Advanced concurrency patterns using GenServer, Supervisor, and Agent
⏱️ 35 min
🏷️ elixir, concurrency, otp, distributed systems, fault tolerance
Prerequisites:
Elixir basics, Understanding of concurrent programming, OTP concepts
# Elixir Concurrency and OTP Examples
# 1. Basic Processes and spawn
defmodule ProcessBasics do
def start_greeter(name) do
spawn(fn -> greeter_loop(name) end)
end
defp greeter_loop(name) do
receive do
{:greet, who} ->
IO.puts("#{name}: Hello, #{who}!")
greeter_loop(name)
{:stop} ->
IO.puts("#{name}: Goodbye!")
:stop ->
IO.puts("#{name}: Goodbye! (atom)")
end
end
def send_message(pid, message) do
send(pid, message)
end
end
IO.puts("=== Basic Process Examples ===")
greeter1 = ProcessBasics.start_greeter("Alice")
greeter2 = ProcessBasics.start_greeter("Bob")
ProcessBasics.send_message(greeter1, {:greet, "World"})
ProcessBasics.send_message(greeter2, {:greet, "Everyone"})
# Give processes time to respond
:timer.sleep(100)
ProcessBasics.send_message(greeter1, :stop)
ProcessBasics.send_message(greeter2, :stop)
:timer.sleep(100)
# 2. Agent for State Management
defmodule Counter do
def start_link(initial_value \ 0) do
Agent.start_link(fn -> initial_value end, name: __MODULE__)
end
def increment do
Agent.update(__MODULE__, &(&1 + 1))
end
def decrement do
Agent.update(__MODULE__, &(&1 - 1))
end
def value do
Agent.get(__MODULE__, &(&1))
end
def reset do
Agent.update(__MODULE__, fn _ -> 0 end)
end
def stop do
Agent.stop(__MODULE__)
end
end
IO.puts("\n=== Agent Examples ===")
{:ok, _pid} = Counter.start_link(10)
IO.puts("Initial counter value: #{Counter.value()}")
Counter.increment()
Counter.increment()
Counter.decrement()
IO.puts("After operations: #{Counter.value()}")
Counter.reset()
IO.puts("After reset: #{Counter.value()}")
Counter.stop()
# 3. Task for Asynchronous Operations
defmodule AsyncOperations do
def slow_operation(name, delay_ms) do
:timer.sleep(delay_ms)
{:ok, "#{name} completed after #{delay_ms}ms"}
end
def run_parallel_tasks do
tasks = [
Task.async(fn -> slow_operation("Task 1", 1000) end),
Task.async(fn -> slow_operation("Task 2", 800) end),
Task.async(fn -> slow_operation("Task 3", 1200) end)
]
results = Task.await_many(tasks, 2000)
Enum.each(results, fn result ->
case result do
{:ok, message} -> IO.puts("Success: #{message}")
{:exit, reason} -> IO.puts("Task failed: #{reason}")
end
end)
end
def run_with_timeout do
task = Task.async(fn ->
:timer.sleep(3000)
"Slow result"
end)
case Task.yield(task, 1000) do
{:ok, result} -> IO.puts("Task completed: #{result}")
nil ->
IO.puts("Task didn't complete in time")
Task.shutdown(task, :brutal_kill)
end
end
end
IO.puts("\n=== Task Examples ===")
IO.puts("Running parallel tasks...")
AsyncOperations.run_parallel_tasks()
IO.puts("\nRunning task with timeout...")
AsyncOperations.run_with_timeout()
# 4. GenServer for Stateful Processes
defmodule GameState do
use GenServer
# Client API
def start_link(initial_state) do
GenServer.start_link(__MODULE__, initial_state, name: __MODULE__)
end
def get_state do
GenServer.call(__MODULE__, :get_state)
end
def make_move(player, move) do
GenServer.call(__MODULE__, {:make_move, player, move})
end
def reset do
GenServer.call(__MODULE__, :reset)
end
def get_history do
GenServer.call(__MODULE__, :get_history)
end
# Server callbacks
@impl true
def init(initial_state) do
state = %{
board: initial_state[:board] || %{player1: nil, player2: nil},
current_player: :player1,
moves: [],
winner: nil
}
{:ok, state}
end
@impl true
def handle_call(:get_state, _from, state) do
{:reply, state, state}
end
@impl true
def handle_call({:make_move, player, move}, _from, state) do
if state.current_player == player && state.winner == nil do
new_board = put_in(state.board[player], move)
new_moves = [{player, move} | state.moves]
new_state = %{state |
board: new_board,
current_player: next_player(player),
moves: new_moves,
winner: check_winner(new_board)
}
{:reply, {:ok, move}, new_state}
else
{:reply, {:error, "Invalid move"}, state}
end
end
@impl true
def handle_call(:reset, _from, _state) do
new_state = %{
board: %{player1: nil, player2: nil},
current_player: :player1,
moves: [],
winner: nil
}
{:reply, :ok, new_state}
end
@impl true
def handle_call(:get_history, _from, state) do
{:reply, Enum.reverse(state.moves), state}
end
@impl true
def handle_info(:timeout, state) do
IO.puts("Game timeout - resetting")
{:noreply, reset_state()}
end
# Private helper functions
defp next_player(:player1), do: :player2
defp next_player(:player2), do: :player1
defp check_winner(%{player1: move1, player2: move2}) when move1 != nil and move2 != nil do
# Simple winner logic (for demonstration)
cond do
String.ends_with?(move1, "win") -> :player1
String.ends_with?(move2, "win") -> :player2
true -> nil
end
end
defp check_winner(_), do: nil
defp reset_state do
%{
board: %{player1: nil, player2: nil},
current_player: :player1,
moves: [],
winner: nil
}
end
end
IO.puts("\n=== GenServer Examples ===")
{:ok, _pid} = GameState.start_link(%{board: %{player1: nil, player2: nil}})
IO.inspect(GameState.get_state(), label: "Initial state")
{:ok, _} = GameState.make_move(:player1, "rock")
IO.inspect(GameState.get_state(), label: "After player1 move")
{:ok, _} = GameState.make_move(:player2, "paper")
IO.inspect(GameState.get_state(), label: "After player2 move")
{:ok, _} = GameState.make_move(:player1, "win_move")
IO.inspect(GameState.get_state(), label: "After player1 winning move")
IO.puts("Move history:")
Enum.each(GameState.get_history(), &IO.inspect/1)
# 5. Supervisor for Process Management
defmodule GameSupervisor do
use Supervisor
def start_link(init_arg) do
Supervisor.start_link(__MODULE__, init_arg, name: __MODULE__)
end
@impl true
def init(_init_arg) do
children = [
{GameState, %{board: %{player1: nil, player2: nil}}},
{Counter, 0}
]
Supervisor.init(children, strategy: :one_for_one)
end
end
IO.puts("\n=== Supervisor Examples ===")
{:ok, _sup_pid} = GameSupervisor.start_link(%{})
IO.puts("Supervisor started with children")
# Check that our processes are running
IO.puts("Game state: #{inspect(GameState.get_state())}")
# 6. Dynamic Supervisor
defmodule DynamicGameSupervisor do
use DynamicSupervisor
def start_link(init_arg) do
DynamicSupervisor.start_link(__MODULE__, init_arg, name: __MODULE__)
end
@impl true
def init(_init_arg) do
DynamicSupervisor.init(strategy: :one_for_one)
end
def start_game(name) do
spec = %{
id: GameState,
start: {GameState, :start_link, [%{board: %{player1: nil, player2: nil}}]},
restart: :temporary
}
DynamicSupervisor.start_child(__MODULE__, spec)
end
end
IO.puts("\n=== Dynamic Supervisor Examples ===")
{:ok, _sup_pid} = DynamicGameSupervisor.start_link(%{})
{:ok, game1} = DynamicGameSupervisor.start_game("game1")
{:ok, game2} = DynamicGameSupervisor.start_game("game2")
IO.puts("Started dynamic games")
IO.puts("Children count: #{DynamicSupervisor.count_children(DynamicGameSupervisor)}")
# 7. Registry for Process Naming
defmodule GameRegistry do
def start_link do
Registry.start_link(keys: :unique, name: __MODULE__)
end
def register_game(name, pid) do
Registry.register(__MODULE__, name, pid)
end
def lookup_game(name) do
case Registry.lookup(__MODULE__, name) do
[{pid, _}] -> {:ok, pid}
[] -> {:error, :not_found}
end
end
def list_games do
Registry.select(__MODULE__, [{{:"$1", :"$2", :"$3"}, [], [:"$1"]}])
end
end
IO.puts("\n=== Registry Examples ===")
{:ok, _reg_pid} = GameRegistry.start_link()
{:ok, game_pid} = GameState.start_link(%{board: %{player1: nil, player2: nil}})
GameRegistry.register_game("championship_game", game_pid)
{:ok, found_pid} = GameRegistry.lookup_game("championship_game")
IO.puts("Found game pid: #{inspect(found_pid)}")
IO.puts("All registered games: #{inspect(GameRegistry.list_games())}")
# 8. ETS (Erlang Term Storage) for In-Memory Database
defmodule GameCache do
use GenServer
# Client API
def start_link do
GenServer.start_link(__MODULE__, %{}, name: __MODULE__)
end
def store(key, value) do
GenServer.call(__MODULE__, {:store, key, value})
end
def get(key) do
GenServer.call(__MODULE__, {:get, key})
end
def delete(key) do
GenServer.call(__MODULE__, {:delete, key})
end
def list_all do
GenServer.call(__MODULE__, :list_all)
end
# Server callbacks
@impl true
def init(_) do
table = :ets.new(:game_cache, [:set, :public, :named_table])
{:ok, %{table: table}}
end
@impl true
def handle_call({:store, key, value}, _from, %{table: table} = state) do
:ets.insert(table, {key, value})
{:reply, :ok, state}
end
@impl true
def handle_call({:get, key}, _from, %{table: table} = state) do
result = case :ets.lookup(table, key) do
[{^key, value}] -> {:ok, value}
[] -> {:error, :not_found}
end
{:reply, result, state}
end
@impl true
def handle_call({:delete, key}, _from, %{table: table} = state) do
:ets.delete(table, key)
{:reply, :ok, state}
end
@impl true
def handle_call(:list_all, _from, %{table: table} = state) do
all = :ets.tab2list(table)
{:reply, all, state}
end
end
IO.puts("\n=== ETS Cache Examples ===")
{:ok, _cache_pid} = GameCache.start_link()
GameCache.store("player1_score", 100)
GameCache.store("player2_score", 150)
GameCache.store("game_status", "in_progress")
{:ok, score1} = GameCache.get("player1_score")
IO.puts("Player 1 score: #{score1}")
{:ok, status} = GameCache.get("game_status")
IO.puts("Game status: #{status}")
all_data = GameCache.list_all()
IO.puts("All cache data: #{inspect(all_data)}")
# 9. PubSub for Event Broadcasting
defmodule GameEvents do
@name __MODULE__
def start_link do
Registry.start_link(keys: :duplicate, name: @name)
end
def subscribe(topic) do
Registry.register(@name, topic, [])
end
def publish(topic, message) do
Registry.dispatch(@name, topic, fn entries ->
for {pid, _} <- entries do
send(pid, {:broadcast, topic, message})
end
end)
end
def unsubscribe(topic) do
Registry.unregister(@name, topic)
end
end
defmodule GameSubscriber do
def start_link(topic) do
spawn(fn ->
GameEvents.subscribe(topic)
listen()
end)
end
defp listen do
receive do
{:broadcast, topic, message} ->
IO.puts("Subscriber received on #{topic}: #{message}")
listen()
end
end
end
IO.puts("\n=== PubSub Examples ===")
{:ok, _pub_pid} = GameEvents.start_link()
subscriber1 = GameSubscriber.start_link("game_updates")
subscriber2 = GameSubscriber.start_link("game_updates")
subscriber3 = GameSubscriber.start_link("system_events")
GameEvents.publish("game_updates", "Player 1 scored!")
GameEvents.publish("game_updates", "Player 2 scored!")
GameEvents.publish("system_events", "Server maintenance scheduled")
:timer.sleep(100)
# 10. Poolboy for Connection Pooling (simplified example)
defmodule WorkerPool do
def start_link(size) do
workers = for i <- 1..size do
spawn(fn -> worker_loop(i) end)
end
Agent.start_link(fn -> %{workers: workers, available: workers, busy: []} end, name: __MODULE__)
end
def checkout do
Agent.get_and_update(__MODULE__, fn %{workers: workers, available: available, busy: busy} = state ->
case available do
[worker | rest] ->
{{:ok, worker}, %{state | available: rest, busy: [worker | busy]}}
[] ->
{{:error, :no_workers}, state}
end
end)
end
def checkin(worker) do
Agent.update(__MODULE__, fn %{workers: workers, available: available, busy: busy} ->
case worker in busy do
true ->
%{workers: workers, available: [worker | available], busy: List.delete(busy, worker)}
false ->
%{workers: workers, available: available, busy: busy}
end
end)
end
defp worker_loop(id) do
receive do
{:work, from, task} ->
result = perform_task(task, id)
send(from, {:result, id, result})
worker_loop(id)
:stop ->
:ok
end
end
defp perform_task(task, worker_id) do
:timer.sleep(:rand.uniform(1000))
"#{task} (completed by worker #{worker_id})"
end
def execute_task(task) do
case checkout() do
{:ok, worker} ->
send(worker, {:work, self(), task})
receive do
{:result, ^worker, result} ->
checkin(worker)
{:ok, result}
after
5000 ->
checkin(worker)
{:error, :timeout}
end
{:error, :no_workers} ->
{:error, :no_workers}
end
end
end
IO.puts("\n=== Worker Pool Examples ===")
{:ok, _pool_pid} = WorkerPool.start_link(3)
tasks = ["Compute taxes", "Generate report", "Send emails", "Backup data"]
results = Enum.map(tasks, &WorkerPool.execute_task/1)
Enum.each(results, fn result ->
case result do
{:ok, message} -> IO.puts("✓ #{message}")
{:error, reason} -> IO.puts("✗ Failed: #{reason}")
end
end)
:timer.sleep(2000)
IO.puts("\n=== All Concurrency and OTP Examples Complete! ===")
💻 Elixir Phoenix Web Development elixir
🟡 intermediate
⭐⭐⭐⭐
Building web applications with Phoenix framework, Ecto, and LiveView
⏱️ 40 min
🏷️ elixir, phoenix, web dev, full stack, real-time
Prerequisites:
Elixir basics, Web development concepts, Understanding of MVC architecture
# Elixir Phoenix Web Development Examples
# Note: These examples demonstrate Phoenix patterns but can be run standalone
# In a real Phoenix application, these would be organized in different files
# 1. Phoenix Controller Patterns
defmodule WebAppWeb.PageController do
use WebAppWeb, :controller
def index(conn, _params) do
render(conn, "index.html",
message: "Welcome to Phoenix!",
current_time: DateTime.utc_now(),
features: [
"LiveView for real-time UI",
"Ecto for database operations",
"Channels for WebSocket communication",
"Plug pipeline for request handling"
]
)
end
def about(conn, %{"team" => team_name}) do
team_info = get_team_info(team_name)
render(conn, "about.html", team: team_info)
end
def create_user(conn, %{"user" => user_params}) do
case Accounts.create_user(user_params) do
{:ok, user} ->
conn
|> put_flash(:info, "User created successfully.")
|> redirect(to: Routes.user_path(conn, :show, user))
{:error, %Ecto.Changeset{} = changeset} ->
render(conn, "new.html", changeset: changeset)
end
end
# API endpoint
def api_stats(conn, _params) do
stats = %{
users_count: Accounts.count_users(),
posts_count: Blog.count_posts(),
server_uptime: get_server_uptime(),
version: Application.spec(:web_app, :vsn)
}
json(conn, %{status: "success", data: stats})
end
# Streaming response
def stream_data(conn, _params) do
conn
|> put_resp_content_type("text/plain")
|> send_chunked(200)
|> stream_lines()
end
defp stream_lines(conn) do
Enum.each(1..10, fn i ->
:timer.sleep(200)
chunk(conn, "Line #{i}\n")
end)
conn
end
defp get_team_info("elixir") do
%{
name: "Elixir Team",
members: ["José Valim", "Chris McCord", "Gary Bernhardt"],
founded: 2011,
language: "Elixir"
}
end
defp get_team_info("phoenix") do
%{
name: "Phoenix Team",
members: ["Chris McCord", "José Valim", "Eric Meadows-Jönsson"],
founded: 2014,
language: "Elixir"
}
end
defp get_team_info(_) do
%{name: "Unknown Team", error: "Team not found"}
end
defp get_server_uptime do
{uptime_msec, _} = :erlang.statistics(:wall_clock)
uptime_msec / 1000
end
end
# 2. Ecto Schema and Models
defmodule WebApp.Accounts.User do
use Ecto.Schema
import Ecto.Changeset
schema "users" do
field :name, :string
field :email, :string
field :age, :integer
field :bio, :string
field :encrypted_password, :string
field :is_active, :boolean, default: true
field :role, :string, default: "user"
has_many :posts, WebApp.Blog.Post
has_many :comments, WebApp.Blog.Comment
timestamps()
end
def changeset(user, attrs) do
user
|> cast(attrs, [:name, :email, :age, :bio, :role, :is_active])
|> validate_required([:name, :email])
|> validate_format(:email, ~r/@/)
|> validate_length(:name, min: 2, max: 100)
|> validate_inclusion(:role, ["user", "admin", "moderator"])
|> validate_number(:age, greater_than: 0, less_than: 150)
|> unique_constraint(:email)
end
def registration_changeset(user, attrs) do
user
|> changeset(attrs)
|> cast(attrs, [:password])
|> validate_required(:password)
|> validate_length(:password, min: 8)
|> put_password_hash()
end
defp put_password_hash(%Ecto.Changeset{valid?: true, changes: %{password: password}} = changeset) do
change(changeset, Argon2.add_hash(password))
end
defp put_password_hash(changeset), do: changeset
end
defmodule WebApp.Blog.Post do
use Ecto.Schema
import Ecto.Changeset
schema "posts" do
field :title, :string
field :content, :text
field :published, :boolean, default: false
field :published_at, :utc_datetime
field :tags, {:array, :string}
field :view_count, :integer, default: 0
belongs_to :user, WebApp.Accounts.User
has_many :comments, WebApp.Blog.Comment
has_many :likes, WebApp.Social.Like
timestamps()
end
def changeset(post, attrs) do
post
|> cast(attrs, [:title, :content, :published, :published_at, :tags])
|> validate_required([:title, :content])
|> validate_length(:title, min: 3, max: 200)
|> validate_length(:content, min: 10)
|> unique_constraint(:title)
|> put_published_timestamp()
end
defp put_published_timestamp(%Ecto.Changeset{valid?: true} = changeset) do
if get_field(changeset, :published) do
put_change(changeset, :published_at, DateTime.utc_now())
else
changeset
end
end
defp put_published_timestamp(changeset), do: changeset
end
# 3. Context Modules (Business Logic)
defmodule WebApp.Accounts do
alias WebApp.Repo
alias WebApp.Accounts.User
import Ecto.Query
def list_users(opts \\ []) do
from(u in User)
|> maybe_filter_by_role(opts[:role])
|> maybe_filter_by_active(opts[:active])
|> Repo.all()
end
def get_user!(id), do: Repo.get!(User, id)
def get_user_by_email(email), do: Repo.get_by(User, email: email)
def create_user(attrs \\ %{}) do
%User{}
|> User.changeset(attrs)
|> Repo.insert()
end
def register_user(attrs \\ %{}) do
%User{}
|> User.registration_changeset(attrs)
|> Repo.insert()
end
def update_user(%User{} = user, attrs) do
user
|> User.changeset(attrs)
|> Repo.update()
end
def delete_user(%User{} = user) do
Repo.delete(user)
end
def authenticate_user(email, password) do
user = get_user_by_email(email)
case user do
nil ->
Argon2.no_user_verify()
{:error, :invalid_credentials}
user ->
if Argon2.verify_pass(password, user.encrypted_password) do
{:ok, user}
else
{:error, :invalid_credentials}
end
end
end
def count_users do
from(u in User, select: count(u.id))
|> Repo.one()
end
def search_users(query) do
from(u in User,
where: ilike(u.name, ^"%#{query}%") or ilike(u.email, ^"%#{query}%")
)
|> Repo.all()
end
defp maybe_filter_by_role(query, nil), do: query
defp maybe_filter_by_role(query, role), do: where(query, [u], u.role == ^role)
defp maybe_filter_by_active(query, nil), do: query
defp maybe_filter_by_active(query, active), do: where(query, [u], u.is_active == ^active)
end
defmodule WebApp.Blog do
alias WebApp.Repo
alias WebApp.Blog.{Post, Comment}
import Ecto.Query
def list_published_posts do
from(p in Post,
where: p.published == true,
order_by: [desc: p.published_at],
preload: [:user, :comments]
)
|> Repo.all()
end
def list_posts_by_user(user_id) do
from(p in Post,
where: p.user_id == ^user_id,
order_by: [desc: p.inserted_at]
)
|> Repo.all()
end
def get_post!(id), do: Repo.get!(Post, id) |> Repo.preload([:user, :comments])
def get_post_with_comments!(id) do
from(p in Post,
where: p.id == ^id,
preload: [:user, comments: :user]
)
|> Repo.one!()
end
def create_post(%User{} = user, attrs \\ %{}) do
%Post{}
|> Post.changeset(attrs)
|> Ecto.Changeset.put_assoc(:user, user)
|> Repo.insert()
end
def update_post(%Post{} = post, attrs) do
post
|> Post.changeset(attrs)
|> Repo.update()
end
def publish_post(%Post{} = post) do
post
|> Ecto.Changeset.change(%{published: true, published_at: DateTime.utc_now()})
|> Repo.update()
end
def delete_post(%Post{} = post) do
Repo.delete(post)
end
def increment_view_count(%Post{} = post) do
from(p in Post, where: p.id == ^post.id)
|> Repo.update_all(inc: [view_count: 1])
end
def search_posts(query) do
from(p in Post,
where: p.published == true and (ilike(p.title, ^"%#{query}%") or ilike(p.content, ^"%#{query}%")),
order_by: [desc: p.published_at],
preload: [:user]
)
|> Repo.all()
end
def get_popular_posts(limit \\ 10) do
from(p in Post,
where: p.published == true,
order_by: [desc: p.view_count],
limit: ^limit,
preload: [:user]
)
|> Repo.all()
end
def count_posts do
from(p in Post, select: count(p.id))
|> Repo.one()
end
end
# 4. Phoenix LiveView
defmodule WebAppWeb.UserLive.Index do
use WebAppWeb, :live_view
alias WebApp.Accounts
alias WebApp.Accounts.User
@impl true
def mount(_params, _session, socket) do
{:ok, stream(socket, :users, Accounts.list_users())}
end
@impl true
def handle_params(params, _url, socket) do
{:noreply, apply_action(socket, socket.assigns.live_action, params)}
end
defp apply_action(socket, :edit, %{"id" => id}) do
socket
|> assign(:page_title, "Edit User")
|> assign(:user, Accounts.get_user!(id))
end
defp apply_action(socket, :new, _params) do
socket
|> assign(:page_title, "New User")
|> assign(:user, %User{})
end
defp apply_action(socket, :index, _params) do
socket
|> assign(:page_title, "Listing Users")
|> assign(:user, nil)
end
@impl true
def handle_info({WebAppWeb.UserLive.FormComponent, {:saved, user}}, socket) do
{:noreply, stream_insert(socket, :users, user)}
end
@impl true
def handle_event("delete", %{"id" => id}, socket) do
user = Accounts.get_user!(id)
{:ok, _} = Accounts.delete_user(user)
{:noreply, stream_delete(socket, :users, user)}
end
end
defmodule WebAppWeb.UserLive.FormComponent do
use WebAppWeb, :live_component
alias WebApp.Accounts
@impl true
def render(assigns) do
~H"""
<div>
<.header>
<%= @title %>
<:subtitle>Manage user records in your database.</:subtitle>
</.header>
<.simple_form
for={@form}
id="user-form"
phx-target={@myself}
phx-change="validate"
phx-submit="save"
>
<.input field={@form[:name]} type="text" label="Name" />
<.input field={@form[:email]} type="email" label="Email" />
<.input field={@form[:age]} type="number" label="Age" />
<.input field={@form[:bio]} type="textarea" label="Bio" />
<.input
field={@form[:role]}
type="select"
label="Role"
options={[{"User", "user"}, {"Admin", "admin"}, {"Moderator", "moderator"}]}
/>
<:actions>
<.button>Save User</.button>
</:actions>
</.simple_form>
</div>
"""
end
@impl true
def update(%{user: user} = assigns, socket) do
changeset = Accounts.change_user(user)
{:ok,
socket
|> assign(assigns)
|> assign_form(changeset)}
end
defp assign_form(socket, %Ecto.Changeset{} = changeset) do
assign(socket, :form, to_form(changeset))
end
@impl true
def handle_event("validate", %{"user" => user_params}, socket) do
changeset =
socket.assigns.user
|> Accounts.change_user(user_params)
|> Map.put(:action, :validate)
{:noreply, assign_form(socket, changeset)}
end
def handle_event("save", %{"user" => user_params}, socket) do
save_user(socket, socket.assigns.action, user_params)
end
defp save_user(socket, :edit, user_params) do
case Accounts.update_user(socket.assigns.user, user_params) do
{:ok, user} ->
notify_parent({:saved, user})
{:noreply,
socket
|> put_flash(:info, "User updated successfully")
|> push_patch(to: socket.assigns.patch)}
{:error, %Ecto.Changeset{} = changeset} ->
{:noreply, assign_form(socket, changeset)}
end
end
defp save_user(socket, :new, user_params) do
case Accounts.create_user(user_params) do
{:ok, user} ->
notify_parent({:saved, user})
{:noreply,
socket
|> put_flash(:info, "User created successfully")
|> push_patch(to: socket.assigns.patch)}
{:error, %Ecto.Changeset{} = changeset} ->
{:noreply, assign_form(socket, changeset)}
end
end
defp notify_parent(msg), do: send(self(), {__MODULE__, msg})
end
# 5. Phoenix Channels for Real-time Communication
defmodule WebAppWeb.Presence do
use Phoenix.Presence,
otp_app: :web_app,
pubsub_server: WebApp.PubSub
def fetch("chat:room:" <> _room_id, presences) do
users =
presences
|> Enum.map(fn {user_id, %{metas: [meta]}} ->
%{
id: user_id,
name: meta.name,
online_at: meta.online_at
}
end)
%{
users: users,
count: length(users)
}
end
end
defmodule WebAppWeb.ChatChannel do
use WebAppWeb, :channel
alias WebApp.Presence
def join("chat:room:" <> room_id, %{"name" => name}, socket) do
send(self(), :after_join)
socket =
socket
|> assign(:room_id, room_id)
|> assign(:name, name)
|> assign(:user_id, socket.assigns.user_id || UUID.uuid4())
{:ok, socket}
end
def handle_info(:after_join, socket) do
{:ok, _} =
Presence.track(socket, socket.assigns.user_id, %{
name: socket.assigns.name,
online_at: DateTime.utc_now()
})
push(socket, "presence_state", Presence.list(socket))
{:noreply, socket}
end
def handle_in("new_message", %{"body" => body}, socket) do
message = %{
id: UUID.uuid4(),
user_id: socket.assigns.user_id,
name: socket.assigns.name,
body: body,
timestamp: DateTime.utc_now()
}
broadcast!(socket, "new_message", message)
{:noreply, socket}
end
def handle_in("typing", _params, socket) do
broadcast_from!(socket, "typing", %{
user_id: socket.assigns.user_id,
name: socket.assigns.name
})
{:noreply, socket}
end
end
# 6. Plugs for Request Pipeline
defmodule WebAppWeb.Plugs.Auth do
import Plug.Conn
import Phoenix.Controller
alias WebAppWeb.Router.Helpers
alias WebApp.Accounts
def init(opts), do: opts
def call(conn, _opts) do
user_id = get_session(conn, :user_id)
cond do
user = conn.assigns[:current_user] ->
assign(conn, :current_user, user)
user = user_id && Accounts.get_user(user_id) ->
assign(conn, :current_user, user)
true ->
assign(conn, :current_user, nil)
end
end
end
defmodule WebAppWeb.Plugs.RequireAuth do
import Phoenix.Controller
import Plug.Conn
alias WebAppWeb.Router.Helpers
def init(opts), do: opts
def call(conn, _opts) do
if conn.assigns.current_user do
conn
else
conn
|> put_flash(:error, "You must be logged in to access this page.")
|> redirect(to: Routes.session_path(conn, :new))
|> halt()
end
end
end
# 7. LiveView Real-time Dashboard
defmodule WebAppWeb.DashboardLive do
use WebAppWeb, :live_view
@impl true
def mount(_params, _session, socket) do
if connected?(socket) do
:timer.send_interval(1000, self(), :update_stats)
end
socket =
socket
|> assign(:user_count, get_user_count())
|> assign(:post_count, get_post_count())
|> assign(:active_users, get_active_users())
|> assign(:recent_posts, get_recent_posts())
|> assign(:system_info, get_system_info())
{:ok, socket}
end
@impl true
def handle_info(:update_stats, socket) do
socket =
socket
|> assign(:user_count, get_user_count())
|> assign(:post_count, get_post_count())
|> assign(:active_users, get_active_users())
|> assign(:system_info, get_system_info())
{:noreply, socket}
end
@impl true
def handle_info({:new_post, post}, socket) do
recent_posts = [post | Enum.take(socket.assigns.recent_posts, 4)]
{:noreply, assign(socket, :recent_posts, recent_posts)}
end
defp get_user_count do
# Simulate database call
:rand.uniform(1000)
end
defp get_post_count do
# Simulate database call
:rand.uniform(5000)
end
defp get_active_users do
# Simulate getting active users from presence
:rand.uniform(50)
end
defp get_recent_posts do
# Simulate recent posts
[
%{title: "Elixir is awesome", author: "Alice"},
%{title: "Phoenix LiveView guide", author: "Bob"},
%{title: "Understanding OTP", author: "Charlie"},
%{title: "Ecto best practices", author: "Diana"}
]
end
defp get_system_info do
{uptime_msec, _} = :erlang.statistics(:wall_clock)
memory_info = :erlang.memory()
%{
uptime: uptime_msec / 1000,
memory_total: memory_info[:total],
memory_processes: memory_info[:processes],
process_count: length(:erlang.processes())
}
end
end
IO.puts("\n=== Phoenix Web Development Examples ===")
IO.puts("These examples demonstrate:")
IO.puts("- Phoenix controller patterns")
IO.puts("- Ecto schemas and contexts")
IO.puts("- LiveView components")
IO.puts("- Real-time channels")
IO.puts("- Authentication plugs")
IO.puts("- Dashboard with real-time updates")
IO.puts("\nIn a real Phoenix application, these would be:")
IO.puts("- Organized in separate files")
IO.puts("- Connected to actual databases")
IO.puts("- Served through the Phoenix web server")
IO.puts("\n=== Examples Complete! ===")