Using Rails Form Helpers with Sequential Custom Classes

I am trying to save a hash of options in a single DB field. The form is able to save data in the database, but cannot receive it again when I proceed to edit it (for example, all other fields are pre-filled, except for the wp_options fields).

class Profile < ActiveRecord::Base serialize :wp_options end 

This is my custom class:

 class WP_Options attr_accessor :wp_name, :wp_desc, :wp_limit end 

In my form:

 <%= form_for(@profile, :remote => true) do |f| %> ... <%= f.fields_for :wp_options do |wp_options| %> <%= wp_options.text_field :wp_name %> <% end %> ... 

In my controller:

 @profile = Profile.new(:wp_options => WP_Options.new) 

In my database column "wp_options":

 --- !map:ActiveSupport::HashWithIndifferentAccess wp_name: Test 

Any advice would be really appreciated.

+6
ruby ruby-on-rails activerecord serialization ruby-on-rails-3
source share
3 answers

This is actually easy. You need to use a class with reader methods. You can create it in different ways, but the simplest is to use the OpenStruct class (note that it will not be able to see the fields that are in the methods of the OpenStruct instance ... this class cannot override methods).

In your form you must add:

 <%= f.fields_for :wp_options, @profile.wp_options do |wp_options| %> 

Instead of @profile (if you have a dynamic variable), you can use f.object.wp_options .

And for the Profile model, you should add the wp_options method.

 def wp_options OpenStruct.new(self.attributes['wp_options']) end 

In this case, it will only work if your serialized wp_options is a Hash class.

Hope this helps.

PS. I used the same technique, but since I had type hash keys, OpenStruct was unable to create it, so I used the simple Struct class. I had a data column:

 def data keys = current_data.keys data = attributes[:data] Struct.new(*keys).new(*keys.map { |k| data[k] }) end 

A little less trivial, but in any case the same approach (before that I created a special class, but now I know that Struct is the best way to create these kinds of things. You can find more ideas here: How to use hash keys as methods in the class ? )

+10
source share

For those who use Dmitryโ€™s solution, on rails 3.0.5 (not sure about previous versions, so try earlier), add

 require 'ostruct' 

into the environment.rb file to include OpenStruct.

If you are going to serialize using JSON, add to your model (oh, and I'm completely new to RoR, so please feel free to analyze and suggest a better โ€œworkingโ€ solution, and then this) (my variable names are changed according to the above above example):

 before_save :json_serialize after_save :json_deserialize after_find :json_deserialize def json_serialize self.wp_options = ActiveSupport::JSON.encode(self.attributes['wp_options']) end def json_deserialize unless (self.attributes['wp_options'].nil?) self.wp_options = ActiveSupport::JSON.decode(self.attributes['wp_options'].to_s) end end 

In a final note: I honestly don't know why it needs to be stored in self.wp_options, not self.attributes ['wp_options'], if someone can explain that it will be cool.

+1
source share

I ran into the same problem. My solution was as follows:

  • Define a method in your helper:

     def wp_options_text_field(profile, wp_options, name) wp_options.text_field name, :value => profile.wp_options[name] end 
  • In your opinion:

     <%= form_for(@profile, :remote => true) do |f| %> ... <%= f.fields_for :wp_options do |wp_options| %> <%= wp_options_text_field :wp_name %> <% end %> ... 

The only problem is that you will need to define methods for each helper method that you will use. In my case, I had only 2 methods, and it didnโ€™t hurt.

0
source share

All Articles