Topic: form_for for a hash instead of model?

My site configuration is stored in a YAML file. The user edits this file via a form on the site. Here's how the form looks:

<% form_tag :action => 'config', :method => :post do %>
<%= text_field 'settings',  'support_email', :size => 20, :value => @hashish['support_email']%>
<%= radio_button 'settings',  'allow_comments', 1, :checked => (@hashish['allow_comments']==1) %>
<%= radio_button 'settings',  'allow_comments', 0, :checked => (@hashish['allow_comments']==0) %>
<% end %>

@hashish was set in the controller: @hashish = YAML.load_file("blah.yml")

This works perfectly, but I have a gut feeling that Rails contains an elegant way of handling this situation?

Edit: While on this topic, where is the best place to put the settings hash? I want it loaded on startup and to just remain in memory because it will be accessed throughout the entire app (views, controllers)

Last edited by Relax (2008-01-05 21:02:16)

Re: form_for for a hash instead of model?

Well, to use form_for, you need an object, and you can use this to do that:
In your controller:

class TestItController < ApplicationController
  def index
    hashit = {:support_email  => "test@test.com",
              :allow_comments => 0}
    @hashit  = Hashit.new(hashit)
  end
end

Hashit is defined in RAILS_ROOT/app/models/hashit.rb although it could probably live in /lib:
class Hashit
  def initialize(hash)
    hash.each do |k,v|
      self.class.send(:define_method, k, proc{v})
    end
  end
end

It takes each key/value pair and defines a method for this class with the key as its name and value for its value, this is what support_email() would be.
Here is the view that uses the @hashit object from the controller:
<% form_for :hashit, :url => {:action => 'index'} do |f| %>
  <%= f.text_field :support_email %>
<% end %>

Now, if you like the idea of turning a hash into an object, then for setting the values you can just do the same thing, you just need a method= method, define_method("method=", ...).

Last edited by pullmonkey (2008-01-06 16:23:52)

Re: form_for for a hash instead of model?

I extended it to set variables too, now all you have to do is the save method.  @hashit.save() would presumably update your yaml.
EDIT: Did the save() method - http://pullmonkey.com/2008/1/6/convert- … ass-object
Here is the new model code:

class Hashit
  def initialize(hash)
    hash.each do |k,v|
      self.instance_variable_set("@#{k}", v)  ##  create instance variable
      self.class.send(:define_method, k, proc{self.instance_variable_get("@#{k}")})  ## method to return instance variable
      self.class.send(:define_method, "#{k}=", proc{|v| self.instance_variable_set("@#{k}", v)})  ## method to set instance variable
    end
  end
end

Example of its use:
 
hashit = {:support_email  => "test@test.com",
          :allow_comments => 0}
@hashit  = Hashit.new(hashit)
logger.error @hashit.inspect        #=> #<Hashit:0xb6a65110 @allow_comments=0, @support_email="test@test.com">
logger.error @hashit.support_email  #=> test@test.com
logger.error "setting variables" 
@hashit.support_email = "new@some_email"
logger.error @hashit.support_email  #=> new@some_email

Last edited by pullmonkey (2008-01-05 22:20:01)