Сериализованное поле Hash и простая форма

У меня есть следующее.

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

Значение fields будет зависеть от категории. Но в качестве примера:

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

В этом примере категория определила "address" и "founded" как пользовательские поля.

Я хочу сказать:

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

Но мне нужно сделать что-то волшебное, чтобы справиться с тем, что @page.founded недействительно

Вместо этого я должен смотреть @page.fields["founded"].

Любые предложения?


Update:

У меня немного ближе

- 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]

Теперь нужно сделать этот DRYer (не хотите указывать имя объекта).

Я посмотрю, могу ли я написать для этого простое расширение простой формы.

Ответ 1

Я столкнулся с аналогичной проблемой, пытаясь использовать simple_fields_for в типе поля Hash модели Mongoid. Монгоидная версия вашего примера, с которым я имел дело, выглядела так:

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

Моя ситуация может несколько отличаться, поскольку я уже знаю ключи хеша, которые я ищу раньше времени, и мне просто нужно simple_fields_for работать с хэш-полем. Наивный метод (базовое использование fields_for), который я использовал, выглядел следующим образом:

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

Но это не заполняло форму должным образом. nicholaides решение обертывания хеша в структуре работало для меня:

- 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

Чтобы избежать необходимости комбинировать материал OpenStruct в моем представлении, я создал патч обезьяны для simple_form, чтобы автоматически обматывать типы хэшей в OpenStruct и помещать его в инициализатор:

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

И теперь я могу использовать свое первоначальное наивное решение без специального кода просмотра.

Ответ 2

Используйте OpenStruct. Он работает следующим образом:

require 'ostruct'

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

Ответ 3

Вот решение, которое вы могли бы легко адаптировать к своей проблеме:

https://gist.github.com/2157877

Сохраняет форму сверхчистой и сохраняет нормальное поведение активной модели (ничего не добавить в контроллер).

Приветствия!

Ответ 4

Если бы я только нашел этот пост раньше, я бы не пропустил три дня по этому вопросу.

Мне не удалось прокомментировать ответ, поэтому просто добавьте, если кто-то пытается fields_for массива хангов mongo (has_many like) дополнительно вам нужно будет предоставить корневой объект OpenStruct с помощью "#{key}_attributes=" (в этот случай fields_attributes =), поэтому fields_for будет идентифицировать это отношение has_many.

Надеюсь, это поможет кому-то;)