Proyek Mix

Struktur proyek, dependensi, dan pengenalan umbrella app.

Daftar isi

Mix adalah alat build bawaan Elixir yang mengurus pembuatan proyek, kompilasi, pengujian, dan pengelolaan dependensi. Topik ini membahas pembuatan proyek dengan mix new, struktur berkas yang dihasilkan, penambahan dependensi, serta pengenalan umbrella app, yaitu satu proyek yang menampung beberapa aplikasi Elixir sekaligus. Setelah topik ini diselesaikan, struktur dasar sebuah proyek Elixir dan peran tiap berkasnya dapat dikenali dengan pasti.

Membuat proyek baru#

Perintah mix new membuat proyek Elixir baru lengkap dengan kerangka berkas yang langsung dapat dikompilasi dan diuji. Nama aplikasi diambil dari nama folder yang diberikan sebagai argumen, sehingga folder kalkulator menghasilkan aplikasi bernama :kalkulator. Contoh berikut membuat proyek tersebut di dalam folder /tmp agar folder kerja utama tetap bersih.

bash
# membuat proyek contoh di /tmp agar folder kerja tetap bersih
cd /tmp && rm -rf kalkulator
mix new kalkulator
text Keluaran
* creating README.md
* creating .formatter.exs
* creating .gitignore
* creating mix.exs
* creating lib
* creating lib/kalkulator.ex
* creating test
* creating test/test_helper.exs
* creating test/kalkulator_test.exs

Your Mix project was created successfully.
You can use "mix" to compile it, test it, and more:

    cd kalkulator
    mix test

Run "mix help" for more commands.

Keluaran di atas merinci setiap berkas yang dibuat, lalu berakhir dengan petunjuk langkah berikutnya. Dua berkas yang paling sering disentuh adalah mix.exs sebagai resep proyek dan lib/kalkulator.ex sebagai modul pertama proyek. Folder test berisi berkas uji pendamping yang akan dibahas pada bagian menjalankan tes.

Nama proyek dapat disesuaikan saat pembuatan. Opsi --app menimpa nama aplikasi yang diambil dari nama folder, opsi --module menimpa nama modul yang dihasilkan, opsi --sup menambahkan pohon supervision lengkap, dan opsi --umbrella membuat proyek penampung yang dibahas pada bagian umbrella. Tanpa opsi apa pun, kerangka yang dihasilkan sudah memuat satu tes yang dapat langsung dijalankan.

Struktur proyek#

Struktur proyek Mix mengikuti konvensi yang sama pada hampir semua proyek Elixir, sehingga pengembang dapat berpindah proyek tanpa mencari-cari letak kode. Tabel berikut merangkum peran tiap berkas dan folder yang dihasilkan mix new.

Berkas atau folderPeran
mix.exsResep proyek, berisi konfigurasi dan daftar dependensi
lib/kalkulator.exModul Kalkulator, kode aplikasi pertama
lib/Seluruh kode aplikasi
test/kalkulator_test.exsBerkas uji untuk modul Kalkulator
test/test_helper.exsMenyalakan ExUnit sebelum tes dijalankan
.formatter.exsKonfigurasi mix format
.gitignoreDaftar berkas yang diabaikan oleh git
README.mdDeskripsi singkat proyek

Konvensi penamaan modul mengikuti nama berkasnya, dengan awal setiap kata ditulis huruf kapital (CamelCase). Berkas lib/kalkulator.ex mendefinisikan modul Kalkulator, sedangkan berkas lib/kalkulator/hitung.ex mendefinisikan modul Kalkulator.Hitung karena berkasnya berada satu tingkat lebih dalam. Aturan yang sama berlaku pada folder test, yaitu test/kalkulator_test.exs mendefinisikan modul KalkulatorTest. Dengan demikian, struktur folder di bawah lib langsung terbaca sebagai struktur modulnya.

bash
# menampilkan isi proyek yang baru dibuat
ls -A /tmp/kalkulator
ls /tmp/kalkulator/lib /tmp/kalkulator/test
text Keluaran
.formatter.exs
.gitignore
README.md
lib
mix.exs
test
/tmp/kalkulator/lib:
kalkulator.ex

/tmp/kalkulator/test:
kalkulator_test.exs
test_helper.exs

Menjalankan tes pertama#

Proyek baru sudah memuat satu berkas uji yang siap dijalankan dengan perintah mix test. Perintah ini mengompilasi aplikasi, menyiapkan lingkungan test, lalu menjalankan seluruh tes melalui ExUnit. Berkas uji bawaan memuat satu doctest dari lib/kalkulator.ex dan satu tes yang memeriksa fungsi hello/0, sehingga jumlah tesnya dua.

bash
# menjalankan tes bawaan; baris waktu dan seed dibuang agar keluaran stabil untuk direkam
cd /tmp/kalkulator && mix test 2>&1 | sed '/^Finished in/d; /^Running ExUnit with seed/d'
text Keluaran
Compiling 1 file (.ex)
Generated kalkulator app

..

Result: 2 passed (1 doctest, 1 test)

Pada eksekusi pertama, Mix mengompilasi aplikasi sehingga baris Compiling 1 file (.ex) dan Generated kalkulator app muncul. Baris Result: 2 passed (1 doctest, 1 test) menyatakan dua pengujian lulus, yaitu satu doctest dan satu tes biasa. Penyaring sed pada perintah di atas membuang dua baris yang nilainya berubah pada setiap eksekusi, yaitu baris Running ExUnit with seed dan baris Finished in. Keluaran yang direkam pun dapat dibandingkan antar eksekusi tanpa perbedaan yang menyesatkan.

Dua berkas di folder test bekerja bersama pada setiap eksekusi. Berkas test/test_helper.exs berisi satu baris ExUnit.start() yang menyalakan kerangka pengujian, sedangkan test/kalkulator_test.exs memuat modul tesnya. Perintah mix test juga menerima argumen berkas untuk menjalankan satu berkas saja, misalnya mix test test/kalkulator_test.exs, atau argumen berkas:baris untuk menjalankan satu tes pada nomor baris tertentu.

Menambahkan dependensi#

Dependensi dinyatakan di dalam fungsi deps/0 pada berkas mix.exs, dalam bentuk tuple yang berisi nama paket dan kendala versi. Paket diunduh dari Hex, yaitu package manager resmi ekosistem Elixir, melalui perintah mix deps.get. Berkas mix.exs berikut sudah dimodifikasi agar memuat dependensi jason, yaitu pustaka penyandi JSON yang umum digunakan.

elixir
# tidak-dijalankan
# berkas mix.exs dijalankan melalui perintah mix, bukan langsung dengan elixir
defmodule Kalkulator.MixProject do
  use Mix.Project

  def project do
    [
      app: :kalkulator,
      version: "0.1.0",
      elixir: "~> 1.20",
      start_permanent: Mix.env() == :prod,
      deps: deps()
    ]
  end

  def application do
    [
      extra_applications: [:logger]
    ]
  end

  defp deps do
    [
      {:jason, "~> 1.4"}
    ]
  end
end

Kendala versi ~> 1.4 berarti versi 1.4.0 dan yang lebih baru sampai sebelum 2.0.0 dapat diterima. Konfigurasi pada project/0 juga menyertakan start_permanent: Mix.env() == :prod, yang menjamin VM ikut berhenti apabila aplikasi berhenti pada lingkungan produksi, misalnya karena supervisor akarnya gagal menyala. Setelah mix.exs disimpan, dependensi diunduh dengan perintah berikut.

bash
# tidak-dijalankan
# mix deps.get mengunduh paket dari Hex sehingga melebihi batas waktu blok
cd /tmp/kalkulator
mix deps.get

Perintah mix deps.get menuliskan versi persis yang terpasang ke berkas mix.lock. Berkas ini harus disimpan bersama kode, karena berkas tersebut menjamin setiap komputer dan lingkungan CI memasang versi dependensi yang sama persis. Tanpa mix.lock, dua mesin dapat memasang versi yang berbeda untuk satu kendala versi yang sama, dan program dapat berperilaku berbeda tanpa penyebab yang jelas.

Umbrella#

Umbrella app adalah satu proyek Mix yang menampung beberapa aplikasi Elixir di dalam folder apps/, dan tiap aplikasi memiliki mix.exs sendiri. Bentuk ini berguna ketika sebuah sistem terbagi atas beberapa bagian yang ingin dikembangkan dan diuji secara terpisah, tetapi tetap dibangun bersama. Contoh berikut membuat proyek umbrella bernama toko yang memisahkan tugas perhitungan pada aplikasi kasir dan pencatatan stok pada aplikasi gudang.

bash
# membuat kerangka umbrella di /tmp
cd /tmp && rm -rf toko
mix new toko --umbrella
text Keluaran
* creating README.md
* creating .formatter.exs
* creating .gitignore
* creating mix.exs
* creating apps
* creating config
* creating config/config.exs

Your umbrella project was created successfully.
Inside your project, you will find an apps/ directory
where you can create and host many apps:

    cd toko
    cd apps
    mix new my_app

Commands like "mix compile" and "mix test" when executed
in the umbrella project root will automatically run
for each application in the apps/ directory.

Aplikasi anggota umbrella dibuat dengan mix new apps/nama --sup agar masing-masing memuat pohon supervision lengkap. Kedua perintah berikut menambahkan aplikasi kasir dan gudang sekaligus.

bash
# menambahkan dua aplikasi ke dalam umbrella
cd /tmp/toko && rm -rf apps
mix new apps/kasir --sup
mix new apps/gudang --sup
text Keluaran
* creating README.md
* creating .formatter.exs
* creating .gitignore
* creating mix.exs
* creating lib
* creating lib/kasir.ex
* creating lib/kasir/application.ex
* creating test
* creating test/test_helper.exs
* creating test/kasir_test.exs

Your Mix project was created successfully.
You can use "mix" to compile it, test it, and more:

    cd apps/kasir
    mix test

Run "mix help" for more commands.
* creating README.md
* creating .formatter.exs
* creating .gitignore
* creating mix.exs
* creating lib
* creating lib/gudang.ex
* creating lib/gudang/application.ex
* creating test
* creating test/test_helper.exs
* creating test/gudang_test.exs

Your Mix project was created successfully.
You can use "mix" to compile it, test it, and more:

    cd apps/gudang
    mix test

Run "mix help" for more commands.
bash
# menampilkan struktur aplikasi di dalam umbrella
ls /tmp/toko/apps
ls -R /tmp/toko/apps/kasir/lib
text Keluaran
gudang
kasir
/tmp/toko/apps/kasir/lib:
kasir
kasir.ex

/tmp/toko/apps/kasir/lib/kasir:
application.ex

Berkas mix.exs pada akar umbrella memuat kunci apps_path: "apps", yaitu penanda bahwa seluruh folder di dalam apps/ adalah aplikasi anggota. Mix menyatukan aplikasi anggota tersebut secara otomatis tanpa perlu didaftarkan satu per satu. Berkas mix.exs aplikasi anggota juga diatur agar menggunakan sumber daya bersama melalui kunci build_path, deps_path, lockfile, dan config_path yang menunjuk ke akar umbrella. Akibatnya, seluruh aplikasi berbagi folder _build dan deps serta berkas mix.lock yang sama, dan config/config.exs berlaku untuk semua anggota. Perintah mix test pada akar umbrella menjalankan tes semua aplikasi sekaligus.

Lingkungan MIX_ENV#

Mix mengatur lingkungan kerja melalui variabel MIX_ENV yang dibaca oleh fungsi Mix.env/0. Lingkungan ini menentukan konfigurasi dan perilaku kompilasi tertentu, misalnya baris start_permanent: Mix.env() == :prod pada mix.exs yang sudah diperkenalkan pada bagian dependensi. Nilai yang tersedia dirangkum pada tabel berikut.

Nilai MIX_ENVKegunaan
dev (bawaan)Kerja harian, tanpa MIX_ENV berarti dev
testDiatur otomatis oleh perintah mix test
prodKompilasi untuk rilis dan deploy

Nilai lingkungan diubah dengan mengawali perintah Mix, misalnya MIX_ENV=prod mix compile. Kompilasi per lingkungan dipisahkan, sehingga artefak produksi tidak tercampur dengan artefak pengembangan di folder _build. Kebiasaan ini membuat pengujian selalu berjalan pada lingkungan test yang terisolasi, meskipun perintahnya diketik dari akar proyek yang sama.

Fungsi Mix.env/0 juga digunakan untuk percabangan di dalam mix.exs, seperti baris start_permanent pada contoh sebelumnya. Konfigurasi yang berbeda per lingkungan, misalnya tingkat log atau alamat layanan, diletakkan pada folder config di proyek yang membutuhkannya. Pendekatan ini menjaga kode aplikasi tetap sama di semua lingkungan, karena perbedaannya hanya berada pada konfigurasi dan artefak kompilasi.

Ringkasan#

  • mix new menghasilkan proyek berisi mix.exs, lib/, test/, dan konfigurasi formatter yang siap dijalankan.
  • Nama modul mengikuti nama berkas dengan konvensi CamelCase, sehingga struktur folder lib mencerminkan struktur modul.
  • Dependensi dinyatakan di fungsi deps/0, diunduh dengan mix deps.get, dan dikunci versinya pada mix.lock.
  • Umbrella menampung beberapa aplikasi melalui apps_path, dengan build, dependensi, dan konfigurasi yang berbagi sumber daya yang sama.

Referensi#