Biểu đồ đóng góp GitHub của bạn toàn màu xám? Bạn có thể tham gia dự án mã nguồn mở. Nhưng không cần bắt đầu từ con số 0. Cách dễ nhất để tạo side project hữu ích là tách nó ra từ ứng dụng bạn đang xây dựng. Cách đó chính là cách Rails ra đời!
Làm sao biết code nào nên tách? Và làm sao biến nó thành gem mà không phá vỡ quy trình làm việc?
Tìm code cần tách
Đâu đó, sâu bên trong ứng dụng, có code không thuộc về nó. Code không cần ứng dụng để thực hiện công việc. Nó ở đâu?
Đôi khi bạn chỉ đoán được. Nhưng tôi thường tìm thấy code có thể tách ở những nơi sau:
-
Validations (Kiểm tra hợp lệ)
Bạn có viết validation tùy chỉnh cho attribute nào không? Chúng có thể thành gem tuyệt vời.
-
Thay đổi bạn làm trên Rails
Thỉnh thoảng bạn phải can thiệp vào Rails để làm việc hook commit trong test hoặc biến attribute rỗng thành NULL trong database. Mỗi khi tôi chuyển logic kiểu này vào gem, nó trở nên ổn định và dễ hiểu hơn.
-
Models không phải ActiveRecord
Bạn có làm nhiều parsing email hoặc số điện thoại đến mức tách thành class riêng? Những class này thường hữu ích cho ứng dụng khác, và dễ biến thành gem.
-
Mock objects và custom assertions
Bạn có thể viết test dễ đọc hơn bằng custom assertions. Khi đã có custom assertions tốt cho một thư viện hoặc pattern, chúng giúp bất kỳ ai dùng thư viện/pattern đó.
Không cần nghĩ to. Những gem yêu thích của tôi chỉ có một file!
Nếu vẫn chưa quyết định được, hãy duyệt danh mục trên RubyToolbox để lấy cảm hứng.
Đặt code vào thư mục giống gem
Khi đã biết code nào sẽ thành gem, hãy di chuyển code sao cho phù hợp cấu trúc gem bên trong ứng dụng.
Trong ứng dụng Rails, tôi dùng lib/ làm khu vực chuẩn bị gem. lib/ là nơi chứa code có tiềm năng thành gem riêng. Ví dụ, nếu tạo gem tên "json_api_client", thư mục lib/ của Rails app có thể trông như:
... my_rails_app/lib/json_api_client.rb
my_rails_app/lib/json_api_client/associations.rb
my_rails_app/lib/json_api_client/connection.rb
...
Hầu hết gem có file dưới lib/ trùng tên gem (lib/json_api_client.rb), và nhiều file trong thư mục cùng tên (tất cả dưới lib/json_api_client/). Hãy áp dụng cùng cấu trúc đó vào Rails app. Sẽ dễ dàng chuyển code vào gem sau này.
Nếu bối rối về cấu trúc gem, hãy xem source của các gem yêu thích trên GitHub. Bạn sẽ nắm bắt quy luật rất nhanh.
Test như thế nào?
Tôi từng theo cấu trúc lib/ bên trong test/unit/:
... my_rails_app/test/unit/json_api_client_test.rb
my_rails_app/test/unit/json_api_client/associations_test.rb
my_rails_app/test/unit/json_api_client/connection_test.rb
...
Hoạt động ổn, dù gộp models và libraries vào cùng thư mục hơi rối.
Giờ Rails dùng test/models/ thay vì test/unit/. Đưa test của lib/ vào test/models/ không hợp lý. Tôi chưa quyết định quy ước cho trường hợp này. Bạn có gợi ý không?
Phá vỡ các phụ thuộc
Khi code nằm trong gem, nó không thể phụ thuộc vào ứng dụng của bạn. Bạn phải duyệt qua code trong lib/, tìm chỗ nó phụ thuộc vào class, object, hoặc hành vi riêng của app.
Nếu tìm thấy phụ thuộc kiểu đó, phải phá vỡ chúng. Có nhiều bài viết hay về cách phá vỡ (hoặc inject) phụ thuộc, nên tôi không đi sâu vào đây.
Tạo gem
Tôi dùng bundle gem để tạo gem. Cụ thể, tạo gem tên bulk_cache_fetcher:
bundle gem bulk_cache_fetcher -t minitest
Tham số -t thêm file helper test và task test vào Rakefile của gem.
Tiếp theo làm việc nhàm chán: điền .gemspec, viết README, chọn LICENSE, v.v.
Rồi, vì đã có code gem trong lib/ của Rails app, bạn chỉ cần di chuyển code và test sang thư mục lib/ và test/ của gem mới.
Thường sẽ có thứ bỏ sót, hoặc code giả định điều gì đó về app nhưng không đúng trong gem. Vì vậy, trước khi tiến xa hơn, hãy chạy test đã chuyển vào gem, đảm bảo tất cả pass.
Sử dụng gem mới trong app
Bây giờ có gem, bạn muốn dùng nó. Nhưng test thay đổi gem bên trong Rails app khá phiền. Bạn phải:
- Sửa gem
- Build gem
- Xóa mọi dấu vết gem khỏi hệ thống, hoặc cập nhật version
- Cài gem
- Khởi động lại server
Quá trình này tệ hại. May mắn là bundler cung cấp cách dễ dàng hơn. Giả sử gem nằm ở ~/Source/bulk_cache_fetcher. Khi test thay đổi gem, bạn viết trong Gemfile của Rails app:
gem "bulk_cache_fetcher", path: "~/Source/bulk_cache_fetcher"
Chạy bundle install, bạn có thể sửa gem như thể code vẫn nằm trong lib/ của app.
Lưu ý quan trọng: hãy xóa path: trước khi commit code! Đường dẫn đó có thể không đúng trên máy khác, nên chỉ chạy được trên máy bạn.
Build, ship, và tận hưởng!
Khi gem sẵn sàng, bạn phát hành cho cộng đồng.
Đăng ký tài khoản RubyGems nếu chưa có, commit thay đổi, chạy rake release. Chúc mừng, bạn đã trở thành tác giả gem! Và khi push gem lên GitHub, bạn sẽ có ô vuông xanh đẹp cho ngày hôm nay.
Bạn có phần nào của app riêng cảm thấy có thể tách ra? Cái gì bạn nghĩ sẽ thành gem hay? Hãy comment bên dưới.
Nếu muốn học rất nhiều hơn về tạo, quản lý, bảo trì Ruby gem, tôi mạnh mẽ khuyên cuốn "Build a Ruby Gem" của Brandon Hilkert. Quy trình trong sách rất gần với quy trình này, và bao quát hơn nhiều.