我有一种情况,我在脚本的顶部定义一个变量,并希望在方法中引用。
#############
# 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成为一个全局变量?我讨厌使用全局变量。有没有红宝石的方法来做到这一点?
答案 0 :(得分:4)
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")
答案 1 :(得分:3)
你是对的。应避免使用全局变量。自然选择是类的实例变量,或常量。
在您的情况下,看起来您在脚本执行过程中有一些不会改变的东西。然后,常数是最合适的。您可以在适当的模块中定义此常量。
TmpDir = "/path/to/tmp/dir"
另请注意,Ruby有一种内置的方式来引用tmp目录。
require "tmpdir"
Dir.tmpdir # => "/tmp" (depending on the environment)
答案 2 :(得分:1)
你是对的。您需要通过在$
前添加变量来使变量成为全局变量。例如:
$tmp_dir = '/path/to/tmp/dir'
除了这样做之外,您还可以将其设为实例变量,或者您可以重构以使其成为类。我建议做Riley Lark所说的。
答案 3 :(得分:1)
除了其他答案之外,要使类定义范围中的局部变量集在方法定义中起作用,您可以使用define_method
这是一种采用块的方法。 Ruby
中的块是闭包,因此它们与设置的环境一起使用:
define_method(:cache) do |page|
begin
%x[wget -q -O #{tmp_dir}/page #{page}]
rescue => msg
puts "error: #{msg}"
exit
end
end
答案 4 :(得分: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
命名,因为它已经是内置类。