2017-09-22 32 views
0

我有以下代碼來表示Ruby中的不同值對象。不同類之間唯一改變的是INITIALIZATION_ATTRIBUTES數組,它表示值對象的屬性列表。我找不到幹這個代碼的方法。我嘗試使用模塊並訪問包含的類的常量,但我遇到了描述爲here的怪異常數查找行爲。本質上,Module代碼被多次評估,它解釋最後評估的類的常量並將其值應用於所有Value Object類。如何在Ruby中烘乾此代碼

有沒有更好的選擇?我也嘗試過一個基礎類,但我無法完成它的工作。

module Values 
    class MaintenanceRegimeSerializer 
     INITIALIZATION_ATTRIBUTES = [:distance_between_services, :months_between_services] 

     def self.load(json) 
     json ||= '{}' 
     hash = JSON.parse json, symbolize_names: true 
     self.new(*INITIALIZATION_ATTRIBUTES.map {|key| hash[key]}) 
     end 

     def self.dump(obj) 
     unless obj.is_a?(self) 
      raise ::ActiveRecord::SerializationTypeMismatch, 
      "Attribute was supposed to be a #{self}, but was a #{obj.class}. -- #{obj.inspect}" 
     end 

     obj.to_json 
     end 

     attr_reader *INITIALIZATION_ATTRIBUTES 

     define_method :initialize do |*args| 
     raise ArgumentError unless INITIALIZATION_ATTRIBUTES.length == args.length 
     INITIALIZATION_ATTRIBUTES.each_with_index do |attribute, index| 
      instance_variable_set "@#{attribute}", args[index] 
     end 
     end  

    end 
    end 
+0

你是說你有幾個類,它們都是一樣的,除了初始化屬性? – EJ2015

+0

你說你想讓這段代碼更加乾爽 - 暗示你在某個地方重複着自己。那會是什麼?這對我來說看起來很「元」。 –

回答

1

這可以通過分層兩個模塊來完成。外部模塊將提供初始化內部模塊的功能。由於使用了類屬性,對於每個包含類都是唯一的,因此包含類屬性的類不能與包含類屬性的其他屬性發生衝突。

module Values 
    module MaintenanceRegimeSerializer 
    extend ActiveSupport::Concern 

    class_methods do 

     def acts_as_maintenance_regime_serializer(attributes) 
     # include the inner module 
     # thereby adding the required methods and class attributes 
     include JsonMethods 
     # set the class variables made available by including the inner module 
     self.serializer_attributes = attributes 
     end 
    end 

    module JsonMethods 
     extend ActiveSupport::Concern 

     included do 
     class_attribute :serializer_attributes 

     def initialize(*args) 
      raise ArgumentError unless self.class.serializer_attributes.length == args.length 
      self.class.serializer_attributes.each_with_index do |attribute, index| 
      instance_variable_set "@#{attribute}", args[index] 
      end 
     end  
     end 

     class_methods do 
     def load(json) 
      json ||= '{}' 
      hash = JSON.parse json, symbolize_names: true 
      new(*serializer_attributes.map {|key| hash[key]}) 
     end 

     def dump(obj) 
      unless obj.is_a?(self) 
      raise ::ActiveRecord::SerializationTypeMismatch, 
       "Attribute was supposed to be a #{self}, but was a #{obj.class}. -- #{obj.inspect}" 
      end 

      obj.to_json 
     end 
     end 
    end 
    end 
end 

# in the including class 

class SomeClass 
    # This might also be put into an initializer patching ActiveRecord::Base 
    # to avoid having to call this in every class desiring the regime serializer functionalit 
    include Values::MaintenanceRegimeSerializer 
    acts_as_maintenance_regime_serializer([:distance_between_services, 
             :months_between_services]) 
end 

# in another including class 

class SomeOtherClass 

    include Values::MaintenanceRegimeSerializer 
    acts_as_maintenance_regime_serializer([:foo, 
             :bar]) 
end