Skip to content

Instantly share code, notes, and snippets.

@mrkn
Created November 21, 2008 01:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save mrkn/27299 to your computer and use it in GitHub Desktop.
Save mrkn/27299 to your computer and use it in GitHub Desktop.
module Measure
class Unit
class << self
def unit_name
return nil
end
alias __inspect__ inspect
def inspect
if unit_name
"#<Measure::Unit[#{unit_name}]>"
else
__inspect__
end
end
end
end
class Context
def initialize
@units = {}
end
def has_unit?(name)
name = name.intern if String === name
return @units.has_key? name
end
def [](name)
name = name.intern if String === name
return nil unless has_unit? name
return @units[name]
end
def def_unit(name)
name = name.intern if String === name
unless has_unit? name
@units[name] = Class.new Unit
@units[name].class_eval do
@@unit_name = name
def self.unit_name
@@unit_name
end
end
end
return @units[name]
end
end
class Quantity
def initialize(value, unit)
@value, @unit = value, unit
end
def to_s
"#{@value} [#{@unit.unit_name}]"
end
end
class << self
def default_context
@@default_context ||= Context.new
return @@default_context
end
def current_context
get_thread_context || default_context
end
def with(ctx, &block)
begin
saved_ctx = get_thread_context
set_thread_context ctx
block.call
ensure
set_thread_context saved_ctx
end
end
private
def has_thread_storage?
return !!Thread.current[:measure]
end
def get_thread_storage(key)
return nil unless has_thread_storage?
return Thread.current[:measure][key]
end
def set_thread_storage(key, value)
Thread.current[:measure] ||= {}
Thread.current[:measure][key] = value
return nil
end
def get_thread_context
return nil unless has_thread_storage?
return get_thread_storage(:context)
end
def set_thread_context(context)
if context == Measure.default_context
set_thread_storage :context, nil
else
set_thread_storage :context, context
end
return context
end
def push_call_stack(*values)
stack = get_thread_storage :call_stack
unless stack
stack = []
set_thread_storage :call_stack, stack
end
stack.push values
return nil
end
def pop_call_stack
stack = get_thread_storage :call_stack
return nil unless stack
return stack.pop
end
def call_stack(index)
stack = get_thread_storage :call_stack
return stack[index] if stack
return nil
end
def dump_call_stack
p get_thread_storage :call_stack
end
def with_context
# dump_call_stack
a = call_stack(-3) # expect Measure.with
b = call_stack(-2) # expect Proc.call with Measure::Context
return get_thread_context if a && a[0] == Measure && a[1] == :with
return b[2] if b && b[0] == Proc && b[1] == :call
return nil
end
def set_context_to_proc(f)
# dump_call_stack
a = call_stack(-3) # expected Measure.with
b = call_stack(-2) # expected Proc.call with Measure::Context
if (a && a[0] == Measure && a[1] == :with) ||
(b && b[0] == Proc && b[1] == :call && b[2])
context = Measure.current_context
f.instance_eval { @_measure_context = context }
end
return f
end
tracer = lambda do |event, file, line, id, binding, klass|
case event
when "call"
next if klass == Measure && id != :with
if klass == Proc && (id == :call || id == :[])
f = binding.eval("self")
context = f.instance_eval { @_measure_context }
Measure.module_eval { push_call_stack Proc, :call, context }
else
Measure.module_eval { push_call_stack klass, id }
end
when "return"
top = Measure.module_eval { call_stack(-1) }
if top && top[0] == klass && top[1] == id
Measure.module_eval { pop_call_stack }
end
end
end
set_trace_func tracer
end
end
$measure = Measure::Context.new
$measure.def_unit :km
module Kernel
alias __lambda__ lambda
def lambda(&block)
f = __lambda__(&block)
return Measure.module_eval { set_context_to_proc f }
end
alias __proc__ proc
def proc(&block)
f = __proc__(&block)
return Measure.module_eval { set_context_to_proc f }
end
end
class Proc
class << self
alias __new__ new
def new(&block)
f = __new__(&block)
return Measure.module_eval { set_context_to_proc f }
end
end
alias __call__ call
def call(*args)
if @_measure_context
begin
ctx = @_measure_context
save_ctx = Measure.current_context
Measure.module_eval { set_thread_context ctx }
return __call__(*args)
ensure
Measure.module_eval { set_thread_context save_ctx }
end
else
return __call__(*args)
end
end
alias [] call
end
class Numeric
def km
context = Measure.module_eval { with_context }
if context
Measure::Quantity.new(self, context[:km])
else
self
end
end
end
def method_outside_with
puts "method_outside_with(1): #{1.km}"
end
method_outside_with
$proc_outside_with = lambda { puts "proc_outside_with(1): #{1.km}" }
Measure.with($measure) do
method_outside_with
puts "inside_with(1 [km]): #{1.km}"
lambda { puts "proc_inside_with_call(1 [km]): #{1.km}" }.call
$proc_inside_with = lambda { puts "proc_inside_with(1 [km]): #{1.km}" }
$proc_outside_with.call
lambda {
$proc_inside_proc_inside_with = lambda {
puts "proc_inside_proc_inside_with(1 [km]): #{1.km}"
}
}.call
lambda {
lambda {
$proc_inside_proc_inside_proc_inside_with = lambda {
puts "proc_inside_proc_inside_proc_inside_with(1 [km]): #{1.km}"
}
}.call
}.call
lambda {
lambda {
Measure.with(nil) do
$context_conservation = lambda {
puts "(false): #{Measure.current_context == $measure}"
}
end
}.call
}.call
$proc_inside_proc_inside_with.call
$context_conservation.call
end
$proc_inside_with.call
$proc_inside_proc_inside_with.call
$proc_inside_proc_inside_proc_inside_with.call
$context_conservation.call
## $ ruby measure_lexical_with_test.rb
## method_outside_with(1): 1
## method_outside_with(1): 1
## inside_with(1 [km]): 1 [km]
## proc_inside_with_call(1 [km]): 1 [km]
## proc_outside_with(1): 1
## proc_inside_proc_inside_with(1 [km]): 1 [km]
## (false): false
## proc_inside_with(1 [km]): 1 [km]
## proc_inside_proc_inside_with(1 [km]): 1 [km]
## proc_inside_proc_inside_proc_inside_with(1 [km]): 1 [km]
## (false): false
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment