Make form_for not call methods on your model

Published January 16, 2012

I'm writing a Rails app that uses a custom model class to interact with Apache CouchDB via couchrest. The app is very schemaless in nature, which is one of the reasons I'm using CouchDB in the first place.

Rails' form_for expects models that have methods for all its attributes. This is a problem, since my custom model doesn't define methods for the attributes. It stores data in a hash, and provides hash-like methods ([] and []=) to access data. Creating methods for my attributes are not viable, since I don't know which attributes my models have beforehand, when I want forms for @user = User.new, for example.

So a form like this:

<%= form_for @user, :path => session_path do |f| %>
  <%= f.form_for :name %>
<% end %>

Basically ends up doing this:

@user.send("name")

What I need it to do, is this:

@user[:name]

To change this behaviour, we unfortunately need to monkey patch. I haven't found any APIs for it, and it boils down to a unconfigurable low level class called ActionView::Helpers::InstanceTag, so creating a custom form_for isn't viable.

I added the snippet below to config/initializers/monkey_patches.rb.

class ActionView::Helpers::InstanceTag
  def self.value(object, method_name)
    object[method_name.to_sym] if object
  end

  def self.value_before_type_cast(object, method_name)
    self.value(object, method_name)
  end
end

Here's the original source code on GitHub for these two methods. As you can see, my particular implementation may not solve your problems, and ignores the _before_type_cast stuff since my particular model class doesn't do type casting.

I did not investigate why method_name is a string, given I originally specified it as a symbol in f.text_field.


Questions or comments?

Feel free to contact me on Twitter, @augustl, or e-mail me at august@augustl.com.