Ruby:自动将实例变量设置为方法参数?

是否有任何计划实现类似于在方法参数列表中指定实例变量名称的CoffeeScriptfunction的ruby行为? 喜欢

class User def initialize(@name, age) # @name is set implicitly, but @age isn't. # the local variable "age" will be set, just like it currently works. end end 

我知道这个问题: 在Ruby中,我可以在initialize方法中以某种方式自动填充实例变量吗? ,但所有的解决方案(包括我自己的)似乎都不符合ruby简单哲学。

并且,这种行为会有任何缺点吗?

UPDATE

其中一个原因是ruby社区的DRY(不要重复自己)哲学。 我经常发现自己需要重复参数变量的名称,因为我希望将它分配给同名的实例变量。

 def initialize(name) # not DRY @name = name end 

我能想到的一个缺点是,如果一个方法没有正文,它看起来好像什么也没做。 如果您正在快速扫描,这可能看起来像一个无操作。 但我认为,只要有时间,我们就可以适应。

另一个缺点:如果您在正文中设置其他实例变量,并且您尝试通过将所有分配放在开头可读,则可能需要更多的认知“权力”才能看到分配也发生在参数列表中。 但我不认为这比看到一个常量或方法调用并且不得不跳到它的定义更难。

 # notice: instance var assignments are happening in 2 places! def initialize(@name) @errors = [] end 

经过一番思考,我想知道是否可以从ruby方法中获取参数名称。 如果是这样,我可以使用像“iv_”这样的特殊参数前缀来指示哪些args应该被设置为实例变量。

并且有可能: 如何使用reflection获取参数名称 。

是! 所以我可以写一个模块来为我处理这个问题。 然后我陷入困境,因为如果我调用模块的辅助方法,它不知道参数的值,因为它们是调用者本地的。 啊,但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] # 

它不会让你有一个空方法体,但它是干的。 我确信只需指定哪些方法应具有此行为(通过alias_method实现),而不是在每个方法中调用slurp_args,就可以进一步增强 – 规范必须在所有方法定义之后。

请注意,模块和帮助器方法名称可能会得到改进。 我只是想到了第一件事。

实际上…

 class User define_method(:initialize) { |@name| } end User.new(:name).instance_variable_get :@name # => :name 

适用于1.8.7,但不适用于1.9.3。 现在,我在哪里了解到这一点……

我认为你回答了自己的问题,它不符合ruby简单哲学。 如果在方法中处理参数的方式会增加额外的复杂性,并将用于管理变量的逻辑移动到方法参数中。 我可以看到这样的论点,即它使代码的可读性降低了,但它确实让我觉得不是很冗长。

@ param必须应对的一些场景:

 def initialize( first, last, @scope, @opts = {} ) def search( @query, condition ) def ratchet( @*arg ) 

所有这些情况都应该有效吗? 刚刚initialize ? 在我看来, @*arg似乎特别冒险。 所有这些规则和排除都使Ruby语言更加复杂。 为了自动实例变量的好处,我认为不值得。