Nói chung, các biến toàn cục là xấu. Nhưng đôi khi, toàn cầu ở đúng nơi có thể làm cho mã của bạn đơn giản hơn rất nhiều.
Trong Rails, bạn có thể đặt dữ liệu một lần trong khi yêu cầu, nhưng hãy sử dụng nó trong mọi lớp ứng dụng của bạn. Người dùng nào đang thực hiện yêu cầu? Họ có quyền gì? Kết nối cơ sở dữ liệu nào nên được sử dụng?
Thông tin này phải có sẵn trong toàn bộ mã của bạn, vì vậy việc chuyển nó đi khắp nơi sẽ chỉ là một đống nhiễu. Nhưng việc sử dụng một biến toàn cục của Ruby, hoặc thiết lập một biến lớp, sẽ gây ra các vấn đề riêng của nó. Nhiều chuỗi có thể ghi đè lên nó và bạn sẽ nhận được một mớ hỗn độn lớn và có thể là dữ liệu xấu không thể khôi phục được.
Bạn cần thứ gì đó mang tính toàn cầu nhưng chỉ yêu cầu của bạn .
Cách Ruby đơn giản
Thông thường, bạn sẽ thấy điều này được xử lý với Thread.current[]
. Nó trông như thế này:
Thread.current[:current_user] = user
Điều này là đủ đơn giản. Đó là một giải pháp tốt. Nhưng nó có hai nhược điểm lớn:
-
Ai đó có thể vô tình ghi đè dữ liệu của bạn.
Điều gì sẽ xảy ra nếu hai người chọn cùng một chìa khóa? Các bạn sẽ dựa trên dữ liệu của nhau. Và nếu bạn đang lưu trữ thứ gì đó giống như người dùng, điều đó sẽ rất tệ. Trong các ứng dụng của riêng bạn, điều này có thể không thành vấn đề. Nhưng nếu bạn đang viết đá quý hoặc ứng dụng Rails của bạn quá lớn và lộn xộn, thì đó là điều bạn cần phải suy nghĩ.
-
Nó không được cấu trúc tốt.
Thread.current[]
chỉ là một túi lớn dữ liệu. Bạn không thể dễ dàng ghi lại nó. Nếu bạn muốn biết những gì bạn có thể lấy ra từ nó, bạn phải tìm kiếm những gì bạn đưa vào nó.Tất nhiên, nếu bạn đang cung cấp đủ dữ liệu toàn cầu cho thấy đây là một vấn đề, bạn còn phải lo lắng nhiều điều hơn là
Thread.current[]
Thiếu cấu trúc. Nhưng đó vẫn là một điểm cần lưu ý.
Vậy có giải pháp nào tốt hơn Thread.current[]
? Như bạn có thể tưởng tượng, bản thân Rails quản lý rất nhiều dữ liệu yêu cầu cục bộ. Và hộp quà tặng ActiveSupport có một mẫu khác để làm theo.
Đường ray
Nếu bạn tìm hiểu qua ActiveSupport, bạn có thể gặp phải ActiveSupport::PerThreadRegistry
. Bạn có thể biết ngay từ cái tên rằng đây chính xác là những gì chúng tôi đang tìm kiếm.
Với ActiveSupport::PerThreadRegistry
, ví dụ trước đó sẽ giống như sau:
class RequestRegistry
extend ActiveSupport::PerThreadRegistry
attr_accessor :current_user, :current_permissions
end
RequestRegistry.current_user = user
RequestRegistry.current_user # => user
Đó là một công việc nhỏ hơn một chút. Nhưng với ActiveSupport::PerThreadRegistry
:
-
Bạn có một nơi để đặt tài liệu. Bất kỳ ai nhìn vào lớp học của bạn sẽ biết chính xác dữ liệu toàn cầu nào bạn đang mong đợi và dữ liệu toàn cầu nào họ có thể sử dụng.
-
Bạn nhận được một API có vẻ tốt. Trên thực tế, có vẻ như bạn chỉ đang thiết lập các biến lớp. Đó có thể là những gì bạn sẽ làm nếu bạn không phải lo lắng về các chuỗi.
-
Mọi dữ liệu bạn đặt trên lớp sẽ được đặt ở vùng tên. Bạn không phải lo lắng về
RequestRegistry.current_user
va chạm vớiPhoneNumberApiRegistry.current_user
, chúng được đối xử hoàn toàn riêng biệt.
Rails sử dụng ActiveSupport::PerThreadRegistry
nội bộ, đối với những thứ như xử lý kết nối ActiveRecord, lưu trữ các truy vấn GIẢI THÍCH và ActiveSupport ::Thông báo. Vì vậy, nếu bạn đang tìm kiếm thêm các ví dụ hay về sức mạnh của PerThreadRegistry
, Bản thân Rails là một nơi tuyệt vời để bắt đầu.
Khi chuỗi và yêu cầu không khớp
Đối với một số máy chủ Rails, một luồng xử lý nhiều yêu cầu. Điều này có nghĩa là bất kỳ thứ gì bạn đưa vào PerThreadRegistry
sẽ tiếp tục cho các yêu cầu tiếp theo. Đây có lẽ không phải là điều bạn muốn.
Bạn có thể làm những gì Rails làm và dọn dẹp những thứ bạn đưa vào PerThreadRegistry
sau khi bạn làm xong với chúng. Tùy thuộc vào những gì bạn đưa vào đó, bạn vẫn có thể thực hiện việc này.
Trong phần bình luận, MattB chỉ ra một giải pháp dễ dàng hơn:request_store. Nó hoạt động giống như Thread.current[]
nhưng sẽ tự xóa sau mỗi yêu cầu.
Điều này khiến cánh cửa rộng mở cho ai đó tạo PerRequestRegistry
, kết hợp sự an toàn của request_store với API của PerThreadRegistry
. Nếu ai đó đã làm xong việc này, tôi rất muốn biết về điều đó!
Hãy dùng thử
Dữ liệu toàn cầu có thể xấu. Quá nhiều hình cầu có thể nhanh chóng dẫn đến mã không thể xác minh được.
Nhưng nếu việc giữ dữ liệu ở vị trí trung tâm cho toàn bộ yêu cầu là hợp lý, hãy vượt ra ngoài Thread.current[]
. Cung cấp ActiveSupport::PerThreadRegistry
hoặc request_store một shot. Nếu không có gì khác, nó sẽ làm cho việc xử lý dữ liệu toàn cầu ít rủi ro hơn một chút.