I have a draft design for a billing system. I am prototyping it with ActiveRecord and can''t find a "clean" way to have ActiveRecord grok my design. I''m not sure whether its my lack of understanding AR, or whether the data model ought to be done differently. Pointers would be much appreciated :) The tables involved are "services", "customers", and "service_deployments". This last table manually connects "services" and "customers" in a many-to-many relationship. So far so good, now for the tricky part: Each row in the "service_deployment" table has a foreign key into a service configuration table. This table varies by the service being configured. It depends on a column in the "services" table, to allow service-specific configurations. The idea is that a "web hosting" service would require a different configuration from a "webmail account" service, say. I want to to able to craft a configuration AR for each service, so that I can deal with service configurations no matter how much they differ in nature. In code, somethign like this is desired: customer = Customer.find(name) services = customer.service_deployments services.each { |deployment| # config will always be an ActiveRecord, # but a varying subclass of ActiveRecord::Base config = deployment.configuration } Here''s my best attempt so far. I dont think this is what I want, especially as I have to call "setup" on each instance of ServiceDeployment after it is created. class Customer < ActiveRecord::Base has_many :service_deployments end class Service < ActiveRecord::Base has_many :service_deployments end class ServiceDeployment < ActiveRecord::Base belongs_to :service belongs_to :customer def setup ServiceDeployment.has_one service.config_class.intern end end cheers Gerret
On 16-okt-2005, at 17:21, Gerret Apelt wrote:> Here''s my best attempt so far. I dont think this is what I want, > especially as I have to call "setup" on each instance of > ServiceDeployment after it is created. > > > class Customer < ActiveRecord::Base > has_many :service_deployments > end > > class Service < ActiveRecord::Base > has_many :service_deployments > end > > class ServiceDeployment < ActiveRecord::Base > belongs_to :service > belongs_to :customer > > def setup > ServiceDeployment.has_one service.config_class.intern > end > endI think you have a "finder SQL" option for relationships, where you can use lazy string evaluation. Besides, you can always write the attribute methods yourself and take it from there -- Julian "Julik" Tarkhanov
> Besides, you can always write the attribute methods yourself and take > it from thereThanks for the pointer. I wrote a module "DynamicAssociation" that can be mixed in with any ActiveRecord to enable dynamic associations (where the name of the associated class varies on a per-instance basis). I use it like this (in the context of my previous post) class ServiceDeployment < ActiveRecord::Base include DynamicAssociation belongs_to :service belongs_to :customer dynamic_has_one :service_config, :resolve_service_config_class # resolves the "service_config" implementation class to be # associated with this instance. # In this case, the service being configured tells us which class # it wants to be configured with. def resolve_service_config_class return @config_class unless @config_class == nil config_class_name = service.config_class @config_class = eval(config_class_name) end end The call to ''dynamic_has_one'' adds five additional instance methods to ServiceDeployment: service_config service_config=(any_service_config) service_config.nil? build_service_config(attributes) create_service_config(attributes) These are he same methods as generated by ActiveRecord''s "has_one", except that they are instance methods rather than class methods. The "dynamic_has_one" must be matched by a regular "belongs_to" call in any class that can serve as the associated class. I haven''t spent much time on analyzing how inefficient this is. If anybody has a slicker/faster solution please let me know. I''d be happy to post the DynamicAssociation module if anybody''s interested. Gerret