lambda/procs and dynamically generated methods

C

Chris Hall

I'm not sure one way is technically better than the other, so if anyone
has any input, I'd be glad to hear it.

class Job

def initialize(file = nil)
raise "need file or block" if fil.nil? || !block_given?
@action = block_given? ? lambda { yield } : eval("lambda {
#{IO.read(file)} }")
end

def do
@action.call
end
end

or

class Job

def initialize(file = nil)
raise "need file or block" if fil.nil? || !block_given?
action = block_given? ? lambda { yield } : eval("lambda {
#{IO.read(file)} }")
self.class.send:)define_method, :task, action)
self.class.send:)private, :task)
end

def do
task
end
end
 
C

Chris Hall

please forgive the typos in the original, i should learn not to type the
code directly into the form.


corrected:


class JobA

def initialize(file = nil)
raise "need file or block" if file.nil? && !block_given?
@action = block_given? ? lambda { yield } : eval("lambda {
#{IO.read(file)} }")
end

def do
@action.call
end
end

class JobB

def initialize(file = nil)
raise "need file or block" if file.nil? && !block_given?
action = block_given? ? lambda { yield } : eval("lambda {
#{IO.read(file)} }")
self.class.send:)define_method, :task, action)
self.class.send:)private, :task)
end

def do
task
end
end
 
T

ThoML

This can be done in almost endless different ways of course. Here are
two other options:

class Job
def initialize(file=nil, &block)
raise "need file or block" unless file or block
@file = file
@block = block
end

def do
if @file
File.read @file
else
@block.call
end
end
end


class Job
def initialize(file=nil, &block)
raise "need file or block" unless file or block
@task = block || lambda { File.read file }
end

def do
@task.call
end
end

The second one is a slightly cleaned up version of your first
proposal.

Regards,
Thomas.
 
A

Adam Shelly

class JobA

def initialize(file = nil)
raise "need file or block" if file.nil? && !block_given?
@action = block_given? ? lambda { yield } : eval("lambda {
#{IO.read(file)} }")
end

def do
@action.call
end
end

I may be missing something important about what you are trying to
accomplish, but
what about simply

class Job
def initialize(file = nil, &block)
raise "need file or block" if file.nil? && !block_given?
@action = block || proc { eval IO.read(file)}
end
def do
@action.call
end
end

(I sure hope you trust the contents of 'file')
-Adam
 
C

Chris Hall

Adam said:
I may be missing something important about what you are trying to
accomplish, but
what about simply

class Job
def initialize(file = nil, &block)
raise "need file or block" if file.nil? && !block_given?
@action = block || proc { eval IO.read(file)}
end
def do
@action.call
end
end

(I sure hope you trust the contents of 'file')
-Adam

The main reason the code is the way it is, is because I don't want to
used named blocks, see:
http://ola-bini.blogspot.com/2007/12/ruby-closures-and-memory-usage.html


The only really difference between the 2 is that in once instance I am
capturing the block in @action and in the other I am creating a method
from the block. I didn't know if one way was more efficient than the
other.

And yes, I will be trusting the code in file. :)

I'm not necessarily looking for the simplest solution, I'm looking for
the best solution. Both of the examples I posted have the same results,
it's just how they are implemented. I didn't know there was some behind
the scenes mo-jo going on that makes one a better choice than the other.
 
T

ThoML

Okay, I missed the deeper meaning of eval in your example.

eval("lambda {#{IO.read(file)} }")

Maybe:

@task = if file
ruby = file.read file
lambda { eval ruby }
else
block
end
I'm not necessarily looking for the simplest solution, I'm looking for
the best solution. Both of the examples I posted have the same results,
it's just how they are implemented.

The second one creates the method in the class though which most
likely
isn't what you want.

irb(main):032:0> j = JobB.new('test.rb')
=> #<JobB:0x7fec2adc>
irb(main):034:0> JobB.instance_method:)do)
=> #<UnboundMethod: JobB#do>

If you want to minimize the context, create a new method that gets
nothing but the file name as argument and returns the block for the
file. I think that's what's meant in the blog post.
 
C

Chris Hall

ThoML said:
Okay, I missed the deeper meaning of eval in your example.

eval("lambda {#{IO.read(file)} }")

Maybe:

@task = if file
ruby = file.read file
lambda { eval ruby }
else
block
end


The second one creates the method in the class though which most
likely
isn't what you want.

irb(main):032:0> j = JobB.new('test.rb')
=> #<JobB:0x7fec2adc>
irb(main):034:0> JobB.instance_method:)do)
=> #<UnboundMethod: JobB#do>

If you want to minimize the context, create a new method that gets
nothing but the file name as argument and returns the block for the
file. I think that's what's meant in the blog post.


so what would be the difference between:

x = eval "lambda { #{IO.read(file)}" }"
x.call

y = lambda { eval IO.read(file) }
y.call


I'm guessing that in the first eval gets called only the one time where
in the second, eval gets called with every y.call. (this is why i did
it the way i did, if i am incorrect, then please let me know)
 

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

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,011
Latest member
AjaUqq1950

Latest Threads

Top