Подтвердить что ты не робот

Что означает class_eval << - "end_eval", __FILE__, __LINE__ означает в Ruby?

Я изучаю, как использовать class_eval в модулях (я немного знаком с class_eval) и наткнулся на этот полезный класс в resource_controller. Там они имеют такие вещи:

class_eval <<-"end_eval", __FILE__, __LINE__

  def #{block_accessor}(*args, &block)
    unless args.empty? && block.nil?
      args.push block if block_given?
      @#{block_accessor} = [args].flatten
    end

    @#{block_accessor}
  end

end_eval

Что делает __FILE__ и __LINE__ в этом контексте? Я знаю, что __FILE__ ссылается на текущий файл, но что делает все это в точности? Не знаю, как это искать:).

4b9b3361

Ответ 1

__FILE__ и __LINE__ представляют собой разновидность динамических констант, которые содержат файл и строку, которые в настоящее время выполняются. Передача их здесь позволяет ошибкам правильно сообщать о своем местоположении.

instance_eval <<-end_eval, __FILE__, __LINE__
  def foo
    a = 123
    b = :abc
    a.send b
  end
end_eval

foo

Когда вы запустите этот

$ ruby foo.rb 
foo.rb:5:in `send': undefined method `abc' for 123:Fixnum (NoMethodError)
    from foo.rb:5:in `foo'
    from foo.rb:11

Обратите внимание, что он говорит файл и строку # 5, хотя это был только текст в eval. Без этого файла/линии трюк вывод будет выглядеть следующим образом:

$ ruby foo.rb 
(eval):5:in `send': undefined method `abc' for 123:Fixnum (NoMethodError)
    from (eval):5:in `foo'
    from foo.rb:11

Трассировка стека просто показывает (eval), что не так полезно.

Ответ 2

<< - начало heredoc. Эта строка является началом многострочной строки. Строка обведена для создания функции. Функция class_eval использует __FILE__ и __LINE__ для добавления отладочной информации.

Ответ 3

Следует также отметить, что при необходимости следует избегать eval -личных строк. В вашем конкретном случае замена #class_eval на #class_exec возможна и должна быть предпочтительной:

class_exec do
  define_method block_accessor do |*args, &block|
    unless args.empty? && block.nil?
      args.push block if block_given?
      instance_variable_set "@#{block_accessor}", [args].flatten
    end
    instance_variable_get "@#{block_accessor}"
  end
end