Most of my ruby/rails development is against a legacy Oracle database. One of the things we needed to fix, was a user-table with not enough fields. Because the user table was shared with another application, we were not able to alter the table. So we added another table, user_params, containing possibly extra parameters for each user. Now it would be nice if each of those possible parameters would behave like a real attribute. So, first implementation: [ruby] class User < ActiveRecord::Base set_primary_key "user_name" set_table_name "stca_user" set_sequence_name "autogenerated" # do not use a sequence at insert! validates_presence_of :user_name validates_presence_of :password # define some virtual attributes for attributes defined in the UserParam table def group_name UserParam.get_param_value(self.user_name, 'UserGroup') end def group_name=(value) UserParam.set_param_value(self.user_name, 'UserGroup', value) end def title UserParam.get_param_value(self.user_name, 'UserTitle') end def title=(value) UserParam.set_param_value(self.user_name, 'UserTitle', value) end def language UserParam.get_param_value(self.user_name, 'UserLanguage') end def language=(value) UserParam.set_param_value(self.user_name, 'UserLanguage', value) end def full_name UserParam.get_param_value(self.user_name, 'UserFullName') end def full_name=(value) UserParam.set_param_value(self.user_name, 'UserFullName', value) end def email UserParam.get_param_value(self.user_name, 'UserEmail') end def email=(value) UserParam.set_param_value(self.user_name, 'UserEmail', value) end # ... snipped away more code end [/ruby] Obviously, this code is not DRY. At lot of repetition, like the stuff i hate about C# or java properties. This could be done better, let's just generate the methods: [ruby] class User < ActiveRecord::Base set_primary_key "user_name" set_table_name "stca_user" set_sequence_name "autogenerated" # do not use a sequence at insert! validates_presence_of :user_name validates_presence_of :password # define some virtual attributes for attributes defined in the UserParam table instance_eval do [['group_name', 'UserGroup'], ['title', 'UserTitle'], ['language','UserLanguage'], ['full_name', 'UserFullName'], ['email', 'UserEmail']].each do |arr| define_method arr[0].to_sym do UserParam.send('get_param_value', self.user_name, arr[1]) end define_method "#{arr[0]}=" do |value| UserParam.send("set_param_value", self.user_name, arr[1], value) end end end end [/ruby] This looks nice. One disadvantage might be that this is not very readable. At first glance it is no longer clear which attributes are available. But with models that is always the case. A few things can still be improved. Every get and set is a query against the database. We might want to improve upon that, and cache results. Also saving the virtual fields is now done when they are being set, but at that time the parent "User" might still not be saved. So this still calls for a better solution. But this does show why i like ruby so much. It makes me feel powerful :)
Comments
Add comment