123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316 |
- #
- # meta_koans.rb is an arduous set of exercises designed to stretch
- # meta-programming muscle. the focus is on a single method "attribute" which
- # behaves much like the built-in "attr", but whose properties require delving
- # deep into the depths of meta-ruby. usage of the "attribute" method follows
- # the general form of
- #
- # class C
- # attribute "a"
- # end
- #
- # o = C.new
- # o.a = 42 # setter - sets @a
- # o.a # getter - gets @a
- # o.a? # query - true if @a
- #
- # but reaches much farther than the standard "attr" method as you will see
- # shortly.
- #
- # your path, should you choose to follow it, is to write a single file
- # "knowledge.rb" implementing all functionality required by the koans below.
- # as a student of meta-programming your course will be guided by a guru whose
- # wisdom and pithy sayings will assist you on your journey.
- #
- # a successful student will eventually be able to do this
- #
- # harp:~ > ruby meta_koans.rb
- # koan_1 has expanded your awareness
- # koan_2 has expanded your awareness
- # koan_3 has expanded your awareness
- # koan_4 has expanded your awareness
- # koan_5 has expanded your awareness
- # koan_6 has expanded your awareness
- # koan_7 has expanded your awareness
- # koan_8 has expanded your awareness
- # koan_9 has expanded your awareness
- # mountains are again merely mountains
- #
- require_relative "meta_guru"
- require_relative "meta_student"
- # knowledge = ARGV.shift or abort "#{ $0 } knowledge.rb"
- koan = ARGV[0]
- rev = ARGV[1]
- student = MetaStudent.new "knowledge/koan_0#{koan}_#{rev}"
- module MetaKoans
- #
- # "attribute" must provide getter, setter, and query for "a" to instances of SomeClass
- #
- def koan_1
- SomeClass.attribute
- o = SomeClass.new
- assert{ not o.a? }
- assert{ o.a = 42 }
- assert{ o.a == 42 }
- assert{ o.a? }
- end
- #
- # the name of the getter, setter, and query methods created by "attribute" must match
- # the value of the given parameter
- #
- def koan_2
- SomeClass.attribute "b"
- o = SomeClass.new
- assert{ not o.b? }
- assert{ o.b = 41 }
- assert{ o.b == 41 }
- assert{ o.b? }
- end
- #
- # multiple calls to "attribute" with different parameter values must generate multiple
- # methods whose names correspond to the parameter value, and whose values must must be
- # independent of each other
- #
- # this koan also illustrates how calling "attribute" through SomeClass.class_eval
- # has the same effect as calling "attribute" directly on SomeClass.
- #
- # additionally, by virtue of the fact that "attribute" is available and working on
- # SomeClass, creating an attribute on an object-specific class should automatically
- # work
- #
- def koan_3
- SomeClass.attribute "c"
- o = SomeClass.new
- assert{ not o.c? }
- assert{ o.c = 39 }
- assert{ o.c == 39 }
- assert{ o.c? }
- SomeClass.class_eval { attribute "d" }
- assert{ not o.d? }
- assert{ o.d = 38 }
- assert{ o.d == 38 }
- assert{ o.d? }
- class << o
- attribute "e"
- end
- assert{ not o.e? }
- assert{ o.e = 37 }
- assert{ o.e == 37 }
- assert{ o.e? }
- end
- #
- # "attribute" must be callable from the singleton class of SomeClass, and must
- # generate corresponding getter, setter, and query methods on the class itself
- # in addition to instances of it.
- #
- def koan_4
- SomeClass.class_eval do
- class << self
- self.attribute "f"
- attribute "g"
- end
- end
- assert{ not SomeClass.f? }
- assert{ SomeClass.f = 36 }
- assert{ SomeClass.f == 36 }
- assert{ SomeClass.f? }
- assert{ not SomeClass.g? }
- assert{ SomeClass.g = 35 }
- assert{ SomeClass.g == 35 }
- assert{ SomeClass.g? }
- end
- #
- # "attribute" must provide a method for providing a default value as hash
- #
- def koan_5
- SomeClass.class_eval do
- attribute "h" => 34
- attribute "i" => 33
- end
- o = SomeClass.new
- assert{ o.h == 34 }
- assert{ o.h? }
- assert{ (o.h = nil).nil? }
- assert{ o.h == nil }
- assert{ not o.h? }
- assert{ o.i == 33 }
- assert{ o.i? }
- assert{ (o.i = nil).nil? }
- assert{ o.i == nil }
- assert{ not o.i? }
- end
- #
- # "attribute" must provide a method for providing a default value as block
- # which is evaluated at instance level
- #
- # instance_eval is called to define an attribute in order to illustrate the
- # difference between it and class_eval. this code should not affect your
- # mastery of this koan
- #
- def koan_6
- SomeClass.class_eval do
- attribute("j"){ thirtytwo }
- attribute("k"){ j - 1 }
- def thirtytwo
- 32
- end
- end
- o = SomeClass.new
- assert{ o.j == 32 }
- assert{ o.j? }
- assert{ (o.j = nil).nil? }
- assert{ o.j == nil }
- assert{ not o.j? }
- o.j = 32
- assert{ o.k == 31 }
- assert{ o.k? }
- assert{ (o.k = nil).nil? }
- assert{ o.k == nil }
- assert{ not o.k? }
- SomeClass.instance_eval do
- class << self
- attribute("l"){ thirtyone }
- end
- def thirtyone
- 31
- end
- end
- assert{ SomeClass.thirtyone == 31 }
- assert{ SomeClass.l == 31 }
- assert{ SomeClass.l? }
- assert{ (SomeClass.l = nil).nil? }
- assert{ SomeClass.l == nil }
- assert{ not SomeClass.l? }
- end
- #
- # "attribute" must be available to any class
- #
- def koan_7
- c = Class.new do
- class << self
- attribute "m"
- end
- attribute "n"
- end
- assert{ not c.m? }
- assert{ c.m = 30 }
- assert{ c.m == 30 }
- assert{ c.m? }
- o = c.new
- assert{ not o.n? }
- assert{ o.n = 29 }
- assert{ o.n == 29 }
- assert{ o.n? }
- end
- #
- # "attribute" must be available to any module
- #
- def koan_8
- m = Module.new do
- attribute "o"
- end
- c = Class.new do
- include m
- extend m
- end
- assert{ not c.o? }
- assert{ c.o = 28 }
- assert{ c.o == 28 }
- assert{ c.o? }
- o = c.new
- assert{ not o.o? }
- assert{ o.o = 27 }
- assert{ o.o == 27 }
- assert{ o.o? }
- end
- #
- # into the void
- #
- def koan_9
- b = Class.new {
- class << self
- attribute "a" => 48
- attribute("b"){ a + 1 }
- end
- include Module.new {
- attribute "a" => 50
- attribute("b"){ a + 1 }
- }
- }
- c = Class.new b
- assert{ c.a == 48 }
- assert{ c.a? }
- assert{ c.a = "forty-two" }
- assert{ c.a == "forty-two" }
- assert{ b.a == 48 }
- c.a = 48
- assert{ c.b == 49 }
- assert{ c.b? }
- assert{ c.b = "forty-two" }
- assert{ c.b == "forty-two" }
- assert{ b.b == 49 }
- o = c.new
- assert{ o.a == 50 }
- assert{ o.a? }
- assert{ o.a = nil; o.a == nil }
- assert{ not o.a? }
- o.a = 50
- assert{ o.b == 51 }
- assert{ o.b? }
- assert{ o.b = nil; o.b == nil }
- assert{ not o.b? }
- end
- def assert()
- bool = yield
- raise StandardError, "assert{ #{ caller.first[%r/^.*(?=:)/] } } #=> #{ bool.inspect }" unless bool
- bool
- end
- end
- MetaGuru.enlighten student
|