Ruby: почему ставит вызов to_ary? - PullRequest
13 голосов
/ 22 января 2012

Я изучаю метапрограммирование в Ruby и просто пытаюсь определить отсутствующие методы с помощью method_missing и define_method. У меня неожиданное поведение, и мне интересно, может кто-нибудь объяснить это. Вот мой класс:

class X
  def method_missing(m, *args, &block)
    puts "method #{m} not found. Defining it."
    self.class.send :define_method, m do
      puts "hi from method #{m}"
    end
    puts "defined method #{m}"
  end  
end

Теперь этот код:

x = X.new

x.some_method
puts
x.some_method
puts
puts x

Производит вывод:

method some_method not found. Defining it.
defined method some_method

hi from method some_method

method to_ary not found. Defining it.
defined method to_ary
#<X:0x007fcbc38e5030>

Что я не получаю, так это последнюю часть: почему Ruby вызывает to_ary при вызове пут? Зачем Ruby пытаться преобразовать мой объект в массив только для его печати?

Я погуглил и нашел следующие ссылки:

Они также говорят о методах method_missing и to_ary, но не конкретно о том, почему путы будут вызывать to_ary.

Я должен также упомянуть, что поведение не меняется, когда я определяю to_s, например,

def to_s
  "I'm an instance of X"
end

Выходные данные «put x»:

method to_ary not found. Defining it.
defined method to_ary
I'm an instance of X

1 Ответ

16 голосов
/ 22 января 2012

puts является синонимом $stdout.puts. $ stdout - это класс IO, поэтому посмотрите документацию для IO.puts :

Записывает данные объекты в ios, как при печати IO #. Пишет запись разделитель (обычно перевод строки) после любого, который еще не заканчивается последовательность новой строки. Если вызывается с аргументом массива, записывает каждый элемент на новой строке.

Это означает, что метод puts предназначен для записи нескольких строк вывода. Таким образом, он пытается вызвать метод to_ary для объекта и, если определено to_ary, то печатает каждый элемент возвращенного Array в новой строке, иначе puts вызывает метод to_s.

to_ary внутреннее использование действительно плохо документировано в документации по Ruby (Мэтц указывает на это в своей книге Ruby Programming Language ).

Методы print и p, с другой стороны, не вызывают to_ary, только to_s.

Sidenote : Интересно, что to_ary должен возвращать реальный Array объект, а не объект, определяющий метод each или что-то еще:

class Test
  def to_ary
    10.downto(1)
  end
end

puts Test.new

#TypeError: can't convert Test to Array (Test#to_ary gives Enumerator)
#        from (irb):28:in `puts'
#        from (irb):28:in `puts'
#        from (irb):28
...