Hướng dẫn sử dụng Rails Engines thành công

Tại Gigster, chúng tôi là fan cuồn của DRY. Bởi lẽ đó, chúng tôi đang liên tục phát triển và duy trì một lượng lớn thư viện và framework tái sử dụng được cho nhiều project khác nhau. Đôi khi, chúng chỉ là một vài công cụ nhỏ giúp chúng chúng tôi giải quyết một số khó khăn, nhưng chúng tôi cũng có nhiều template cho các kiểu project nổi tiếng hơn.

Gần đây, tôi được yêu cầu phát triển một template tái sử dụng được cho các website crowdfunding. Template này sẽ ở dạng framework có thể được “cắm” vào chương trình có sẵn để mang dến các tính năng giống với Kickstarter.

TopDev Techtalk #51: Golang, Ruby: Hướng đi nào trong tương lai?

*Hồ Chí Minh: 18h00 – 21h00 ngày 24/11/2016.

Tôi có biết về Rails engines (và, như phần lớn lập trình viên Rails, tôi dùng rất nhiều engine mỗi ngày) nhưng tôi lại chả bao giờ có thời gian hay nhu cầu tự tạo engine cả. Vì vậy, với những kiến thức liên quan từ Rails guide, tôi đã quyết định bắt tay tạo ra Rails engine xác định được đầu tiên của mình.

Dưới đây là một loạt các thách thức kỹ thuật tôi gặp phải trong quá trình phát triển Crowdster và cách tôi giải quyết chúng. Đây chả phải những thủ pháp gì mới: tất cả đều có thể tìm được ở các Rails engines nổi tiếng.

Phương thức “configuration block”

Điều đầu tiên tôi cần làm là cung cấp cách thiết đặt engine cho người dùng.

Hầu như tất cả engine thiết đặt được đều có xuất hiện API như API được dùng trong Rail initializer sau:

Vẫn có một số biến thể, nhưng ý tưởng chính vẫn là: bạn sẽ sử dụng method để tạo object thiết đặt. Object này sau đó sẽ được người dùng sử dụng để thiết đặt cho engine.

Bắt đầu từ kết quả tôi muốn đạt được, tôi tạo thêm một class thiết đặt, class này chỉ đơn giản đóng vai trò container cho tất cả tùy chọn trong phần thiết đặt:

Giờ thì sau khi có class, tôi chỉ cần thêm point of entry (điểm nhập thôi):

Tôi cũng có thêm vào một vài logic để xác thực thông tin nhập vào.

Base controller inheritance

Vấn đề này hơi hóc búa hơn một chút. Vì Crowdster không xử lý xác minh người dùng, tôi cần phải tiếp cận được với một số helper trong base controller của ứng dụng mẹ.

Sau nhiều giải pháp bất thành, và có phần thái quá, tôi đã quyết định chỉ cho phép đặt tên của base controller thông qua object thiết đặt thôi, và sau đó mở rộng ra trong ApplicationController của engine, giống như sau:

Sau đó, trong initializer:

Tất cả engine controller mở rộng ApplicationController, vì vậy giờ tôi đã có thể tiếp cận được helper từ ứng dụng mẹ.

Tôi vẫn không chắc liệu đây đã là giải pháp tối nhất hay liệu có gây xung đột nào hay không, nhưng đây là phương pháp đến nay vẫn hoạt động rất tốt trong trường hợp của chúng tôi.

Mở rộng classes (via class_eval)

Sau khi đã lo xong phần cơ bản, tôi có thể thực sự lập trình nghiêm túc rồi.

Tôi tạo một model trong engine thuộc về user class trong ứng dụng me. Tên class, một lần nữa, có thể thiết đặt được thông qua initializer, nên mọi thứ vẫn làm việc tốt.

Tuy nhiên, tôi cần tạo thêm qua hệ has_many tương ứng trên user model trong ứng dụng me. Tôi phải làm sao đây?

Cuối cùng, tôi tìm đến giải pháp khá đơn giản:

Mở rộng classes (via acts_as_*)

Phương thức acts_as là phương thức nổi tiếng trong nhiều engine.

Như vậy, bạn sẽ patch ActiveRecord::Base (hoặc đối tượng tương đượng trong ORM) để cung cấp class method giúp bơm hành vi mong muốn và class hiện tại.

Trong trường hợp của tôi, tôi tạo method acts_as_campaigner, yêu cầu phải bơm logic liên quan vào user model:

Với giải pháp acts_as, bạn hoàn toàn có thể xác định thiết đặt cho mỗi model, giúp engine của chúng ta được linh hoạt hơn.

Extending classes (via modules)

Bạn có thấy ta dùng module được để mở rộng chức năng ActiveRecord::Base thay vì mở class trực tiếp chứ?

Bạn có thể đi theo hướng tương tự để mở rộng class User:

Các này sẽ rút gọn số lượng code cần gõ, và có thể cô lập hơn, vì chúng ta không pollute  ActiveRecord::Base. Nhưng cách này lại không cài đặt linh hoạt được như giải pháp “acts_as”.

Test engine với RSpec

Theo mặc định, Rails generator dùng MiniTest để test engine.

Nếu bạn thích dùng RSpec (giống tôi), bạn sẽ phải thực hiện một số thay đổi để có thể chạy spec cho phù hợp.

Sau khi cài đặt RSpec, hãy chuyển thư mục test/dummy sang spec/dummy, sau đó xóa thư mục test. Tiếp đến, hãy chỉnh sửa rails_helper.rb giống như sau:

Như vậy, RSpec sẽ load môi trường và database migration từ dummy app, thay vì tìm kiếm trong engine.

Khi tạo một database migration, bạn sẽ phải copy vào dummy app trước khi thực thi:

Để chạy được controller spec, bạn cũng sẽ cần thông báo RSpec cần phải dùng tập hợp route nào:

Vậy là xong rồi, các bạn giờ đã có thể dùng testing framework tùy thích rồi đấy!

Để có thể hiểu rõ hơn về những điểm mạnh, điểm yếu và biết được nhiều hơn nữa về Ruby & Golang bạn hãy nhanh chóng đăng ký tham gia buổi TopDev Techtalk: Golang, Ruby- Hướng đi nào cho tương lai? để lắng nghe những đánh giá và chia sẻ kinh nghiệm chuyên sâu của các chuyên gia, những người đang làm việc thực tế và sử dụng các ngôn ngữ này.

*Hồ Chí Minh

Thời gian: 18h00 – 21h00 ngày 24/11/2016.

Địa điểm: ĐH Hoa Sen, 08 Nguyễn Văn Tráng, Quận 1.

9

Mọi thông tin hỗ trợ vui lòng liên hệ:

Tel: 08 6273 3497

Hotline : 0944 685 243 – Ms. Ngọc  |   0963 651 587 – Ms. Nguyên