Cet article est publié sous licence CC BY-NC-SA
Quand je pense “magie de Rails”, la gestion du nombre d’un mot me vient tout de suite en tête. Si vous avez déjà essayé de faire un scaffold avec “people” vous voyez surement de quoi je veux parler !
Toute cette couche est contenue dans une partie d’ActiveSupport dédié aux inflexions (“inflector” en anglais). Les inflexions regroupent toute un panoplie de méthodes destinées à gérer la casse, le nombre et les séparateurs de vos chaînes de caractères. Ces méthodes sont par exemple très utile lorsque vous générez un contrôleur ou un modèle via la commande rails
.
Dans cet article on ajoutera des règles de pluriel à une application Rails. Cela vous sera utile si vous souhaitez créer une application en Français. On décortiquera ensuite la gem Rails pour comprendre sa gestion des pluriels !
Nous allons commencer par un simple scaffold avec le mot “people” qui devient “person” au singulier.
❯ rails g scaffold people name:string
On observe dans l’arborescence de notre projet que Rails prend parfaitement en compte la gestion du nombre de nos fichiers.
Plus fort que la lévitation d’un éléphant, Rails reconnaît le singuliers de people et attribut le bon nom à chaque fichier. D’ailleurs au moment de l’exécution, Rails nous avertit avec un warning :
[WARNING] The model name 'people' was recognized as a plural, using the singular 'person' instead. Override with --force-plural or setup custom inflection rules for this noun before running the generator.
invoke active_record
create db/migrate/20150826075951_create_people.rb
create app/models/person.rb
invoke test_unit
create test/models/person_test.rb
create test/fixtures/people.yml
invoke resource_route
route resources :people
invoke scaffold_controller
create app/controllers/people_controller.rb
invoke erb
create app/views/people
...
Lorsqu’on tape une commande Rails, on passe par les railties de notre gem. Cette partie va notamment gérer les commandes console de Rails. Ainsi, dans le générateur de ressource de Rails on trouve bien :
railties/lib/rails/generators/resource_helpers.rb
assign_controller_names!(controller_name.pluralize)
Qui fait donc appel à pluralize
qu’on verra en détail plus bas.
Cette méthode est accessible depuis n’importe ou dans votre application Rails.
Si je lance la console Rails :
❯ rails c
Loading development environment (Rails 4.2.3)
irb(main):001:0> "person".pluralize
=> "people"
irb(main):002:0> "dog".pluralize
=> "dogs"
irb(main):003:0> "cats".singularize
=> "cat"
Cette méthode est aussi accessible depuis nos views
dans notre application :
app/views/people/index.html.erb
<%= "person".pluralize %>
<%= "dog".pluralize %>
<%= "cats".singularize %>
Notre page affichera alors :
Malgré sa puissance, cette magie reste faillible. En effet celle-ci se limite par défaut à la plupart des mots anglophones ce qui pourrait poser problème à nous autres mangeurs de grenouilles. Heureusement, votre application Rails vous laisse la possibilité d’ajouter facilement vos propres inflexions :
config/initializers/inflections.rb
ActiveSupport::Inflector.inflections(:fr) do |inflect|
inflect.plural(/$/, 's')
inflect.plural(/(hib|ch|bij|caill|p|gen|jouj)ou$/i, '\1oux')
end
Ici nous définissons qu’en Français, le pluriel prend un “s” et que dans le cas où l’on souhaite mettre hibou, chou, bijou… au pluriel, notre inflexion remplacera le “s” par “x”. Testons ce code depuis la console :
❯ rails c
Loading development environment (Rails 4.2.3)
irb(main):001:0> "hibou".pluralize(:fr)
=> "hiboux"
irb(main):002:0> "matou".pluralize(:fr)
=> "matous"
Avant de détailler pluralize
, sachez que les deux fonctions de nombre sont très similaires, seule la valeur de référence et de remplacement change, ainsi que le nom de la méthode appelée. Ainsi en détaillant pluralize
il vous sera facile de comprendre singularize
.
Lorsqu’on écrit "user".pluralize
dans Rails, on fait appel à la méthode pluralize
définit sur la classe string
d’ActiveSupport.
active_support/core_ext/string
def pluralize(count = nil, locale = :en)
locale = count if count.is_a?(Symbol)
if count == 1
self.dup
else
ActiveSupport::Inflector.pluralize(self, locale)
end
end
Ce premier pluralize
nous permet d’appliquer la méthode directement sur notre chaîne en faisant un simple "string".pluralize
plutôt que de la passer en paramètre.
Dans un premier temps, si on précise une langue (avec un symbole donc) sans préciser de nombre ex: "chien".pluralize(:fr)
, alors la valeur de local
devient la valeur du premier paramètre count
— ici le symbole :fr
.
C’est une petite astuce sympa lorsque l’on a deux paramètres facultatifs.
Ensuite, si on précise que count
est égal à 1
ou que notre chaîne est vide, alors on retourne une copie de la valeur de notre instance, sinon on fait appel aux inflexions d’ActiveSupport.
active_support/inflector/methods
def pluralize(word, locale = :en)
apply_inflections(word, inflections(locale).plurals)
end
La méthode pluralize
nous sert ici à définir le type d’inflexion que recevra la méthode apply_inflections()
. Dans ce cas l’inflexion sera inflections(locale).plurals
. La méthode singularize
changera simplement le .plurals
en .singulars
.
active_support/inflector/methods
def apply_inflections(word, rules)
result = word.to_s.dup
if word.empty? || inflections.uncountables.include?(result.downcase[/\b\w+\Z/])
result
else
rules.each { |(rule, replacement)| break if result.sub!(rule, replacement) }
result
end
end
Ici, on stocke dans result
la valeur de word
qu’on a d’abord pris soin de convertir en chaîne de caractère.
Puis, on retourne result
si la chaîne est vide ou si elle fait partie de la liste des invariables (uncountables en anglais).
Dans le cas contraire, on boucle sur rules
qui fait appel à la liste des inflexions pour remplacer la valeur sortante par son pluriel dans notre cas.
Par défaut, la liste d’inflexions de la gem Rails se présente comme ceci :
active_support/inflections
Inflector.inflections(:en) do |inflect|
inflect.plural(/$/, 's')
…
inflect.singular(/s$/i, '')
…
inflect.irregular('person', 'people')
…
inflect.uncountable(%w(equipment information rice money species series fish sheep jeans police))
end
Bien entendu, beaucoup de règles remplacent les …
.
Comme vous pouvez le voir, ces règles ont une forme similaire à celles définies dans notre application Rails. C’est en réalité la forme que reçoit la fonction .sub!()
qui substitue nos chaînes grâce aux regexp.
On remarque aussi que plusieurs types d’inflexions sont définis comme des irréguliers avec notre fameux person
qui devient people
. Pour en ajouter à votre application, il suffira de faire :
config/initializers/inflections.rb
inflect.irregular('singulier', 'pluriel')
Enfin, on retrouve les invariables qu’on ajoutera à la liste des uncountable en les séparant par un simple espace :
config/initializers/inflections.rb
inflect.uncountable(%w(beaucoup printemps temps))
Pour aller au bout de notre exploration, sachez que inflect.plural()
est défini comme ceci :
active_support/inflector/inflections
def plural(rule, replacement)
@uncountables.delete(rule) if rule.is_a?(String)
@uncountables.delete(replacement)
@plurals.prepend([rule, replacement])
end
@uncountables
et @plurals
sont des tableaux regroupant les règles définies par la liste des inflexions.
Dans un premier temps la méthode supprime les doublons dans le tableau des invariables.
Pour l’illustrer, si j’ajoute un invariable sur un mot ayant une définition au pluriel comme ceci:
active_support/inflector/inflections
ActiveSupport::Inflector.inflections(:fr) do |inflect|
inflect.plural(/$/, 's')
inflect.plural(/(hib|ch|bij|caill|p|gen|jouj)ou$/i, '\1oux')
inflect.uncountable(%w(hibou))
end
Alors, la règle de pluriel l’emportera. Ici, “hibou” prendra toujours un “x”.
Finalement, on ajoute rule
et replacement
dans notre tableau @plurals
contenant toutes nos règles de pluriel traitées par la fonction .sub!()
.
Maintenant que vous connaissez “le truc”, il ne vous reste plus qu’à le partager en cliquant frénétiquement sur les boutons facebook, twitter et google+ !
L’équipe Synbioz.
Libres d’être ensemble.
Nos conseils et ressources pour vos développements produit.