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
How to Switch to Ruby 1.9.3 Installed Using Homebrew
How Would You Parse a Url in Ruby to Get the Main Domain
How to Get Ruby/Homebrew/Rvm to Work on Yosemite
Getaddrinfo: Nodename Nor Servname Provided, or Not Known
How to Remove the Bom from a Utf-8 Encoded File
Using God to Monitor Unicorn - Start Exited with Non-Zero Code = 1
Recommended Development Web Server for Ruby on Rails 3
How to Intercept Method Call in Ruby
How to Display Ruby on Rails Form Validation Error Messages One At a Time
Ruby on Rails Best Practices - Big Controller VS Small Controller
Confusion With Atomic Grouping - How It Differs from the Grouping in Regular Expression of Ruby
Why Isn't Self Always Needed in Ruby/Rails/Activerecord
Rake "Already Initialized Constant Wfkv_" Warning