经过一番思考,我想知道是否可以从 ruby 方法中实际获取参数名称。如果是这样,我可以使用像“iv_”这样的特殊参数前缀来指示应该将哪些参数设置为实例变量。
并且有可能:如何使用反射获取参数名称。
是的!所以我也许可以写一个模块来为我处理这个。然后我被卡住了,因为如果我调用模块的辅助方法,它不知道参数的值,因为它们是调用者的本地。啊,但是 ruby 有 Binding 对象。
这是模块(仅限 ruby 1.9):
module InstanceVarsFromArgsSlurper
# arg_prefix must be a valid local variable name, and I strongly suggest
# ending it with an underscore for readability of the slurped args.
def self.enable_for(mod, arg_prefix)
raise ArgumentError, "invalid prefix name" if arg_prefix =~ /[^a-z0-9_]/i
mod.send(:include, self)
mod.instance_variable_set(:@instance_vars_from_args_slurper_prefix, arg_prefix.to_s)
end
def slurp_args(binding)
defined_prefix = self.class.instance_variable_get(:@instance_vars_from_args_slurper_prefix)
method_name = caller[0][/`.*?'/][1..-2]
param_names = method(method_name).parameters.map{|p| p.last.to_s }
param_names.each do |pname|
# starts with and longer than prefix
if pname.start_with?(defined_prefix) and (pname <=> defined_prefix) == 1
ivar_name = pname[defined_prefix.size .. -1]
eval "@#{ivar_name} = #{pname}", binding
end
end
nil
end
end
这是用法:
class User
InstanceVarsFromArgsSlurper.enable_for(self, 'iv_')
def initialize(iv_name, age)
slurp_args(binding) # this line does all the heavy lifting
p [:iv_name, iv_name]
p [:age, age]
p [:@name, @name]
p [:@age, @age]
end
end
user = User.new("Methuselah", 969)
p user
输出:
[:iv_name, "Methuselah"]
[:age, 969]
[:@name, "Methuselah"]
[:@age, nil]
#<User:0x00000101089448 @name="Methuselah">
它不会让你有一个空的方法体,但它是 DRY。我确信它可以通过仅指定哪些方法应该具有此行为(通过 alias_method 实现)而不是在每个方法中调用 slurp_args 来进一步增强 - 规范必须在定义所有方法之后。
请注意,模块和辅助方法名称可能会得到改进。我只是使用了想到的第一件事。