Article:
Rails internal
675
alide.myopenid.com 32Updated over 2 years ago |
Rails gồm nhiều thành phần, M: ActiveRecord, VC: ActivePack, ActionMailer, ActionWebservice, ActiveSupport.
Có thể nói ActiveSupport là thành phần bí ẩn nhất vì tài liệu khá khiệm tốn khi giải thích về nó. Ví dụ chính nhờ ActiveSupport mà khi kết quả của 1.kilobyte là 1024.
Tính năng validation hoạt động thế nào?
ActiveRecord chia làm nhiều thành phần. Các database model chúng ta khai báo trong chương trình đều dẫn xuất từ class ActiveRecord::Base. Để thực hiện validation, ActiveRecord::Base include module Validations. Điều này có nghĩa bản thân ActiveRecord::Base không có tính năng validation (không có thì vẫn chạy được), nhưng để có tính năng này thì chỉ cần đơn giản include Validations. Thiết kế này cho phép người viết phần ActiveRecord::Base làm việc độc lập với người viết phần Validations.
Trong ActiveRecord::Base có phương thức save. Để thực hiện tính năng validation, phương thức này được chặn lại và xử lí thích hợp bởi Validations. Việc chặn lại này khả thi bởi Ruby có tính năng alias.
Cụ thể, trong validations.rb có đoạn sau:
def self.included(base) # :nodoc: base.extend ClassMethods base.class_eval do alias_method_chain :save, :validation alias_method_chain :save!, :validation alias_method_chain :update_attribute, :validation_skipping end end alias_method_chain là phương thức do ActiveSupport cung cấp. Dòng
alias_method_chain :save, :validation tạo ra 2 phương thức mới là save_with_validation và save_without_validation, trong đó save_without_validation chính là phương thức gốc.
Cũng trong validations.rb:
def save_with_validation(perform_validation = true) if perform_validation && valid? || !perform_validation save_without_validation else false end end def valid? errors.clear run_validations(:validate) validate if new_record? run_validations(:validate_on_create) validate_on_create else run_validations(:validate_on_update) validate_on_update end errors.empty? end def run_validations(validation_method) validations = self.class.read_inheritable_attribute(validation_method.to_sym) if validations.nil? then return end validations.each do |validation| if validation.is_a?(Symbol) self.send(validation) elsif validation.is_a?(String) eval(validation, binding) elsif validation_block?(validation) validation.call(self) elsif validation_class?(validation, validation_method) validation.send(validation_method, self) else raise( ActiveRecordError, "Validations need to be either a symbol, string (to be eval'ed), proc/method, or " + "class implementing a static validation method" ) end end end Nội dung phương thức valid? cho thấy thứ tự validation như sau: - Những khai báo sử dụng phương thức của class, như validates_length_of, validates_numericality_of, validates_presence_of…
- Những khai báo ghi trong phương thức validation
- Những khai báo khi trong phương thức validate_on_create hoặc validate_on_update
Ngoài ra, đối với attribute nào đó, giả sử xảy ra nhiều error, thì thứ tự của các message trong mảng trả về từ <object>.errors.on(<attribute_name>) sẽ ngược với thứ tự validation trên.
Chỉ cần tìm hiểu thêm chút ít (ví dụ về phương thức read_inheritable_attribute, nằm trong ActiveSupport) là ta đã rõ cách hoạt động của tính năng validation. Qua đó cũng có thể thấy nhóm phát triển Rails chỉ cần vài trăm dòng Ruby mà đã thực hiện xong tính năng này. Không biết nhóm phát triển Struts phải bù đầu (và cả chúng ta khi sử dụng nữa!) với bao nhiêu dòng Java nhỉ :P ?
Rails
32