繁体   English   中英

如何在Ruby中干燥此代码

[英]How to DRY this code in Ruby

我有以下代码来表示Ruby中的不同Value Object。 不同类之间唯一发生变化的是INITIALIZATION_ATTRIBUTES数组,该数组表示值对象的属性列表。 我找不到干燥该代码的方法。 我尝试使用Module并访问包含的类的Constants,但是遇到了此处描述的怪异的Constant查找行为。 本质上,对模块代码进行多次评估,并解释最后评估的类的常量,并将其值应用于所有值对象类。

还有更好的选择吗? 我也尝试过使用基类,但是无法使其正常工作。

  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

这可以通过分层两个模块来完成。 外部模块将提供初始化内部模块的功能。 因为使用的类属性对于每个包含类都是唯一的,所以一个包含类的属性不能与另一个包含类的属性冲突。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM