Samoht :
J’ai trouvé.
J’ai voulu simplifié ma question alors j’ai traduit mon problème en le
ramenant au choix du sexe d’une personne, donc objet “Person” et “Gender”
avec “homme” ou “femme”…ERREUR de ma part!
Je ne comprends pas. ça voudrait dire que ce code :
<%= select ‘person’, ‘gender’, {:f => ‘Femme’, :h => ‘Homme’ }.invert %>
devrait marcher chez toi.
Car dans mon cas j’utilise autre chose.
En effet la liste select s’applique sur un objet “Logement” et
sur le paramètre “type” qui peut être “T1”, “T2”, etc…or apparement,
“type” est un mot réservé qui a semé l’embrouille dans mon code.
J’ai renomé dans ma table “type” par “type_logement” et tout est
rentré dans l’ordre.
Oui, il ne faut pas utiliser type et en plus si on fait du STI (single
table
inheritance), Active Record se sert de ce nom.
Avis aux experts qui sauront le pourquoi du comment!
Voilà comment j’explique les choses :
<%= select ‘logement’, ‘type’, { ‘T1’ => … } %>
ActionView (AV) à un moment a besoin de déterminer la valeur actuelle
du type de ta variable d’instance @logement. ça revient à faire un
@logement.type. Or, au lieu de retourner l’attribut de la colonne ‘type’
de
ta table logements, il appelle Object#type c’est à dire Object#class,
méthodes héritées par ton objet Logement, donc ça retourne la
classe Logement.
Quand options_for_select est appelée, l’argument de la méthode
selected vaut Logement (la classe) (et non ‘T1’ ou ‘T2’).
On arrive à la ligne 116 :
is_selected = ( ( selected.respond_to?(:include?) &&
!selected.is_a?(String) ? selected.include?(element.last) :
element.last == selected))
on a selected = Logement, est-ce que Logement a une méthode d’instance
include?, oui car une Classe est un Module, donc Logement hérite de
Module#include? qui, comme l’a remarqué Thibaut réclame un Module en
argument.
!selected.is_a?(String) ? selected n’est pas une chaîne de caractères,
c’est une classe donc finalement le code :
selected.include?(element.last)
est invoqué et boum. qu’element.last soit une String ou un Symbol,
ça plante.
options_for_select fait du Ducktyping, il se dit, j’ai besoin de
savoir si selected est une collection (Hash, Array, classe
énumerable…) en mettant de côté le cas particulier de String
qui peut être vu comme une collection de caractères ou parce
que String#include? dit si une sous-chaîne est présente.
Or Class (via Module) a aussi une méthode d’instance
include? pour vérifier si un module est inclus dans une classe
(ou un module).
Autres remarques :
J’ai peu d’expérience que j’ai en ruby, cela dit je trouve cette
affectation très interressante! un peu barbare avec plusieurs
if…then…else sous forme ? : imbriqués.
Je trouve l’utilisation de l’opérateur ternaire ? : moins
appropriéequand la ligne de code devient trop longue.
Je préfère l’écriture (que certains peuvent détester) :
is_selected = if selected.respond_to?(:include?) &&
!selected.is_a?(String)
selected.include?(element.last)
else
element.last == selected
end
plus claire. (éviter de répéter la lvalue c’est DRY )
Apparement :include est considiré comme un symbole “(:include?)”,
ouais mon diagnostic n’était pas complètement juste, finalement
il n’y pas de confusion entre des méthodes include et include?
Rq : si tu fais include :MonModule au lieu de include MonModule,
tu as la même erreur :
TypeError: wrong argument type Symbol (expected Module)
- La convention pour une méthode qui retourne true ou false
c’est de la terminer par un point d’interrogation, tu trouveras
ça très pratique et très lisible :
if mon_objet.is_a?(String)
if current_user.admin?
Une autre convention (convention Ruby) est pour les
méthodes qui modifie l’objet ou les méthodes dangereuses,
consiste à mettre un point d’exclamation :
my_string.strip!
- Ruby repose sur le passage de messages. C’est à dire
que tu envoies un message à un objet (le receveur) avec
éventuellement des paramètres pour invoquer une méthode.
mon_objet.send(:nom_de_la_methode, argument_un, argument_deux…)
revient à faire :
mon_objet.nom_de_la_methode(argument_un, argument_deux…)
Pour select, la signature est :
select(object, method, choices, options = {}, html_options = {})
et AV va se servir de l’argument method pour faire un passage
de message à @logement.
- enfin pour les Symboles, :foo et :foo? sont deux objets Symbol
bien distincts.
Pour un passage de paramètres, les méthodes invoquées
ne seront pas les mêmes :
ga.send :buzomeu et ga.send :buzomeu?
sont bien différents.
Pour ma part, milles excuses j’aurais du citer l’exemple réel!
Clair. Je n’arrivais pas à reproduire le bug.
Pour les autres, la morale de cette histoire est qu’il faut faire
attention aux noms de symboles que l’on donne dans notre
code…ils pourraient être réservés à Ruby ou Rails.
Un problème assez similaire est le nom des actions pour
un contrôleur. Comme ton contrôleur est un objet, il hérite
des méthodes d’instance d’Object. Ainsi par exemple,
appeler une action ‘display’ ne marchera pas.
Pour fini, Simon a bien fait de te demander le traceback…
Car c’est la victoire de Samoht trace.
– Jean-François (en forme).