В вашем примере вы используете разные вещи, которые все называются CSV
.
CSV {|v|v}
Здесь вы вызываете метод с именем CSV
с блоком. Вы получаете возвращаемое значение этого метода обратно. Такие методы обычно используются в качестве конвертеров. Например, есть метод Integer()
, который принимает аргумент (например, String), который преобразуется в объект Integer
. Обычно эти методы вызываются так же, как и класс объекта, который они возвращают.
a=CSV
Здесь вы присваиваете значение константы CSV
(которая является классом CSV
) для a
переменная. В своем коде вы можете использовать константу CSV
или переменную a
для ссылки на объект класса.
В этих случаях, когда у вас одно и то же имя, ссылаются на разные вещи (класс и метод соответственно), Ruby может различать guish, какой из них вызывать / возвращать в зависимости от того, как вы его используете. Только вы явно и однозначно вызываете «вещь» (то есть, передавая блок или любые другие аргументы), Ruby вызовет метод.
Во всех других случаях, если вы ссылаетесь на вещь с имя, начинающееся с заглавной буквы, Ruby ожидает его от константы и возвращает его указанный объект (который в данном случае является объектом класса CSV
).
a {|v|v}
Здесь вы получаете ошибку поскольку Ruby пытается вызвать метод с именем a
(который не существует). Даже если это сработает, переменная a
на данный момент является ссылкой на класс CSV
(который не может быть вызван напрямую).
(Примечание: для вызова метода, имя которого у вас есть сохраненный в переменной, вы можете использовать метод send
, например, my_receiver.send(a)
.)
Теперь, чтобы решить вашу начальную проблему, вы можете создать объект метода и использовать его для вызова нужного метода, например
method_proc = @glob&.method(:each) || method(:crawl_dir).curry(@base_dir)
method_proc.call do |file|
puts file
end
Однако, это не очень идиоматизм c Ruby. Здесь ссылки на методы редко переносятся (а не в Javascript или Python, где вы регулярно делаете это с переданными функциональными объектами). Более идиоматическая c реализация в Ruby может быть:
def handle_file(file)
puts file
end
if @glob.respond_to(:each)
@glob.each { |file| handle_file(file) }
else
crawl_dir(@base_dir) { |file| handle_file(file) }
end
Еще более идиоматическая c будет, если ваш метод crawl_dir
будет (необязательно) возвращать объект Enumerator
, в котором Если вы могли бы упростить вызывающий код.
Здесь я предполагаю, что @glob
является либо nil
, либо Enumerable
объектом (таким как Array
или Enumerator
, который, таким образом, отвечает непосредственно на each
). Это позволяет нам еще больше упростить код.
def crawl_dir(directory)
# Return an Enumerator object for the current method invocation if no
# block was passed, similar to how the standard `Enumerable#each` method
# works.
return enum_for(__method__) unless block_given?
# the rest of the method here...
end
enumerable = @glob || crawl_dir(@base_dir)
enumerable.each do |file|
puts file
end
Здесь происходит то, что вы берете @glob
(который мы предполагаем как Enumerable
объект, если он существует, и, таким образом, ответ делает * 1054). *) или crawl_dir(@base_dir)
без блока. Из вашего crawl_dir
метода вы получите обратно Enumerator
объект, который снова будет Enumerable
. Затем вы можете l oop над этим объектом с помощью each
. Таким образом, оба ваших объекта имеют одинаковый тип возвращаемого значения, поэтому могут использоваться одинаково.