Here’s a variant that lets you access local variables from nested
dynamic scopes, though I’m not sure that’s a good idea
def with_current_context
finder = catch(:context) { yield }
finder.call(Proc.new.binding) if finder
## ? Is there an easier way to get caller’s binding?
end
def find_in_context(name)
callcc do |again|
throw(:context,
proc {|b|
begin
val = eval(name, b)
again.call(val)
rescue NameError
raise “Can’t find context value for #{name}”
end
})
end
end
def update_widget
name = find_in_context(“name”)
color = find_in_context(“color”)
puts “<#{color}>#{name}</#{color}>”
end
def update_screen
update_widget
end
name = ‘dave’; color = ‘red’
with_current_context do
update_screen
end
And, a variant that allows nested contexts to be cumulative, at the cost
of an additional outer wrapper around code that uses it.
def with_context(params)
finder, = catch(:context) { yield }
finder.call(params) if finder
end
def find_in_context(name)
callcc do |again|
throw(:context, [proc {|params|
if params.has_key?(name)
again.call(params[name])
else
again.call(find_in_context(name))
end
}, name])
end
end
def use_dynamic_biding
attempt, name = catch(:context) { yield }
if attempt
raise “Can’t find context value for #{name}”
end
end
def update_widget
name = find_in_context(:name)
color = find_in_context(:color)
puts “<#{color}>#{name}</#{color}>”
end
def update_screen
update_widget
end
use_dynamic_biding do
with_context(:name => ‘dave’, :color => ‘red’) do
with_context(:foo => ‘bar’) do
update_screen
end
end
end
I’m sure there are easier ways. I just hacked this one out, so I’m happy
to be humiliated with better solutions.
def with_context(params)
Thread.current[:dynamic] ||=
Thread.current[:dynamic].push params
begin
yield
ensure
Thread.current[:dynamic].pop
end
end
def find_in_context(name)
Thread.current[:dynamic].reverse_each {|params|
return params[name] if params.has_key? name
}
raise “Can’t find context value for #{name}”
end
I’m sure there are easier ways. I just hacked this one out, so I’m
happy
to be humiliated with better solutions.
def with_context(params)
Thread.current[:dynamic] ||=
Thread.current[:dynamic].push params
begin
yield
ensure
Thread.current[:dynamic].pop
end
end
def find_in_context(name)
Thread.current[:dynamic].reverse_each {|params|
return params[name] if params.has_key? name
}
raise “Can’t find context value for #{name}”
end
Very nice indeed! Couldn’t we save one lookup by doing
def with_context(params)
( Thread.current[:dynamic] ||= ).push params
begin
yield
ensure
Thread.current[:dynamic].pop
end
end