Cli*_*chl 5 ruby metaprogramming
我正在尝试创建一个简单的类,该类在设置或读取字段时自动将一组字段转换为指定的 Ruby 类型。
这是我到目前为止所拥有的,并且有效。然而,它不是 DRY 并且我的元编程是基本的。
有没有更好、更干净的方法来实现这一点?
class BasicModel
def self.fields(params)
params.each do |name, type|
# Define field writers
define_method("#{name}=") {|v| @fields[name] = v}
# Define field readers
case type.name
when 'String'
define_method(name) { @fields[name].to_s }
when 'Integer'
define_method(name) { @fields[name].to_i }
when 'Float'
define_method(name) { @fields[name].to_f }
else raise 'invalid field type'
end
end
end
fields(
name: String,
qty: Integer,
weight: Float
)
def initialize
@fields = {}
end
end
# specification
m = BasicModel.new
m.name => ""
m.name = 2 => 2
m.name => "2"
m.qty => 0
m.qty = "1" => "1"
m.qty => 1
m.weight => 0.0
m.weight = 10 => 10
m.weight => 10.0
Run Code Online (Sandbox Code Playgroud)
对读者和作者进行类型转换的缺点/优势是什么?例如,以下代码对作者进行类型转换,而不是对读者(上图)。我也把case里面的define_method.
class BasicModel
def self.fields(params)
params.each do |name, type|
define_method(name) { @fields[name] }
define_method("#{name}=") do |val|
@fields[name] = case type.name
when 'Integer' then val.to_i
when 'Float' then val.to_f
when 'String' then val.to_s
else raise 'invalid field type'
end
end
end
end
Run Code Online (Sandbox Code Playgroud)
我在想一个可能的问题是决策树(例如 case 语句)应该放在define_method. 我假设每次设置/读取该字段时都会毫无意义地评估该语句。这样对吗?
您真正需要的是对每个字段使用的类型转换方法的引用。您可以在定义 setter 方法之前确定类型转换方法,并send在调用 setter 时用于执行类型转换。
这是一个例子:
class BasicModel
def self.fields(params)
params.each do |name, type|
operator = case type.name
when 'Integer' then :to_i
when 'Float' then :to_f
when 'String' then :to_s
else raise 'invalid field type'
end
define_method(name) { @fields[name] }
define_method("#{name}=") do |val|
@fields[name] = val.send(operator)
end
end
end
def initialize
@fields = {}
end
end
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
301 次 |
| 最近记录: |