红宝石:自动设置实例变量的方法参数?(Ruby: Automatically set instanc

2019-06-25 02:40发布

是否有实施类似的方法的参数列表中指定的实例变量名的CoffeeScript的功能红宝石行为的计划吗? 喜欢

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中我可以自动初始化方法莫名其妙地填充实例变量? ,但所有的解决方案(包括我自己)似乎并不适合简单的红宝石理念。

而且,会有对有这种行为的任何缺点?

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

Answer 1:

经过一番琢磨之后,我不知道是否有可能实际上是从一个红宝石方法得到的参数名。 如果是这样,我可以使用像“iv_”特殊参数的前缀来指示ARGS应设置为实例变量。

它是可能的: 如何使用反射来获取参数的名称 。

是! 所以,我也许可以写一个模块来处理这对我来说。 然后,我卡住了,因为如果我调用模块的helper方法,它不知道的参数值,因为它们是地方给调用者。 啊,但红宝石已经绑定的对象。

这里的模块(仅红宝石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">

它不会让你有一个空的方法体,但它是干的。 我敢肯定,这可以进一步通过仅仅指定哪些方法应该有这样的行为(通过alias_method实现),而不是调用每个方法slurp_args增强 - 规范必须是在所有的方法虽然定义。

注意模块和辅助方法的名字很可能得到改善。 我只是用浮现在脑海的第一件事。



Answer 2:

实际上...

class User
  define_method(:initialize) { |@name| }
end

User.new(:name).instance_variable_get :@name
# => :name

工程在1.8.7,但不是在1.9.3。 现在,只要在那里才知道这个...



Answer 3:

我想你回答了你自己的问题,它不适合红宝石简单的理念。 这将为参数是如何的方法处理增加额外的复杂性和移动逻辑管理变量成方法的参数。 我可以看到的说法,它使代码的可读性变差一折腾,但它确实让我最不能非常详细。

一些情景@ PARAM将不得不与之抗衡:

def initialize( first, last, @scope, @opts = {} )

def search( @query, condition )

def ratchet( @*arg  ) 

如果所有这些情况下是有效的? 就在initialize ? 该@*arg似乎在我的脑海特别冒险的。 所有这些规则和排除使Ruby语言更加复杂。 对于自动实例变量的利益,我不认为这将是值得的。



文章来源: Ruby: Automatically set instance variable as method argument?