
Metakoans is an old Ruby metaprogramming challenge from RubyQuiz, the weekly programming challenge for Ruby programmers that comes out every Friday on the RubyTalk mailing list. I first heard about metakoans during one of Neal Ford's Ruby tutorials, but only recently did I get around to solving it. If you haven't already taken the quiz you should.
Initially I struggled with the koans, because you need to know all about methods like instance_eval, class_eval, and the eigenclass. So I went back and read the metaprogramming chapter of Matz's new book The Ruby Programming Language. That, plus some dandruff commercial style head scratching was all it took to solve them.
So now I have a solution (it isn't pretty), but if I am completely honest with myself I don't think I did the exercise correctly. My solution to koan 1 made the first 5 koans pass. That made me feel like a smarty pants, but It also meant I didn't bother reading koans 2-4. Can you really say you passed a koan if you never had to look at it? No. So the real koan challenge is: Can you write 9 solutions to the metakoan quiz where each solution only passes all solutions up to the current koan you are working on? So solution 1 will just make koan 1 pass. Solution 2 will only make koan 1 and koan 2 pass, etc.
I struggled to make a good exclusive solution to koan 1. The best I have done so far uses a technique I like to call cheating. I break my attribute function so that it doesn't work if it is being called from the eigenclass. Ok, so all's fair in love, war, and metaprogramming, but it seems like there should be a more natural solution.
#Exclusively pass Koan 1.
class Module
def attribute (p)
return if self.ancestors.include?(Class)
define_method(:a?){return @a}
class_eval{attr_accessor :a}
end
end
But even if it turns out that such an exercise is only solveable through contrived answers at least you will have to look closely at each koan. And once I have done this I fully expect that mountains will once again become merely mountains. Stop reading now if you don't want to see a solution to the original metakoans problem.
#it's ugly but it works.
class Module
def attribute(p, &block)
if p.is_a? Hash
define_attribute_methods(p.keys.first, p.values.first)
else
define_method(:default){block ? instance_eval(&block) : nil}
define_attribute_methods(p, false)
end
end
def define_attribute_methods(param_name, default_value)
default = default_value ? "@@default" : "default"
class_eval("@@default = #{default_value}") if default_value
class_eval("def #{param_name}; return #{default} unless defined? @#{param_name}; @#{param_name}; end")
class_eval("def #{param_name}=(val); @#{param_name} = val; end")
class_eval("def #{param_name}?; return #{default} unless defined? @#{param_name}; @#{param_name}; end")
end
end

No comments:
Post a Comment