2008
04.11
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.
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