Ruby a été une technologie qui a pas mal été traitée sur la chaîne mais c'est un sujet que j'ai un peu abandonné avec le temps et je voulais aujourd'hui vous donner quelques explications sur les raisons qui ont fait que j'ai abandonné cette technologie.
À l'époque où j'ai commencé à faire du back-end (vers 2010), j'entendais souvent que tel ou tel framework était inspiré de Ruby on Rails et à force d'entendre ça, j'ai fini par me demander si l'herbe n'était pas plus verte ailleurs que PHP.
Ma première découverte de Ruby on Rails (vers 2011) n'a pas été très concluante à cause de plusieurs facteurs.
Tout d'abord j'ai voulu "speedrun" mon apprentissage en apprenant Ruby on Rails sans comprendre le langage Ruby qui permet de le faire fonctionner. Sans comprendre le langage de base, les problèmes de compréhension se sont rapidement accumulés et m'ont très rapidement freiné.
Le second problème était mon système d'exploitation. À l'époque j'étais sur Windows et pas mal de gem (dépendances sur Ruby) ne s'installaient pas correctement (comme la gem mysql par exemple). Docker ou le WSL n'existaient pas encore et la seule alternative était l'utilisation d'une machine virtuelle (lourd à mettre en place et à utiliser).
Pour lutter contre ma dépendance au jeu League of Legends, je décide de changer de système d'exploitation et de passer sur Linux. Sur ce nouvel environnement je me dis que l'apprentissage de Ruby & Rails sera plus simple et je décide de lui redonner sa chance. Cette fois-ci je fais les choses bien en commençant par me former sur le langage Ruby avant d'attaquer Ruby on Rails.
Et là c'est la révélation ! Le langage Ruby est un langage très expressif avec lequel je prends un réel plaisir à écrire du code (surtout si on compare sa syntaxe à celle de PHP que j'utilisais jusqu'alors).
require 'net/http'
require 'json'
def list_usernames(url)
uri = URI(url)
response = Net::HTTP.get(uri)
users = JSON.parse(response)
users.map { |user| user['name'] }
end
puts "Liste des utilisateurs :"
list_usernames("https://jsonplaceholder.typicode.com/users").each do |name|
puts "- #{name}"
end
Ruby on rails suit la même philosophie et cette fois l'apprentissage se passe beaucoup mieux avec de bonnes bases sur Ruby.
# Exemple de CRUD sur Ruby on Rails
class RecipesController < ApplicationController
before_action :set_recipe, only: [:show, :edit, :update, :destroy]
# GET /recipes
def index
@recipes = Recipe.all
end
# GET /recipes/1
def show
end
# GET /recipes/new
def new
@recipe = Recipe.new
end
# GET /recipes/1/edit
def edit
end
# POST /recipes
def create
@recipe = Recipe.new(recipe_params)
if @recipe.save
redirect_to @recipe, notice: 'Recipe was successfully created.'
else
render :new
end
end
# PATCH/PUT /recipes/1
def update
if @recipe.update(recipe_params)
redirect_to @recipe, notice: 'Recipe was successfully updated.'
else
render :edit
end
end
# DELETE /recipes/1
def destroy
@recipe.destroy
redirect_to recipes_url, notice: 'Recipe was successfully destroyed.'
end
private
def set_recipe
@recipe = Recipe.find(params[:id])
end
# Only allow a list of trusted parameters
def recipe_params
params.require(:recipe).permit(:title, :description, :instructions, :image)
end
end
À l'époque Ruby on Rails était pas mal à la mode et j'ai rapidement pu trouver des missions freelance qui utilisaient ce framework. De la même manière j'ai été convaincu par l'efficacité du framework et j'ai décidé de migrer le code source de Grafikart de CakePHP vers Ruby on Rails.
Vu comment j'encense le framework on peut se demander pourquoi j'ai arrêté de traiter Ruby et Ruby on rails sur le site. Pourquoi abandonner une technologie que je trouve très bien ?
Au début de ma carrière je voyais le typage comme un frein dans l'écriture de code mais avec le temps mon avis a changé et j'ai de plus en plus de mal à utiliser un langage avec un typage faible et qui est trop dynamique. Ce problème est amplifié par le fait qu'en freelance je suis amené à sauter de projet en projet et sans types il est donc difficile de se souvenir des méthodes qui sont disponibles sur le code qu'on explore.
Ruby est un langage dynamique qui utilise extensivement le duck typing
Si ça marche comme un canard et que ça fait "couac" comme un canard, alors c'est un canard
On ne s'intéresse pas au type des paramètres mais plutôt aux méthodes qu'ils implémentent pour savoir s'ils sont compatible avec la logique de notre fonction.
def maMethode (file)
raise TypeError unless file.respond_to?(:read)
# Le code...
end
Mais ce type de vérification est très succinct et laisse passer pas mal de problèmes.
Une initiative a été lancée par Stripe, nommée Sorbet, pour apporter une analyse statique dans Ruby. Mais cette solution reste un "hack" et n'est pas aussi efficace qu'un typage qui serait conçu au niveau du langage directement.
Ce problème est amplifié par le fait que dans le cadre de Ruby les classes sont ouvertes et peuvent être redéfinies.
# Je peux créer une classe Integer même si elle existe déjà
class Integer
def double
self * 2
end
end
puts 2.double
On peut ainsi faire du Monkey Patching pour rajouter des méthodes à n'importe quel objet. Ce n'est pas forcément exclusif à Ruby (on peut faire la même chose en JavaScript) mais c'est une pratique qui est assez répandue dans Rails. Par exemple, des méthodes sont ajoutées sur les entiers pour manipuler les dates :
1.days.from_now
Le problème est qu'un même objet peut avoir des méthodes différentes en fonction du projet sur lequel on travaille. Avec la multiplication des projets, les inconvénients du Monkey patching dépassent les avantages.
Un autre frein à l'analyse statique réside dans l'utilisation du meta-progamming qui permet à Ruby de modifier son code à l'exécution. Encore une fois ce n'est pas une spécificité de Ruby mais c'est une pratique qui est très utilisée dans Rails.
class User < ApplicationRecord
attr_accessor :name, :email, :message
validates :name, :email, :message, presence: true
belongs_to :group
has_many :posts
end
Par exemple, attr_accessor
va créer des accesseurs et des mutateurs pour les différentes propriétés passées en paramètre. Les méthodes belongs_to
et has_many
permettent de définir des relations et vont aussi ajouter des méthodes à la volée.
Ruby on Rails se base sur des conventions qui permettent de deviner les méthodes disponibles à partir des méthodes utilisées. Le problème de cette approche est qu'il faut justement avoir ces conventions en tête quand on travaille sur un projet et il n'est pas rare de les oublier quand elles commencent à s'accumuler.
Un autre problème que j'ai rencontré avec Ruby on Rails était son hébergement.
La plupart des projets pour lesquels je travaillais à l'époque utilisaient des systèmes d'hébergements mutualisés où il suffisait de déplacer les fichiers pour mettre en ligne le site. La mise en ligne d'application Rails était plus complexe car nécessitait l'utilisation d'un serveur qu'il fallait savoir configurer (le "cloud" n'était pas aussi développé qu'aujourd'hui et les coûts étaient aussi plus importants).
Ruby est un langage généraliste et il existe plusieurs implémentation de serveur HTTP :
De mon côté j'ai dû pas mal expérimenter avant de m'arrêter sur Puma mais la configuration de ce serveur web constitue une complexité supplémentaire (surtout la première fois qu'on essaie de le mettre en place).
En fait, beaucoup des choses qui m'ont fait aimer Ruby et Rails initialement sont devenues des défauts avec le temps. En parallèle PHP, le langage que j'avais quitté pour Ruby, a évolué dans un sens qui correspond plus à mes préférences actuelles avec une intégration de plus en plus profonde des types et moins de magie.
// Même sans connaitre le projet je comprends la forme des paramètres
function hashPassword (User $user, string $salt): string
{
// ...
}
Aussi, la communauté tend vers une approche moins magique / dynamique avec l'injection de dépendance et une obligation de typage pour les librairies tiers qui s'aligne plus avec ma vision actuelle.
Malgré tout ce que je viens d'énoncer je ne regrette absolument pas d'avoir essayé Ruby et Rails. Ce langage et ce framework m'ont permis de découvrir une autre manière de faire les choses et d'affiner aussi mes préférences personnelles lors du choix d'une solution pour mes projets.