Utilisation de la méthode select de F ormOptionHelper


#1

Salut,

je sèche un peu là .
J’ai une table de hachage du type “code” => “valeur”, par exemple “H” =>
“Homme”.
Je voudrai à partir de cette table créer une balise select avec comme
clés/valeurs celles de la table de hashage.

Cela dit la doc n’est pas très explicite…et j’ai l’impression que
l’exemple fournit dans le bouquin O’Reilly “Pratique de Ruby On Rails”
n’est
plus d’actualité.

Ou alors je suis à côté de plaque!..en tant que bon débutant c’est bien
probable!

Quelqu’un à une idée?

Thomas


#2

Ola Thomas,

Grace a toi, j’ai trouve un truc donc je ne soupconnais pas l’existence.
En
gros, j’ai ouvert ma console RoR (script/console) et cree rapidos une
hash:

test = { :foo => ‘foo’, :bar => ‘bar’ }
et la, je sais pas ce qu’il m’a pris mais j’ai tape un truc incense
test.collect
O miracle (je rigole pas en plus), le resultat fut exactement ce que je
voulais
=> [[:foo, “foo”], [:bar, “bar”]]

Le resultat obtenu peut etre utilise avec la methode options_for_select
(avec select_tag) ou bien directement avec select. A toi de voir.

Did

p.s.: c’est fou ruby qq fois, le fait de pouvoir comme cela “ameliorer”
les
classes de base est vraiment qq fois de fascinant.

Le 01/03/07, Samoht T. removed_email_address@domain.invalid a écrit :


#3

didier lafforgue wrote:

=> [[:foo, “foo”], [:bar, “bar”]]

Le resultat obtenu peut etre utilise avec la methode
options_for_select (avec select_tag) ou bien directement avec select.
A toi de voir.
Euh, par contre il faut appeler la method invert sur la hash pour avoir

valeur:

{:f => ‘Femme’, :h => ‘Homme’ }.collect
=> [[:f, “Femme”], [:h, “Homme”]]

{:f => ‘Femme’, :h => ‘Homme’ }.invert.collect
=> [[“Femme”, :f], [“Homme”, :h]]

Mais encore mieux, options_for_select() accepte indifferemment un
tableau ou une hash:

helper.options_for_select( {:f => ‘Femme’, :h => ‘Homme’
}.invert.collect)
=> “<option value=“f”>Femme\n<option
value=“h”>Homme”

helper.options_for_select( {:f => ‘Femme’, :h => ‘Homme’ }.invert)
=> “<option value=“f”>Femme\n<option
value=“h”>Homme”

Et select() egalement

helper.select :person, :gender, {:f => ‘Femme’, :h => ‘Homme’
}.invert
=> “<select id=“person_gender” name=“person[gender]”><option
value=“f”>Femme\n<option value=“h”>Homme”

Simon


#4

Et rebonjour après quelques jours, ma formation Ruby On Rails est
décidemment trop distillée dans le temps!

Pour en revenir au sujet, j’ai l’erreur suivante:

wrong argument type Symbol (expected Module)

Avec le code suivant:

13:
14:

H/F?

15: <%= select ‘person’, ‘gender’, {:f => ‘Femme’, :h => ‘Homme’
}.invert %>


16:

Je ne vois pas du tout le rapport avec un “Module”…enfin je ne sais
pas
trop…quelqu’un a une idée?

Thomas


#5

Tu pourrais nous fournir la pile d’exécution complète pour savoir quelle
méthode génère cette erreur?

Simon


#6

Simon :

Tu pourrais nous fournir la pile d’exécution complète pour savoir quelle
méthode génère cette erreur?

Rien à rajouter par rapport à Simon, j’ai la même question.

Mais je viens seulement de percuter à propos de Samoht.
Coïncidence, une autre personne (Frédéric) a posté récemment
et a aussi une adresse électronique à l’envers. C’est une mode ?

– Jean-François.


#7

Voici donc la trace:

c:/ruby/lib/ruby/gems/1.8/gems/actionpack-1.13.2/lib/action_view/helpers/form_options_helper.rb:116:in
include?' c:/ruby/lib/ruby/gems/1.8/gems/actionpack-1.13.2/lib/action_view/helpers/form_options_helper.rb:116:inoptions_for_select’
c:/ruby/lib/ruby/gems/1.8/gems/actionpack-1.13.2/lib/action_view/helpers/form_helper.rb:349:in
inject' c:/ruby/lib/ruby/gems/1.8/gems/actionpack-1.13.2/lib/action_view/helpers/form_options_helper.rb:114:ineach’
c:/ruby/lib/ruby/gems/1.8/gems/actionpack-1.13.2/lib/action_view/helpers/form_options_helper.rb:114:in
inject' c:/ruby/lib/ruby/gems/1.8/gems/actionpack-1.13.2/lib/action_view/helpers/form_options_helper.rb:114:inoptions_for_select’
c:/ruby/lib/ruby/gems/1.8/gems/actionpack-1.13.2/lib/action_view/helpers/form_options_helper.rb:302:in
to_select_tag' c:/ruby/lib/ruby/gems/1.8/gems/actionpack-1.13.2/lib/action_view/helpers/form_options_helper.rb:66:inselect’
#{RAILS_ROOT}/app/views/essei/_form_persons.rhtml:15:in
`_run_rhtml_47app47views47persons47_form_persons46rhtml’

Pour l’adresse à l’envers, cela remonte à bien longtemps… donc à la
limite
on pourra dire “c’était une mode”

Thomas


#8

Interressant!

Je n’ai pas touché à form_options_helper.rb, cela dit il se pourrait
qu’il
soit corrompu…
J’ai la même version d’action pack que la tienne, et voici la ligne 116:

is_selected = ( (selected.respond_to?(:include?) &&
!selected.is_a?(String)
? selected.include?(element.last) : element.last == selected))

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.
Apparement :include est considiré comme un symbole “(:include?)”, tandis
que
c’est une méthode ailleurs “selected.include”.
Bref, c’est bien pédagogique tout ça…!

Toutes explications sont les bien venues

Thomas


#9

erratum, il n’y qu’un seul test ? : … le reste sont des noms de
méthodes
de test contenant “?”

Thomas (qui admire de plus en plus ruby)


#10

Samoht :

Voici donc la trace:

c:/ruby/lib/ruby/gems/1.8/gems/actionpack-1.13.2/lib/
action_view/helpers/form_options_helper.rb:116:in `include?’

Pour une raison qui m’échappe encore, il semblerait que
ta version d’ActionView confonde include? avec include.
Aurais-tu édité et modifié form_options_helper.rb ?
Le fichier form_options_helper.rb corrompu d’une manière
ou d’une autre ?

Voici la somme md5 de form_options_helper.rb chez moi
(ActionPack 1.13.2)

ff1ce62913f5e3ba133936b1df7e51d1 form_options_helper.rb

Vérifie en particulier la ligne 116 de la méthode options_for_select.

D’autre part, avec :

<%= select ‘person’, ‘gender’, { ‘f’ => ‘Femme’, ‘h’ => ‘Homme’ }.invert
%>

tu devrais avoir l’erreur :

wrong argument type String (expected Module)

– Jean-François.


#11

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!

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.

Avis aux experts qui sauront le pourquoi du comment!

Pour ma part, milles excuses j’aurais du citer l’exemple réel!

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.

Thomas


#12

Hello,

(1.13.2http://dev.rubyonrails.org/browser/tags/rel_1-2-2/actionpack/lib/action_view/helpers/form_options_helper.rb)
on a:

116: is_selected = ( (selected.respond_to?(:include?) &&
!selected.is_a?(String) ? selected.include?(element.last) : element.last

selected) )

pour une raison à déterminer, dans ton contexte le paramètre selected
semble
être une instance de Module (qui a une methode include?, qui attend un
module en paramètre, d’où probablement le ‘wrong argument type Symbol’
lorsque selected.include? est appelé, vu que element.last doit être un
symbole ici).

Il faudrait voir pourquoi selected est un module ici (si c’est bien le
cas!).

Tu peux commencer par placer un breakpoint juste au dessus pour voir ce
que
contient selected (ou bien placer un throw selected).


#13

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 :slight_smile: )

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).


#14

rires!

Excellentes explications…comme quoi sur un petit problèmes on peut en
apprendre beaucoup.

Pour info le code:

<%= select ‘person’, ‘gender’, {:f => ‘Femme’, :h => ‘Homme’ }.invert %>

fonctionnait cependant je n’avais cop/col que la partie {:f => ‘Femme’,
:h
=> ‘Homme’ }.invert (pensant que le problème venait de là …)

Mes chers amis, je vous remercie!

Thomas