ActiveRecord Trong Rails
Bài đăng này đã không được cập nhật trong 9 năm
I. Design of ActiveRecord
ActiveRecord là một phần của Rails nó sẽ đảm nhiệm việc mapping giữa các đối tượng với các bảng trong cơ sở dữ liệu. Nó còn được gọi là object-relation-mapping. Trong Rails app ta sử dụng ActiveRecord trong model (trong đó có domain object và nghiệp vụ logic), ý tưởng ở đây chính là làm việc trên các đối tượng để quản lý nghiệp vụ logic, còn sử dụng ActiveRecord để đảm bảo các đối tượng đó được lưu (persisted) xuống database.
I.1 ActiveRecord::Base
ActiveRecord::Base là thành phần quan trọng nhất trong ActiveRecord. Nó không chỉ chưa các class method phục vụ việc kết nối với database mà nó còn là base class của các mapped class (User, Product ..)
class Duck < ActiveRecord::Base
validates_length_of :name, maximum: 6
end
Trong ví dụ trên thì validates_length_of( )
là một Class Macro, nó đảm bảo trường name không vượt quá 6 ký tự.
ActiveRecord sẽ tự động map các đối tượng Duck vào trong bảng ducks của database, ActiveRecord cũng định nghĩa các Ghost Method để truy cập vào các trường.
Giờ hãy nhìn vào ví dụ trên bạn sẽ nghĩ rằng validates_length_of
là một method của ActiveRecord::Base thế nhưng thực tế thì không phải vậy nó nằm trong module ActiveRecord::Validation, ActivRecor::Validation validate các trường bên trong model không phải trong database, Như ở ví dụ trên thì trong database trường name của bảng ducks vẫn có thể dài hơn 6 ký tự, bạn có thể thêm vào bằng cách thêm thủ công bằng câu lệnh sql.
Giả sử model của bạn kế thừa ActiveRecord::Base class, autoload sẽ load activerecord/base.rb file và sẽ định nghĩa một class Base
module ActiveRecord
class Base
class << self # Class methods
def find(*args) # ...
def first(*args) # ...
def last(*args) # ...
end
public
def id # ...
def save # ...
def save! # ...
def delete # ...
# ...
end
end
end
I.2 ActiveRecord::Validations
duck = Duck.new(name: "Conan")
duck.valid?
Như phần trên ta đã đề cập tới method validates_length_of ( )
nó nằm trong ActiveRecord::Validation, điều này là do khi kế thừa từ ActiveRecord::Base, class Base đã include module Validations
module ActiveRecord
Base.class_eval do
# ...
include Validations
include Locking::Optimistic, Locking::Pessimistic
include AttributeMethods
include Dirty
include Callbacks, Observing, Timestamp
# ...
end
end
Khi ActiveRecord:Base includes ActiveRecord::Validations nó sẽ thêm các instance methods và class methods vào trong ActiveRecord::Base. Đồng thời thì Active::Validations::Included() cũng include module ActiveSupport::Callbacks, điều này suy ra ActiveRecord::Base cũng includes ActiveRecord::Callbacks
ActiveRecord::Validations module sử dụng các alias method, các phương thức mà bạn hay sử dụng như save()
, save()!
thực ra là bạn đang gọi alias method.
module ActiveRecord
module Validations
def self.included(base)
base.extend ClassMethods
base.class_eval do
alias_method_chain :save, :validation
alias_method_chain :save!, :validation
end
base.send :include, ActiveSupport::Callbacks
# ...
end
def save_with_validation(perform_validation = true) # ...
# ...
end
II. Bên trong ActiveRecord
II.1 Dynamic Attributes
Xét ví dụ sau:
class Task < ActiveRecord::Base
end
task = Task.new
task.description = 'Clean up garage'
task.completed = true
task.save
Các phương thức description=()
hay completed?
thực ra là các Ghost Mehthods . Lần đầu nó được gọi thông qua method_missing()
của ActiveRecord::Base và sau đó nó sẽ gọi phương thức define_attribute_methods ( )
phương thức này sẽ định nghĩa phương thức read, write cho các trường trong database. Trong các lần gọi tiếp theo khi bạn gọi phương thức description()
hay bất kỳ một thức nào khác được map với các cột trong database nó sẽ không gọi thông qua method_missing()
nữa mà nó gọi phương thức thực (đã được định nghĩa thông qua define_attribute_methods()
)
Khi đi vào method_missing thì description
method trong ví dụ trên trở thành một Ghost Method, Sau đó method_missing() sẽ gọi Dynamic Dispatch (sử dụng send()
method), Việc gọi này chỉ được thực thi một lần cho mỗi class kế thừa từ ActiveRecord::Base.
II.2 Dynamic Finders
ActiveRecord cũng cung cấp cá dynamic finders, bằng cách gọi phương thức find kết hợp với tên của các attriubtes
task = Task.find_by_description("clean")
Task.find_by_description_and_completed('Clean' , true)
#...
Các phương thức find này được định nghĩa trong mising_method (để biết rõ hơn bạn có thể xem trong lib của Rails), tên của phương thức phải được bắt đầu bằng find
theo sau là các attributes Sau đó nó sẽ định các phương thức find thực như find_by_description
, find_by_completed
..
References
Metaprograming
Rubydoc
Stackoverflow
All rights reserved