Techniques Rails avancées : Allez au-delà des bases


Vous maîtrisez les bases de Rails ? Il est temps de passer au niveau supérieur ! Découvrons ensemble des techniques avancées qui rendront votre code plus expressif, maintenable et... "railsy". Dans cet article, nous explorerons comment utiliser les class methods, organiser votre code avec les concerns, créer vos propres générateurs et même étendre Rails avec des fonctionnalités personnalisées.


Alors qu'est-ce qu'on attend ? C'est partie :)


L'article a été écrit suite à la présentation de Chris Oliver à Rails World 2025, retrouvez la vidéo d'origine à la fin.

Qu'est-ce qui rend le code "railsy" ?


Rails a cette capacité unique de rendre le code lisible comme si vous lisiez un livre. Pensez aux méthodes comme `has_many :subscribers` ou `has_one_attached :featured_image`. C'est magique, non ? Cette expressivité vient de Ruby et de la philosophie Rails qui veut que votre code soit sur un pied d'égalité avec celui du framework lui-même.


Dans les contrôleurs, on retrouve cette même élégance avec `before_action :authenticate_user`. Rails 8 pousse ce concept encore plus loin avec des méthodes comme :


allow_unauthenticated_access
rate_limit to: 10, within: 5.minutes


Ces méthodes se distinguent visuellement des `before_action` classiques, et c'est volontaire !

Créer ses propres class methods Rails

Au-delà des before_action traditionnels

Vous pourriez écrire ceci dans vos contrôleurs :

class AdminController < ApplicationController
before_action :require_admin
private
def require_admin
# logique d'autorisation
end
end


Mais pourquoi ne pas faire comme Rails et créer une class method plus expressive ?


class AdminController < ApplicationController
admin_access_only
end

C'est quand même plus clair, non ?

(Et en plus, ça fait moins mal aux yeux que de chercher le bon `before_action` dans une liste de dix !)

Implémenter des class methods personnalisées

Voici comment créer cette magie dans un concern d'autorisation :


module Authorization
extend ActiveSupport::Concern
class_methods do
def admin_access_only(message: "Access denied", **options)
before_action(options) do
redirect_to root_path, alert: message unless current_user&.admin?
end
end
end
end


L'avantage des class methods ? Vous pouvez passer des arguments ! Fini de créer dix méthodes différentes pour dix cas d'usage.

Organiser son code avec les dossiers personnalisés

Sortir des sentiers battus

Rails ne vous oblige pas à tout fourrer dans `models`, `views` et `controllers`. Vous pouvez créer vos propres dossiers dans `app/` :


  1. `app/api_clients/` pour vos clients d'API
  2. `app/ui_components/` pour vos composants d'interface
  3. `app/validators/` pour vos validations personnalisées
  4. `app/notifiers/` pour vos notifications
  5. `app/services/` pour vos services


Rails chargera automatiquement ces dossiers. C'est comme avoir un tiroir pour chaque type d'ustensile dans sa cuisine ! Cela rendra votre code tellement plus agréable à lire et à maintenir.

Exemple concret : clients d'API personnalisés

Au lieu d'utiliser une gem externe qui implémente 200 endpoints dont vous n'avez besoin que d'un seul, créez votre propre client :


# app/api_clients/github_client.rb
class GithubClient
BASE_URI = "https://api.github.com"
def invite_to_organization(username, organization)
# Une seule méthode, une seule responsabilité
end
end


Plus simple, plus maintenable, et zéro dépendance externe !

Générateurs Rails personnalisés

Créer ses propres outils

Rails vous permet de créer vos propres générateurs. C'est parfait pour standardiser les patterns de votre équipe :


rails generate generator api_client


Cela crée un générateur qui hérite de `NamedBase`, gérant automatiquement les arguments et les chemins de fichiers :


class ApiClientGenerator < Rails::Generators::NamedBase
source_root File.expand_path("templates", __dir__)
class_option :url, type: :string, desc: "Base URL for the API"
def create_api_client
template "api_client.rb.erb", "app/api_clients/#{file_name}_client.rb"
end
end

Personnaliser les templates existants

Vous utilisez encore les scaffolds pour vous simplifier la vie. Mais vous avez marre des `<%= notice %>` alors que vous l'avez déjà dans votre layout ?


Bah facile Monique. Surchargez le template :


<!-- lib/templates/erb/scaffold/show.html.erb.tt -->
<!-- Supprimez juste la ligne du notice -->


Maintenant vos scaffolds s'adaptent à votre application !

Concerns pour l'organisation, pas seulement la réutilisation

Organiser par fonctionnalité

Les concerns ne servent pas qu'à partager du code. Vous pouvez, et certains diraient même devez, les utiliser pour organiser les modèles par fonctionnalité :


class User < ApplicationRecord
include User::Billing
include User::Mentions
end


Ces modules vivent dans `app/models/user/` et contiennent toute la logique relative à chaque fonctionnalité. Fini les modèles de 500 lignes où tout se mélange !


# app/models/user/billing.rb
module User::Billing
extend ActiveSupport::Concern
included do
has_many :subscriptions
has_many :invoices
end
def billing_active?
# toute la logique de facturation ici
end
end


Exemple complet : intégration Cloudflare Turnstile

Qu'est-ce que Cloudfare Turnstile

Cloudflare Turnstile est une alternative moderne aux CAPTCHA traditionnels (tu sais, ces tests agaçants où il faut identifier des feux de circulation ou des passages piétons 😅).


Le problème qu'il résout :

  1. Les bots : Des programmes automatisés peuvent spammer tes formulaires, créer de faux comptes, etc.
  2. Les CAPTCHA classiques : Frustrants pour les utilisateurs, parfois difficiles à résoudre, pas toujours accessibles


Turnstile vérifie que l'utilisateur est humain de manière invisible ou presque. Il analyse le comportement du navigateur en arrière-plan, sans embêter l'utilisateur avec des puzzles. C'est gratuit et plus respectueux de la vie privée que reCAPTCHA.

Le problème initial

Vous voulez intégrer Turnstile dans votre formulaire d'inscription. Premier réflexe :

def create
if verify_turnstile(params[:turnstile_token])
@user = User.new(user_params)
if @user.save
redirect_to root_path
else
render :new
end
else
flash[:error] = "Turnstile verification failed"
render :new
end
end


Pourquoi c'est frustrant ?

Imagine ce scénario :

  1. Un utilisateur remplit ton formulaire d'inscription
  2. Il oublie de remplir son email (erreur de validation)
  3. Il soumet le formulaire
  4. Turnstile passe (il est humain), mais l'email manque
  5. Le formulaire se recharge avec l'erreur "Email requis"
  6. Il ajoute son email et re-soumet
  7. Nouveau challenge Turnstile ! 😤

Le token Turnstile n'est valable qu'une seule fois. Chaque soumission = nouveau challenge.

La solution élégante

Créons un concern réutilisable :

# app/models/concerns/challengeable.rb
module Challengeable
extend ActiveSupport::Concern
included do
attr_accessor :challenge_token
validates :challenge_token, turnstile: true
end
end
  1. attr_accessor :challenge_token : Crée un attribut temporaire (pas dans la base de données) pour stocker le token Turnstile
  2. validates :challenge_token, turnstile: true : Ajoute une validation qui vérifie le token avec Cloudflare

Pourquoi un concern ? Tu peux facilement l'ajouter à n'importe quel modèle (User, Comment, ContactForm, etc.)


Maintenant créons un validateur personnalisé

# app/validators/turnstile_validator.rb
class TurnstileValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
unless TurnstileClient.new.verify(value, record.current_ip)
record.errors.add(attribute, "Verification failed")
end
end
end


Ce qui se passe :

  1. Rails appelle automatiquement ce validateur quand tu fais @user.save
  2. Il vérifie le token avec l'API Cloudflare
  3. Si ça échoue, il ajoute une erreur comme n'importe quelle autre validation


Maintenant dans votre modèle :

class User < ApplicationRecord
include Challengeable
end

Une seule ligne, et ton User a maintenant la protection Turnstile.


Et votre contrôleur redevient simple :


def create
@user = User.new(user_params)
if @user.save
redirect_to root_path
else
render :new # Affiche TOUTES les erreurs, y compris Turnstile
end
end


## Pourquoi c'est génial ?
### Avant (approche naïve) :
Utilisateur soumet → Turnstile vérifié ✅ → Email manquant ❌
Formulaire rechargé → Utilisateur corrige email → Soumet
NOUVEAU Turnstile requis ❌ (frustration ++)
## Après (approche élégante) :
Utilisateur soumet → Validation en un bloc
Erreurs affichées : "Email requis" + "Turnstile invalide"
Utilisateur corrige TOUT → Soumet une seule fois ✅

Étendre Rails avec des template handlers

Le principe

Rails utilise un système de template handlers pour traiter différents formats. `index.html.erb` ? Le handler ERB traite le fichier. `index.json.jbuilder` ? C'est le handler JBuilder qui s'en occupe.


Vous pouvez créer vos propres handlers ! (Bon, l'exemple PHP dans la présentation était... disons... "original", mais le principe reste valable.)

Exemple pratique : Farem PDF

Voici un exemple plus sérieux avec la gem Farem PDF qui génère des PDFs sans Node.js :


# Dans votre contrôleur
def invoice
respond_to do |format|
format.html
format.pdf { render farem_pdf: { landscape: true } }
end
end


Le renderer personnalisé :

  1. Capture le HTML rendu
  2. Lance Chrome en mode headless
  3. Génère le PDF
  4. Le retourne au navigateur


Tout ça de manière transparente !

Conclusion


Ces techniques avancées transforment Rails d'un framework web en un véritable langage métier. En utilisant les class methods, l'organisation par concerns, les générateurs personnalisés et les extensions de Rails, vous créez un code qui raconte une histoire et se lit comme un livre.


Le secret ? Ne pas avoir peur d'étendre Rails selon vos besoins. Le framework est conçu pour être personnalisé, alors profitez-en ! Et n'oubliez pas : si votre code ne se lit pas comme de l'anglais, c'est qu'il y a probablement une façon plus "railsy" de l'écrire.



La présentation d'origine par Chris Oliver :









Share it: