argument hash to string to proc to instance variables

N

Nasir Khan

It is idiomatic to pass a number of related arguments (usually config
related) as the last (braceless argument) to a method ( heavily used
in Rails ) e.g .

def my_method(arg1, arg2)
end

where arg1 is anything and arg2 is a hash

my_method("hello", :a=>"nasir", :b => 30, :c="khan") etc.

It is also quite popular to instance_eval a block to initialize a
number of attributes rather than pass them all upfront to "new". e.g.

class A
attr_accessor :a, :b, :c
def initialize(&block)
instance_eval(&block) if block_given?
end
end

x = A.new do
self.a = "hello";
self.b = 1;
self.c = "bye";
end

It is also trivial to use these two techniques together i.e. take the
hash argument in the method and use it to create a proc which can be
evaled as a block e.g.

def my_method(arg1, arg2)
str = "lambda { "
arg2.each_pair {|k,v| str << sprintf("self.%s=%s;", k, v) }
str << " }"
p = eval str
x = A.new &p
end
end

Here "my_method" returns the instance of class A initialized with the
attributes given
as hash which is converted to block and instance_evaled in the
initialize method of class A.

All this works but I am doing *two* evals (one for string to proc in
my_method and one the instance_evaling of block in initialize, not to
mention constructing the string for proc.

Is there is a simpler/faster alternative that I have overlooked?

- Nasir
 
B

Brian Candler

It is also quite popular to instance_eval a block to initialize a
number of attributes rather than pass them all upfront to "new". e.g.

class A
attr_accessor :a, :b, :c
def initialize(&block)
instance_eval(&block) if block_given?
end
end

x = A.new do
self.a = "hello";
self.b = 1;
self.c = "bye";
end

It is also trivial to use these two techniques together i.e. take the
hash argument in the method and use it to create a proc which can be
evaled as a block e.g.

def my_method(arg1, arg2)
str = "lambda { "
arg2.each_pair {|k,v| str << sprintf("self.%s=%s;", k, v) }
str << " }"
p = eval str
x = A.new &p
end
end

Here "my_method" returns the instance of class A initialized with the
attributes given
as hash which is converted to block and instance_evaled in the
initialize method of class A.

All this works but I am doing *two* evals (one for string to proc in
my_method and one the instance_evaling of block in initialize, not to
mention constructing the string for proc.

Is there is a simpler/faster alternative that I have overlooked?

Don't worry about instance_eval with a block. All it does it change 'self'
and run the existing block; you are not running the compiler.

It's the string form of eval which should concern you, as (a) you're
compiling code on the fly, which is inefficient, and (b) you have to be very
careful about injection attacks, e.g.

my_method(nil, :foo => 'system("rm -rf /*")')

You could use 'send' or 'instance_variable_set' instead. i.e.

send("#{k}=", v) # invoke the #{k}= method
or
instance_variable_set("@#{k}", v) # set @{k} to v

You'll see the first form in ActiveRecord quite a lot.

Regards,

Brian.
 

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,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top