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! ===")