Thêm thuộc tính động vào Model ActiveRecord Rails
Trong quá trình làm việc thì chắc hẳn ai trong chúng ta cũng đã gặp trường hợp không thể nắm bắt hết tất cả các thuộc tính của 1 model hoặc thuộc tính sẽ phát sinh trong quá trình xây dựng ứng dụng. Một ví dụ điển hình là hệ thống theo dõi các địa chỉ liên lạc có một model Contact bao gồm name, ...
Trong quá trình làm việc thì chắc hẳn ai trong chúng ta cũng đã gặp trường hợp không thể nắm bắt hết tất cả các thuộc tính của 1 model hoặc thuộc tính sẽ phát sinh trong quá trình xây dựng ứng dụng. Một ví dụ điển hình là hệ thống theo dõi các địa chỉ liên lạc có một model Contact bao gồm name, email, phone... Đặt trường hợp là ở đây chúng ta không thể biết trước được các thuộc tính mà model Contact chúng ta cần phải có.
Configuring model
Đầu tiên chúng ta cần cài đặt active_dymamic game
gem 'active_dynamic'
$ bundle
Sau đó chạy lệnh
rails generate active_dynamic rake db:migrate
Nó sẽ copy migration và configuration files vào dự án sau đó chạy migration Tiếp theo chúng ta tạo model Contact và thêm has_dynamic_attributes vào
class Contact < ActiveRecord::Base has_dynamic_attributes end
Bây giờ chúng ta cần phải cho active_dynamic biết nơi để tìm thông tin về các thuộc tính mà nó cần thêm vào model Contact. Để làm điều đó, chúng ta tạo ra một lớp cung cấp dịch vụ mà phải tuân thủ hai quy tắc:
- Nó cần phải chấp nhận một model class như là đối số khởi tạo duy nhất
- Nó cần phải có phương thức call trả về một mảng các định nghĩa thuộc tính
Định nghĩa thuộc tính chỉ là một lớp helper cho phép chỉ định tên hiển thị của một thuộc tính sẽ được tự động thêm vào model của chúng ta, và tùy chọn kiểu dữ liệu của nó, tên hệ thống, xác nhận sự hiện diện và giá trị mặc định. Dưới đây là provider class đơn giản xác định thuộc tính age và description:
class ContactAttributeProvider def initialize(model) @model = model end def call [ ActiveDynamic::AttributeDefinition.new('age', datatype: ActiveDynamic::DataType::Integer, default_value: 18), ActiveDynamic::AttributeDefinition.new('description') ] end end
Để làm cho chúng trở nên thực sự năng động, chúng ta có thể tải các định nghĩa thuộc tính từ bộ nhớ ngoài - cơ sở dữ liệu, tệp YAML, cấu hình XML hoặc cái gì đó khác. Trong ứng dụng demo, chúng ta sử dụng một bảng contact_attributes để lưu và thao tác danh sách các định nghĩa thuộc tính, do đó, ContactAttributeProvider sẽ như sau:
class ContactAttributeProvider def initialize(model) @model = model end def call ContactAttribute.all.map do |attr| ActiveDynamic::AttributeDefinition.new( attr.name, datatype: attr.datatype, required: attr.required?) end end end
Cuối cùng, chúng ta cần phải cho active_dynamic sử dụng lớp provider này. Trong tập tin cấu hình config / initializers / active_dynamic.rb thêm dòng:
config.provider_class = ContactAttributeProvider
Bây giờ mỗi khi một thể hiện của Liên hệ được tạo ra, nó sẽ có bất cứ thuộc tính nào bạn liệt kê trong lớp cung cấp thuộc tính của bạn. Vì vậy, bây giờ chúng ta có thể sử dụng nó như thế này:
contact = Contact.new # first_name is a regular attribute mapped to # a column in contacts table contact.first_name = 'John' # age attribute was added dynamically contact.age = 27 contact.save # we can also do this second_contact = Contact.new(first_name: 'Jane', age: 25)