+1

ActiveRecord Trong Rails

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

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí