Arbitrary "Columns" for ActiveRecord

I’m having a bit of a tough time coming up with a good solution for what
I’m trying to do. I’ve come up with a couple of solutions that work
pretty well but many use way too many queries or are just not very good.

Basically I want to store something of a property list for a model.

class Model < AR
has_many :properties, :dependent => :delete
end

class Property < AR
belongs_to :model
belongs_to :property_key
end

|------------------

properties
id (serial)
value (varchar)
property_key_id (int)
model_id (int)
------------------

Now an issue is that I also store possible KEYs for my properties.

class PropertyKey < AR
has_many :properties, :dependent => :delete
end

|-----------------

property_keys
id (serial)
key (varchar)
-----------------

I’d like to access a model’s properties somewhat like:

Model.property_key = value # where property_key is dynamic/magic

OR

Model.get_property(key, value)

The problem here is, that with this set up, the way I have it set up, I
have Model#get_property set to this:

def get_property(key)
if key = PropertyKey.find_by_key(key)
self.properties.find(:first, :condition => [“property_key_id = ?”,
key.id]).value
else
false
end
end

and Model#set_property

def set_property(key, value)
if row = properties.find(:first, :conditions => [“properties.key = ?”,
key], :include => :property_key)
if value.empty?
return row.destroy
else
row.value = value
return row.save
end
else
key = PropertyKey.find_by_key(key)
properties << Property.new(:key => key, :value => value) unless
value.empty?
end
end

This is what I have so far. Any help at all would be greatly appreciated
in optimizing this code and making it better! Thanks!!

Rick Martinez wrote:

I’m having a bit of a tough time coming up with a good solution for what
I’m trying to do

im not clear on what youre trying to do. do you want static models like
the
symbiotic db-schema/model.rb relationship dictates? if so, and you just
want
to decorate records with the occasional extra attribute, a serialized
hash
may do the trick (as ezra offers).

in any case, rails has already taken a SQL result hash and ‘objectized’
it.
to treat that object as a generic ‘Model’ or ‘Property’ class again,
then
layer dynamic properties on top of this again, results in a loss of 2
things: performance and magic…

ie, a model with 5 columns besides the id can go from 1 query to 6. and
Model.find_by_property() will no longer work, nor will you get free
setter/getter methods, unless you add them back via either rewriting
portions of activerecord or providing your own additional
method_missings.

This is what I have so far. Any help at all would be greatly

i’d consider how important model refactorability and schema dynamism is
to
your application. if it is important, i’d consider swapping out
ActiveRecord
for Redland, ActiveRDF, or something custom built on eg BerkeleyDB,
perhaps
bypassing the ‘objectification’ process entirely and sticknig with a
generic
‘Resource’ class but eliminating the abuse of an ActiveRecord class for
this
purpose…


View this message in context:
http://www.nabble.com/Arbitrary-"Columns"-for-ActiveRecord-tf2098713.html#a5789846
Sent from the RubyOnRails Users forum at Nabble.com.

On Aug 13, 2006, at 3:40 AM, Rick Martinez wrote:

I’m having a bit of a tough time coming up with a good solution for
what
I’m trying to do. I’ve come up with a couple of solutions that work
pretty well but many use way too many queries or are just not very
good.

Basically I want to store something of a property list for a model.

Hey Rick-

Not sure if this is exactly what you would need but it might get you

going in the right direction.

class DbHash < ActiveRecord::Base
class << self
def
pair = find_by_key(key.to_s)
pair.value unless pair.nil?
end

 def []=(key, value)
   pair = find_by_key(key)
   unless pair
     pair = new
     pair.key, pair.value = key.to_s, value
     pair.save
   else
     pair.value = value
     pair.save
   end
   value
 end

 def to_hash
   Hash[ *find_all.map { |pair| [pair.key, pair.value] }.flatten ]
 end

end
end

 create_table "configurations", :force => true do |t|
   t.column "key", :string, :limit => 40, :default => "", :null

=> false
t.column “value”, :string, :default => “”
end

You could add a foreign key or even go polymorphic to get this to

relate to your other models. But the way it is right here it will
just act like a hash that is backed by the db.

-Ezra