Ruby - 如何在没有 .new 的情况下创建 MatchData 对象?
Ruby - How is MatchData object created without .new?
我正在为一个对象创建一个 class(比如,Bar
),以便通过另一个 class(比如,Foo#bar
)的方法返回一个对象,差不多 MatchData
object is returned by Regexp#match
.
但是classMatchData
没有.new
!
我知道我不需要模仿MatchData
实现,但我想了解它并在我觉得有趣时知道如何去做。假设我不希望客户创建 Bar
对象,除非通过调用 Foo#bar
.
问题:
- 在内部,如何在没有
.new
的情况下创建 MatchData
对象?
- 如何实现(是否模仿
MatchData
)?
MatchData.new
方法正在 explicitly undefined:
rb_cMatch = rb_define_class("MatchData", rb_cObject);
rb_define_alloc_func(rb_cMatch, match_alloc);
rb_undef_method(CLASS_OF(rb_cMatch), "new"); // <- here
您可以通过 undef_method
:
在纯 Ruby 中执行相同的操作
class Bar
class << self
undef_method :new
end
def initialize
@bar = '123' # <- for demonstration purposes
end
end
尝试调用 Bar.new
现在会导致错误:
Bar.new #=> undefined method `new' for Bar:Class (NoMethodError)
要创建一个没有 new
方法的新实例,您可以手动调用 allocate
(也可能 initialize
):
bar = Bar.allocate #=> #<Bar:0x007f9eba047cd8>
Bar.send(:initialize) #=> "123"
bar #=> #<Bar:0x007fd8e0847658 @bar="123">
(需要send
因为initialize
是私有的)
让我从你不应该的开始。不顾一切地限制用户做他们想做的事,即使不是 public 界面,这也是 arubyic。一种更惯用的方法是更明确地表明它不是 public 接口的一部分。您可以通过将 class 设为私有来做到这一点:
class RegexMockery
class MatchDataMockery
def initialize(whatever)
puts "I'm being created #{whatever}"
end
def [](_)
'42'
end
end
private_constant :MatchDataMockery
def match(string)
MatchDataMockery.new(string)
end
end
match_result = RegexMockery.new.match('foo')
# I'm being created foo
# => #<RegexMockery::MatchDataMockery:0x007fe990de2ed0>
match_result[0] # => '42'
RegexMockery::MatchDataMockery # !> NameError: private constant RegexMockery::MatchDataMockery referenced
但是如果你坚持让人们讨厌你,保存方法,取消定义并在你想要创建实例时调用它:
class Foo
def initialize(whatever)
puts "Now you see me #{whatever}"
end
def brag
puts "I can create Foos and you can't!!!1!!"
end
end
class Bar
foos_new = Foo.method(:new)
Foo.singleton_class.send :undef_method, :new
define_method(:sorcery) do
foos_new.call('bar').brag
end
end
Bar.new.sorcery
# Now you see me bar
# I can create Foos and you can't!!!1!!
Foo.new # !> NoMethodError: undefined method `new' for Foo:Class
我正在为一个对象创建一个 class(比如,Bar
),以便通过另一个 class(比如,Foo#bar
)的方法返回一个对象,差不多 MatchData
object is returned by Regexp#match
.
但是classMatchData
没有.new
!
我知道我不需要模仿MatchData
实现,但我想了解它并在我觉得有趣时知道如何去做。假设我不希望客户创建 Bar
对象,除非通过调用 Foo#bar
.
问题:
- 在内部,如何在没有
.new
的情况下创建MatchData
对象? - 如何实现(是否模仿
MatchData
)?
MatchData.new
方法正在 explicitly undefined:
rb_cMatch = rb_define_class("MatchData", rb_cObject);
rb_define_alloc_func(rb_cMatch, match_alloc);
rb_undef_method(CLASS_OF(rb_cMatch), "new"); // <- here
您可以通过 undef_method
:
class Bar
class << self
undef_method :new
end
def initialize
@bar = '123' # <- for demonstration purposes
end
end
尝试调用 Bar.new
现在会导致错误:
Bar.new #=> undefined method `new' for Bar:Class (NoMethodError)
要创建一个没有 new
方法的新实例,您可以手动调用 allocate
(也可能 initialize
):
bar = Bar.allocate #=> #<Bar:0x007f9eba047cd8>
Bar.send(:initialize) #=> "123"
bar #=> #<Bar:0x007fd8e0847658 @bar="123">
(需要send
因为initialize
是私有的)
让我从你不应该的开始。不顾一切地限制用户做他们想做的事,即使不是 public 界面,这也是 arubyic。一种更惯用的方法是更明确地表明它不是 public 接口的一部分。您可以通过将 class 设为私有来做到这一点:
class RegexMockery
class MatchDataMockery
def initialize(whatever)
puts "I'm being created #{whatever}"
end
def [](_)
'42'
end
end
private_constant :MatchDataMockery
def match(string)
MatchDataMockery.new(string)
end
end
match_result = RegexMockery.new.match('foo')
# I'm being created foo
# => #<RegexMockery::MatchDataMockery:0x007fe990de2ed0>
match_result[0] # => '42'
RegexMockery::MatchDataMockery # !> NameError: private constant RegexMockery::MatchDataMockery referenced
但是如果你坚持让人们讨厌你,保存方法,取消定义并在你想要创建实例时调用它:
class Foo
def initialize(whatever)
puts "Now you see me #{whatever}"
end
def brag
puts "I can create Foos and you can't!!!1!!"
end
end
class Bar
foos_new = Foo.method(:new)
Foo.singleton_class.send :undef_method, :new
define_method(:sorcery) do
foos_new.call('bar').brag
end
end
Bar.new.sorcery
# Now you see me bar
# I can create Foos and you can't!!!1!!
Foo.new # !> NoMethodError: undefined method `new' for Foo:Class