meta_koans.rb 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. #
  2. # meta_koans.rb is an arduous set of exercises designed to stretch
  3. # meta-programming muscle. the focus is on a single method "attribute" which
  4. # behaves much like the built-in "attr", but whose properties require delving
  5. # deep into the depths of meta-ruby. usage of the "attribute" method follows
  6. # the general form of
  7. #
  8. # class C
  9. # attribute "a"
  10. # end
  11. #
  12. # o = C.new
  13. # o.a = 42 # setter - sets @a
  14. # o.a # getter - gets @a
  15. # o.a? # query - true if @a
  16. #
  17. # but reaches much farther than the standard "attr" method as you will see
  18. # shortly.
  19. #
  20. # your path, should you choose to follow it, is to write a single file
  21. # "knowledge.rb" implementing all functionality required by the koans below.
  22. # as a student of meta-programming your course will be guided by a guru whose
  23. # wisdom and pithy sayings will assist you on your journey.
  24. #
  25. # a successful student will eventually be able to do this
  26. #
  27. # harp:~ > ruby meta_koans.rb
  28. # koan_1 has expanded your awareness
  29. # koan_2 has expanded your awareness
  30. # koan_3 has expanded your awareness
  31. # koan_4 has expanded your awareness
  32. # koan_5 has expanded your awareness
  33. # koan_6 has expanded your awareness
  34. # koan_7 has expanded your awareness
  35. # koan_8 has expanded your awareness
  36. # koan_9 has expanded your awareness
  37. # mountains are again merely mountains
  38. #
  39. require_relative "meta_guru"
  40. require_relative "meta_student"
  41. # knowledge = ARGV.shift or abort "#{ $0 } knowledge.rb"
  42. koan = ARGV[0]
  43. rev = ARGV[1]
  44. student = MetaStudent.new "knowledge/koan_0#{koan}_#{rev}"
  45. module MetaKoans
  46. #
  47. # "attribute" must provide getter, setter, and query for "a" to instances of SomeClass
  48. #
  49. def koan_1
  50. SomeClass.attribute
  51. o = SomeClass.new
  52. assert{ not o.a? }
  53. assert{ o.a = 42 }
  54. assert{ o.a == 42 }
  55. assert{ o.a? }
  56. end
  57. #
  58. # the name of the getter, setter, and query methods created by "attribute" must match
  59. # the value of the given parameter
  60. #
  61. def koan_2
  62. SomeClass.attribute "b"
  63. o = SomeClass.new
  64. assert{ not o.b? }
  65. assert{ o.b = 41 }
  66. assert{ o.b == 41 }
  67. assert{ o.b? }
  68. end
  69. #
  70. # multiple calls to "attribute" with different parameter values must generate multiple
  71. # methods whose names correspond to the parameter value, and whose values must must be
  72. # independent of each other
  73. #
  74. # this koan also illustrates how calling "attribute" through SomeClass.class_eval
  75. # has the same effect as calling "attribute" directly on SomeClass.
  76. #
  77. # additionally, by virtue of the fact that "attribute" is available and working on
  78. # SomeClass, creating an attribute on an object-specific class should automatically
  79. # work
  80. #
  81. def koan_3
  82. SomeClass.attribute "c"
  83. o = SomeClass.new
  84. assert{ not o.c? }
  85. assert{ o.c = 39 }
  86. assert{ o.c == 39 }
  87. assert{ o.c? }
  88. SomeClass.class_eval { attribute "d" }
  89. assert{ not o.d? }
  90. assert{ o.d = 38 }
  91. assert{ o.d == 38 }
  92. assert{ o.d? }
  93. class << o
  94. attribute "e"
  95. end
  96. assert{ not o.e? }
  97. assert{ o.e = 37 }
  98. assert{ o.e == 37 }
  99. assert{ o.e? }
  100. end
  101. #
  102. # "attribute" must be callable from the singleton class of SomeClass, and must
  103. # generate corresponding getter, setter, and query methods on the class itself
  104. # in addition to instances of it.
  105. #
  106. def koan_4
  107. SomeClass.class_eval do
  108. class << self
  109. self.attribute "f"
  110. attribute "g"
  111. end
  112. end
  113. assert{ not SomeClass.f? }
  114. assert{ SomeClass.f = 36 }
  115. assert{ SomeClass.f == 36 }
  116. assert{ SomeClass.f? }
  117. assert{ not SomeClass.g? }
  118. assert{ SomeClass.g = 35 }
  119. assert{ SomeClass.g == 35 }
  120. assert{ SomeClass.g? }
  121. end
  122. #
  123. # "attribute" must provide a method for providing a default value as hash
  124. #
  125. def koan_5
  126. SomeClass.class_eval do
  127. attribute "h" => 34
  128. attribute "i" => 33
  129. end
  130. o = SomeClass.new
  131. assert{ o.h == 34 }
  132. assert{ o.h? }
  133. assert{ (o.h = nil).nil? }
  134. assert{ o.h == nil }
  135. assert{ not o.h? }
  136. assert{ o.i == 33 }
  137. assert{ o.i? }
  138. assert{ (o.i = nil).nil? }
  139. assert{ o.i == nil }
  140. assert{ not o.i? }
  141. end
  142. #
  143. # "attribute" must provide a method for providing a default value as block
  144. # which is evaluated at instance level
  145. #
  146. # instance_eval is called to define an attribute in order to illustrate the
  147. # difference between it and class_eval. this code should not affect your
  148. # mastery of this koan
  149. #
  150. def koan_6
  151. SomeClass.class_eval do
  152. attribute("j"){ thirtytwo }
  153. attribute("k"){ j - 1 }
  154. def thirtytwo
  155. 32
  156. end
  157. end
  158. o = SomeClass.new
  159. assert{ o.j == 32 }
  160. assert{ o.j? }
  161. assert{ (o.j = nil).nil? }
  162. assert{ o.j == nil }
  163. assert{ not o.j? }
  164. o.j = 32
  165. assert{ o.k == 31 }
  166. assert{ o.k? }
  167. assert{ (o.k = nil).nil? }
  168. assert{ o.k == nil }
  169. assert{ not o.k? }
  170. SomeClass.instance_eval do
  171. class << self
  172. attribute("l"){ thirtyone }
  173. end
  174. def thirtyone
  175. 31
  176. end
  177. end
  178. assert{ SomeClass.thirtyone == 31 }
  179. assert{ SomeClass.l == 31 }
  180. assert{ SomeClass.l? }
  181. assert{ (SomeClass.l = nil).nil? }
  182. assert{ SomeClass.l == nil }
  183. assert{ not SomeClass.l? }
  184. end
  185. #
  186. # "attribute" must be available to any class
  187. #
  188. def koan_7
  189. c = Class.new do
  190. class << self
  191. attribute "m"
  192. end
  193. attribute "n"
  194. end
  195. assert{ not c.m? }
  196. assert{ c.m = 30 }
  197. assert{ c.m == 30 }
  198. assert{ c.m? }
  199. o = c.new
  200. assert{ not o.n? }
  201. assert{ o.n = 29 }
  202. assert{ o.n == 29 }
  203. assert{ o.n? }
  204. end
  205. #
  206. # "attribute" must be available to any module
  207. #
  208. def koan_8
  209. m = Module.new do
  210. attribute "o"
  211. end
  212. c = Class.new do
  213. include m
  214. extend m
  215. end
  216. assert{ not c.o? }
  217. assert{ c.o = 28 }
  218. assert{ c.o == 28 }
  219. assert{ c.o? }
  220. o = c.new
  221. assert{ not o.o? }
  222. assert{ o.o = 27 }
  223. assert{ o.o == 27 }
  224. assert{ o.o? }
  225. end
  226. #
  227. # into the void
  228. #
  229. def koan_9
  230. b = Class.new {
  231. class << self
  232. attribute "a" => 48
  233. attribute("b"){ a + 1 }
  234. end
  235. include Module.new {
  236. attribute "a" => 50
  237. attribute("b"){ a + 1 }
  238. }
  239. }
  240. c = Class.new b
  241. assert{ c.a == 48 }
  242. assert{ c.a? }
  243. assert{ c.a = "forty-two" }
  244. assert{ c.a == "forty-two" }
  245. assert{ b.a == 48 }
  246. c.a = 48
  247. assert{ c.b == 49 }
  248. assert{ c.b? }
  249. assert{ c.b = "forty-two" }
  250. assert{ c.b == "forty-two" }
  251. assert{ b.b == 49 }
  252. o = c.new
  253. assert{ o.a == 50 }
  254. assert{ o.a? }
  255. assert{ o.a = nil; o.a == nil }
  256. assert{ not o.a? }
  257. o.a = 50
  258. assert{ o.b == 51 }
  259. assert{ o.b? }
  260. assert{ o.b = nil; o.b == nil }
  261. assert{ not o.b? }
  262. end
  263. def assert()
  264. bool = yield
  265. raise StandardError, "assert{ #{ caller.first[%r/^.*(?=:)/] } } #=> #{ bool.inspect }" unless bool
  266. bool
  267. end
  268. end
  269. MetaGuru.enlighten student