views:

45

answers:

2

Hi,

I am currently working on a Ruby on Rails app which will function in some ways like a site-specific social networking site. As part of this, each user on the site will have a profile where they can fill in their contact information (phone numbers, addresses, email addresses, employer, etc.).

A simple solution to modeling this would be to have a database column per piece of information I allow users to enter. However, this seems arbitrary and limited. Further, to support allowing users to enter as many phone numbers as they would like requires the addition of another database table and joins.

It seems to me that a better solution would be to serialize all the contact information entered by a user into a single field in their row. Since I will never be conditioning a SQL query on this information, such a solution wouldn't be any less efficient.

Ideally, I would like to use a vCard as my serialization format. vCards are the standard solution to storing contact information across the web, and reusing tested solutions is a Good Thing. Alternative serialization formats would include simply marshaling a ruby hash, or YAML. Regardless of serialization format, supporting the reading and updating of this information in a rails-like way seems to be a major implementation challenge.

So, here's the question: Has anyone seen this approach used in a rails application? Are there any rails plugins or gems that make such a system easy to implement?

Ideally what I would like is an acts_as_vcard to add to my model object that would handle editing the vcard for me and saving it back to the database.

+1  A: 

vCard is good for an API, but for the actual database I would use a 1-many design. Each person can have many phone numbers, addresses, email addresses, past employers. For current employer, you could do a 1-1 relationship. I think your aversion to joins is misplaced. With proper indexes, performance should be fine. Implementation will be much simpler than if you are constantly serializing and deserializing a denormalized string representation. You won't have to reinvent the wheel as you're contemplating.

Matthew Flaschen
My aversion isn't to joins. My aversion is to having a table for phone numbers, a table for addresses, a table for email addresses, etc. Using a vCard would mean that adding the ability to store a new type of information is simply a view change and doesn't require the creating of a new database table.Contact information is complex. I think that complexity deserves to be handled at the application layer, not the database layer. DB changes are always more dangerous and difficult to make than app layer changes and I don't know that my specific requirements are static.
Anthony Chivetta
A: 

1) if you do want to go the serialization route rails has built in support for storing a hash

from http://api.rubyonrails.org/classes/ActiveRecord/Base.html

class User < ActiveRecord::Base
    serialize :preferences
end

user = User.create(:preferences => { "background" => "black", "display" => large })
User.find(user.id).preferences # => { "background" => "black", "display" => large }

if you use this technique I would put the serialized field on its own model/table object, otherwise it will be included in every User find call, which is not ideal, maybe

class User < ActiveRecord::Base
    has_one :contact_info
end

class ContactInfo < ActiveRecord::Base
    has_many :users
    serialize :data
end

# ...
user.contact_info.data[:phone_numbers] # => ['999 999-9999', '000 000-0000']

2) or if you want to go the noSql route rails has support for mongodb, you would basically embed the contact info into the User model/document

3) or just go with the additional tables, it's not as bad as it seems, rails migrations can help a lot here with changing requirements

house9
The serialization route is actually pretty close to what I am looking for. (I can't believe I didn't know rails had that option build-in.) However, my hope was really for something that masked itself behind the typical active record setters/getters.
Anthony Chivetta