What Does Class_Eval ≪≪-"End_Eval", _File_, _Line_ Mean in Ruby

What does class_eval - end_eval , __FILE__, __LINE__ mean in Ruby?

__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.

understanding __FILE__

Since you are using a relative path in '../../views', you need a reference point to expand the path. The second argument, which happens to be __FILE__ in this case, overrides the default reference point, which is the current working directory.

Ruby Doc File.expand_path

Ruby - Is there a way to overwrite the __FILE__ variable?

My tip? Refactor!

How can I access source file location of a Ruby class at runtime?

You could try calling:


caller.first

That will print off the file name and line number. Using your demonstration files above (with slight modifications:

file1.rb:

module MetaFoo
class << Object
def bar
puts caller.first # <== the magic...
end
end
end

file2.rb:

require './file1.rb'

class Foo
bar
end

When I run ruby file2.rb, I get the following output:

nat$ ruby file2.rb 
file2.rb:4:in `<class:Foo>'

That's what you want, right?

How do I use class_eval?

The short answer is: you probably want to avoid using class_eval like this.

Here's an explanation of your code:

The %{hello} is just another way to write a string literal in Ruby, without having to worry about escaping double or single quotes within the string:

%{hello "world"} == "hello \"world\""  # => true

The val in your code is an argument of the method being defined.

The class_eval is used to define some methods by computing the text one would write to do the definition and then evaluating it. It is not necessary here, BTW. An equivalent code would be:

class Module
def attr_ (*syms)
syms.each do |sym|
define_method "#{sym}=" do |val|
instance_variable_set "@#{sym}", val
end
end
end
end

This is just equivalent to the builtin attr_writer.

Update: There can actually be a significant difference between the two...

The class_eval version is vulnerable if you can't trust the argument syms. For example:

class Foo
attr_ "x; end; puts 'I can execute anything here!'; val=42; begin; val"
end

The class_eval version will print "I can execute anything here" twice, proving it can execute anything. The define_method version won't print anything.

This type of code was pivotal to create major vulnerability for all installed Rails apps.



Related Topics



Leave a reply



Submit