Sejak memasang "dark" theme, saya cenderung menjadi malas menulis. Untuk sementara, dark theme saya disable dulu yaa. Terima kasih (^_^) (bandithijo, 2024/09/15) ●

بسم الله الرحمن الرحيم

Pendahuluan

Beberapa hari ini, saya sedang mengerjakan side project membuat web scraper menggunakan Ruby.

Project ini terus berkembang, hingga saya sampai pada kebutuhan untuk membuat Rake task sendiri.

Tujuannya

Tujuan yang saya inginkan dalam membuat Rake tasks sendiri adalah untuk merangkum beberapa perintah sekaligus, agar tidak perlu menulis perintah yang panjang secara berulang-ulang. Bahasa kerennya, mungkin disebut “automatisasi”.

Instalasi

Rake benar-benar tool yang sangat powerful untuk melakukan proses automatisasi. Rake juga sangat praktis dan dapat dipasang pada project yang belum secara default memasang Rake. Bahkan juga dapat dipasang pada project yang bukan berbasis Ruby. Keren!

Pertama, pastikan teman-teman sudah memasang Bundler gem yaa.

$ bundler -v
Bundler version 2.1.4

Biasanya kalo Ruby project pasti sudah memiliki Gemfile, tinggal kita tambahkan Rake gem saja.

FILEGemfile
1
2
3
source 'https://rubygems.org'

gem 'rake'

Lalu jalankan instalasi gem baru dengan bantuan Bundler.

$ 
$ bundle install

Konfgurasi

Pada catatan ini ada 2 konfigurasi yang akan saya tuliskan.

  1. Konfigurasi Rakefile untuk project apa saja.
  2. Konfigurasi Rakefile untuk project yang menggunakan Standalone Migrations gem.

Namun sebelumnya, siapkan dahulu direktori untuk menyimpang file .rake.

Sebaiknya kita mengikuti convention (aturan) yang sudah disepakati bersama, agar struktur project kita dapat dibaca dan dipahami oleh orang lain.

Untuk itu, kita perlu membuat struktur direktori seperti ini, lib/tasks.

root-project-dir/
│
...
...
├── Gemfile
├── Gemfile.lock
├── lib/
│   └── tasks/
│       └── *.rake
│
└── Rakefile

Nantinya, kita akan meletakkan Rake tasks di dalam direktori tersebut.

Selanjutnya tinggal mengkonfigurasi Rakefile sesuai dengan preferensi project yang digunakan.

Konfigurasi Rakefile untuk Project Apapun

Buat Rakefile pada project root direktori. Kemudian isikan seperti di bawah.

FILERakefile
1
Dir.glob(File.join('lib/tasks/**/*.rake')).each { |file| load file }

Dapat dibaca, kalau baris di atas akan memerintahkan Rake untuk menjalankan file .rake yang ada di dalam direktori lib/tasks/.

Konfigurasi Rakefile untuk Standalone Migrations Gem

Untuk yang membuat Ruby project menggunakan gem Standalone Migrations, biasanya karena ingin memanfaatkan migration dari gem Active Record –yang merupakan komponen dari Ruby on Rails– agar dapat menggunakan migration di luar Ruby on Rails project.

Isi dari Rakefile pada konfigurasi Rake menggunakan gem Standalone Migrations, akan seperti ini.

FILERakefile
1
2
require 'standalone_migrations'
StandaloneMigrations::Tasks.load_tasks

Perintah di atas juga akan menjalankan Rake task yang kita simpan di dalam direktori lib/tasks/.

Menulis Rake Task

Dalam menulis Rake task, sebaiknya kita juga mengikuti convention yang sudah ada.

Morfologi Rake Task

Morfologi dari Rake task, adalah seperti ini:

  1. Description desc, akan memberikan deskripsi yang akan ditampilkan pada saat kita menjalankan perintah $ rake --tasks di Terminal.
  2. Task name :name, nama dari task yang akan dipanggil pada saat menjalankan Rake.
  3. Code block, adalah code atau perintah yang akan dijalankan ketika task dipanggil.
FILERakefile
1
2
3
4
desc 'Description'
task :name do
  # task code ...
end

Meskipun sintaks dari Rake task ditulis dengan bahasa Ruby, namun ekstensi dalam meberikan nama file tetap menggunakan akhiran .rake bukan .rb.

Tugas Sederhana

Misalnya hanya ada satu jenis tugas, kita dapat mendefiniskan seperti ini.

Buat dulu Rake file pada direktori lib/tasks/, kasih nama bebas.

Misalkan saya kasih nama sesuai dengan tugas yang akan dikerjakan, yaitu run.rake.

FILElib/tasks/run.rake
1
2
3
4
desc "Menjalankan main script"
tasks :run do
    system "ruby app/main.rb"
end

Saya punya Ruby script dengan nama app/main.rb yang ingin saya jalankan.

Kalau kita cek dengan perintah $ rake --tasks, akan menampilkan output:

rake middleware                      # Prints out your Rack middleware stack
rake restart                         # Restart app by touching tmp/restart.txt
rake run                             # Menjalankan main script
rake secret                          # Generate a cryptographically secure secret key (this is typically used to generate a secret for cookie sessions)
rake stats                           # Report code statistics (KLOCs, etc) from the application or engine

Dengan begini, saya tidak perlu lagi menjalankan command $ ruby app/main.rb yang cukup panjang, cukup menjalankan dengan $ rake run, maka hasilnya pun akan sama namun dengan command yang lebih pendek. =P

Tugas Bercabang (Bertingkat) / Namespace

Tingkat Satu

Tugas yang bercabang atau bertingkat ini, maksudnya seperti kita punya kategori tugas yang sama, namun detail pekerjaannya yang berbeda. Kalau yang pernah menggunakan Ruby on Rails, pasti pernah menggunakan perintah $ rake db:create, $ rake db:migrate, $ rake db:rollback, dll.

Nah, kira-kira begini cara buatnya (blok codenya hanya ilustrasi yaa, bro).

Karena ada banyak tugas yang merupakan tugas yang mirip, yaitu untuk mengintervensi database, maka saya beri nama database.rake.

FILElib/tasks/database.rake
1
2
3
4
5
6
7
8
9
10
11
namespace :db do
  desc "Create database for current environment"
  task :create do
    ActiveRecord::Tasks::DatabaseTasks.create_all
  end

  desc "Drop database for current environment"
  task :drop do
    ActiveRecord::Tasks::DatabaseTasks.drop_all
  end
end

Perhatikan strukturnya, bahwa masing-masing tasks tetap memiliki morfologi yang sudah menjadi convention seperti yang sudah saya tulis di atas.

Kalau kita cek dengan perintah $ rake --tasks, akan menampilkan output:

rake app:template                    # Applies the template supplied by LOCATION=(/path/to/template) or URL
rake app:update                      # Update configs and some other initially generated files (or use just update:configs or update:bin)
rake db:create                       # Create database for current environment
rake db:drop                         # Drop database for current environment
rake db:environment:set              # Set the environment value for the database
rake db:fixtures:load                # Loads fixtures into the current environment's database

Secara sederhana namesapce akan mengkategorikan task yang sejenis, dalam hal ini, task yang memiliki tugas untuk berinteraksi dengan database db akan dimasukkan ke dalam namespace ini agar task list menjadi lebih rapi dan terorganisir.

Tingkat Lebih dari Satu

Nah, mungkin teman-teman yang menggunakan Ruby on Rails juga pernah melihat perintah $ rake db:migrate:status, pasti sudah bisa ketebak dong yaa, gimana cara membuatnya.

FILElib/tasks/database.rake
1
2
3
4
5
6
7
8
9
10
11
12
13
namespace :db do
  desc "Migrate the database (options: VERSION=x, VERBOSE=false, SCOPE=blog)."
  task :migrate do
    ActiveRecord::Tasks::DatabaseTasks.migrate
  end

  namespace :migrate do
    desc "Display status of migrations"
    task :status do
        ActiveRecord::Tasks::DatabaseTasks.migrate_status
    end
  end
end

Kalau kita cek dengan perintah $ rake --tasks, akan menampilkan output:

rake db:environment:set              # Set the environment value for the database
rake db:fixtures:load                # Loads fixtures into the current environment's database
rake db:migrate                      # Migrate the database (options: VERSION=x, VERBOSE=false, SCOPE=blog)
rake db:migrate:status               # Display status of migrations
rake db:new_migration[name,options]  # Creates a new migration file with the specified name
rake db:prepare                      # Runs setup if database does not exist, or runs migrations if it does

Memasukkan Argument

Terkadang kita membutuhkan argument yang akan diolah di dalam task.

Misalkan kita punya Rake task seperti ini.

FILE
1
2
3
4
desc "Menjalankan operasi penjumlahan"
task :penjumlahan do
  puts 1 + 2
end
$ rake penjumlahan
# => 3

Nah, kita ingin membuat dua angka yang dijumlahkan tersebut menjadi dinamis, diambil dari argumen yang diberikan.

Berikut ini adalah 4 cara dalam memasukkan argument ke dalam Rake task.

1. The Rake Way

Rake memiliki built-in function yang dapat menerima argument, caranya seperti ini.

FILE
1
2
3
4
desc "Menjalankan operasi penjumlahan"
task :penjumlahan, [:num1, :num] do |t, args|
  puts args[:num1].to_i + args[:num].to_i
end
$ rake penjumlahan[1,2]
# => 3
Informasi

Namun, cara ini memiliki kelemahan apabila kita menggunakan ZSH Shell. Biasanya kita akan mendapatkan error berupa,

zsh: no matches found ...

Untuk mengatasi hal ini, kita perlu menambahkan escape charater pada perintahnya. Menjadi seperti ini:

$ rake penjumlahan\[1,2\]
# => 3

Terlihat tidak cantik yaa.

Selain itu, kita tidak boleh menggunakan spasi setelah tanda koma "," diantara argument, hal ini akan menyebabkan error yang lain.

2. Environment Variables

Kita akan menggunakan cara environment variables seperti yang biasa dijalankan di terminal, misal RAILS_ENV=.

Nah, kita dapat menggunakan metode yang sama.

FILE
1
2
3
4
desc "Menjalankan operasi penjumlahan"
task :penjumlahan do
  puts ENV['NUM1'].to_i + ENV['NUM2'].to_i
end

Cara menjalankannya akan seperti ini:

$ rake penjumlahan NUM1=1 NUM2=2
# => 3

Cara ini dapat dilakukan, namun agak sia-sia kalau menjalankan task dengan melakukan pengesetan/pendefinisian environment variables.

3. Menggunakan ARGV

ARGV adalah command line argument. Untuk teman-teman yang pernah membuat Ruby script dan ingin mengambil inputan dari user bersamaan dengan menjalankan command, pasti pernah menggunakannya.

$ ruby script.rb nama

Nah, nama itu adalah command line argument yang akan digunakan di dalam script.rb tersebut. Inilah yang disebut Ruby ARGV.

Berikut ini adalah contoh task yang memanfaatkan Ruby ARGV.

1
2
3
4
5
desc "Menjalankan operasi penjumlahan"
task :penjumlahan do
  ARGV.each { |a| task a.to_sym do ; end }
  puts ARGV[1].to_i + ARGV[2].to_i
end

Cara menjalankannya akan seperti ini:

$ rake penjumlahan 1 2
# => 3

4. Menggunakan Ruby OptionParser

Cara memberikan argument dengan OptionParser ini mirip seperti yang kita lakukan untuk menjalankan commmand dengan penambahan flag, misal yang umum kita gunakan untuk mengetahui command help, yaitu dengan menambahkan flag -h.

Nah, yang akan kita buat, mirip seperti itu.

FILE
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
require 'optparse'

desc "Menjalankan operasi penjumlahan"
task :penjumlahan do
  options = {}

  OptionParser.new do |opts|
    opts.banner = "Gunakan: rake penjumlahan [options]"
    opts.on("-o", "--one ARG", Integer) { |num1| options[:num1] = num1 }
    opts.on("-t", "--two ARG", Integer) { |num2| options[:num2] = num2 }
  end.parse!

  puts options[:num1].to_i + options[:num2].to_i
  exit
end

Cara menjalankannya seperti ini:

$ rake penjumlahan -- -o 1 -t 2

atau,

$ rake penjumlahan -- --one 1 --two 2

Melihat Daftar Rake Task

Untuk melihat daftar task apa saja, sekaligus task yang sudah kita buat, jalankan perintah berikut.

$ rake --tasks

atau,

$ rake -T

Pesan Penulis

Yang saya tuliskan pada catatan ini adalah hal sederhana dalam membuat Rake tasks. Apabila teman-teman mendapatkan kasus yang lebih kompleks, dapat menelusuri Google sendiri yaa.

Silahkan mendalami daftar referensi yang saya berikan di bawah.

Mudah-mudahan dapat bermanfaat buat teman-teman.

Terima kasih.

(^_^)

Referensi

  1. cobwwweb.com/how-to-write-a-custom-rake-task
    Diakses tanggal: 2020/06/26

  2. cobwwweb.com/4-ways-to-pass-arguments-to-a-rake-task
    Diakses tanggal: 2020/06/26

  3. www.rubyguides.com/2019/02/ruby-rake
    Diakses tanggal: 2020/06/26

  4. ruby-doc.org/stdlib-2.7.1/libdoc/optparse/rdoc/OptionParser.html
    Diakses tanggal: 2020/06/26


Penulis

bandithijo

My journey kicks off from reading textbooks as a former Medical Student to digging bugs as a Software Engineer – a delightful rollercoaster of career twists. Embracing failure with the grace of a Cat avoiding water, I've seamlessly transitioned from Stethoscope to Keyboard. Armed with ability for learning and adapting faster than a Heart Beat, I'm on a mission to turn Code into a Product.

- Rizqi Nur Assyaufi

944e8edeccab170ecee65673676b75514b2f62ed