Bat*_*ler 1 ruby ruby-on-rails
在test1.rb中有这段代码
my_var = 42
def my_func()
42
end
class MyCLS
attr_accessor :prop
def initialize()
@prop = 42
end
end
Run Code Online (Sandbox Code Playgroud)
然后在解释器中我需要在irb中
> require './test1.rb'
> MyCLS.new().prop
=> 42
> my_func()
=> 42
> my_var
NameError: undefined local variable or method `my_var' for main:Object
Run Code Online (Sandbox Code Playgroud)
我很困惑,ruby似乎很高兴用类和函数来污染全局名称空间,但是拒绝对my_var做同样的事情?我想这是为了避免名称冲突和错误。但是该问题只能部分解决,因为它仍然存在于Class和Function中。也许不那么容易发生?
所以现在想象第二个文件test2.rb
def my_func()
43
end
class MyCLS
attr_accessor :prop
def initialize()
@prop = 43
end
end
Run Code Online (Sandbox Code Playgroud)
然后执行
> require './test1.rb'
> require './test2.rb'
> MyCLS.new().prop
=> 43
> my_func()
=> 43
Run Code Online (Sandbox Code Playgroud)
以前的全局变量MyCLS和my_func被默默覆盖是否正常?这难道不是因为gem决定在某处添加/重命名Class或Function来破坏软件吗?所有这些似乎非常脆弱和危险。
我知道模块,但尝试得很少成功(尴尬,它们又是全局变量)
有什么方法可以防止这种情况或减轻似乎是语言设计的缺陷?
编辑:另一个例子
# test1.rb
def my_func()
42
end
# test2.rb
puts my_func()
# test3.rb
class Example
require './test1.rb'
end
class AnotherExample
require './test2.rb'
end
# command line
$ ruby test3.rb
42
Run Code Online (Sandbox Code Playgroud)
Ruby中的常量(以大写字母开头的内容)始终在on上创建为常量Object,除非它们明确地是另一个常量的成员(即,module Foo::Bar在该Foo常量下创建一个模块,而该模块本身就是该Object常量下的)。
此外,还有一个名为“ main”的特殊顶级Object实例。在顶层定义的任何方法都在Object上定义为私有方法,因此可以从访问main。
当您require创建文件时,该机制为:
main用该模块扩展。始终遵守这些规则;您不能在文件中定义顶级方法,然后通过巧妙地放置require语句将该文件包含到命名空间中。对该文件进行解析后,Ruby会找到一个顶层方法并对其进行扩展main,而无需考虑require从何处调用。
如果您想将一个方法混入另一个类中,则通常将其放入一个模块中,然后将该模块混入您的类中。
# test1.rb
module Bar
def my_func
42
end
end
# test2.rb
require 'test1'
class Foo
include Bar
end
my_func => # NameError: undefined local variable or method `my_func' for main:Object
Foo.new.my_func # => 42
Run Code Online (Sandbox Code Playgroud)
在Ruby中,期望每个文件都将对要公开的常量和方法进行完全命名空间。实际上,您绝不会在大多数真正的Ruby项目中编写顶级方法。几乎不用担心事情会被无意地覆盖,因为有人需要显式地进入您的命名空间来覆盖事物。
如果要在不扩展主对象的情况下执行文件,则可以将Kernel#load与wrap参数一起使用,该参数将负载包装在匿名模块中(但使其内部无法访问,除非您在该文件中进行了某些操作以暴露该文件中的方法和常量):
load "test1", true
MyCLS # => NameError: uninitialized constant MyCLS
Run Code Online (Sandbox Code Playgroud)
您可以通过自定义加载程序获得这种范围的加载:
# test1.rb
def foo
42
end
# test2.rb
def relative_load(file)
Module.new.tap {|m| m.module_eval open(file).read }
end
class Foo
include relative_load("test1.rb")
end
Foo.new.foo # => 42
foo # => NameError: undefined local variable or method `foo' for main:Object
Run Code Online (Sandbox Code Playgroud)
顺便说一句,在您的第一个示例中,MyCLS类没有被覆盖。它与现有的MyCLS类合并。因为两者都声明initialize,所以后一个声明优先。例如:
# test1.rb
class MyCLS
attr_accessor :prop
# This definition will get thrown away when we overwrite it from test2.
# It is test2's responsibility to make sure that behavior is preserved;
# this can be done with reimplementation, or by saving a copy of this
# method with `alias` or similar and invoking it.
def initialize(prop)
@prop = prop
end
end
# test2.rb
class MyCLS
attr_accessor :another_prop
def initialize(prop, another_prop)
@prop = prop
@another_prop = prop
end
end
# test3.rb
require 'test1'
c = MyCLS.new(1, 2) # => ArgumentError: wrong number of arguments (2 for 1)
c = MyCLS.new(1)
c.prop => 1
c.another_prop => # => NoMethodError: undefined method `another_prop'
require 'test2'
c = MyCLS.new(1) # => ArgumentError: wrong number of arguments (1 for 2)
c = MyCLS.new(1, 2)
c.prop => 1
c.another_prop => 2
Run Code Online (Sandbox Code Playgroud)