Configuration d'une gem

20 views
Skip to first unread message

Simon Courtois

unread,
Mar 17, 2014, 10:52:20 AM3/17/14
to rubyfr...@googlegroups.com
Bonjour tout le monde,


Je me demandais quelle techniques vous utilisez, préférez, recommandez quand il s'agit de
la configuration d'une gem.

* MyGem.something
* MyGem.config.something
* MyGem.config[:something]
* MyGem::Config.something
* MyGem::Config.instance.something
* MyGem::Config.get(:something)

Il y a plein de façon de stocker/récupérer les options de configuration d'une gem mais au delà de ça,
je me posais la question de l'accès depuis l’intérieur du projet.

Quelque part ça me dérange d'avoir `MyGem.config.something` ou autre écrit un peu partout
dans le code.
On peut aussi avoir une méthode `config` qui retourne `MyGem.config` dans chaque classe mais
c'est laid et redondant.
On peut, pourquoi pas, avoir un module `WithConfig` qui ajoute cette méthode mais je trouve pas
franchement ça mieux...

J'avoue que jusque là je me suis jamais trop pris la tête, un hash dans le module de la gem ou, si
besoin d'un peu plus fat, une classe singleton dédiée à ça.

Honnêtement, aujourd'hui je n'arrive toujours pas à me décider sur le sujet et à trouver une solution
que je trouve propre, qui ne pollue pas tout le code, qui soit DRY, etc.

Est-ce que vous avez une idée ?

Bonne journée ;)

Jean-Hadrien Chabran

unread,
Mar 17, 2014, 11:51:51 AM3/17/14
to rubyfr...@googlegroups.com
Hello, 

Là comme ça, il m'apparait que si tu as à écrire trop de fois MyGem.config c'est qu'il y a un soucis quelque part. 
En gros, les variables de config que tu stockes dans ton hash sont au final sensiblement la même chose que des vars globales. 

J'aurais donc tendance à dire qu'il faut voir pour que ta config serve à initialiser les objets clés de ta librairie, les valeurs se répandant ainsi au sein du projet sans garder une référence explicite à ta config. 

En gros, je pense qu'il vaut mieux écrire UrlBuilder.new(:host => MyGem.config.url) qu'avoir UrlBuilder.new où le call à MyGem.config.url est implicite. Cela découple tes classes de la configuration. Et si tu as une clase en haut de ton arborescence qui boote tout le reste, autant lui filer le hash de config direct :)

My 2 cents :) 


--
Vous recevez ce message, car vous êtes abonné au groupe Google Groupes "RubyFR public".
Pour vous désabonner de ce groupe et ne plus en recevoir les messages, envoyez un e-mail à l'adresse rubyfr-publi...@googlegroups.com.
Pour publier un message dans ce groupe, envoyez un e-mail à l'adresse rubyfr...@googlegroups.com.
Visitez ce groupe à l'adresse http://groups.google.com/group/rubyfr-public .
Pour obtenir davantage d'options, consultez la page https://groups.google.com/d/optout.



--
Jean-Hadrien Chabran

Simon Courtois

unread,
Mar 17, 2014, 12:14:54 PM3/17/14
to rubyfr...@googlegroups.com
C’est une autre méthode que j’ai utilisée aussi, injection de dépendance de la config :)

Le souci c’est que parfois le constructeur de la classe est public et l’utilisation
de la configuration n’est que dans les méthodes privées. Du coup ça me dérange
un peu d’exposer un détail d’implémentation dans l’initialize :/

Simon Courtois

Thibaut Barrère

unread,
Mar 17, 2014, 12:44:55 PM3/17/14
to rubyfr...@googlegroups.com
Un autre point à prendre en compte pour éviter des bugs atroces: le multi-threading.

Voir la gem twitter par exemple:


Envoyé de mon iPhone

lucas di cioccio

unread,
Mar 17, 2014, 5:25:10 PM3/17/14
to rubyfr...@googlegroups.com
A mon avis, ce qui est laid c'est d'avoir de leaker des "config" dans des objets qui n'ont rien à voir.
Le fait qu'une valeur ait pour origine une configuration n'a pas grand chose à voir avec la logique de ton code (par exemple préparer une connexion). Il y a une ligne très peu claire qui sépare ce qu'on appelle une configuration et ce qu'on appelle un paramètre.

Par exemple si tu as le code suivant

class Toto
  def do_something
      url = Config.toto.url
      ....
  end
end

Alors ça signifie que Toto est paramétrisé par une URL, et qu'il faudrait étudier l'utilisation d'un accessor.

class Toto
  attr_reader :url
  def do_something
    ...
  end
end

ou encore d'une structure pour encapsuler les paramètres/la configuration si tu préfères ce mot

class Toto
  Params = Struct.new(:url)
  attr_reader :params
  def do_something
    url = params.url
    ...
  end
end

A toi de choisir lequel des trois te semble le plus convenable/facile à utiliser/à intérioriser/à tester. Perso j'écarte le premier.

Après, la question est de comment setter le @url ou le @params? Et bien il faut réitèrer un niveau au dessus. La profondeur de l'arbre "de configuration" ne devrait pas être trop profonde. En fonctionnel on utilise beaucoup l'application partielle pour ce genre de cas, j'imagine que Javascript utilise des closures aussi, tandis qu'en Java on passe par des Factories. Souvent tout n'est qu'une histoire de goût mais au final c'est bcp de tripotage pour la même chose. Utiliser une globale c'est un raccourci pour ne pas explicitement faire apparaître les dépendances. Mais ça signifie aussi qu'utiliser une globale ne fait pas apparaître les dépendances de manière explicite.

L'autre truc c'est le format de la structure à utiliser. D'expérience, faut éviter les hash, et rejeter en bloc les hash nested ou les hash avec des listes comme valeurs: ça signifie qu'il y a bcp de logique associée aux données. A chaque fois que je l'ai fait je m'en suis mordu les doigts.


Pour conclure, un bon principe d'écriture de Gem/bibliothèque c'est que c'est celui qui fait du code outrageusement "profond" ou "magique" qui doit paier le prix de sa stupidité, pas tous les utilisateurs de ta Gem.

--Lucas

Simon Courtois

unread,
Mar 18, 2014, 8:32:04 AM3/18/14
to lucas di cioccio, rubyfr...@googlegroups.com

Pour remettre ma question dans le contexte, je bosse sur la gem simple-navigation.

Dedans on trouve Item qui représente un élément de navigation avec ses attributs et sous-éléments.

Un item est ajouté à un ItemContainer soit en faisant

container.item :key, 'name', 'url', options_hash

soit

container.items = [item, item]

qui ajoute les nouveaux items (ce qui est stupide parce que contre-intuitif).

Dans Item et dans ItemContainer, la configuration globale est utilisée pour vérifier des flags et des valeurs. Du coup oui c’est sale d’accéder à la conf comme ça mais ça un certain sens vu de l’extérieur.

J’essaie justement de retirer le couplage dans le code petit à petit mais dans le cas de la config c’est un peu compliqué de changer l’API en mode c’est-la-fête. Je peux pas demander à l’utilisateur de fournir la config lui-même et j’ai pas toujours la main sur la création des items non plus.

Simon Courtois

Pour vous désabonner de ce groupe et ne plus recevoir d'e-mails le concernant, envoyez un e-mail à l'adresse rubyfr-publi...@googlegroups.com.
Pour envoyer un message à ce groupe, envoyez un e-mail à l'adresse rubyfr...@googlegroups.com.

Philippe Creux

unread,
Mar 20, 2014, 5:22:11 PM3/20/14
to rubyfr...@googlegroups.com
Reply all
Reply to author
Forward
0 new messages