2012-03-08 13 views
10

Tengo lo siguiente.campo Hash serializado y formulario simple

class Page < ActiveRecord::Base 
    belongs_to :category 
    serialize :fields 
end 

El valor de fields dependerá de la categoría. Pero como un ejemplo;

{"address" => "8 finance street, hong kong", 
"founded" => "1973"} 

En este ejemplo, la categoría ha definido "address" y "founded" como los campos personalizados.

Lo que quiero decir es;

= simple_form_for(@page) do |f| 
    = f.association :category 
    - f.object.category.fields.each do |field| 
    = f.input field.name 

Pero tengo que hacer algo mágico que lidiar con el hecho de que @page.founded no es válida

En lugar de eso debe mirar @page.fields["founded"].

¿Alguna sugerencia?


Actualización:

tengo un poco más cerca

- if f.object.category 
    - f.object.category.fields.each do |field| 
    = f.input field.name do 
     = text_field_tag "post[fields][#{field.name}]", f.object.fields[file.name] 

ahora tiene que hacer esta secadora (no desee especificar el nombre del objeto).

Veré si puedo escribir una extensión decente de forma simple para esto.

Respuesta

25

Me encontré con un problema similar al tratar de usar simple_fields_for en un tipo de campo hash de un modelo Mongoid. La versión Mongoid de su ejemplo que estaba tratando con veía así:

class Page 
    include Mongoid::Document 
    field :fields, type: Hash 
end 

Mi situación podría ser ligeramente diferente, aunque, como ya conozco las claves hash Estoy buscando antes de tiempo, y sólo necesitaba simple_fields_for a trabajar con el campo hash. El método ingenuo (uso básico fields_for) que estaba usando era la siguiente:

= simple_form_for(@page) do |f| 
    = f.simple_fields_for :fields do |ff| 
    = ff.input :address 
    = ff.input :founded 

Pero eso no fue poblando la forma adecuada.solución de envolver el hash en una estructura de Nicholaides trabajó para mí:

- require 'ostruct' 
= simple_form_for(@page) do |f| 
    = f.simple_fields_for :fields, OpenStruct.new(@page.fields) do |ff| 
    = ff.input :address 
    = ff.input :founded 

para no tener que mezclar las cosas OpenStruct en mi punto de vista, he creado un parche mono para simple_form se ajuste automáticamente los tipos de hash en un OpenStruct y ponerlo en un inicializador:

require 'ostruct' 

module SimpleForm::ActionViewExtensions::Builder 
    def simple_fields_for_with_hash_support(*args, &block) 
    if args[0] && !args[1] 
     field = object.send(args[0]) 
     args << OpenStruct.new(field) if field.respond_to?(:has_key?) 
    end 
    simple_fields_for_without_hash_support(*args, &block) 
    end 
    alias simple_fields_for_without_hash_support simple_fields_for 
    alias simple_fields_for simple_fields_for_with_hash_support 
end 

Y ahora puedo usar mi solución ingenua original sin código de vista especial.

+0

+1 para los campos "f.simple_fields_for: OpenStruct.new (@ page.fields) do | ff |". Arruine mi cerebro muchas horas para descubrir por qué no recuperaba los valores correctamente hasta que agregué el bit de OpenStruct. – gk0r

+1

Esto es increíble. Una nota adicional para los usuarios de parámetros potentes: debe definir cada campo que agregue explícitamente. Por ejemplo, 'params.require (: page) .permit (: fields => [: address,: founded])' –

+0

Thx para la solución, funciona perfectamente, con una excepción: No sé, cómo agregar validación errores a '@ page.fields'. –

3

Use OpenStruct. Funciona así:

require 'ostruct' 

struct = OStruct.new("address" => "8 finance street, hong kong", "founded" => "1973") 
struct.address # => 8 
struct.founded # => "1973" 
+0

que está muy bien para la "exigibilidad" (recuerdo usando un 'CallableHash' de vuelta en el day'). Pero mi pregunta específica es sobre SimpleForm, realmente. Quiero una solución ordenada. –

1

Aquí es una solución que podría fácilmente adaptarse a su problema:

https://gist.github.com/2157877

Mantiene la forma súper limpia y mantiene el comportamiento normal de un modelo activo (nada para agregar en su controlador).

¡Salud!

1

Si solo he encontrado esta publicación antes, no me habría perdido tres días sobre este tema.

yo no era capaz de comentar sobre el answear tan sólo para añadir, si alguien trata de fields_for una matriz mongo de hash (has_many similares), además, tendrá que suministrar el objeto OpenStruct raíz con un "#{key}_attributes=" (en este caso fields_attributes =) método para que fields_for lo identifique como una relación has_many.

espero que ayude a alguien;)

Cuestiones relacionadas