3

我有一种情况,我在脚本顶部定义了一个变量,并想在一个方法中引用它:

#############
# Variables #
#############
tmp_dir = '/path/to/tmp/dir'

###########
# Methods #
###########
def cache(page)
  begin
    %x[wget -q -O #{tmp_dir}/page #{page}]
  rescue => msg
    puts "error: #{msg}"
    exit
  end
 end

cache("http://somepage.com")

我收到此错误:

undefined local variable or method `tmp_dir' for main:Object

我猜我需要创建tmp_dir一个全局变量?我讨厌使用全局变量。有没有一种 Ruby 风格的方法来做到这一点?

4

5 回答 5

5

tmp_dir在类定义中被定义为变量,但在实例函数中,您正在寻找在类的实例tmp_dir上定义的。这就是函数内部未定义的原因。tmp_dir

您可以将其设为全局或将其设为类变量作为快速修复。我认为有一个更好的选择:将其包装在自己的知道如何缓存的类中,然后tmp_dir在不使其成为全局或类变量的情况下进行初始化:

class Cacher
    def initialize(tmp_dir)
        @tmp_dir = tmp_dir
    end

    def cache(page)
        wget "#{@tmp_dir}/page"
    end
end

# in your main file:
cacher = Cacher.new('/path/to/tmp/dir') # here's your configuration line, but with no global!

# later

cacher.cache("index.html")
于 2013-07-08T13:48:40.437 回答
3

你说的对。应避免使用全局变量。自然的选择是类上的实例变量或常量。

在你的情况下,看起来你有一些在脚本执行过程中不会改变的东西。那么,常数是最合适的。您可以在适当的模块中定义此常量。

TmpDir = "/path/to/tmp/dir"

另请注意,Ruby 有一个内置的方式来引用 tmp 目录。

require "tmpdir"
Dir.tmpdir # => "/tmp" (depending on the environment)
于 2013-07-08T13:55:35.080 回答
2

你是对的。您需要通过为变量添加前缀来使其成为全局变量$。前任:

$tmp_dir = '/path/to/tmp/dir'

除了这样做,您还可以将其设为实例变量,或者您可以重构以使其成为类。我建议按照 Riley Lark 所说的去做。

于 2013-07-08T13:45:35.213 回答
1

除了其他答案之外,要使类定义范围内的局部变量集在方法定义中起作用,您可以使用define_methodwhich 是一个采用块的方法。其中的块Ruby是闭包,因此它们与它们所在的环境一起使用:

define_method(:cache) do |page|
  begin
    %x[wget -q -O #{tmp_dir}/page #{page}]
  rescue => msg
    puts "error: #{msg}"
    exit
  end
end
于 2013-07-08T14:03:13.120 回答
0

简短的回答

听起来您正在编写一个简短的脚本。如果是这种情况,没有理由不能使用可修改的全局变量,例如$tmp_dir. 但是如果你不需要修改它,你应该使用@sawa 的全局常量解决方案,比如TMP_DIR. 在这种情况下,您应该调用.freeze字符串以避免意外修改。

更长的答案

如果脚本变得更长或更复杂,您应该重构为类。在这种情况下,该TMP_DIR解决方案仍然有效。但是如果需要修改值,可以创建一个ConfigObject类来对这些变量进行分组。

例子:

ConfigObject = Struct.new(:tmp_dir, :file_limit)
# it's a good idea to create this before everything else
$config = ConfigObject.new('/tmp/dir', 10)

class Foo
  def do_something
    $config.file_limit  # use this somehow
    $config.file_limit = 5  # change
  end
end

类似的技术使用类变量来完成同样的事情:

class ConfigObject
  class << self
    attr_accessor :tmp_dir, :file_limit
  end
  @tmp_dir = '/tmp/dir'
  @file_limit = 10
end

class Foo
  def do_something
    ConfigObject.file_limit  # use this somehow
    ConfigObject.file_limit = 5  # change
  end
end

考虑到ConfigObject其视为其他类使用的“服务”。如果您的应用程序变得复杂到需要多个交互服务,您可能需要设置一种服务注册表来保存对服务的引用(谷歌“依赖注入”以获取更多信息)。

注意:你不应该为你的类命名Config,因为它已经是一个内置类。

于 2013-07-08T16:31:43.127 回答