Object#instance_eval but better

R

Richard Crowley

I have a bit of code here that I'd love to make better:

class Foo
def initialize(path)
self.instance_eval(File.read(path))
end
end

I don't want to continue down the path of reading an entire file into a
string and then eval'ing that string just to get the contents of the
file executed as if it were the body of a method in a class. The file
passed to the constructor above might contain something like:

@foo = 'This will become an instance variable when you do Foo.new(path)'

I've tried all manner of hacks to make Kernel#load and friends execute
code as if it were in the calling scope. In every case I've seen the
loaded file leave no lasting effect on the instance variables of the
calling method. My goal is to be able to do exactly this:

class Foo
def initialize(path)
load_in_scope path
end
end
f = Foo.new('load_me.rb')
f.instance_variables # => ['@foo']

My assumption here is that reading a file into a string and eval'ing it
is very slow and is bad form. Am I right?

Is this possible without reading the file into a string? Is this
possible without hacking the C code? Should I go bother ruby-core?

Thanks,

Richard
 
T

Trans

I have a bit of code here that I'd love to make better:

class Foo
=A0 =A0 =A0def initialize(path)
=A0 =A0 =A0 =A0 =A0self.instance_eval(File.read(path))
=A0 =A0 =A0end
end

I don't want to continue down the path of reading an entire file into a
string and then eval'ing that string just to get the contents of the
file executed as if it were the body of a method in a class. =A0The file
passed to the constructor above might contain something like:

@foo =3D 'This will become an instance variable when you do Foo.new(path)= '

I've tried all manner of hacks to make Kernel#load and friends execute
code as if it were in the calling scope. =A0In every case I've seen the
loaded file leave no lasting effect on the instance variables of the
calling method. =A0My goal is to be able to do exactly this:

class Foo
=A0 =A0 =A0def initialize(path)
=A0 =A0 =A0 =A0 =A0load_in_scope path
=A0 =A0 =A0end
end
f =3D Foo.new('load_me.rb')
f.instance_variables # =3D> ['@foo']

My assumption here is that reading a file into a string and eval'ing it
is very slow and is bad form. =A0Am I right?

Is this possible without reading the file into a string? =A0Is this
possible without hacking the C code? =A0Should I go bother ruby-core?

Ultimately, it's the same thing, whether you do it in Ruby or C. I
doubt it would be that much faster in C than it is in pure Ruby b/c it
is dynamic --the path can change. However, you may actually intend:

class Foo
def initialize(path)
eval(File.read(path), __FILE__, __LINE__)
end
end

T.
 
K

Ken Bloom

I have a bit of code here that I'd love to make better:

class Foo
     def initialize(path)
         self.instance_eval(File.read(path))
     end
end

I don't want to continue down the path of reading an entire file into a
string and then eval'ing that string just to get the contents of the
file executed as if it were the body of a method in a class.  The file
passed to the constructor above might contain something like:

@foo = 'This will become an instance variable when you do
Foo.new(path)'

I've tried all manner of hacks to make Kernel#load and friends execute
code as if it were in the calling scope.  In every case I've seen the
loaded file leave no lasting effect on the instance variables of the
calling method.  My goal is to be able to do exactly this:

class Foo
     def initialize(path)
         load_in_scope path
     end
end
f = Foo.new('load_me.rb')
f.instance_variables # => ['@foo']

My assumption here is that reading a file into a string and eval'ing it
is very slow and is bad form.  Am I right?

I can't imagine that it would be slower than simply loading the ruby
file. If there's a hint of bad form in it (and I'm not personally going
to say whether there is or isn't), it would have to relate to security
implications. And the only way to comment on that is to ask what exactly
you're trying to do with this construction.
Ultimately, it's the same thing, whether you do it in Ruby or C. I doubt
it would be that much faster in C than it is in pure Ruby b/c it is
dynamic --the path can change. However, you may actually intend:

class Foo
def initialize(path)
eval(File.read(path), __FILE__, __LINE__)
end
end

T.

He almost certainly doesn't intend __FILE__, __LINE__ because that is the
default behavior of eval. (and besides it won't work since the second
parameter has to be a binding)

irb(main):014:0> eval "puts __FILE__\nfail", __FILE__
TypeError: wrong argument type String (expected Proc/Binding)
from (irb):14:in `eval'
from (irb):14
from :0


Propably he wants
eval(File.read(path),binding,path)

(the filename and line number passed indicate the location used for
__FILE__, __LINE__ and exception stack traces that occur in the eval'ed
code

irb(main):013:0> eval "puts __FILE__\nfail", binding, 'foo'
foo
RuntimeError:
from foo:2
from (irb):13
from :0
 
A

ara.t.howard

have a bit of code here that I'd love to make better:

class Foo
def initialize(path)
self.instance_eval(File.read(path))
end
end

I don't want to continue down the path of reading an entire file
into a string and then eval'ing that string just to get the contents
of the file executed as if it were the body of a method in a class.
The file passed to the constructor above might contain something like:

@foo = 'This will become an instance variable when you do
Foo.new(path)'

I've tried all manner of hacks to make Kernel#load and friends
execute code as if it were in the calling scope. In every case I've
seen the loaded file leave no lasting effect on the instance
variables of the calling method. My goal is to be able to do
exactly this:


reverse your logic: don't force the code to eval inside a context -
provide the context.




cfp:~ > cat a.rb
class Foo
class << self
attr_accessor :current

def initializing &block
caller = eval 'self', block

if caller.is_a?(Foo)
previous = current
begin
Foo.current = caller
block.call
ensure
Foo.current = previous
end
else
current.instance_eval &block
end
end
end

def initialize path
Foo.initializing{ load path }
end
end

foo = Foo.new('b.rb')
p foo.instance_variables




cfp:~ > cat b.rb
Foo.initializing do
@foo = 42
end



cfp:~ > ruby a.rb
["@foo"]





a @ http://codeforpeople.com/
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,773
Messages
2,569,594
Members
45,124
Latest member
JuniorPell
Top