Bercerita dengan Code - Toko Kopi dan Pelanggan

Bercerita dengan Code - Toko Kopi dan Pelanggan


Sinopsis

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_kasir

irb(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