# http://eigenclass.org/hiki.rb?bounded+space+instance_exec class Object module InstanceExecHelper; end include InstanceExecHelper def instance_exec(*args, &block) begin old_critical, Thread.critical = Thread.critical, true n = 0 n += 1 while respond_to?(mname="__instance_exec#{n}") InstanceExecHelper.module_eval{ define_method(mname, &block) } ensure Thread.critical = old_critical end begin ret = send(mname, *args) ensure InstanceExecHelper.module_eval{ remove_method(mname) } rescue nil end ret end end module Sloop NotFound = Object.new class Object def self.map2sloop(name) # textual eval since we may need to pass blocks. 1.9 where art thou? class_eval <<-END def #{name.to_s}(*args, &block) method_missing(#{name.inspect}, *args, &block) end END end map2sloop :inspect attr_reader :_slots def initialize(parent=Sloop::Root, &block) @_slots = {:_parent => parent} instance_eval &block if block end def initialize_copy(from) @_slots = from._slots.clone end def method_missing(name, *args, &block) # A special convention? if name.to_s =~ /\Adef_(.*)/ return self.def($1, *args, &block) end # A slot? value = _lookup(name) case value when Method return _execute(value, *args, &block) when NotFound # do nothing else return value end # A default setter? if name.to_s =~ /(.*)=\z/ if args.size > 1 raise ArgumentError, "sloop setter can only take one value" end return @_slots[$1.to_sym] = args.first end # Too bad... raise NoMethodError, "undefined sloop method `#{name}'" end def _lookup(name, start=self) if @_slots.has_key?(name) && (!@_slots.has_key?(:_condition) || start._execute(@_slots[:_condition])) @_slots[name] else if @_slots.has_key?(:_parent) case @_slots[:_parent] when Sloop::Object @_slots[:_parent]._lookup(name, start) when Array @_slots[:_parent].each { |parent| v = parent._lookup(name, start) if v != NotFound return v end } NotFound when nil NotFound else raise "_parent broken (#{parent.class})" end else NotFound end end end def _execute(meth, *args) instance_exec(*args, &meth) end def def(name, value=nil, &block) if block @_slots[name.to_sym] = Method.new(&block) else @_slots[name.to_sym] = value end end def condition(&block) self.def(:_condition, &block) end end class Method < Proc end end Sloop::Root = Sloop::Object.new(nil) { def_inspect { if self.eql? Sloop::Root "#(the root object)" else "#(an object)" end } def_inherit { |*objects| self._parent = [_parent] unless Array === _parent _parent.unshift *objects } def_mixin { |*objects| self._parent = [_parent] unless Array === _parent _parent.concat objects } } def sloop(*args, &block) Sloop::Object.new(*args, &block) end