Rake và ứng dụng thực tế
Rake (viết tắt của Ruby Make) là một task runner phổ biến được viết bằng Ruby, với mục đích gom nhóm các đoạn code ruby thường xuyên được sử dụng vào một task chung để sử dụng lại nhiều lần. Vậy task là gì? Task ở đây có thể là những tác vụ như: Tạo backup database Cleanup database Chạy ...
Rake (viết tắt của Ruby Make) là một task runner phổ biến được viết bằng Ruby, với mục đích gom nhóm các đoạn code ruby thường xuyên được sử dụng vào một task chung để sử dụng lại nhiều lần.
Vậy task là gì? Task ở đây có thể là những tác vụ như:
- Tạo backup database
- Cleanup database
- Chạy test
- ...
Rake dùng Rakefile và các file có đuôi .rake để định nghĩa một tập các task.
Khi dùng Rails, ta đã quen với các câu lệnh như: rake routes, rake db:migrate, rake db:seed... Thực chất đó là những Rake task đã được định nghĩa sẵn trong Rails nên ta chỉ việc gọi ra dùng thôi :v
Trong Rails, Rake được dùng để định nghĩa các task ở cấp độ quản lý chung. Để xem các task được định nghĩa sẵn và mô tả của chúng, ta chạy lệnh:
rake -T
Ta sẽ có kết quả sau đây:
Lưu ý: Từ Rails 5.0 trở đi, bạn có thể dùng rails thay cho rake để chạy các task trong Rails. Do vậy ta có thể viết rails db:migrate thay vì rake db:migrate
Ngoài những task mặc định được Rails định nghĩa, ta cũng override những task mặc định hoặc tạo các task mới để phục vụ mục đích riêng. Chúng ta cùng tìm hiểu cách dùng nhé :v
Tạo Rake task đơn giản:
Bạn có thể tạo file .rake trong thư mục lib/tasks hoặc chạy lệnh sau để tạo task:
rails g task [namespace] [task]
Ví dụ, ta tạo 1 task như sau: Lệnh này sẽ tạo 1 file show.rake trong lib/tasks, ta thêm vào file như sau:
namespace :show do desc "TODO" task hello: do puts "Hello World" end end
Ta chạy thử task vừa tạo: rails show:hello. Kết quả console sẽ in ra "Hello world" thôi :v Dễ hiểu mà
Sử dụng namespace
Vì các task có thể trùng tên nhau và cũng để gom nhóm tiện cho việc quản lý nên Rake đã tạo ra namespace
Ví dụ:
namespace :backup do task :create do # ... end task :list do # ... end task :restore do # ... end end
Để chạy task, ta sử dụng:
rails [namespace]:[task]
Dependent Tasks
Rake cho phép bạn chạy 1 danh sách task trước khi chạy task hiện tại
task create_examples: :load_database do # ... end
Ví dụ trên: load_database sẽ được chạy trước khi ta chạy create_examples
Bạn cũng có thể truyền vào một danh sách các Dependent Tasks dưới dạng mảng các String hoặc Symbol.
Các tasks này sẽ được thực hiện tuần tự theo đúng thứ tự trong mảng truyền vào.
Dependency :environment
Khi tạo task cần thao tác với model để thực hiện truy vấn database,.. Bạn cần chạy task environment trước để load code của rails app ra trước khi chạy task:
namespace :user do task create: :environment do User.create name: "Hau Nguyen" end end
Chạy task trong task
Rake cho phép gọi task trong 1 task khác với execute hoặc invoke
namespace :show do desc "Hello" task :hello do puts "Hello World" end desc "Task 1" task task1: :hello do puts "Task 1" end desc "Task 2" task :task2 do puts "Task 2" Rake::Task["show:task1"].execute # OR Rake::Task["show:task1"].invoke end end
Sự khác nhau giữa execute và invoke:
- execute: chạy task mà KHÔNG chạy các Dependent Task của nó
- invoke: chạy task và các Dependent Task của nó
Override Task mặc định
Rails cũng cho phép chúng ta định nghĩa lại 1 task mặc định, chẳng hạn như db:migrate.
Lưu ý: Task mới mà chúng ta định nghĩa lại sẽ không override lại hoàn toàn mà chỉ đơn giản là thêm các dòng lệnh phía sau task gốc đó.
Để định nghĩa lại 1 task, cần dùng cùng tên, cùng namespace:
namespace :db do task migrate: :environment do puts "Hello, I override db:migrate task." end end
Truyền tham số hoặc biến ENV
Tham số:
namespace :calculate do task :add, [:num1, :num2] do |_, args| puts args[:num1].to_i + args[:num2].to_i end end
Chạy thử task:
rake calculate:add[10,20]
Biến ENV:
task :add_env do puts ENV['NUM1'].to_i + ENV['NUM2'].to_i end
Chạy thử task:
rake calculate:add_env NUM1=1 NUM2=20
Default Rake task
Nếu không chỉ rõ task được chạy thì Rake sẽ chạy default task được định nghĩa trong Rakefile. Bạn có thể định nghĩa 1 default task như sau:
Rakefile
task :default => ["my_default_task"]
Sau đó, khi chạy lệnh rake, Rake sẽ mặc định chạy task my_default_task
Làm việc với File
Rake cung cấp 1 số câu lệnh hữu ích như:
- ruby: để chạy 1 file ruby
- sh: để chạy 1 lệnh trên system commands
Rake còn tích hợp module FileUtils cho ta có hàng loạt method thao tác với file như cp, mkdir, rm, mv..
Ví dụ, copy file exam.rb sang file exam_copy.rb dùng rake task:
task :copy do FileUtils.cp "exam.rb", "exam_copy.rb" end
Bạn có thể xem thêm về module FileUtils tại đây
Triết lý của Rake là gom nhóm và tự động hóa các task nhỏ, vì vậy ta có thể định nghĩa ra các task để giúp quá trình phát triền project trở nên nhanh gọn hơn.
Ví dụ, khi init project rails, ta cần duplicate các file chứa thông tin cần bảo mật:
config/database.yml config/credentials.yml.enc config/storage.yml
Đổi tên chúng thành đuôi .example và thêm vào .gitignore. Ngoài ra, ta cũng muốn xóa bỏ comment, các dòng trống, chuyển nháy đơn sang nháy kép trong một số file.
Ok, vậy mình sẽ viết task để thực hiện tự động việc đó:
namespace :file do desc "Create example file" task :create_example do gitignore = File.open Rails.root.join(".gitignore"), "a" file_names = [ "config/database.yml", "config/credentials.yml.enc", "config/storage.yml"] file_names.each do |file| FileUtils.cp file, file + ".example" gitignore << file + " " end gitignore.close end desc "Remove default comments" task :rm_comment do file_names = ["Gemfile", "config/application.rb"] file_names.each do |file_name| text = File.read file_name # Xoa comment new_contents = text.gsub(/^s*#.*/, "") # Chuyen nhay don => nhay kep new_contents.gsub!(/'/, '"') # Xoa dong thua new_contents.gsub!(/^$ /, "") puts new_contents File.open(file_name, "w") {|file| file.puts new_contents } end end end
Mỗi khi muốn tạo file .example, ta chỉ cần chạy rake file:create_example là xong :v Rất chính xác và tiết kiệm thời gian phải không
Mình đã trình bày xong về cách sử dụng cơ bản của Rake và những ví dụ mình thấy là cần thiết dùng Rake. Mong rằng các bạn sẽ có thể sử dụng Rake task vào project của riêng mình
- https://guides.rubyonrails.org/command_line.html#custom-rake-tasks
- https://www.rubyguides.com/2019/02/ruby-rake/
- http://jacobswanner.com/development/2013/rake-file-tasks/