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.

问题:

  1. 在内部,如何在没有 .new 的情况下创建 MatchData 对象?
  2. 如何实现(是否模仿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