ruby on rails - Single Table Inheritance or Type Table -


i facing design decision cannot solve. in application user have ability create campaign set of different campaign types available them.

originally, implemented creating campaign , campaigntype model campaign has campaign_type_id attribute know type of campaign was.

i seeded database possible campaigntype models. allows me fetch campaigntype's , display them options users when creating campaign.

i looking refactor because in solution stuck using switch or if/else blocks check type campaign before performing logic (no subclasses).

the alternative rid of campaigntype table , use simple type attribute on campaign model. allows me create subclasses of campaign , rid of switch , if/else blocks.

the problem approach still need able list available campaign types users. means need iterate campaign.subclasses classes. works except means need add bunch of attributes each subclass methods displaying in ui.

original

campaigntype.create! :fa_icon => "fa-line-chart", :avatar=> "spend.png", :name => "spend based", :short_description => "spend x y" 

in sti

class spendbasedcampaign < campaign    def name     "spend based"   end    def fa_icon     "fa-line-chart"   end    def avatar     "spend.png"   end   end 

neither way feels right me. best approach problem?

a not performant solution using phantom methods. technique works ruby >= 2.0, because since 2.0, unbound methods modules can bound object, while in earlier versions, unbound method can bound objects kind_of? class defining method.

# app/models/campaign.rb class campaign < activerecord::base   enum :campaign_type => [:spend_based, ...]    def method_missing(name, *args, &block)     campaign_type_module.instance_method(name).bind(self).call   rescue nameerror     super   end    def respond_to_missing?(name, include_private=false)     super || campaign_type_module.instance_methods(include_private).include?(name)   end    private   def campaign_type_module     campaigns.const_get(campaign_type.camelize)   end end  # app/models/campaigns/spend_based.rb module campaigns   module spendbased     def name       "spend based"     end      def fa_icon       "fa-line-chart"     end      def avatar       "spend.png"     end    end end 

update

use class macros improve performance, , keep models clean possible hiding nasty things concerns , builder.

this model class:

# app/models/campaign.rb class campaign < activerecord::base   include campaignattributes    enum :campaign_type => [:spend_based, ...]   campaign_attr :name, :fa_icon, :avatar, ... end 

and campaign type definition:

# app/models/campaigns/spend_based.rb campaigns.build 'spendbased'   name    'spend based'   fa_icon 'fa-line-chart'   avatar  'spend.png' end 

a concern providing campaign_attr model class:

# app/models/concerns/campaign_attributes.rb module campaignattributes   extend activesupport::concern    module classmethods     private     def campaign_attr(*names)       names.each |name|         class_eval <<-eos, __file__, __line__ + 1           def #{name}             campaigns.const_get(campaign_type.camelize).instance_method(:#{name}).bind(self).call           end         eos       end     end   end end 

and finally, module builder:

# app/models/campaigns/builder.rb module campaigns   class builder < basicobject     def initialize       @mod = ::module.new     end      def method_missing(name, *args)       value = args.shift       @mod.send(:define_method, name) { value }     end      def build(&block)       instance_eval &block       @mod     end   end      def self.build(module_name, &block)     const_set module_name, builder.new.build(&block)   end end 

Comments

Popular posts from this blog

python - TypeError: start must be a integer -

c# - DevExpress RepositoryItemComboBox BackColor property ignored -

django - Creating multiple model instances in DRF3 -