Exemples Elixir
Exemples essentiels de programmation Elixir pour les systèmes concurrents et distribués
Key Facts
- Category
- Programming Languages
- Items
- 3
- Format Families
- sample
Sample Overview
Exemples essentiels de programmation Elixir pour les systèmes concurrents et distribués This sample set belongs to Programming Languages and can be used to test related workflows inside Elysia Tools.
💻 Elixir Hello World elixir
🟢 simple
⭐⭐
Programme Hello World de base et concepts fondamentaux de programmation fonctionnelle
⏱️ 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
defprotocol Greeter do
@doc "Greets any type that implements this protocol"
def greet(data)
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! ===")
💻 Concurrence Elixir et OTP elixir
🟡 intermediate
⭐⭐⭐⭐
Patterns avancés de concurrence utilisant GenServer, Supervisor et 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! ===")
💻 Développement Web Elixir Phoenix elixir
🟡 intermediate
⭐⭐⭐⭐
Construction d'applications web avec framework Phoenix, Ecto et 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! ===")