From: Luke Blanshard Date: 2006-02-19T23:05:49+09:00 Subject: [/QUIZ] metakoans.rb (#67) --------------090205020702090600080900 Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit My solution is attached. Actually, two different styles of the same solution. Neither one is anywhere near 13 lines -- I'll be very interested to see the work of people who actually know this language. A couple of subtleties. (1) The first time the attribute is set, I redefine the setter and getter to just be ivar accessors. (2) I only ever evaluate the block once: the initial version of the getter calls the setter with the result of evaluating the block. And I'll echo everyone else: excellent quiz. Luke Blanshard --------------090205020702090600080900 Content-Type: text/plain; name="knowledge.rb" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="knowledge.rb" style = :code # or :string case style when :string class Module def attribute desc, &block if desc.is_a? Hash name = desc.keys[0] default = desc[name] raise "Hash or block, not both" if block else name, default = desc, nil end module_eval <<-"end;", __FILE__, __LINE__ def #{name}?; #{name} != nil end # Permanent definition of query method def __#{name}__ivarget; @#{name} end # ivar getter and setter def __#{name}__ivarset v; @#{name}=v end # Initial def of attr reader define_method(name) { self.#{name} = (block ? instance_eval(&block) : default) } def #{name}= value # Initial def of attr writer (class << self; self; end).class_eval do alias_method :#{name}, :__#{name}__ivarget # Subsequent calls use ivar getter and setter alias_method :#{name}=, :__#{name}__ivarset end @#{name} = value end end; end end when :code class Module def attribute desc, &block if desc.is_a? Hash name = desc.keys[0] default = desc[name] raise "Hash or block, not both" if block else name, default = desc, nil end ivar_name = "@#{name}" module_eval do define_method("#{name}?") {send(name) != nil} define_method("__#{name}__ivarget") { instance_variable_get(ivar_name) } define_method("__#{name}__ivarset") {|v| instance_variable_set(ivar_name, v) } define_method(name) { send("#{name}=", block ? instance_eval(&block) : default) } define_method("#{name}=") do |value| (class << self; self; end).class_eval do alias_method "#{name}", "__#{name}__ivarget" alias_method "#{name}=", "__#{name}__ivarset" end instance_variable_set(ivar_name, value) end end end end end --------------090205020702090600080900--