2008
04.11

So there’s BlankSlate, and today tmm1 was doing something where he was delegating to Array, but wanted to actually have Array in the ancestor chain (for Class#===), so I came up with this:

def BS(superclass = nil, name = :BlankSlate)
  klass = eval(%w[
    class #{name} #{" < #{superclass}" if superclass}
    end
    #{name}
  ])
  klass.class_eval {
    instance_methods.each { |m| undef_method m unless m =~ /^__/ }
  }
  klass
end

And then he came up with the anti-eval solution:

class BlankSlate
  instance_methods.each { |m| undef_method m unless m =~ /^__/ }
end
 
def BlankSlate superclass = nil
  if superclass
    @blank_slates ||= {}
    @blank_slates[superclass] ||= Class.new(superclass) do
      instance_methods.each { |m| undef_method m unless m =~ /^__/ }
    end
  else
    BlankSlate
  end
end

refactors to:

class BlankSlate
  instance_methods.each { |m| undef_method m unless m =~ /^__/ }
end
 
def BlankSlate superclass = BlankSlate
  @blank_slates ||= Hash.new do |h,k|
    h[k] = Class.new(superclass) do
      instance_methods.each { |m| undef_method m unless m =~ /^__/ }
    end
  end
  @blank_slates[superclass]
end

But watch out!!!:

a = StrokeDB::LazyMappingArray.new([1,2,3]) # => [1, 2, 3]
Array === a # => true
Array.new(a) # => []
[a].flatten # => []
 
# The last two are 'incorrect' behaviour (probably work on rubinius,
# mind you, so did the delegation approach).

Anyway, you get the idea, mix and match, (feedback?), whatever. Enjoy.

Share and Enjoy:
  • Print
  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Mixx
  • Google Bookmarks
  • HackerNews
  • LinkedIn
  • Reddit
  • StumbleUpon
  • Technorati
  • Twitter

1 comment so far

Add Your Comment
  1. Bug in the last refactor, the Hash is defined with “superclass” memoized with whatever class BlankSlate() is first called.

    Try:
    h[k] = Class.new(k) do