在Ruby中将参数命名为局部变量

Mat*_*dan 10 ruby variables arguments

在使用方法的命名参数时,我发现自己经常在Ruby中编写我认为不必要的代码.

以下面的代码为例:

def my_method(args)
  orange = args[:orange]
  lemon = args[:lemon]
  grapefruit = args[:grapefruit]

  # code that uses 
  # orange, lemon & grapefruit in this format which is way prettier & concise than 
  # args[:orange] args[:lemon] args[:grapefruit]

  puts "my_method variables: #{orange}, #{lemon}, #{grapefruit}" 
end
my_method :orange => "Orange", :grapefruit => "Grapefruit"
Run Code Online (Sandbox Code Playgroud)

我真的不喜欢这段代码的是我必须接受args并将值传递给局部变量,这些变量与DRY原则相反,并且通常占用我的方法中的空间.如果我不使用局部变量并且只使用args [:symbol]语法引用所有变量,那么代码就会变得有些难以理解.

我已经尝试过解决这个问题,但是我不知道如何在方法范围内使用eval或使用任何其他技术来定义局部变量.以下是下面的许多尝试之一,这会导致错误

def my_method_with_eval(args)
  method_binding = binding
  %w{ orange lemon grapefruit}.each { |variable| eval "#{variable} = args[:#{variable}]", method_binding; }

  # code that uses 
  # orange, lemon & grapefruit in this format which is way prettier & concise than 
  # args[:orange] args[:lemon] args[:grapefruit]

  puts "my_method_with_eval variables: #{orange}, #{lemon}, #{grapefruit}" 
end
my_method_with_eval :orange => "Orange", :grapefruit => "Grapefruit"
Run Code Online (Sandbox Code Playgroud)

运行该代码时,我得到了

NameError: undefined local variable or method ‘orange’ for main:Object method my_method_with_eval in named_args_to_local_vars at line at top level in named_args_to_local_vars at line 9

任何人都有任何想法我怎么能以某种方式简化这个,以便我不必用var = args [:var]代码的负载启动我的命名参数方法?

谢谢, Matthew O'Riordan

Gre*_*ell 6

我不相信在Ruby中有任何方法可以做到这一点(如果有人想出一个,请告诉我,我会更新或删除这个答案来反映它!) - 如果没有定义局部变量然而,没有办法用绑定动态定义它.你可以想象orange, lemon, grapefruit = nil在调用eval之前做一些事情,但是你可能遇到其他问题 - 例如,如果args [:orange]是字符串"Orange",你最终会orange = Orange用你当前的实现进行评估.

但是,这可以使用标准库中的OpenStruct类(通过"可以工作",我的意思是"这取决于你的风格是否a.orange比任何更好args[:orange]"):

require 'ostruct'

def my_method_with_ostruct(args)
  a = OpenStruct.new(args)
  puts "my_method_with_ostruct variables: #{a.orange}, #{a.lemon}, #{a.grapefruit}"
end
Run Code Online (Sandbox Code Playgroud)

如果您不需要轻松访问此方法的接收器上的任何状态或方法,则可以使用instance_eval,如下所示.

def my_method_with_instance_eval(args)
  OpenStruct.new(args).instance_eval do
    puts "my_method_with_instance_eval variables: #{orange}, #{lemon}, #{grapefruit}"
  end
end
Run Code Online (Sandbox Code Playgroud)

你甚至可以用method_missing一些棘手的事情(参见这里了解更多)以允许访问"主要"对象,但性能可能不会很好.

总而言之,我认为可能最简单/可读的方式是使用较少干扰您的初始解决方案.


gna*_*nab 6

合并Greg和Sand的答案:

require 'ostruct'

def my_method(args = {}) 
  with args do
    puts a
    puts b
  end 
end

def with(args = {}, &block)
  OpenStruct.new(args).instance_eval(&block)
end

my_method(:a => 1, :b => 2)
Run Code Online (Sandbox Code Playgroud)