49

I'm learning how to use class_eval in modules (I'm somewhat familiar with class_eval) and came across this helpful class in resource_controller. In there they have things like this:

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

What does __FILE__ and __LINE__ do in that context? I know __FILE__ references the current file, but what does that whole thing do exactly? Don't really know how to search for that :).

Lance
  • 75,200
  • 93
  • 289
  • 503

3 Answers3

58

__FILE__ and __LINE__ are sort of dynamic constants that hold the file and line that are currently executing. Passing them in here allow errors to properly report their location.

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

foo

When you run this

$ 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

Note it says the file and line #5 even though that was just text in an eval. Without those the file/line trick the output would look like this:

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

The stack trace simply shows (eval) which isn't as helpful.

Alex Wayne
  • 178,991
  • 47
  • 309
  • 337
  • 1
    It might be good to mention that using `define_method` instead won't need this kind of tricks, plus it won't open vulnerabilities (see http://stackoverflow.com/questions/3003328/how-do-i-use-class-eval/3003509#3003509) – Marc-André Lafortune Jan 31 '13 at 19:49
4

The << is the start of a heredoc. That line is the start of a multiline string. The string is evaled to create the function. The class_eval function uses the __FILE__ and __LINE__ to add debug information.

Mark Byers
  • 811,555
  • 193
  • 1,581
  • 1,452
  • 1
    But but but...aren't you the golfing wizard from anarchy golf? You should know Ruby like the back of your hand! :-D (That was a comment on the wishy-washiness of your statement about what `class_eval`'s behaviour is. If your intent wasn't to sound uncertain, I take my comment back.) – C. K. Young Mar 22 '10 at 22:09
  • Haha... I never needed that function because it was too many characters. – Mark Byers Mar 22 '10 at 22:11
  • In your case it adds the filename and line number before error messages. Look at the example in the link: http://ruby-doc.org/core/classes/Module.html#M001650 – Mark Byers Mar 22 '10 at 22:31
2

Let's also note, that eval-ling strings should be avoided where possible. In your particular case, replacing #class_eval with #class_exec is possible, and should be preferred:

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
Boris Stitnicky
  • 12,444
  • 5
  • 57
  • 74