Lambda/procs and dynamically generated methods

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

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

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.

On 2/14/08, Chris H. [email protected] wrote:

@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

Adam S. wrote:

On 2/14/08, Chris H. [email protected] wrote:

@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

The main reason the code is the way it is, is because I don’t want to
used named blocks, see:

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. :slight_smile:

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.

irb(main):034:0> JobB.instance_method(:do)
=> #<UnboundMethod: JobB#do>

This should be :task of course. Sorry.

ThoML wrote:

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.

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)

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.