Avec Quickpress, nous construisons des articles éditoriaux. Nous voulons maintenant construire des Pages avec des Tiles, comme nous avons créer des Articles avec des Blocs.

Chaque Tile pourra contenir des codes qui seront compilés dans l’application et ajoutés en mode édition dans Quickpress. Nous devons donc regarder comment assembler ces pages, générer ces codes et les compiler pour les ajouter au projet.

Organisation du projet

J’imagine pouvoir créer les pages dynamiquelment à partir d’un chargement de module depuis la base de données. Par contre j’ai besoin d’avoir du code elixir dans mes Tiles qui doit être compilé pour fonctionner en mode run. Est-cil possible de compiler du code pour un projet dans le projet lui-même ou vaut il mieux avoir une partie déclarative de conception et un projet à côté dans lequel on génére le code du projet ? Est-ce possible d’ajouter du code compilé dans un projet en cours d’execution ?

Proposition

Il est possible de compiler du code Elixir dans le cadre d’un projet existant, mais cela nécessite une compréhension des mécanismes de compilation et des limitations d’Elixir. Voici quelques points à considérer :

Compilation Dynamique dans un Projet Elixir

  1. Utilisation de Mix :
  • Mix est l’outil de gestion de projet d’Elixir qui permet de créer, compiler et gérer les dépendances des projets Elixir[1][2].
  • Pour compiler le code, vous utilisez la commande mix compile, qui génère les fichiers bytecode .beam nécessaires pour l’exécution sur la machine virtuelle Erlang[6].
  1. Compilation Dynamique :
  • Il n’est pas possible d’ajouter directement du code compilé dans un projet en cours d’exécution. Cependant, vous pouvez utiliser des fonctionnalités comme Code.eval_string/2 pour évaluer dynamiquement du code Elixir au runtime, bien que cela ne compile pas le code de manière traditionnelle[8].
  • Pour gérer du code qui doit être compilé et utilisé dynamiquement, il est souvent préférable de concevoir une architecture modulaire où les composants sont précompilés et chargés dynamiquement.
  1. Approche Modulaire avec Projets Séparés :
  • Une approche consiste à avoir un projet principal et un ou plusieurs projets satellites où le code est généré et compilé séparément. Ces modules peuvent ensuite être chargés dans le projet principal via des chemins de code configurés dynamiquement[8].
  • Cette méthode permet de séparer la logique de génération et d’exécution du code, assurant ainsi une meilleure gestion des dépendances et des mises à jour.
  1. Limitations et Considérations :
  • Les modifications dynamiques du code en production doivent être gérées avec précaution pour éviter les incohérences ou les interruptions de service.
  • Utiliser des macros et le métaprogrammation peut permettre une flexibilité accrue lors de la conception des Tiles, mais cela nécessite une bonne compréhension des implications sur la compilation et l’exécution[4].

En conclusion, bien qu’il soit techniquement possible de travailler avec du code Elixir dynamique dans un projet, il est souvent plus sûr et efficace d’adopter une approche modulaire avec une séparation claire entre la génération de code et son exécution.

Citations:
[1] https://app.studyraid.com/fr/read/5702/124244/premiers-pas-avec-elixir
[2] https://elixirschool.com/fr/lessons/basics/mix
[3] https://www.tutorialspoint.com/execute_elixir_online.php
[4] https://grafikart.fr/tutoriels/presentation-elixir-896
[5] https://www.bortzmeyer.org/programming-elixir.pdf
[6] https://www.synbioz.com/blog/tech/introduction-a-elixir
[7] https://elixirforum.com/t/how-to-compile-ex-code-into-s-assembly-like-erl-s/47274
[8] https://hexdocs.pm/elixir/Code.html

Application modulaire avc Elixir

Pour mettre en œuvre une approche modulaire avec un projet principal et des projets satellites, voici comment cela peut être structuré :

Concept de Projets Satellites

L’idée est de diviser votre application en plusieurs projets plus petits, chacun étant responsable d’une partie spécifique de la logique ou des fonctionnalités. Ces projets satellites peuvent être développés, testés et compilés indépendamment du projet principal. Une fois compilés, ils peuvent être intégrés dynamiquement au projet principal.

Étapes pour Mettre en Place cette Architecture

  1. Création des Projets Satellites :
  • Utilisez Mix pour créer des nouveaux projets Elixir pour chaque module ou fonctionnalité que vous souhaitez isoler.
  • Par exemple, si vous avez une fonctionnalité de gestion d’utilisateurs, vous pouvez créer un projet satellite appelé user_management.
  1. Compilation des Projets Satellites :
  • Chaque projet satellite peut être compilé indépendamment en utilisant mix compile.
  • Cela génère les fichiers .beam nécessaires qui peuvent ensuite être partagés avec le projet principal.
  1. Intégration au Projet Principal :
  • Le projet principal peut charger dynamiquement les modules compilés des projets satellites en configurant les chemins de code.
  • Vous pouvez utiliser les options de configuration d’Elixir pour ajouter les chemins vers les fichiers .beam des projets satellites.
  1. Chargement Dynamique :
  • Utilisez Code.load_file/1 ou Code.require_file/1 pour charger les modules compilés au runtime si nécessaire.
  • Assurez-vous que le chemin vers ces modules est correctement configuré dans votre application principale.

Exemple

Supposons que vous avez un projet principal appelé main_app et un projet satellite appelé user_management.

  1. Création du Projet Satellite :
   mix new user_management
  1. Développement et Compilation :
  • Développez la logique spécifique dans le répertoire user_management/lib.
  • Compilez le projet avec :
    bash cd user_management mix compile
  1. Intégration au Projet Principal :
  • Dans le fichier mix.exs de main_app, ajoutez le chemin vers les fichiers compilés de user_management.
   defmodule MainApp.MixProject do
     use Mix.Project

     def project do
       [
         app: :main_app,
         version: "0.1.0",
         elixir: "~> 1.12",
         start_permanent: Mix.env() == :prod,
         deps: deps(),
         elixirc_paths: elixirc_paths(Mix.env())
       ]
     end

     defp elixirc_paths(:dev), do: ["lib", "../user_management/_build/dev/lib/user_management/ebin"]
     defp elixirc_paths(_), do: ["lib"]

     def application do
       [
         extra_applications: [:logger]
       ]
     end

     defp deps do
       []
     end
   end
  1. Chargement et Utilisation :
  • Dans votre code, vous pouvez maintenant utiliser les modules définis dans user_management comme s’ils faisaient partie du projet principal.

Cette approche permet une flexibilité accrue et facilite la gestion des différents composants de votre application tout en gardant une structure modulaire et maintenable.

Citations:
[1] https://exploreur.univ-toulouse.fr/des-milliers-de-satellites-proteger
[2] https://www.bpifrance.fr/sites/default/files/inline-files/Recueil%20AAP%20France%202030%20Bpifrance.pdf
[3] https://www.data-terra.org/activites/projets-techniques-scientifiques/projets-europeens/le-projet-blue-cloud-services/
[4] https://grafikart.fr/tutoriels/presentation-elixir-896
[5] https://app.studyraid.com/fr/read/5702/124244/premiers-pas-avec-elixir
[6] https://www.bortzmeyer.org/programming-elixir.pdf
[7] https://www.synbioz.com/blog/tech/introduction-a-elixir
[8] https://elixirschool.com/fr/lessons/basics/mix

Complément

Pour intégrer des projets satellites dans un projet principal en Elixir, il est effectivement nécessaire de suivre certaines étapes spécifiques pour configurer correctement les chemins de code. Voici une explication détaillée avec un exemple :

Étapes pour Intégrer des Projets Satellites

  1. Création et Compilation des Projets Satellites :
  • Vous devez d’abord créer et compiler vos projets satellites avant de configurer le projet principal.
  • Par exemple, créez un projet satellite user_management :
    bash mix new user_management cd user_management mix compile
  1. Configuration du Projet Principal :
  • Une fois que le projet satellite est compilé, vous pouvez configurer le projet principal pour inclure les chemins vers les fichiers compilés du projet satellite.
  • Créez le projet principal main_app :
    bash mix new main_app
  1. Modification du mix.exs du Projet Principal :
  • Dans le fichier mix.exs de main_app, ajoutez le chemin vers les fichiers compilés du projet satellite. Assurez-vous que le chemin est correct et accessible.
   defmodule MainApp.MixProject do
     use Mix.Project

     def project do
       [
         app: :main_app,
         version: "0.1.0",
         elixir: "~> 1.12",
         start_permanent: Mix.env() == :prod,
         deps: deps(),
         elixirc_paths: elixirc_paths(Mix.env())
       ]
     end

     defp elixirc_paths(:dev), do: ["lib", "../user_management/_build/dev/lib/user_management/ebin"]
     defp elixirc_paths(_), do: ["lib"]

     def application do
       [
         extra_applications: [:logger]
       ]
     end

     defp deps do
       []
     end
   end
  1. Chargement Dynamique des Modules :
  • Utilisez Code.append_path/1 pour ajouter dynamiquement le chemin au runtime si nécessaire.
   Code.append_path("../user_management/_build/dev/lib/user_management/ebin")
  1. Utilisation des Modules Satellites :
  • Vous pouvez maintenant utiliser les modules définis dans user_management depuis votre projet principal comme s’ils faisaient partie intégrante de celui-ci.

Exemple Complet

Voici un exemple complet de la configuration d’un projet principal utilisant un module d’un projet satellite :

# Dans user_management/lib/user_management.ex
defmodule UserManagement do
  def greet_user(name) do
    "Hello, #{name}!"
  end
end

# Dans main_app/lib/main_app.ex
defmodule MainApp do
  def start do
    IO.puts UserManagement.greet_user("Alice")
  end
end

# Dans main_app/mix.exs (comme montré ci-dessus)

Avec cette approche, vous pouvez organiser votre application en modules indépendants, facilitant la maintenance et l’évolution du code tout en gardant une structure modulaire et flexible[2][3].

Citations:
[1] https://sametmax2.com/ajouter-un-chemin-a-la-variable-denvironnement-path-sous-windows/index.html
[2] https://hexdocs.pm/elixir/Code.html
[3] https://elixirschool.com/fr/lessons/basics/mix
[4] https://grafikart.fr/tutoriels/presentation-elixir-896
[5] https://www.synbioz.com/blog/tech/introduction-a-elixir
[6] https://app.studyraid.com/fr/read/5702/124244/premiers-pas-avec-elixir
[7] https://www.bortzmeyer.org/programming-elixir.pdf
[8] https://www.tutorialspoint.com/execute_elixir_online.php

Chargement dynamique dans l’application principale

Dans l’exemple donné, nous n’avons pas utilisé Code.append_path("../user_management/_build/dev/lib/user_management/ebin") parce que nous avons configuré le chemin de compilation directement dans le fichier mix.exs du projet principal. Cette méthode permet à Elixir de connaître les chemins des modules compilés au moment de la compilation du projet principal, sans nécessiter de manipulation dynamique des chemins de code au runtime.

Pourquoi Utiliser Code.append_path/1

Code.append_path/1 est utilisé lorsque vous souhaitez ajouter dynamiquement un chemin au chemin de recherche de code de la machine virtuelle Erlang pendant l’exécution. Cela peut être nécessaire si vous avez besoin de charger des modules qui ne sont pas connus au moment de la compilation ou si vous souhaitez ajouter des chemins supplémentaires après que le projet a démarré[1][2].

Quand Utiliser Code.append_path/1

  • Chargement Dynamique : Si vous avez des modules qui ne sont pas disponibles au moment de la compilation du projet principal et que vous souhaitez les charger dynamiquement pendant l’exécution.
  • Flexibilité : Pour les applications qui nécessitent une flexibilité accrue pour charger des modules à partir de différents emplacements, par exemple, lors du développement ou du déploiement sur différents environnements.

Exemple d’Utilisation

Voici un exemple simple d’utilisation de Code.append_path/1 :

# Ajoute dynamiquement un chemin au code path
case Code.append_path("../user_management/_build/dev/lib/user_management/ebin") do
  true -> IO.puts("Path added successfully.")
  {:error, :bad_directory} -> IO.puts("Failed to add path.")
end

# Charger un module après avoir ajouté le chemin
if Code.ensure_loaded?(UserManagement) do
  IO.puts(UserManagement.greet_user("Bob"))
else
  IO.puts("Module UserManagement could not be loaded.")
end

Dans cet exemple, Code.append_path/1 est utilisé pour ajouter un chemin au runtime. Ensuite, Code.ensure_loaded?/1 vérifie si le module UserManagement est chargé et prêt à être utilisé. Cette approche est utile dans les situations où les modules doivent être chargés après le démarrage de l’application.

Citations:
[1] https://hexdocs.pm/elixir/1.12/Code.html
[2] https://hexdocs.pm/elixir/Code.html
[3] https://manpages.ubuntu.com/manpages/focal/man1/elixir.1.html
[4] https://elixirschool.com/fr/lessons/basics/mix
[5] https://www.synbioz.com/blog/tech/introduction-a-elixir
[6] https://sametmax2.com/ajouter-un-chemin-a-la-variable-denvironnement-path-sous-windows/index.html
[7] https://app.studyraid.com/fr/read/5702/124244/premiers-pas-avec-elixir
[8] https://runebook.dev/fr/docs/elixir/-index-

Analyse des options possibles

Nous pouvons créer des Tiles qui seront compilées séparemment puis intégrées dans le projet par chargement dynamique. Nous pouvons imaginer que la page se construit avec toutes les Tiles chargées dynamiquement et compilées à part. Dans ce cas, la page est décrite dans la base de données avec les Tiles dans la base de données qui connait le lien vers « ebin » pour le code du Tile, et un Mapper permettant de charger les modules avec Code.append_path puis utilisés apres vérification par Code.ensure_loaded. Est-ce que cela fonctionne aussi avec LiveView et Phoenix ?

Oui, il est possible d’utiliser Phoenix LiveView avec des Tiles compilées séparément et intégrées dynamiquement dans un projet. Voici comment cela peut être réalisé :

Intégration Dynamique des Tiles avec Phoenix LiveView

  1. Structure de la Base de Données :
  • Chaque Tile peut être décrite dans la base de données avec des informations sur son chemin de code compilé (par exemple, le chemin vers le répertoire ebin).
  • Un Mapper peut être utilisé pour récupérer ces informations et charger les modules correspondants.
  1. Chargement Dynamique des Modules :
  • Utilisez Code.append_path/1 pour ajouter dynamiquement le chemin de chaque Tile compilée au chemin de recherche de code d’Erlang.
  • Vérifiez que le module est chargé avec Code.ensure_loaded?/1 avant de l’utiliser.
  1. Utilisation avec Phoenix LiveView :
  • LiveView fonctionne en établissant une connexion WebSocket entre le client et le serveur, permettant des mises à jour en temps réel sans rechargement complet de la page[1][4].
  • Vous pouvez intégrer les Tiles dynamiquement chargées dans vos LiveViews en utilisant les modules chargés pour rendre des parties spécifiques de la page.

Exemple d’Implémentation

Supposons que vous ayez une table tiles dans votre base de données avec une colonne code_path qui stocke le chemin vers le code compilé du Tile.

# Exemple de fonction pour charger et utiliser un Tile
defmodule TileLoader do
  def load_tile(tile_id) do
    # Récupérer le chemin du code depuis la base de données
    tile = Repo.get!(Tile, tile_id)
    path = tile.code_path

    # Ajouter dynamiquement le chemin au code path
    Code.append_path(path)

    # Vérifier si le module est chargé
    if Code.ensure_loaded?(tile.module_name) do
      # Utiliser le module pour rendre une partie de la page
      apply(tile.module_name, :render, [])
    else
      {:error, "Module not loaded"}
    end
  end
end

# Exemple d'utilisation dans un LiveView
defmodule MyAppWeb.PageLive do
  use Phoenix.LiveView

  def mount(_params, _session, socket) do
    {:ok, assign(socket, tiles: load_tiles())}
  end

  defp load_tiles() do
    # Charger et rendre chaque Tile nécessaire pour la page
    Enum.map(Tile.all(), &TileLoader.load_tile(&1.id))
  end
end

Avantages et Limitations

  • Avantages :
    • Modularité : Permet de structurer l’application en composants réutilisables.
    • Scalabilité : Les Tiles peuvent être développées et mises à jour indépendamment.
  • Limitations :
    • Complexité : La gestion dynamique des chemins et des modules peut ajouter une couche de complexité.
    • Performance : Le chargement dynamique peut avoir un impact sur les performances si mal géré.

En résumé, cette approche est compatible avec Phoenix LiveView et permet une grande flexibilité dans la construction dynamique des pages tout en maintenant les avantages du rendu en temps réel offert par LiveView.

Citations:
[1] https://elixirschool.com/blog/live-view-with-pub-sub
[2] https://www.paulfioravanti.com/blog/internationalisation-phoenix-liveview/
[3] https://elixirforum.com/t/backpex-a-highly-customizable-admin-panel-for-phoenix-liveview-applications/64314
[4] https://www.verytechnology.com/iot-insights/phoenix-liveview-the-iot-front-end-solution
[5] https://www.a4word.com/articles/liveview-examples
[6] https://runebook.dev/fr/docs/elixir/-index-
[7] https://www.reddit.com/r/elixir/comments/18m0dfl/whats_the_catch_with_phoenix_liveview/
[8] https://hexdocs.pm/elixir/1.12/Code.html

Tiles et modules métiers

Dans certains cas, les Tiles doivent pouvoir accéder à des modules métier. Exemple, si nous avons une Tile pour afficher des articles, nous avons besoin d’accéder au code qui permet d’afficher un article. Comment fait on pour avoir une compilation de la Tile qui « connaisse » le module métier Articles ?

Pour permettre à une Tile de connaître et d’accéder à un module métier comme Articles, vous devez vous assurer que le module métier est disponible au moment de la compilation de la Tile. Voici comment vous pouvez procéder :

Étapes pour Intégrer un Module Métier dans une Tile

  1. Définir les Dépendances :
  • Assurez-vous que le module métier Articles est défini dans un projet ou une bibliothèque accessible par votre projet principal et les Tiles.
  1. Utiliser Code.ensure_compiled!/1 :
  • Avant de compiler la Tile, utilisez Code.ensure_compiled!/1 pour vous assurer que le module Articles est bien compilé et disponible. Cela garantit que le module est prêt à être utilisé lors de la compilation de la Tile.
  • Par exemple : defmodule MyTile do @moduledoc """ A tile that displays articles. """ # Assure que le module Articles est compilé Code.ensure_compiled!(Articles) def render(article_id) do article = Articles.get_article(article_id) # Logic to render the article end end
  1. Configuration du Projet :
  • Si Articles est dans un projet séparé, assurez-vous que ce projet est listé comme dépendance dans le fichier mix.exs du projet principal. Cela garantit que toutes les dépendances nécessaires sont compilées avant l’utilisation.
  1. Chargement Dynamique des Tiles :
  • Lors du chargement dynamique des Tiles, utilisez Code.append_path/1 pour ajouter les chemins nécessaires si les Tiles sont compilées séparément.
  • Utilisez ensuite Code.ensure_loaded?/1 pour vérifier que les modules requis sont chargés avant leur utilisation.

Exemple d’Intégration

Supposons que vous ayez un module Articles qui fournit des fonctions pour accéder aux articles :

defmodule Articles do
  def get_article(id) do
    # Logique pour récupérer l'article depuis la base de données
  end
end

Et une Tile qui affiche un article :

defmodule ArticleTile do
  @moduledoc """
  A tile for displaying an article.
  """

  # Vérifie que le module Articles est compilé et chargé
  Code.ensure_compiled!(Articles)

  def render(article_id) do
    case Code.ensure_loaded?(Articles) do
      true ->
        article = Articles.get_article(article_id)
        # Logique pour afficher l'article

      false ->
        {:error, "Module Articles not loaded"}
    end
  end
end

Conclusion

En suivant ces étapes, vous pouvez vous assurer que vos Tiles ont accès aux modules métiers nécessaires lors de leur compilation et exécution. Cela permet une intégration fluide avec Phoenix LiveView, tout en maintenant une architecture modulaire et flexible.

Citations:
[1] https://hexdocs.pm/elixir/Module.html
[2] https://hexdocs.pm/elixir/1.12/Code.html
[3] https://hexdocs.pm/elixir/Code.html
[4] https://hexdocs.pm/elixir/introduction-to-mix.html
[5] https://runebook.dev/fr/docs/elixir/-index-
[6] https://elixirschool.com/fr/lessons/basics/mix
[7] https://www.paulfioravanti.com/blog/internationalisation-phoenix-liveview/
[8] https://elixirforum.com/t/backpex-a-highly-customizable-admin-panel-for-phoenix-liveview-applications/64314

LiveComponent et module dynamique

Nous pouvons construire des modules LiveComponent compilés à part et les intégrer dans le projet grâce à un Mapper qui chargera les informations du LiveComponent depuis la base de données et chargera le code dynamiquement grâce au chemin connu dans la base de données.

Pour créer un ArticleTileLiveComponent qui est compilé séparément et intégré dynamiquement dans une page LiveView, vous pouvez suivre les étapes suivantes. Cela inclut l’utilisation de HEEx pour le rendu des composants et le chargement dynamique des modules.

Étapes pour Créer et Intégrer un LiveComponent

1. Création du Composant ArticleTileLiveComponent

Définissez votre composant en utilisant Phoenix.LiveComponent. Ce composant peut être compilé séparément et doit inclure les fonctions nécessaires pour gérer son état et son rendu.

defmodule MyAppWeb.ArticleTileLiveComponent do
  use Phoenix.LiveComponent

  def mount(socket) do
    {:ok, socket}
  end

  def update(assigns, socket) do
    # Charger dynamiquement le module métier si nécessaire
    path = assigns[:tile_code_path]
    module_name = assigns[:tile_module_name]

    case Code.append_path(path) do
      true -> IO.puts("Path added successfully.")
      {:error, :bad_directory} -> IO.puts("Failed to add path.")
    end

    if Code.ensure_loaded?(module_name) do
      {:ok, assign(socket, assigns)}
    else
      {:error, "Module not loaded"}
    end
  end

  def render(assigns) do
    ~H"""
    <div class="article-tile">
      <h2><%= @article.title %></h2>
      <p><%= @article.content %></p>
    </div>
    """
  end
end

2. Intégration dans une Page LiveView avec HEEx

Utilisez HEEx pour rendre le composant dans votre page LiveView. Assurez-vous de passer les paramètres nécessaires, y compris le chemin du code et le nom du module.

defmodule MyAppWeb.PageLive do
  use Phoenix.LiveView

  def mount(_params, _session, socket) do
    # Supposons que vous récupérez les articles et les informations des Tiles depuis la base de données
    articles = Articles.get_all()
    tiles_info = fetch_tiles_info() # Fonction fictive pour récupérer les infos des Tiles

    {:ok, assign(socket, articles: articles, tiles_info: tiles_info)}
  end

  def render(assigns) do
    ~H"""
    <div id="articles">
      <%= for {article, tile_info} <- Enum.zip(@articles, @tiles_info) do %>
        <.live_component 
          module={MyAppWeb.ArticleTileLiveComponent} 
          id={article.id} 
          article={article} 
          tile_code_path={tile_info.code_path} 
          tile_module_name={tile_info.module_name} />
      <% end %>
    </div>
    """
  end
end

Explications

  • Chargement Dynamique : Utilisez Code.append_path/1 pour ajouter le chemin vers le code compilé de la Tile. Vérifiez ensuite que le module est chargé avec Code.ensure_loaded?/1.
  • HEEx : Utilisez la syntaxe <.live_component> pour intégrer le composant dans votre template HEEx.
  • Assignations Dynamiques : Passez les informations nécessaires au composant via les assigns, ce qui permet au composant de gérer son propre état et d’accéder aux modules requis.

Cette approche permet de structurer votre application de manière modulaire tout en tirant parti des capacités dynamiques de Phoenix LiveView et HEEx pour rendre des composants interactifs et réactifs.

Citations:
[1] https://adopt-liveview.lubien.dev/guides/live-component/en
[2] https://hexdocs.pm/phoenix_live_view/Phoenix.LiveComponent.html
[3] https://hexdocs.pm/phoenix_live_view/Phoenix.Component.html
[4] https://blog.appsignal.com/2022/01/11/build-interactive-phoenix-liveview-uis-with-components.html
[5] https://elixirforum.com/t/when-to-use-a-live-component-instead-of-a-functional-component/58775
[6] https://curiosum.com/blog/phoenix-component
[7] https://stackoverflow.com/questions/76850638/how-to-get-app-html-heex-to-use-assigns-properly-with-liveview
[8] https://elixirschool.com/blog/live-view-with-pub-sub

Architecture du projet

Nous venons de voir que nous pouvons construire des LiveComponent pour chaque Tile. Les Tiles sont compilés à part et le lien vers le code généré pour chaque Tile est enregistré dans la base de données. Puis nous pouvons utilisés ces Tiles pour construire la page comme ce que nous avons fait pour les articles, avec la particularité e plus de pouvoir ajouter du code dans les Tiles, code qui sera compilé lors de la création du LiveComponent de la Tile.

Nous avons envisagé d’avoir une base de données « spec »qui contiendra la description du projet et les étapes de sa construction. Nous venons maintenant d’ajouter à cette architecture la possibilité de construire des Tiles comme des articles. Ensuite à partir de la base « spec » , nous pouvons construire les modules, les compilés et les fournis à l’application. Nous devons maintenant regarder s’il est possible de stocket le code généré pour les Tiles dans la base de données.

Stocker le code généré pour les Tiles dans une base de données est une approche intéressante qui peut offrir une grande flexibilité pour la gestion et la mise à jour de votre application. Voici comment vous pouvez procéder pour stocker, compiler, et intégrer dynamiquement le code des Tiles à partir de la base de données :

Étapes pour Stocker et Utiliser le Code des Tiles

1. Structure de la Base de Données

Créez une table pour stocker les informations relatives aux Tiles, y compris le code source. Voici un exemple de structure :

CREATE TABLE tiles (
  id SERIAL PRIMARY KEY,
  name VARCHAR(255),
  module_name VARCHAR(255),
  code TEXT,
  compiled_path VARCHAR(255)
);
  • name : Nom de la Tile.
  • module_name : Nom du module Elixir associé à la Tile.
  • code : Le code source Elixir de la Tile.
  • compiled_path : Chemin vers le répertoire où le code compilé est stocké.

2. Stockage et Récupération du Code

Lorsque vous créez ou mettez à jour une Tile, stockez son code source dans la colonne code. Vous pouvez utiliser Ecto pour gérer ces opérations.

3. Compilation du Code

Pour compiler le code stocké dans la base de données :

  • Récupérez le code source depuis la base de données.
  • Écrivez ce code dans un fichier temporaire.
  • Utilisez Mix.Task.run("compile", ["--force"]) ou Code.compile_file/1 pour compiler le fichier.

Voici un exemple en Elixir :

defmodule TileCompiler do
  alias MyApp.Repo
  alias MyApp.Tile

  def compile_tile(tile_id) do
    tile = Repo.get!(Tile, tile_id)

    # Écrire le code source dans un fichier temporaire
    file_path = "/tmp/#{tile.module_name}.ex"
    File.write!(file_path, tile.code)

    # Compiler le fichier
    case Code.compile_file(file_path) do
      {:ok, _modules} ->
        # Mettre à jour le chemin compilé dans la base de données
        Repo.update!(Tile.changeset(tile, %{compiled_path: "/tmp/#{tile.module_name}.beam"}))
        :ok

      {:error, reason} ->
        {:error, reason}
    end
  end
end

4. Intégration Dynamique dans l’Application

Une fois compilé, utilisez Code.append_path/1 et Code.ensure_loaded?/1 pour charger dynamiquement le module dans votre application LiveView.

Considérations

  • Sécurité : Assurez-vous que le code stocké et exécuté ne contient pas de vulnérabilités ou d’exploits potentiels.
  • Performance : La compilation dynamique peut être coûteuse en termes de performances. Assurez-vous d’optimiser ce processus.
  • Gestion des Erreurs : Implémentez une gestion des erreurs robuste pour gérer les échecs de compilation ou d’exécution.

En résumé, cette approche permet une grande flexibilité et modularité en permettant de stocker et de gérer dynamiquement les composants d’une application directement depuis une base de données.

Stockage en fichier ou en base du code compilé

Stocker du code compilé directement dans une base de données présente plusieurs défis techniques et conceptuels. Voici quelques points à considérer :

Possibilité de Stocker du Code Compilé

  1. Nature du Code Compilé :
  • Le code compilé en Elixir est généralement transformé en fichiers bytecode .beam, qui sont ensuite exécutés par la machine virtuelle Erlang.
  • Ces fichiers .beam peuvent être considérés comme des blobs binaires, qui pourraient théoriquement être stockés dans une base de données.
  1. Stockage des Blobs dans une Base de Données :
  • Certaines bases de données, comme PostgreSQL, permettent le stockage de données binaires sous forme de blobs (Binary Large Objects). Cela pourrait être utilisé pour stocker les fichiers .beam[6].
  1. Considérations Pratiques :
  • Performance : Récupérer et charger des fichiers compilés à partir d’une base de données pourrait introduire une latence significative, surtout si le volume est important.
  • Complexité : La gestion des mises à jour et des versions du code compilé dans la base peut devenir complexe.
  • Sécurité : Stocker du code exécutable dans une base de données pose des questions de sécurité, notamment en ce qui concerne l’injection de code malveillant.

Alternatives et Recommandations

  • Stockage sur le Système de Fichiers : Une approche plus courante consiste à stocker le code compilé sur le système de fichiers, tout en gardant les chemins d’accès dans la base de données. Cela permet un accès plus rapide et simplifie la gestion des fichiers[5].
  • Utilisation d’un Système de Gestion des Versions : Pour gérer les versions et les mises à jour du code compilé, un système de gestion des versions (comme Git) peut être utilisé parallèlement au stockage des chemins dans la base.
  • Déploiement Automatisé : Utiliser des scripts pour déployer automatiquement le code compilé sur les serveurs, en s’assurant que les dernières versions sont toujours disponibles.

En conclusion, bien que techniquement possible, stocker directement le code compilé dans une base de données n’est pas recommandé en raison des complications potentielles liées à la performance, la sécurité, et la complexité opérationnelle. Une approche hybride utilisant le système de fichiers pour le stockage réel et la base de données pour la gestion des métadonnées est souvent plus efficace.

Citations:
[1] https://developer.4d.com/docs/fr/18/Concepts/interpreted-compiled/
[2] https://learn.microsoft.com/fr-fr/sql/relational-databases/in-memory-oltp/native-compilation-of-tables-and-stored-procedures?view=sql-server-ver16
[3] https://cloud.google.com/appengine/docs/standard/storage-options?hl=fr
[4] https://hello-pomelo.com/articles/quel-systeme-de-gestion-de-bases-de-donnees-choisir/
[5] https://code-garage.fr/blog/stocker-des-donnees-dans-un-fichier-ou-une-bdd
[6] https://sgbd.developpez.com/actu/360800/SQLite-la-base-de-donnees-la-plus-utilisee-au-monde-est-35-pourcent-plus-rapide-que-le-systeme-de-fichiers-et-utilise-environ-20-pourcent-d-espace-disque-en-moins-que-le-stockage-des-blobs-dans-des-fichiers-individuels/
[7] https://www.synbioz.com/blog/tech/introduction-a-elixir
[8] https://www.jedha.co/blog/gerer-des-bases-de-donnees-sql

Utilisation de Git

Peux tu être plus précis, je ne connais pas bien git. Supposons que je souhaite ajouter un nouveau Tile. comment dois-je faire. Pour l’instant, je vais avoir mon éditeur PlugCode qui va me permettre de décrire mon Tile. Puis j’ai une étape de génération du code source de ma Tile que je vais ranger dans ma base de données pour ma Tile et enfin j’ai la compilation de mon code avec les chemins pour l’utilisation dans le projet. Comment s’inscrit l’utilisation de Git la dedans ?

Pour intégrer l’utilisation de Git dans votre flux de travail pour ajouter un nouveau Tile, voici un guide étape par étape qui vous aidera à comprendre comment gérer vos Tiles avec Git, depuis la création jusqu’à l’intégration dans votre projet :

Étapes pour Ajouter un Nouveau Tile avec Git

1. Initialisation du Dépôt Git

Si ce n’est pas déjà fait, initialisez un dépôt Git dans votre projet principal où toutes vos Tiles seront gérées.

cd /chemin/vers/votre/projet
git init

2. Création d’une Branche pour le Nouveau Tile

Créez une nouvelle branche pour le développement de votre nouveau Tile. Cela permet de travailler sur des modifications sans affecter la branche principale.

git checkout -b feature/nouveau-tile

3. Développement et Génération du Code Source

  • Utilisez votre éditeur PlugCode pour décrire et générer le code source de votre nouveau Tile.
  • Stockez ce code source dans la base de données associée à la Tile.

4. Ajout du Code Source au Dépôt

Une fois que le code source est généré et prêt, ajoutez-le au dépôt Git. Cela inclut généralement les fichiers de code source et tout autre fichier nécessaire.

git add chemin/vers/le/code/source
git commit -m "Ajout du nouveau Tile"

5. Compilation et Stockage

Compilez le code source pour générer les fichiers .beam. Vous pouvez également stocker ces fichiers compilés sur le système de fichiers ou dans la base de données si nécessaire.

6. Intégration et Test

Intégrez le nouveau Tile dans votre projet principal pour tester son fonctionnement. Assurez-vous que toutes les dépendances sont correctement résolues et que le module est chargé dynamiquement si nécessaire.

7. Fusionner les Modifications

Une fois que vous êtes satisfait des modifications, fusionnez la branche feature/nouveau-tile avec la branche principale (par exemple, main ou master).

git checkout main
git merge feature/nouveau-tile

8. Pousser les Modifications vers un Dépôt Distant

Si vous utilisez un service comme GitHub ou GitLab, poussez vos modifications vers le dépôt distant pour sauvegarder votre travail et permettre la collaboration avec d’autres développeurs.

git push origin main

Avantages de cette Approche

  • Isolation des Changements : Travailler sur une branche séparée permet d’isoler les changements jusqu’à ce qu’ils soient prêts à être fusionnés.
  • Historique Clair : Chaque commit documente un changement spécifique, facilitant le suivi des modifications.
  • Collaboration : Les autres développeurs peuvent examiner vos modifications via des pull requests avant qu’elles ne soient fusionnées.

En suivant ces étapes, vous pouvez efficacement gérer l’ajout et l’intégration de nouveaux Tiles dans votre projet tout en utilisant les meilleures pratiques de gestion de version avec Git.

Citations:
[1] https://www.axopen.com/blog/2017/02/git-comprendre-la-gestion-de-versions/
[2] https://developer.android.com/codelabs/wear-tiles?hl=fr
[3] https://www.univ-orleans.fr/iut-orleans/informatique/intra/tuto/git-tutoriel/systemes-gestion-version.html
[4] https://data-ai.theodo.com/fr/parlons-data/qu-est-ce-que-git-guide-complet-sur-le-systeme-de-gestion-de-versions-du-code
[5] https://github.com/collective/collective.tiles.collection
[6] https://switch2osm.org/serving-tiles/manually-building-a-tile-server-20-04-lts/
[7] https://stackoverflow.com/questions/66231282/how-to-add-a-github-personal-access-token-to-visual-studio-code
[8] https://git-scm.com/book/fr/v2/Commandes-Git-Cr%C3%A9ation-de-branches-et-fusion

Si vous avez aimé l'article vous êtes libre de le partager :-)