Sinceramente no se como te puede funcionar en el caso de new/create,
pero al menos entiendo porque no te funciona en el caso del
edit/update.
Cuando se envia un formulario el navegador tiene que decidir cuales
controles enviar, y cuales no. Los controles que se deben enviar se
denominan “exitosos”. Los checkbox solo son exitosos si están
activados, si no lo están no se envian. Por eso Rails cuando crea un
checkbox crea además un input hidden con el mismo nombre justo debajo.
Los controles exitosos se envian en orden de aparición, por lo que si
el checkbox está pulsado se enviará delante del input hidden y el
servidor cuando lea los parámetros leerá el valor del checkbox y
interpretará que ha sido pulsado. Si el checkbox no ha sido pulsado,
no se enviará al servidor, pero el input hidden sÃ, por lo que el
servidor verá el valor falso (o vacio, como en tu caso).
Rails, para enviar varios valores bajo el mismo nombre realiza el
truco de terminar el nombre del control con “[]”, que Rails interpreta
como un array y recoge todos los valores que se encuentran en la
petición del navegador, en vez del primero que recogeria si no llevase
los corchetes al final.
Después de esta explicación, vamos con tu problema, que a mi me sucede
tanto en new/create como en edit/update (lo he probado tanto en Rails
2.0.2 como en 2.1, que es lo que dice que utiliza el enlace que
enviaste).
Este es mà código en la vista:
<% @languages.each do |language| -%>
<%= f.check_box :language_ids, {:checked =>
user_speaks_language?(language)}, "#{language.id}", "" -%> <%=
language.name -%>
<% end %>
Que genera este código HTML:
English
Spanish
German
French
Como ves (al igual que en tu código) hay un input checkbox y un input
hidden justo a continuación. Todos con el mismo nombre, pero los
checkbox tienen value 1, 2, 3, 4 y los hidden tiene un valor vacio.
Esto es lo que envia en un POST (new/create) y en un PUT (edit/update)
si selecciono los tres últimos checkbox de los 4. Lo he editado un
poco para que quede más entendible (por cierto Firebug no me ha
funcionado porque pierde los contenidos del POST/PUT y los intenta
realizar otra vez. He utilizado otra extensión llamada Live HTTP
Headers).
user[name]=U5&
user[language_ids]=&
user[language_ids]=2&
user[language_ids]=&
user[language_ids]=3&
user[language_ids]=&
user[language_ids]=4&
user[language_ids]=&
commit=Create
Como ves se ha enviado los ids 2, 3, 4, y cuatro user[language_ids] en
blanco (uno por cada hidden, que siempre son existosos). Como Rails
analiza esta cadena de principio a final encuentra el primer
language_ids vacio y lo toma como válido, como se ve en los parámetros
que le llegan a nuestro controlador:
Parameters: {“user”=>{“name”=>“U5”, “language_ids”=>""},
“commit”=>“Create”,
“authenticity_token”=>“1ef350705e4d86b8813982fcf9a3d203a62f5e1e”,
“action”=>“create”, “controller”=>“users”}
Ese language_ids=>"" está tomado del primer hidden, y obviamente
provoca todo tipo de comportamientos inesperados. En el caso de
seleccionar el primer checkbox se envia el id del primer checkbox
antes de su hidden y ese valor sà que queda almacenado, pero no ningún
otro.
Pero como digo, si a tà te funciona el new/create algo debes de tener
diferente.
Como por ejemplo lo que yo he cambiado para que me funcione. En las
vistas (new y edit):
<% @languages.each do |language| -%>
<%= f.check_box :language_ids, {:checked =>
user_speaks_language?(language), :name => 'user[language_ids][]'},
"#{language.id}", "" -%> <%= language.name -%>
<% end %>
El detalle está en ese nuevo parámetro :name donde creo la cadena que
crearÃa Rails, pero con el indicador de que queremos un array (hay
otro modo poniendo de segundo parámetro en vez de “:language_ids”
poner “language_ids][”, sÃ, con los corchetes al revés, pero nos crea
un atributo id inválido).
Con ese cambio todo parece que me funciona como deberÃa, aunque se
crean muchos más input hidden de los necesarios (con uno bastarÃa).
Por cierto, en la documentación de Edge Rails el método check_box está
mucho más comentado estas situaciones, e inclusa una similar a esta
que te molestaba:
http://github.com/rails/rails/tree/master/actionpack/lib/action_view/helpers/form_helper.rb#L444.
Curiosamente siguen sin explicar demasiado el parámetro :checked.
Como conclusión parece ser que check_box de Rails está más destinado a
valores true/false o similares. Para estas cosas me parece más útil
check_box_tag, que si bien es un poco más tedioso de utilizar con
modelos te da un control más fino (y siempre que no te olvides de un
input hidden al final para el caso de que el usuario no seleccione
ningún checkbox). Esa es la solución que utilizo en el plugin.
Bueno, suerte con tus usuarios y tus lenguajes, espero que consigas lo
que quieres.