Ruby punya beberapa fitur syntax yang bikin kode bisa dibaca mendekati bahasa manusia.
Yang pertama adalah keyword arguments (dengan_harga:). Method menyajikan("Kopi Susu", dengan_harga: 18_000) terbaca seperti kalimat deklaratif — kedai menyajikan kopi susu dengan harga 18 ribu. Ruby memperbolehkan named parameters yang berfungsi ganda sebagai dokumentasi dan enforcer kontrak.
Yang kedua adalah method chaining lewat return self. Karena setiap method mengembalikan objek yang sama, kamu bisa menulis kedai.menyajikan(...).menyajikan(...) seperti narasi berurutan tanpa variabel perantara. Pattern yang sama berlaku di PenerimaanPesanan — .pesan(...).pesan(...).selesai mengalir seperti percakapan antara pelanggan dan kasir.
Yang ketiga adalah underscore sebagai separator numerik. 18_000 dan Rp18.000 menyampaikan hal yang sama, tapi Ruby memilih underscore supaya angka besar tetap terbaca tanpa ambiguitas titik desimal.
Yang keempat adalah blocks dan iterator. @menu.each do |minuman, harga| membaca seperti "untuk setiap minuman dan harganya, lakukan ini." Block di Ruby bukan sekadar loop — ia unit narasi yang mendeskripsikan apa yang terjadi pada setiap elemen.
Kalau kamu jalankan file ini di terminal (ruby kedai_kopi.rb), outputnya juga mengalir seperti cerita: kedai buka, pelanggan datang dan memesan, ada satu item yang ditolak karena belum tersedia, lalu kedai tutup kasir dengan rekap pendapatan.
Output ada di akhir cerita! Selamat membaca.
# ☕ Sebuah Pagi di Kedai Kopi Kecil
#
# Ruby punya kemampuan unik: kode bisa dibaca seperti kalimat.
# Method names jadi kata kerja, blocks jadi paragraf,
# dan seluruh program mengalir seperti cerita.
# --- Bab 1: Mengenal Menu ---
class Kedai
attr_reader :nama, :menu
def initialize(nama)
@nama = nama
@menu = {}
@pesanan_hari_ini = []
end
def menyajikan(minuman, dengan_harga:)
@menu[minuman] = dengan_harga
self
end
def buka_hari_ini
puts "═" * 50
puts " #{@nama} buka hari ini!"
puts " Menu tersedia:"
@menu.each do |minuman, harga|
puts " ▸ #{minuman} — Rp#{harga.to_s.reverse.gsub(/(\d{3})(?=\d)/, '\\1.').reverse}"
end
puts "═" * 50
puts
self
end
def terima_pesanan_dari(pelanggan)
PenerimaanPesanan.new(self, pelanggan)
end
def catat(pesanan)
@pesanan_hari_ini << pesanan
end
def tutup_kasir
total = @pesanan_hari_ini.sum(&:total)
puts "\n#{"─" * 50}"
puts " Rekap hari ini di #{@nama}:"
puts " #{@pesanan_hari_ini.size} pesanan dilayani"
puts " Total pendapatan: Rp#{format_rupiah(total)}"
puts "#{"─" * 50}"
end
private
def format_rupiah(angka)
angka.to_s.reverse.gsub(/(\d{3})(?=\d)/, '\\1.').reverse
end
end# --- Bab 2: Pelanggan Datang ---
class Pelanggan
attr_reader :nama
def initialize(nama)
@nama = nama
end
def to_s
@nama
end
end# --- Bab 3: Proses Memesan ---
class PenerimaanPesanan
def initialize(kedai, pelanggan)
@kedai = kedai
@pelanggan = pelanggan
@item = []
end
def pesan(minuman, jumlah: 1)
harga = @kedai.menu[minuman]
unless harga
puts " ✗ Maaf #{@pelanggan}, #{minuman} belum tersedia."
return self
end
@item << { minuman: minuman, jumlah: jumlah, harga: harga }
puts " ✓ #{@pelanggan} memesan #{jumlah}x #{minuman}"
self
end
def selesai
pesanan = Pesanan.new(@pelanggan, @item)
@kedai.catat(pesanan)
puts "\n Pesanan #{@pelanggan} diterima!"
puts " Total: Rp#{pesanan.format_total}"
puts " \"Ditunggu ya, #{@pelanggan}!\" ☕"
puts
pesanan
end
end# --- Bab 4: Pesanan Tercatat ---
class Pesanan
attr_reader :pelanggan, :item
def initialize(pelanggan, item)
@pelanggan = pelanggan
@item = item
@waktu = Time.now
end
def total
@item.sum { |i| i[:harga] * i[:jumlah] }
end
def format_total
total.to_s.reverse.gsub(/(\d{3})(?=\d)/, '\\1.').reverse
end
end# ┌──────────────────────────────────────────────┐
# │ Cerita dimulai di sini... |
# └──────────────────────────────────────────────┘
# Pagi itu, sebuah kedai kopi kecil membuka pintunya.
kedai = Kedai.new("Kedai Pagi Tenang")
kedai
.menyajikan("Kopi Susu", dengan_harga: 18_000)
.menyajikan("Americano", dengan_harga: 22_000)
.menyajikan("Matcha Latte", dengan_harga: 25_000)
.menyajikan("Teh Tarik", dengan_harga: 15_000)
.menyajikan("Roti Bakar Coklat", dengan_harga: 12_000)
kedai.buka_hari_ini
# Pelanggan pertama datang — seorang programmer yang butuh kafein.
andi = Pelanggan.new("Andi")
kedai.terima_pesanan_dari(andi)
.pesan("Americano", jumlah: 2)
.pesan("Roti Bakar Coklat")
.selesai
# Tak lama kemudian, datang Maya bersama temannya.
maya = Pelanggan.new("Maya")
kedai.terima_pesanan_dari(maya)
.pesan("Matcha Latte")
.pesan("Kopi Susu")
.pesan("Es Kelapa") # belum ada di menu
.pesan("Teh Tarik", jumlah: 2)
.selesai
# Sore menjelang, kedai bersiap tutup.
kedai.tutup_kasirirb(main):170>
══════════════════════════════════════════════════
Kedai Pagi Tenang buka hari ini!
Menu tersedia:
▸ Kopi Susu — Rp18.000
▸ Americano — Rp22.000
▸ Matcha Latte — Rp25.000
▸ Teh Tarik — Rp15.000
▸ Roti Bakar Coklat — Rp12.000
══════════════════════════════════════════════════
✓ Andi memesan 2x Americano
✓ Andi memesan 1x Roti Bakar Coklat
Pesanan Andi diterima!
Total: Rp56.000
"Ditunggu ya, Andi!" ☕
✓ Maya memesan 1x Matcha Latte
✓ Maya memesan 1x Kopi Susu
✗ Maaf Maya, Es Kelapa belum tersedia.
✓ Maya memesan 2x Teh Tarik
Pesanan Maya diterima!
Total: Rp73.000
"Ditunggu ya, Maya!" ☕
──────────────────────────────────────────────────
Rekap hari ini di Kedai Pagi Tenang:
2 pesanan dilayani
Total pendapatan: Rp129.000
──────────────────────────────────────────────────
=> nil