简体   繁体   中英

How to DRY this code in Ruby

I have the following code to represent different Value Objects in Ruby. The only thing that changes between different classes is the INITIALIZATION_ATTRIBUTES array, which represents the list of attributes of the value object. I can't find a way to DRY this code. I tried to use a Module and accessing the included classes' Constants, but I run into the weird Constant lookup behavior described here . Essentially, the Module code is evaluated multiple times and it interprets the constant of the lastly evaluated class and applies its values to all the Value Object classes.

Is there any better alternative? I also tried with a base class, but I couldn't make it work.

  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

This can be done by layering two modules. The outer module will provide the functionality to initialize the inner module. Because class attributes are used, which are unique for every including class, one including class' attributes can not conflict with another including class' attributes.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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