3

I'm serializing a hash that is stored in a settings field in a table, and would like to be able to edit that hash in a form field.

class Template < ActiveRecord::Base
  serialize :settings
end

But I just do <%= f.text_area :settings %> then the text area just shows the serialized data instead of the hash.

How can I get the hash to show in the text area?

Shpigford
  • 24,748
  • 58
  • 163
  • 252
  • What do you expect to see in the textarea? Something like `{:foo=>'bar'}`? One problem will be that when you submit the form the settings field will be serialized as a string, not as a hash, unless you add some logic to your app. The logic should preferably go in the model. – Wizard of Ogz Jun 30 '11 at 20:09
  • Correct. I'd like to see `{:foo=>'bar'}`. – Shpigford Jun 30 '11 at 20:25

3 Answers3

4

Maybe setting up another accessor for your model would work.

class Template < ActiveRecord::Base
  serialize :settings
  attr_accessor :settings_edit

  before_save :handle_settings_edit, :if => lambda {|template| template.settings_edit.present? }

  def settings_edit
    read_attribute(:settings).inspect   # should display your hash like you want
  end

  protected
    def handle_settings_edit
      # You may want to perform eval in your validations instead of in a 
      # before_save callback, so that you can show errors on your form.
      begin
        self.settings = eval(settings_edit)
      rescue SyntaxError => e
        self.settings = settings_edit
      end
    end  
end

Then in your form use <%= f.text_area :settings_edit %>.

I have not tested any of this code, but in theory it should work. Good luck!

WARNING: Using eval like this is very dangerous, in this example a user could delete the entire Template table with one line in the edit box Template.destroy_all. Use a different method to convert the string to a hash if user input is involved.

Ryan
  • 9,340
  • 5
  • 39
  • 42
Wizard of Ogz
  • 12,543
  • 2
  • 41
  • 43
  • Actually...this doesn't seem to be working. When I edit the form and submit (to UPDATE), it doesn't save the changes to `settings`...it keeps the previous data. – Shpigford Jul 01 '11 at 15:15
  • Did something change? Are you using `attr_accessible`? If so, make sure you allow mass assignment to `settings_edit`. – Wizard of Ogz Jul 01 '11 at 22:08
  • Nothing changed and I'm not using `attr_accessible`. – Shpigford Jul 05 '11 at 14:30
  • This is close. The problem is that the statement "eval(settings_edit)" is reading the method you defined above. I got this working by removing the method def settings_edit and instead adding the line "@template.settings_edit = @template.read_attribute(:settings).inspect" to my edit action in the controller. – genkilabs Apr 18 '12 at 15:28
  • Also, you should add and empty hash in the new action when you create the model, @template = Template.new( :settings => {} ) – genkilabs Apr 18 '12 at 15:32
  • 1
    Using `eval` on user input is not safe. A malicious user could stick any Ruby code in form field and have it executed on your server. – Wizard of Ogz Apr 18 '12 at 19:36
  • I Agree with Wizard of Ogz, using an `eval` like that is super dangerous. That gives the user unlimited access to run whatever ruby code they want. Such as `Template.destroy_all`. – Ryan Jan 28 '14 at 17:24
  • Use `self.settings = eval(@settings_edit)` instead of `self.settings = eval(settings_edit)` – Ccastillop Aug 17 '15 at 01:59
2

... or you could use something like this (without any logic in model):

<% @template.settings.each do |name, value| %>
  <div>
    <%= label_tag name %>
    <%= text_field_tag "template[settings][#{name}]", value %>
  </div>
<% end %>
Anton Rogov
  • 189
  • 2
  • Yes, this works, but you would need more logic in your form to add a new key/value pair to the settings hash. – Wizard of Ogz Jul 01 '11 at 14:14
  • yep, and it will be pretty complex actually :) so this way works if you have pre-defined config key and don't want keys to be added or removed – Anton Rogov Jul 02 '11 at 08:10
-2

you should use something like

class Template < ActiveRecord::Base
  serialize :settings, Hash
end
MKumar
  • 1,524
  • 1
  • 14
  • 22