Episode ini membahas metaprogramming Ruby: send, define_method, method_missing dan respond_to_missing?, instance_eval serta class_eval untuk membangun DSL, lalu internals seperti singleton class, method lookup chain, dan hooks untuk plugin architecture.

Sebagian besar kekuatan Ruby berasal dari dinamismenya: program bisa menulis program. Metaprogramming adalah teknik membuat method, mengubah objek, dan mendefinisikan perilaku saat runtime. Episode 19 menjelaskan teknik-teknik ini dan internals yang mendasarinya.
Setelah episode 18, kalian bisa mengetik. Sekarang kita belajar fleksibilitas yang membuat framework seperti Rails dan Sinatra begitu ekspresif — dari send dan define_method hingga DSL yang memetakan konfigurasi jadi kode.
send memanggil method secara dinamis — nama method bisa berupa string atau simbol yang baru diketahui saat runtime. define_method membuat method baru pada kelas:
class Klien
def panggil(nama, *args)
send(nama, *args)
end
def self.buat_getter(attr)
define_method(attr) { instance_variable_get("@#{attr}") }
end
endsend(nama, *args) memanggil method bernama nama dengan argumen apa pun. define_method(attr) membuat getter otomatis untuk setiap atribut — pola yang dipakai untuk menghindari kode berulang. Hati-hati: jangan memanggil send dengan nama method dari input pengguna tanpa whitelist.
Saat method tidak ditemukan, Ruby memanggil method_missing. Ini dasar dari banyak DSL — objek bisa menangkap panggilan yang tidak dikenal:
class Pengaturan
def method_missing(nama, *args)
if nama.to_s.end_with?("=")
instance_variable_set("@#{nama.to_s.chop}", args.first)
else
super
end
end
def respond_to_missing?(nama, include_private = false)
nama.to_s.end_with?("=") || super
end
enddef method_missing(nama, *args) menangkap pemanggilan seperti pengaturan.port = 3000. respond_to_missing? wajib di-override agar respond_to? juga mengakui method dinamis ini — tanpa itu, protocol objek seperti serialisasi bisa salah.
instance_eval mengevaluasi block dengan self objek itu sendiri, sehingga method dan variabel instance terbuka langsung. Inilah cara framework membuat konfigurasi seperti config.port = 3000 terasa seperti DSL:
class Server
def initialize(&block)
instance_eval(&block)
end
def port(value)
@port = value
end
def workers(value)
@workers = value
end
end
server = Server.new do
port 3000
workers 4
endServer.new do ... end mengeksekusi block dalam konteks instance, sehingga port 3000 menjadi pemanggilan method, bukan assignment. class_eval bekerja serupa untuk level kelas. DSL seperti ini membuat API kalian ekspresif dan dekat dengan bahasa manusia.
Setiap objek Ruby memiliki singleton class — kelas tak terlihat yang menyimpan method khusus objek tersebut. Method yang didefinisikan dengan def self. atau def obj.method tinggal di sini:
server = Server.new { port 8080 }
def server.ping
"pong"
end
puts server.singleton_class.ancestors.firstdef server.ping menambahkan method hanya ke satu objek. server.singleton_class mengakses kelas tersembunyi tersebut. Pahami singleton class karena ia ikut menentukan cara Ruby mencari method.
Ketika kalian memanggil method, Ruby menelusuri rantai: singleton class objek, lalu kelasnya, modul yang di-include, kelas induk, dan seterusnya hingga BasicObject. Urutan inilah yang menentukan method mana yang menang:
puts Server.ancestors
puts server.singleton_class.ancestorsServer.ancestors mencetak rantai kelas dan modul dari atas ke bawah. prepend memasukkan modul di posisi terdepan sehingga method-nya menang atas kelas. Memahami urutan ini adalah kunci memprediksi perilaku di kode yang banyak memakai mixin.
Hooks adalah method yang dipanggil Ruby saat kejadian tertentu terjadi — misalnya ketika modul di-include (included) atau ketika method baru ditambahkan (method_added). Kombinasi hooks dan metaprogramming membangun plugin architecture:
module Audit
def self.included(base)
base.extend(ClassMethods)
puts "#{base} memakai plugin Audit"
end
module ClassMethods
def catat_aksi
puts "aksi dicatat"
end
end
end
class Transaksi
include Audit
endmodule Audit dipanggil included ketika dimasukkan ke kelas; base.extend(ClassMethods) membuat method modul menjadi class method. Plugin yang ditulis dengan pola ini bisa menambahkan perilaku, memvalidasi, atau mencatat aktivitas tanpa mengubah kelas target secara manual.
Warning
Metaprogramming adalah pisau bermata dua. Gunakan untuk membangun API yang ekspresif, tetapi jaga agar kode tetap mudah dibaca dan di-debug. Lebih sedikit keajaiban lebih baik, kecuali keajaiban itu benar-benar menyederhanakan pemakaian.
Inti yang harus dibawa pulang:
send memanggil method dengan nama dinamis saat runtime.define_method menciptakan method baru secara programatik.method_missing menangkap panggilan yang tidak dikenal dan wajib berpasangan dengan respond_to_missing?.instance_eval membangun DSL dengan mengeksekusi block dalam konteks objek.Server.ancestors menunjukkan urutan method lookup yang menentukan pemenang.included dan method_added menjadi fondasi plugin architecture.Di episode 20 kita menjaga kualitas: testing dengan Minitest dan RSpec lengkap dengan mocks, stubs, dan fixtures, alur kerja TDD, lalu RuboCop untuk gaya kode dan SimpleCov untuk coverage. Setelah bisa menulis kode ajaib, kita belajar menuliskannya dengan disiplin.