I'm developing a Rails-based web interface/API for a large, vendor-supplied, legacy Oracle database. One of the constraints is that I can't change the schema other than adding views, because it still has to support the vendor's application.
The table for customer data has a limited number of columns, and extended attributes are stored in customer_def_field_value. These attributes are defined in customer_def_field_def.
so I end up with the classes:
class FieldDef < ActiveRecord::Base
set_table_name 'customer_def_field_def'
set_primary_key :customer_def_field_def_id
set_sequence_name 's_customer_def_field_def'
alias_attribute :field_def_id, :customer_def_field_def_id
has_many :field_values
def symbol
self.title.parameterize('_').to_sym
end
end
class FieldValue < ActiveRecord::Base
set_table_name 'customer_def_field_value'
set_primary_keys :cust_id, :customer_def_field_def_id
set_sequence_name :autogenerated
alias_attribute :field_def_id, :customer_def_field_def_id
belongs_to :customer, :foreign_key => :cust_id
belongs_to :field_def, :foreign_key => :customer_def_field_def_id
end
In order to get associations on Customer for each of the 'custom fields', I have this code in the Customer class:
FieldDef.find_each do |field|
has_one field.symbol, :class_name => 'FieldValue', :foreign_key => :cust_id, :conditions => proc{ "customer_def_field_def_id = #{field.id}"}
accepts_nested_attributes_for field.symbol
delegate :field_value, :to => field.symbol, :prefix => true
delegate :field_value=, :to => field.symbol, :prefix => true
attr_accessor ('set_' + field.symbol.to_s).to_sym
end
This works like a charm, but it queries the DB every time a Customer is instantiated in order to generate the method definitions.
It hasn't been a performance issue... yet.
New custom field types aren't added very often, so I think I might be able to do this in an initializer rather than in my model, and just restart the application if new fields have to be added... but I wanted to get some feedback. Thanks!
1 Answer 1
I think the query of the database would only happen once in production mode (the class would get cached, in development mode, the class gets reloaded every request that references a Customer object). You can confirm this by setting:
config.log_level = :debug
in your production.rb file and seeing if the query runs more than once. It might be worth having an after_save callback for FieldDef that does the same thing as the "FieldDef.find_each do |field|" block you have so you don't have to restart the server when new FieldDefs are created.
-
\$\begingroup\$ Thanks... I'll give this a try. I especially like the idea of using the
after_save
callback to avoid server restarts. \$\endgroup\$Jason Lewis– Jason Lewis2012年01月16日 05:48:00 +00:00Commented Jan 16, 2012 at 5:48 -
\$\begingroup\$ You're absolutely right. The query to generate the associations is only run the first time, when the class is cached. Sadly, the
after_save
callback won't save me from restarting periodically, as new custom fields are usually added from the vendor's application. Thanks! \$\endgroup\$Jason Lewis– Jason Lewis2012年01月17日 14:16:53 +00:00Commented Jan 17, 2012 at 14:16