diff --git a/cours/sem6/sem6.md b/cours/sem6/sem6.md
deleted file mode 100644
index 7bbec21..0000000
--- a/cours/sem6/sem6.md
+++ /dev/null
@@ -1,712 +0,0 @@
----
-marp: true
-theme: default
-paginate: true
-backgroundColor: #fff
-footer: WEB : Ruby - Semaine 6 - Active Record / Sessions / Authentification
----
-
-
-
-
-
-# ActiveRecord / Sessions
-
-* inverse_of
-* Includes
-* Join
-* Cookies
-* Session
-* Authentification
-
----
-
-### Associations bi-directionnelle : Détection automatique
-
-* Rails détecte les associations bi-directionnelle à partir du nom des associations.
-
-```ruby
-class Author < ApplicationRecord
- has_many :books
-end
-
-class Book < ApplicationRecord
- belongs_to :author
-end
-```
-
-```ruby
-irb> author = Author.first
-irb> book = authors.books.first
-irb> author.first_name == book.first.author.first_name # => true
-irb> author.first_name = 'David'
-irb> author.first_name == book.author.first_name # => true ; author.object_id == book.author.object_id
-```
-
-* Active Record ne charge qu'une seule copie de l'objet Author, ça évite une requête à la base de donnée, et évite les données incohérentes.
-
----
-
-### Associations bi-directionnelle : Détection automatique impossible
-
-* Rails ne parvient pas à determiner les associations inverses quand on utilise `:through`, `:foreign_key`, `:order`,
-
-```ruby
-class Author < ApplicationRecord
- has_many :books, inverse_of: 'writer'
-end
-
-class Book < ApplicationRecord
- belongs_to :writer, class_name: 'Author', foreign_key: 'author_id'
-end
-```
-
-```ruby
-irb> author = Author.first
-irb> book = authors.books.first
-irb> author.first_name == book.first.author.first_name # => true
-irb> author.first_name = 'David'
-irb> author.first_name == book.author.first_name # => false ; author.object_id != book.author.object_id
-```
-
----
-
-### Associations bi-directionnelle : inverse_of
-
-* `inverse_of` permet d'indiquer quelle est la relation inverse d'un `belongs_to`, `has_one`, `has_many`
-
-```ruby
-class Author < ApplicationRecord
- has_many :books, inverse_of: 'writer'
-end
-
-class Book < ApplicationRecord
- belongs_to :writer, class_name: 'Author', foreign_key: 'author_id'
-end
-```
-
-```ruby
-irb> author = Author.first
-irb> book = authors.books.first
-irb> author.first_name == book.first.author.first_name # => true
-irb> author.first_name = 'David'
-irb> author.first_name == book.author.first_name # => true ; author.object_id == book.author.object_id
-```
-
----
-
-### Includes : eager loader les données
-
-* Eviter le N+1
-
-```erb
-<% Message.limit(25).each do |message| %>
-
- <%= message.title %>
- <%= message.author.name %>
-
-<% end %>
-
-# (0.2ms) SELECT "messages".* FROM "messages"
-# (0.2ms) SELECT "authors".* FROM "authors" WHERE "author"."id" = ?
-# (0.2ms) SELECT "authors".* FROM "authors" WHERE "author"."id" = ?
-# (0.2ms) SELECT "authors".* FROM "authors" WHERE "author"."id" = ?
-....
-# => 26 requêtes
-```
-
----
-
-### Includes : eager loader les données
-
-* Toutes les associations spécifiées sont chargées en utilisant le nombre minimum de requêtes possible.
-
-```erb
-<% Message.includes(:author).limit(10).each do |message| %>
-
- <%= message.title %>
- <%= message.author.name %>
-
-<% end %>
-
-# (0.2ms) SELECT "messages".* FROM "messages"
-# (0.3ms) SELECT "authors".* FROM "authors" WHERE "authors"."id" IN (?, ?, ..)
-
-# => 2 requêtes
-```
-
----
-
-### Includes : eager loader les associations
-
-* On peut spécifier une liste d'associations
-
-```ruby
-Message.includes(:author, :comments)
-```
-
-* Et des associations imbriquées
-
-```ruby
-Customer.includes(orders: {books: [:supplier, :author]}).find(1)
-```
-
----
-
-### Join : INNER JOIN
-
-* Jointure à partir d'une ou plusieurs associations
-
-```ruby
-Book.joins(:comments) # Les livres ayant des commentaires
-# SELECT books.* FROM books
-# INNER JOIN comments ON comments.book_id = books.id
-```
-
-* Récupère un livre pour chaque livre avec un commentaire
-
- * Attention aux doublons, pour les éviter : `Book.joins(:comments).distinct`
-
-* Jointure à partir de plusieurs associations
-
-```ruby
-Book.joins(:author, :comments) # Les livres avec leur auteur qui ont au moins un commentaire
-# SELECT books.* FROM books
-# INNER JOIN authors ON authors.id = books.author_id
-# INNER JOIN comments ON comments.book_id = books.id
-```
----
-
-### Join : INNER JOIN
-
-* Jointures imbriquées
-
-```ruby
-Book.joins(comments: :customer) # Les livres qui ont un commentaire par un client
-# SELECT books.* FROM books
-# INNER JOIN reviews ON reviews.book_id = books.id
-# INNER JOIN customers ON customers.id = reviews.customer_id
-```
-
-* Spécifier des conditions
-
-```ruby
-Book.joins(:comments).where('comments.created_at' => (1.week.ago..Time.now).distinct
-
-# Les livres uniques avec des commentaires datant de moins d'une semaine.
-
-Book.joins(:author).where('author.first_name' => "Nabilla" ).distinct
-
-# Les livres uniques avec un auteur qui se prénomme Nabilla.
-```
----
-
-### Cookies
-
-* Les cookies permettent de stocker des informations sur le navigateur de l'utilisateur.
-
-* Limité à une petite quantité (4ko) de texte
-
-* Ils sont stockés en clair sur la navigateur et facilement accessibles, recopiables, modifiables
-
- * On ne stocke donc dedans aucune donnée sensible !
-
-* Ils ont une date d'expiration (par défaut à la fin de la session du navigateur)
-
-* On peut aussi y accéder en Javascript (sauf si on l'interdit explicitement)
-
----
-
-### Cookies - Header HTTP Set-cookie
-
-* Si le serveur souhaite créer ou modifier des cookies, ils sont envoyés dans le header `Set-Cookie` de la réponse HTTP.
-
-```
-HTTP/2.0 200 OK
-Content-Type: text/html
-Set-Cookie: delicieux_cookie=choco
-Set-Cookie: savoureux_cookie=menthe
-
-[contenu de la page]
-```
-
-* Peut spécifier la date d'expiration (`Expires`), restreindre l'accès (`Secure`, `HttpOnly`), et définir où les cookies sont envoyés (`Domain`, `Path`, `SameSite`)
-
-```
-Set-Cookie: savoureux_cookie=menthe; Expires=Mon, 27 Mar 2050 07:28:00 GMT; Secure; HttpOnly
-```
-
----
-
-### Cookies - Header HTTP Cookie
-
-* Pour toutes requêtes suivantes, le client envoie tous les cookies enregistrés (qui ont le droit d'être envoyés), dans le header `Cookie` de la requête HTTP.
-
-```
-GET /page_exemple.html HTTP/2.0
-Host: www.example.org
-Cookie: delicieux_cookie=choco; savoureux_cookie=menthe
-```
-
-* Dans Rails, le module `ActionController#cookies` permet de lire et écrire les cookies HTTP.
-
----
-
-### Cookies - ActionController#cookies - Écrire un cookie
-
-* Écrire un cookie basique
-
-```ruby
-cookies[:user_name] = 'david'
-
-# On doit sérialiser "à la main" les données
-cookies[:lat_lon] = JSON.generate([47.68, -122.37])
-```
-
-* Préciser une date d'expiration
-
-```ruby
-cookies[:login] = { value: "XJ-122", expires: Time.utc(2020, 10, 15, 5) }
-
-cookies[:login] = { value: "XJ-122", expires: 1.hour }
-
-# Définir un cookie 'permanent', qui expire dans 20 ans
-cookies.permanent[:login] = "XJ-122"
-```
----
-
-### Cookies - ActionController#cookies - Écrire un cookie (suite)
-
-* Sécuriser les cookies
-
-```ruby
-# Définir un cookie signé, qui empêche la modification de la valeur du cookie
-cookies.signed[:user_id] = current_user.id
-
-# Définir un cookie chiffré, qui empêche la modification et la lecture de sa valeur
-cookies.encrypted[:discount] = 45
-
-# Définir un cookie chiffré, qui expire dans 20 ans
-cookies.signed.permanent[:login] = 'XJ-122'
-```
-
-* Supprimer un cookie
-
-```ruby
-cookies.delete :user_name
-```
-
----
-### Cookies - ActionController#cookies
-
-* `cookies[:name]` permet de lire le cookie
-
-```ruby
-cookies[:user_name] # => "david"
-cookies[:login] # => "XJ-122"
-
-# On doit sérialiser les données le cas échéant
-JSON.parse(cookies[:lat_lon]) # => [47.68, -122.37]
-
-# Lire des cookies sécurisés
-# encrypted et signed retournent nil si le cookie a été modifié
-cookies.encrypted[:discount] # => 45
-cookies.signed[:login] # => "XJ-122"
-
-# Pour connaître le nombre de cookies
-cookies.size # => 2
-```
-
----
-
-### Sessions: HTTP : Protocole stateless
-
-* HTTP est un protocole sans état, c'est à dire que le serveur oubli le client entre chaque requête.
-
-* Chaque requête est indépendante, et tous les clients sont traités de la même manière.
-
-* Plus simple pour le serveur, mais peu rendre compliqué et lourd le mécanisme d'identification
-
-* Plutôt utilisé par les APIs
-
-* Un exemple d'authentification stateless: La `basic authentication` - On fournit un header spécifique
- * `Authorization: Basic `
- * Où `` = base64(:)
-
-
----
-
-### Sessions: HTTP : Authentification stateless
-
-```ruby
-class ApplicationController < ActionController::Base
- before_action :authenticate_http_basic
-
- def authenticate_http_basic
- # Helper de rails pour parser le header Authorization en mode basic
- authenticate_with_http_basic do |username, password|
- # Gestion des passwords chiffrés
- user = User.find_by(username: username)
- @current_user = user.authenticate(password) if user
- end
- end
-end
-```
-
----
-
-### Sessions : HTTP "stateful"
-
-* Permet d'ajouter du "stateful" à l'application, en fournissant une session par utilisateur pour y lire et y stocker des données.
-
-* Lors d'une requête, si le serveur ne reçoit pas d’identifiant de session, il va en créer un et va le transmettre dans sa réponse dans un cookie.
-
-* Le client va donc transmettre ensuite cet identifiant de session à chaque requête.
-
-* Par défaut Rails stocke toute la session (id et contenu) sur le client dans un cookie chiffré et ne nécessite aucune configuration.
-
- * On stockera en général peu de choses en session. (ce n'est pas utile et on est limité par la taille max du cookie)
-
- * On peut modifier ce comportement et stocker les sessions en cache, en base de donnée, ou via memcached (mais cela a ses inconvénients).
----
-
-### Sessions : Accès
-
-* Écriture
-
-```ruby
-session[:user_id] = 5
-session[:last_activity] = Time.zone.now
-session[:active_filters] = {
- department: :all,
- groups: [:web1, :web2, :web3],
-}
-```
-
-* Lecture
-
-```ruby
-current_user = User.find(session[:user_id])
-session[:last_activity].class # => ActiveSupport::TimeWithZone
-session[:active_filters][:groups].include?(:web3) # => true
-```
-
-* Rails serialize automatiquement les données à la lecture et l'écriture
-
----
-
-### Sessions : Reset
-
-* `reset_session` supprime la session actuelle et en créé une nouvelle vide
-
-```ruby
-class UserSessionsController < ActionController::Base
- def logout
- reset_session
- @current_user = nil
- end
-end
-```
-
----
-
-### Sessions : Authentification stateful - Login
-
-```ruby
-class UserSessionsController < ApplicationController
- skip_before_action :check_login # slide suivante
-
- def create
- if params[:username] && params[:password]
- user = User.find_by(username: params[:username])
- if user && user.authenticate(params[:password])
- # La session est disponible partout
- session[:current_user] = user
- redirect_to root_url
- return
- end
- end
-
- redirect_to(login_url)
- end
-end
-```
----
-
-### Sessions : Retrouver un utilisateur logué
-
-```ruby
-class ApplicationController < ActionController::Base
- before_action :check_login
-
- def check_login
- if session[:current_user]
- @current_user = session[:current_user]
- else
- redirect_to(login_url)
- end
- end
-end
-```
-
----
-
-
-
-
-
-
-# Correction TP
-
-* Strong Params
-* CRUD
-* Enums
-* Migration en 2 (ou 3 temps)
-* Gérer les foreign keys
-* Filtrer des queries
-
----
-
-### Strong Parameters
-
-* Permettent de valider des inputs
-
-* Rails auto-wrap les params dans une clef qui correspond à la resource du contrôleur
-Exemple: `CreaturesController`, rails wrappe tout dans `creature`.
-
-```ruby
-class CreaturesController
- private
- def create_params
- permitted = params.require(:creature).permit(:name)
- permitted.require(:name) # rend le nom obligatoire
- permitted
- end
-end
-```
-
-On peut forcer ou autoriser la présence d'un paramètre.
-
----
-
-### CRUD + L
-
-```ruby
-class CreaturesController < ApplicationController
- # route: get '/creatures', to: 'creatures#index'
- def index # liste les créatures
- end
- # route: get '/creatures/:id', to: 'creatures#show'
- def show # affiche une créature
- end
- # route: post '/creatures', to: 'creatures#create'
- def create # créer une créature
- end
- # route: put '/creatures/:id', to: 'creatures#update'
- def update # mettre à jour une créature
- end
- # route: delete '/creatures', to: 'creatures#destroy'
- def destroy # supprimer une créature
- end
-end
-```
-
----
-
-### Enumération
-
-* Concept ActiveRecord pour gérer des flags facilement, basé sur des colonnes `integer`
-
-```ruby
-class AddSizeColumnToCreatures < ActiveRecord::Migration[7.0]
- def change
- add_column :creatures, :size, :integer
- end
-end
-```
-
-```ruby
-class Creature < ApplicationRecord
- enum :size, [:small, :big, :giant]
-end
-```
-
-```ruby
-creature = Creature.new(name: "Big Chungus", health_points: 42, size: :big)
-```
-
----
-
-### Migrations en plusieurs phases
-
-Si on veut ajouter un nouveau champ en base de données :
-
-1. On met à jour le schéma pour ajouter le champ (1 migration)
-
-2. On met à jour la partie du code qui affecte le champ (1 update de code)
-
-3. On met à jour les records de la base qui n'ont pas le champ (1 migration)
-
-4. On met à jour le schéma pour forcer la présence du champ (1 migration optionnelle)
-
-5. On affiche et on utilise le champ pour les raisons métier (# updates de code)
-
-Dans le TP :
-
-Ajout de `size` -> Ajout du callback pour setter la `size` -> Update des records en DB -> Affichage de la `size`
-
----
-
-### Ajouter des Foreign Keys
-
-ActiveRecord vient avec le concept de référence qui simplifie. Il ajoute :
-
-* Le champ pour l'id (ex: `left_fighter_id`)
-* L'index pour le champ id (ex: `index_on_left_fighter_id`)
-* La Foreign Key pour le champ en question pour lier les tables
-* Si le champ référence ne s'appelle pas comme une table (ex: `creature_id`), il faut préciser la table.
-
-```ruby
-create_table :combats do |t|
- # ...
- t.references :left_fighter, null: false, foreign_key: { to_table: 'creatures' }
- t.references :right_fighter, null: false, foreign_key: { to_table: 'creatures' }
- t.references :winner, null: true, foreign_key: { to_table: 'creatures' }
-end
-```
-
----
-
-### Ajouter des Foreign Keys
-
-On oubliera pas d'ajouter les relations sur les modèles :
-
-```ruby
-class Combat < ApplicationRecord
- enum :result, [:draw, :domination]
-
- belongs_to :left_fighter, class_name: 'Creature'
- belongs_to :right_fighter, class_name: 'Creature'
- # belongs_to rend la relation obligatoire, ici c'est facultatif
- belongs_to :winner, class_name: 'Creature', optional: true
-end
-```
-
----
-
-### Utilisation des Foreign Keys
-
-On évitera globalement de manipuler des identifiants et on utilisera des records.
-
-```ruby
-def baston!
- return if left_fighter.nil? || right_fighter.nil?
-
- left_hp = left_fighter.health_points
- right_hp = right_fighter.health_points
- left_fighter.health_points -= right_hp
- right_fighter.health_points -= right_hp
-
- if left_fighter.alive?
- self.winner = left_fighter
- self.result = :domination
- elsif right_fighter.alive?
- self.winner = right_fighter
- self.result = :domination
- else
- self.result = :draw
- end
-end
-```
-
----
-
-### Ne pas oublier de sauvegarder les records liés
-
-* C'est différent de la création automatique des records liés
-
-```ruby
- def create
- left_fighter = Creature.find(params[:left_fighter_id])
- right_fighter = Creature.find(params[:right_fighter_id])
-
- @combat = Combat.new(create_params)
- @combat.left_fighter = left_fighter
- @combat.right_fighter = right_fighter
- @combat.baston! # baston met à jour les créatures
- @combat.left_fighter.save! # il faut save pour persister la perte de pv
- @combat.right_fighter.save!
- @combat.save!
-
- render json: @combat.as_json(include: COMBATS_RENDER_CONFIG)
- rescue ActiveRecord::RecordNotFound
- render json: {}, status: 404
- end
-```
-
----
-
-### Filtrer des relations à la volée
-
-```ruby
- def index
- @combats = Combat.all
-
- # on supporte uniquement les réels résultats
- # possibles qui sont listés sur le modèle Combat
- if Combat.results.include?(params[:result])
- @combats = @combats.where(result: params[:result])
- end
-
- # Si on fournit une query, on l'utilise pour la requête like
- if params.include?(:query)
- @combats = @combats.where("name LIKE ?", "%#{params[:query]}%")
- end
-
- render json: @combats.as_json(include: COMBATS_RENDER_CONFIG)
- end
-```
\ No newline at end of file
diff --git a/cours/sem6/sem6.pdf b/cours/sem6/sem6.pdf
new file mode 100644
index 0000000..882ed4b
Binary files /dev/null and b/cours/sem6/sem6.pdf differ