Skip to content

Instantly share code, notes, and snippets.

@apeiros
Created January 28, 2010 19:50
Show Gist options
  • Save apeiros/289074 to your computer and use it in GitHub Desktop.
Save apeiros/289074 to your computer and use it in GitHub Desktop.
#!/usr/bin/ruby -w
module Kernel
ValidLocalVariableName = /\A[a-z_]\w*\z/
def empty_binding
proc{ binding }.call
end
# Dynamically define a bunch of local variables from a hash.
# Overrides existing local variables.
#
# IMPORTANT: This is an anti-pattern. Think thrice before using this. It
# almost always means you're doing it wrong and should use a Hash instead.
#
# Arguments:
# binding: The binding the variables should appear in, usually this is
# the current binding which you get via Kernel#binding
# variables: A Hash containing the variable names and their values. The
# keys must be Symbols. The values can be any object.
# valid_names: Optional. An array with valid local variable names.
#
# Example:
# eval_with_locales('puts foo, bar', :foo => 1, :bar => 2)
#
# Also see Kernel#safe_extract
#
def eval_with_locales(code, variables, valid_names=nil)
# Verify against provided variable names
if valid_names && !(variables.keys-valid_names).empty? then
raise ArgumentError,
"Contains invalid names #{(variables.keys-valid_names).join(', ')}"
end
# Verify that all names are really names to prevent code injection
# only do it if the user didn't provide a list with valid names
unless valid_names || variables.keys.all? { |name| name.to_s =~ ValidLocalVariableName }
raise ArgumentError, "Contains invalid identifiers"
end
# Deposit the data somewhere threadsafe without corrupting the namespace
Thread.current[:__extract__] = variables
# Assign the lvars from our deposit
code = "#{variables.keys.join(', ')} = Thread.current[:__extract__]." \
"values_at(:#{variables.keys.join(', :')})\n" \
"Thread.current[:__extract__] = nil\n" \
+code
eval(code, empty_binding)
end
module_function :eval_with_locales
end
class Blah
def x
eval_with_locales('puts foo, bar', :foo => 1, :bar => 2)
end
end
Blah.new.x
# eof
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment