Michael Karavaev
Michael Karavaev

Reputation: 1527

editing form for serialized model

I have model Info.rb for keeping info of all exhibitions and contests

class Info < ActiveRecord::Base

  serialize :exhibition, Array
  serialize :contest, Array

end

I want to have ability of editing updating and adding new elements to exhibiton and contest arrays . This is my info_controller.rb

class Admin::InfoController < ApplicationController

 before_action :find_info, only: [:edit, :update]

 def index
   @info = Info.all
 end

 def edit

 end

 def update
   @info.update_attributes(info_params)
   redirect_to admin_info_index_path
 end

 private

   def find_info
     @info = Info.first
   end

   def info_params
     params.require(:info).permit({:contest => []}, {:exhibition => []})     
   end

end

I can't get how create proper edit form with single field for every element in arrays. Will appreciate any help.

Upvotes: 0

Views: 274

Answers (1)

blotto
blotto

Reputation: 3407

In your model, you can dynamically assign attributes from the keys of the serialized Hash, or Array. I've done something similar so am munging my code for a few examples below. Basically , by dynamically creating virtual attributes, using a DSL like Formtastic becomes much easier.

With serialized Arrays :

 class Info < ActiveRecord::Base

   serialize :exhibition, Array
   serialize :contest, Array

   after_initialize :explode_exhibition
   after_initialize :explode_contest

   before_save :implode_exhibition
   before_save :implode_contest

   private 

   def explode_exhibition()
       exhibition.each_with_index do |v,i|
            self.class.send(:attr_accessor, "@exhibition_#{i}")
            self.instance_variable_set("@exhibition_#{i}", v)
       end
   end

   def explode_contest()
       contest.each_with_index do |v,i|
            self.class.send(:attr_accessor, "@contest_#{i}")
            self.instance_variable_set("@contest_#{i}", v)
       end
   end


   def implode_exhibition()
      exhibition = []
      self.methods.grep(/exhibition_/).each do |method_name|
            exhibition << self.instance_variable_get("@#{method_name}", v)
      end
   end

   def implode_contest()
      contest = []
      self.methods.grep(/contest_/).each do |method_name|
            contest << self.instance_variable_get("@#{method_name}", v)
      end
   end

 end

With serialized Hashes :

 class Info < ActiveRecord::Base

   serialize :exhibition, Hash
   serialize :contest, Hash

   after_initialize :explode_exhibition
   after_initialize :explode_contest

   before_save :implode_exhibition
   before_save :implode_contest

   private 

   def explode_exhibition()
       exhibition.each do |k,v|
            self.class.send(:attr_accessor, "@exhibition_#{k}")
            self.instance_variable_set("@exhibition_#{k}", v)
       end
   end

   def explode_contest()
       contest.each do |k,v|
            self.class.send(:attr_accessor, "@contest_#{k}")
            self.instance_variable_set("@contest_#{k}", v)
       end
   end


   def implode_exhibition()
      exhibition.each do |k,v|
            exhibition[k] = self.instance_variable_get("@exhibition_#{k}", v)
       end
   end

   def implode_contest()
      contest.each do |k,v|
            contest[k] = self.instance_variable_get("@contest_#{k}", v)
       end
   end

 end

And then in Formtastic you can access the attributes and build a form dynamically for each virtual attribute. Note the regex, can use some iteration, as it currently will only match self.exhibition_0 through self.exhibition_9, but it also excludes any setter methods by not matching methods like self.exhibition_0=

 f.object.methods.grep(/^exhibition_.*?(?<!=)$/).each do |method_name|
    f.input  method_name
  end

I haven't fully tested this munged code, but the majority of this I have working in a few of my own classesn. Also, there may be a need to dynamically create the setter methods as well. Let me test this out a bit, and improve on this idea.

Upvotes: 2

Related Questions