8/7/2019 Designing Rub Yap Is
1/141
Designing BeautifulRuby APIs
[email protected]/4/25 & 6/26
RubyConf Taiwan & RubyConf China
V.2
mailto:[email protected]:[email protected]8/7/2019 Designing Rub Yap Is
2/141
About Me
a.k.a. ihower http://ihower.tw
http://twitter.com/ihower Rails Developer since 2006 The Organizer of Ruby Taiwan Community http://ruby.tw http://rubyconf.tw
http://rubyconf.tw/http://ruby.tw/http://twitter.com/ihowerhttp://ihower.tw/bloghttp://rubyconf.tw/http://rubyconf.tw/http://ruby.tw/http://ruby.tw/http://twitter.com/ihowerhttp://twitter.com/ihowerhttp://ihower.tw/bloghttp://ihower.tw/blog8/7/2019 Designing Rub Yap Is
3/141
Ruby Taiwan
8/7/2019 Designing Rub Yap Is
4/141
RubyConf Taiwan 2010
8/7/2019 Designing Rub Yap Is
5/141
8/7/2019 Designing Rub Yap Is
6/141
Define beautiful
Readable: easy to understand Efficient: easy to write
Flexible: easy to extend
8/7/2019 Designing Rub Yap Is
7/141
1.Argument Processing
8/7/2019 Designing Rub Yap Is
8/141
Pseudo-Keyword
Argumentsdef blah(options)
puts options[:foo] puts options[:bar]end
blah(:foo => "test", :bar => "test")
8/7/2019 Designing Rub Yap Is
9/141
Treating Arguments as
an Arradef sum(*args) puts args[0] puts args[1] puts args[2] puts args[3]end
sum(1,2,3)
# 1# 2# 3# nil
8/7/2019 Designing Rub Yap Is
10/141
# USAGE-1 without block 'posts'%>
# USAGE-2 with block 'posts'do %>
Posts list
Rails helper usage
example
8/7/2019 Designing Rub Yap Is
11/141
# Rails3's source code
def link_to(*args, &block) ifblock_given?
options = args.first || {}html_options = args.second
link_to(capture(&block), options, html_options) else
name = args[0]options = args[1] || {}
html_options = args[2]
html_options = convert_options_to_data_attributes(options, html_options)url = url_for(options)
if html_options
html_options = html_options.stringify_keyshref = html_options['href']
tag_options = tag_options(html_options) else
tag_options = nil end
href_attr = "href=\"#{url}\""unless href
"#{ERB::Util.h(name || url)}".html_safe end
end
8/7/2019 Designing Rub Yap Is
12/141
ActiveSupport#extract_options!extract hash from *args
def foobar(*args)options = args.extract_options!
end
foobar(1, 2)# options is {}
foobar(1, 2, :a => :b)# options is { :a => :b }
8/7/2019 Designing Rub Yap Is
13/141
2.Code Blocks
8/7/2019 Designing Rub Yap Is
14/141
A trivial example 1def call_block puts"start" yield(" foobar") puts"end"end
call_block do |str| puts" here" puts str puts" here"end
# start# here# foobar# here# end
8/7/2019 Designing Rub Yap Is
15/141
A trivial example 2def call_block(&block) puts"start"block.call("foobar")
puts"end"end
call_block do |str| puts"here" puts str puts"here"end
# start# here# foobar# here# end
8/7/2019 Designing Rub Yap Is
16/141
pre- and Post-processing
usage examplef = File.open("myfile.txt", 'w')f.write("Lorem ipsum dolor sit amet")
f.write("Lorem ipsum dolor sit amet")f.close
# using blockFile.open("myfile.txt", 'w') do |f|
f.write("Lorem ipsum dolor sit amet")f.write("Lorem ipsum dolor sit amet")
end
8/7/2019 Designing Rub Yap Is
17/141
pre- and Post-
processing# without code blockdef send_message(msg)
socket = TCPSocket.new(@ip, @port) # Pre-socket.puts(msg)response = socket.gets
ensuresocket.close # Post-
endend
8/7/2019 Designing Rub Yap Is
18/141
# with code blockdef send_message(msg)
connection do |socket|socket.puts("foobar")socket.gets
endend
def connectionsocket = TCPSocket.new(@ip, @port) # Pre-
yield(socket) ensure
socket.close # Post-
endend
8/7/2019 Designing Rub Yap Is
19/141
Dynamic CallbacksSinatra usage example
get '/posts'do #.. show something .. end
post '/posts'do #.. create something .. end
put '/posts/:id'do #.. update something .. end
delete '/posts/:id'do #.. annihilate something .. end
8/7/2019 Designing Rub Yap Is
20/141
Dynamic Callbacksserver = Server.new
server.handle(/hello/) do puts"Hello at #{Time.now}"end
server.handle(/goodbye/) do puts"goodbye at #{Time.now}"end
server.execute("/hello")# Hello at Wed Apr 21 17:33:31 +0800 2010server.execute("/goodbye")# goodbye at Wed Apr 21 17:33:42 +0800 2010
8/7/2019 Designing Rub Yap Is
21/141
class Serverdef initialize
@handlers = {} enddef handle(pattern, &block)
@handlers[pattern] = block end
def execute(url) @handlers.each do |pattern, block| if match = url.match(pattern)
block.call break end end endend
8/7/2019 Designing Rub Yap Is
22/141
Self Yieldgemspec example
# using blockGem::Specification.new do |s|s.name = "foobar"
s.version = "1.1.1" #...end
spec = Gem::Specification.newspec.name = "foobar"spec.version = "1.1.1"
8/7/2019 Designing Rub Yap Is
23/141
class Gem::Specification def initialize name = nil, version = nil
# ... yieldselfifblock_given? # ... endend
Self Yieldgemspec example
8/7/2019 Designing Rub Yap Is
24/141
3.Module
8/7/2019 Designing Rub Yap Is
25/141
A trivial examplemodule Mixin def foo puts"foo" end
end
class A include Mixinend
A.new.foo # foo
8/7/2019 Designing Rub Yap Is
26/141
obj.extend(Mod)Implementing class behavior
module Mixin def foo
puts"foo" endend
A.extend(Mixin)
A.foo # class method
class A extend Mixinend
the same as
class
8/7/2019 Designing Rub Yap Is
27/141
obj.extend(Mod)Implementing per-object behavior
module Mixin def foo puts"foo"
endend
a = "test"a.extend(Mixin)a.foo # foo
b = "demo"b.foo # NoMethodError
class
8/7/2019 Designing Rub Yap Is
28/141
8/7/2019 Designing Rub Yap Is
29/141
8/7/2019 Designing Rub Yap Is
30/141
module Mixin
# self.included is a hook method defself.included(base)
base.extend(ClassMethods) enddef foo
puts"foo" end
module ClassMethods
def bar puts"bar" end endend
class MyClass include Mixinend
mod le Mi in
8/7/2019 Designing Rub Yap Is
31/141
module Mixindefself.included(base)base.extend(ClassMethods)base.send(:include, InstanceMethods)
endmodule InstanceMethods
def foo puts"foo" end endmodule ClassMethods
def bar puts"bar"
end endend
class MyClass include Mixin
end
8/7/2019 Designing Rub Yap Is
32/141
4.method_missing?
8/7/2019 Designing Rub Yap Is
33/141
class Person < ActiveRecord::Baseend
p1 = Person.find_by_name("ihower")p2 = Person.find_by_email("[email protected]")
ActiveRecord example
8/7/2019 Designing Rub Yap Is
34/141
8/7/2019 Designing Rub Yap Is
35/141
class Car def go(place) puts"go to #{place}"
enddef method_missing(name, *args)
if name.to_s =~ /^go_to_(.*)/go($1)
else
super end endend
car = Car.new
car.go_to_taipei# go to taipei
car.blah # NoMethodError: undefined method `blah`
8/7/2019 Designing Rub Yap Is
36/141
Dont abuse method
missing method_missing? is slow only use it when you can not define
method in advance
Meta-programming can define methodsdynamically
8/7/2019 Designing Rub Yap Is
37/141
8/7/2019 Designing Rub Yap Is
38/141
8/7/2019 Designing Rub Yap Is
39/141
8/7/2019 Designing Rub Yap Is
40/141
5.const_missing
8/7/2019 Designing Rub Yap Is
41/141
8/7/2019 Designing Rub Yap Is
42/141
classModuleoriginal_c_m = instance_method(:const_missing)
define_method(:const_missing) do |name|
if name.to_s =~ /^U([0-9a-fA-F]{4})$/
[$1.to_i(16)].pack("U*") else
original_c_m.bind(self).call(name) end endend
puts U0123 # puts U9999 #
Global constant
8/7/2019 Designing Rub Yap Is
43/141
Localized constant(you can use super here)
class Color
defself.const_missing(name) if name.to_s =~ /[a-zA-Z]/
const_set(name, new)
else super end endend
Color::RED#Color::GREEN#
8/7/2019 Designing Rub Yap Is
44/141
6.Methods chaining
8/7/2019 Designing Rub Yap Is
45/141
8/7/2019 Designing Rub Yap Is
46/141
8/7/2019 Designing Rub Yap Is
47/141
Object#tapRuby 1.8.7 later
puts"dog".reverse.tap{ |o| puts"reversed: #{o}" } # return original object.upcase
# output
reversed: godGOD
8/7/2019 Designing Rub Yap Is
48/141
8/7/2019 Designing Rub Yap Is
49/141
8/7/2019 Designing Rub Yap Is
50/141
NilClass#try example
person = Person.find_by_email(params[:email])# but we don't know @person exists or not
# Without try@person ? @person.name : nil# With [email protected](:name)
8/7/2019 Designing Rub Yap Is
51/141
classNilClass def try(*args)
nil endend
8/7/2019 Designing Rub Yap Is
52/141
8/7/2019 Designing Rub Yap Is
53/141
classNumeric
KILOBYTE = 1024MEGABYTE = KILOBYTE * 1024
GIGABYTE = MEGABYTE * 1024
def bytes self
end alias:byte:bytes
def kilobytes
self * KILOBYTE end
alias:kilobyte:kilobytes
def megabytes self * MEGABYTE
end alias:megabyte:megabytes
def gigabytes
self * GIGABYTE end
alias:gigabyte:gigabytes
end
8/7/2019 Designing Rub Yap Is
54/141
classObject
8/7/2019 Designing Rub Yap Is
55/141
def blank?
respond_to?(:empty?) ? empty? : !self end
def present?!blank? end
end
classNilClass
def blank? true
endend
classFalseClass
def blank? true
endend
classTrueClass
def blank? false
end
end
8/7/2019 Designing Rub Yap Is
56/141
lf
8/7/2019 Designing Rub Yap Is
57/141
self
current ob ectclass Demo
putsself# Demo
def blah
putsself# object
[1,2,3].each do |i|
putsself# object end
end
class AnotherDemo putsself# Demo::AnotherDemo
endend
8/7/2019 Designing Rub Yap Is
58/141
Everything in Ruby is
ob ect, even class.
8/7/2019 Designing Rub Yap Is
59/141
Ruby Object Model
class Aend
class B < Aend
obj = B.new
obj.class# B
B.superclass # AB.class# Class
obj B
Object
Class
A
class class
super
super
class object is an object
8/7/2019 Designing Rub Yap Is
60/141
class object is an object
of the class Class
obj B
Object
Class
A
class class
super
super
class
class
class
class Aend
class B < Aend
obj = B.new
obj.class# B
B.superclass # AB.class# Class
8/7/2019 Designing Rub Yap Is
61/141
module
obj D
A
MixinB,C
class
super
super
class Aend
module Bend
module Cend
class D < A
include B include Cend
h l ?
8/7/2019 Designing Rub Yap Is
62/141
class A def foo endend
obj1 = A.newobj2 = A.new
whats metaclass?
obj2
Object
A
class
super
obj2 class
metaclass
8/7/2019 Designing Rub Yap Is
63/141
obj2
Object
A
class
super
obj2s
metaclass
obj2 class
super
class A def foo endend
obj1 = A.newobj2 = A.new
def obj2.bar # only obj2 has bar method,# called singleton method
end
# another wayclass
8/7/2019 Designing Rub Yap Is
64/141
AAs
metaclass
super
Class
classB
class
class A # way1
defself.foo end# way2
class
8/7/2019 Designing Rub Yap Is
65/141
classClass def blah puts"all class has blah class method" end
end
class Aend
A.blah # all class has blah class methodString.blah # all class has blah class method
If we define method
inside class Class
th d d fi iti ( t l )
8/7/2019 Designing Rub Yap Is
66/141
method definition(current class)"def" defines instance method for current class
class Demo# the current class is Demo
def foo puts"foo" endend
Demo.new.foo # foo
8/7/2019 Designing Rub Yap Is
67/141
8/7/2019 Designing Rub Yap Is
68/141
8/7/2019 Designing Rub Yap Is
69/141
8/7/2019 Designing Rub Yap Is
70/141
8/7/2019 Designing Rub Yap Is
71/141
Meta-programmingHow to write code to write code?
8/7/2019 Designing Rub Yap Is
72/141
Two types of meta-
programming Code Generation (Not talk about it today)
eg. Rails scaffold Reflection
eg. Class Macro (talk later)
8/7/2019 Designing Rub Yap Is
73/141
Specifically, How to write amethod to define method?
class Demo
8/7/2019 Designing Rub Yap Is
74/141
class Demo
# the current class is Demo
def define_blah1 # the current class is Demo
def blah1 puts"blah1"
end end
defself.define_blah2
# the current class is Demo (the same as above)
def blah2puts"blah2"
end
end
end
Demo.new.blah1 # NoMethodError: undefined method `blah1'Demo.new.define_blah1
Demo.new.blah1 # blah1
Demo.new.blah2 # NoMethodError: undefined method `blah2'Demo.define_blah2
Demo.new.blah2 #blah2
8/7/2019 Designing Rub Yap Is
75/141
class Demo
8/7/2019 Designing Rub Yap Is
76/141
def define_blah1
# self is Demo's instance defself.blah1
puts"blah1"# define singleton method end
end
defself.define_blah2 #self is Demo
defself.blah2 puts"blah2"# define singleton method (class method)
end end
end
a = Demo.new
a.define_blah1
a.blah1 # blah1Demo.new.blah1 # NoMethodError: undefined method `blah1'
Demo.new.blah2 # NoMethodError: undefined method `blah2'Demo.define_blah2
Demo.blah2 #blah2
8/7/2019 Designing Rub Yap Is
77/141
Not useful really, we
need more dynamicpower
8/7/2019 Designing Rub Yap Is
78/141
The key of power isvariable sco e!!
8/7/2019 Designing Rub Yap Is
79/141
module MyDemovar = 1
class Demovar = 2
def foovar = 3
end
end
end
{ {{Variable scope
8/7/2019 Designing Rub Yap Is
80/141
block variable scope
var1 = "foo"
[1,2,3].each do |i| puts var
var1 = "bar"var2 = "baz"
end
puts var1 # foo
puts var2 # NameError: undefined local variable or method
8/7/2019 Designing Rub Yap Is
81/141
8/7/2019 Designing Rub Yap Is
82/141
8/7/2019 Designing Rub Yap Is
83/141
classClass
def define_more_methods # define instance methods
["aaa", "bbb", "ccc"].each do |name|define_method(name) do
puts name.upcase end
end
# define class methods
class
8/7/2019 Designing Rub Yap Is
84/141
odu e
module ClassMethods def define_more_methods
# define instance methods["aaa", "bbb", "ccc"].each do |name|
define_method(name) doputs name.upcase
end end
# define class methods
class
8/7/2019 Designing Rub Yap Is
85/141
So we maybe need those methods
8/7/2019 Designing Rub Yap Is
86/141
So we maybe need those methodsinside define_method:
(because the scope is not changed)
Object#instance_variable_get
Object#instance_variable_set Object#remove_instance_variable
Module#class_variable_get
Module#class_variable_set Module#remove_class_variable
8/7/2019 Designing Rub Yap Is
87/141
Not dynamic enough?Because class
8/7/2019 Designing Rub Yap Is
88/141
we need even moredynamic power
8/7/2019 Designing Rub Yap Is
89/141
8/7/2019 Designing Rub Yap Is
90/141
8/7/2019 Designing Rub Yap Is
91/141
But how to define
singleton method usingclass_eval and
define_method?
8/7/2019 Designing Rub Yap Is
92/141
8/7/2019 Designing Rub Yap Is
93/141
8/7/2019 Designing Rub Yap Is
94/141
instance eval for any
8/7/2019 Designing Rub Yap Is
95/141
instance_eval for any
ob ectobj = "blah"obj.instance_eval do putsself# obj
# the current class is obj's metaclass
def foo puts"foo" endend
obj.foo # singleton method
h b l b ?
8/7/2019 Designing Rub Yap Is
96/141
how about class object?
String.instance_eval do putsself# String# the current class is String's metaclass
def foo puts"bar" endendString.foo # singleton method (class method)
8/7/2019 Designing Rub Yap Is
97/141
8/7/2019 Designing Rub Yap Is
98/141
8. Class Macro(Rubys declarative style)
A i R d l
8/7/2019 Designing Rub Yap Is
99/141
ActiveRecord example
class User < ActiveRecord::Basevalidates_presence_of :login
validates_length_of :login, :within => 3..40validates_presence_of :email
belongs_to :grouphas_many :posts
end
Class Bodies Arent
8/7/2019 Designing Rub Yap Is
100/141
Class Bodies Aren t
S ecialclass Demoa = 1
puts a
defself.say puts"blah" endsay # you can execute class method in class body
end
# 1# blah
Memorize example
8/7/2019 Designing Rub Yap Is
101/141
Memorize example
class Account
def calculate @calculate ||= begin sleep10# expensive calculation 5
end endend
a = Account.newa.caculate # need waiting 10s to get 5a.caculate # 5a.caculate # 5a.caculate # 5
memoize method
8/7/2019 Designing Rub Yap Is
102/141
memoize methodclass Accountdef calculate
sleep2# expensive calculation 5
end
memoize :calculateend
a = Account.newa.calculate # need waiting 10s to get 5a.calculate # 5
8/7/2019 Designing Rub Yap Is
103/141
8/7/2019 Designing Rub Yap Is
104/141
8/7/2019 Designing Rub Yap Is
105/141
8/7/2019 Designing Rub Yap Is
106/141
9.instance_evalDSL calls it create implicit context
R k l
8/7/2019 Designing Rub Yap Is
107/141
Rack example
Rack::Builder.new douse Some::Middleware, param
use Some::Other::Middlewarerun Application end
How is instance eval
8/7/2019 Designing Rub Yap Is
108/141
How is instance_eval
doing? It changes the self (current object) to
caller Any object can call instance_eval (unlike
class_eval)
t i i l l
8/7/2019 Designing Rub Yap Is
109/141
a trivial exampleclass Demo def initialize @a = 99 endend
foo = Demo.new
foo.instance_eval doputs self # foo instance
puts@a# 99
end
8/7/2019 Designing Rub Yap Is
110/141
8/7/2019 Designing Rub Yap Is
111/141
8/7/2019 Designing Rub Yap Is
112/141
10.Class.new
8/7/2019 Designing Rub Yap Is
113/141
8/7/2019 Designing Rub Yap Is
114/141
8/7/2019 Designing Rub Yap Is
115/141
8/7/2019 Designing Rub Yap Is
116/141
8/7/2019 Designing Rub Yap Is
117/141
Parameterized
8/7/2019 Designing Rub Yap Is
118/141
subclassing exampledef Person(name) if name == "ihower" Class.new do def message puts"good"
end end else Class.new do def message
puts"bad" end end endend
8/7/2019 Designing Rub Yap Is
119/141
8/7/2019 Designing Rub Yap Is
120/141
Conclusion
8/7/2019 Designing Rub Yap Is
121/141
Story 1:
DSL or NoDSL by Jos Valim
at Euruko 2010http://blog.plataformatec.com.br/2010/06/dsl-or-nodsl-at-euruko-2010/
a DSL
http://blog.plataformatec.com.br/2010/06/dsl-or-nodsl-at-euruko-2010/http://blog.plataformatec.com.br/2010/06/dsl-or-nodsl-at-euruko-2010/8/7/2019 Designing Rub Yap Is
122/141
class ContactForm < MailForm::Baseto "[email protected]"from "contact_form@app_name.com"subject "Contact form"
attributes :name, :email, :messageend
ContactForm.new(params[:contact_form]).deliver
a DSL
8/7/2019 Designing Rub Yap Is
123/141
8/7/2019 Designing Rub Yap Is
124/141
Other examples
8/7/2019 Designing Rub Yap Is
125/141
Other examples
Rake v.s. Thor
RSpec v.s. Unit::test
8/7/2019 Designing Rub Yap Is
126/141
8/7/2019 Designing Rub Yap Is
127/141
8/7/2019 Designing Rub Yap Is
128/141
8/7/2019 Designing Rub Yap Is
129/141
Story 2:
Rails 2 to 3API changes
Routesnice DSL
8/7/2019 Designing Rub Yap Is
130/141
nice DSL
# Rails 2
map.resources :people, :member => { :dashboard => :get, :resend => :post, :upload => :put } do |people|
people.resource :avatraend
# Rails 3resources :peopledo
resource :avatarmember do
get :dashboard
post :resendput :upload
endend
AR queries (1)
8/7/2019 Designing Rub Yap Is
131/141
AR queries (1)method chaining
# Rails 2users = User.find(:all, :conditions => { :name =>'ihower' }, :limit => 10, :order => 'age')
# Rails 3users = User.where(:name => 'ihower').limit(20).order('age')
8/7/2019 Designing Rub Yap Is
132/141
AR queries (2)
8/7/2019 Designing Rub Yap Is
133/141
# Rails 3users = User
users = users.some_scope if params[:some]users = users.where( :name => params[:name] ) if params[:name]
users = users.where( :age => params[:age] ) if params[:age]users = users.order( params[:sort] || "id" )
q ( )Unify finders, named_scope, with_scope to Relation
AR queries (3)
8/7/2019 Designing Rub Yap Is
134/141
# Rails 3class Product < ActiveRecord::Base
scope :discontinued, where(:discontinued => true)scope :cheaper_than, lambda { |price| where("price < ?", price) }
end
# Rails 3, prefer this way more
class Product < ActiveRecord::Base
scope :discontinued, where(:discontinued => true)
defself.cheaper_than(price)
where("price < ?", price)end
end
Using class methods instead of scopes when you need lambda
8/7/2019 Designing Rub Yap Is
135/141
AR validation (2)
8/7/2019 Designing Rub Yap Is
136/141
( )custom validator
# Rails 3
class User < ActiveRecord::Basevalidates :email, :presence => true,
:uniqueness => true,:email_format => true
end
class EmailFormatValidator < ActiveModel::EachValidatordef validate_each(object, attribute, value)
unless value =~ /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/iobject.errors[attribute]
8/7/2019 Designing Rub Yap Is
137/141
ActionMailer# Rails 2class UserMailer < ActionMailer::Base
def signup(user)recipients user.emailfrom '[email protected]'
body :name => user.namesubject "Signup"
end
end
UserMailer.deliver_registration_confirmation(@user)
ActionMailer
8/7/2019 Designing Rub Yap Is
138/141
ActionMailer# Rails 3class UserMailer < ActionMailer::Base
default :from => "[email protected]"
def signup(user) @name = user.namemail(:to => user.email, :subject => "Signup" )
end
end
UserMailer.registration_confirmation(@user).deliver
8/7/2019 Designing Rub Yap Is
139/141
I think its a Ruby APIsparadigm shift
Apparently, Rails is the most successful Ruby open source codebasewhich you can learn from.
References
http://yehudakatz.com/2009/11/15/metaprogramming-in-ruby-its-all-about-the-self/8/7/2019 Designing Rub Yap Is
140/141
References
Ruby Best Practices, OReilly The Ruby Object Model and Metaprogrammin, Pragmatic Programming Ruby 1.9, Pragmatic
Metaprogramming Ruby, Pragmatic Advanced Rails, OReilly The Building Blocks of Ruby
http://yehudakatz.com/2010/02/07/the-building-blocks-of-ruby/
The Importance of Executable Class Bodieshttp://yehudakatz.com/2009/06/04/the-importance-of-executable-class-bodies/
Metaprogramming in Ruby: Its All About the Selfhttp://yehudakatz.com/2009/11/15/metaprogramming-in-ruby-its-all-about-the-self/
Three implicit contexts in Rubyhttp://yugui.jp/articles/846
http://yehudakatz.com/2009/11/15/metaprogramming-in-ruby-its-all-about-the-self/http://yehudakatz.com/2009/11/15/metaprogramming-in-ruby-its-all-about-the-self/http://yehudakatz.com/2010/02/07/the-building-blocks-of-ruby/http://yugui.jp/articles/846http://yugui.jp/articles/846http://yehudakatz.com/2009/11/15/metaprogramming-in-ruby-its-all-about-the-self/http://yehudakatz.com/2009/11/15/metaprogramming-in-ruby-its-all-about-the-self/http://yehudakatz.com/2010/02/07/the-building-blocks-of-ruby/http://yehudakatz.com/2010/02/07/the-building-blocks-of-ruby/8/7/2019 Designing Rub Yap Is
141/141