如何在 Ruby 中 DRY 这段代码
How to DRY this code in Ruby
我有以下代码来表示 Ruby 中的不同值对象。不同 classes 之间唯一不同的是 INITIALIZATION_ATTRIBUTES 数组,它表示值对象的属性列表。我找不到 DRY 这段代码的方法。我尝试使用模块并访问包含的 classes 的常量,但我 运行 进入 here 描述的奇怪的常量查找行为。本质上,模块代码被多次计算,它解释最后计算的常量 class 并将其值应用于所有值对象 classes.
有没有更好的选择?我也试过用一个 base class,但我无法让它工作。
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
这可以通过分层两个模块来完成。外部模块将提供初始化内部模块的功能。因为使用了class属性,每个包含class的属性都是唯一的,一个包含class'的属性不能与另一个包含class'的属性冲突。
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
我有以下代码来表示 Ruby 中的不同值对象。不同 classes 之间唯一不同的是 INITIALIZATION_ATTRIBUTES 数组,它表示值对象的属性列表。我找不到 DRY 这段代码的方法。我尝试使用模块并访问包含的 classes 的常量,但我 运行 进入 here 描述的奇怪的常量查找行为。本质上,模块代码被多次计算,它解释最后计算的常量 class 并将其值应用于所有值对象 classes.
有没有更好的选择?我也试过用一个 base class,但我无法让它工作。
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
这可以通过分层两个模块来完成。外部模块将提供初始化内部模块的功能。因为使用了class属性,每个包含class的属性都是唯一的,一个包含class'的属性不能与另一个包含class'的属性冲突。
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