Created
January 28, 2010 19:50
-
-
Save apeiros/289074 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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