0

The Design of ActiveRecord

Giới thiệu về ActiveRecord

ActiveRecord là một phần của Rails mà sẽ làm nhiệm vụ trừu tượng hóa trong việc thao tác với các bảng cơ sở dữ liệu. Chức năng này được gọi là lập quan hệ với đối tượng, và nó cho phép bạn có thể sử dụng được cả 2 cách liên kết là object-data và object-oriented programming.

Chúng ta có thể sử dụng ActiveRecord trong một ứng dụng Rails hay một phần của ứng dụng định kỳ. Trong một ứng dụng Rails, chúng ta có thể sử dụng ActiveRecord trong phần model của mô hình thiết kế MVC(Model - View - Controller). Ý tưởng ở đây là chúng ta sử dụng các đối tượng ruby để quản lý logic và sử dụng ActiveRecord đảm bảo để các đối tượng được luu vào trong database.

Giả sử chúng ta có một database SQLite với một bảng dữ liệu là ducks và trong bảng có một trường tên là name, Chúng ta có thể sử dụng ActiveRecord và mở kết nối với database.

require 'activerecord'
ActiveRecord::Base.establish_connection :adapter => "sqlite3" ,
:database => "dbfile"

ActiveRecord::Base là một lớp quan trong nhất trong ActiveRecord. Nó không chỉ chứa các methods quan trọng để mở kết nối với database , nó cũng có các phương thức được dùng ở nhiều class khác nhau, như Duck

class Duck < ActiveRecord::Base
  validates_length_of :name, :maximum => 6
end

phương thức validates_length_of() là một Class Macro, trong trương hợp này trường name trong bảng Duck có độ dài không vượt quá 6 kí tự.

Theo quy ước, ActiveRecord sẽ tự động ánh xạ các đối tượng Duck tới bảng dữ liệu Ducks. Bằng cách nhìn vào lược đồ dữ liệu, và nó cũng tìm thấy thuộc tính name trong class Duck, và nó sẽ định nghĩa một phương thức Ghost để truy cập thuộc tính này. Và nhờ đó chúng ta có thể sử dụng lớp Duck để lưu thông tin.

my_duck = Duck.new
my_duck.name = "Test"
my_duck.valid? # => true
my_duck.save!

ActiveRecord::Base

Đầu tiền, các classes và methods trong ActiveRecord có thế làm chúng ta mất phương hướng,nếu như nhìn vào ví dụ trước chúng ta không tìm thấy phương thức validates_length_of() trong ActiveRecord::Base. Tìm vòng quanh ta có thể thấy method này nằm trong ActiveRecord::Validations. Khi gọi require 'active_record' thì tất cả các module được khai báo trong namespace ActiveRecord sẽ được tự tải.

module ActiveRecord
  autoload :Base, 'active_record/base'
  autoload :Batches, 'active_record/batches'
  autoload :Calculations, 'active_record/calculations'
  autoload :Callbacks, 'active_record/callbacks'
  # ...
  autoload :Timestamp, 'active_record/timestamp'
  autoload :Transactions, 'active_record/transactions'
  autoload :Validations, 'active_record/validations'
  # ...
end

ActiveRecord sẽ tải từng module thông qua phương thức autoload(), phương thức này giúp chúng ta loại bỏ nhiều file mà chúng ta thật sự không cần thết khi load nhiều file cùng một lúc. Nó đảm bảo các module và các file sẽ được load khi chúng ta dùng tới.

module ActiveRecord
class Base
  class << self # Class methods
  def find(*args) # ...
  def first(*args) # ...
  # ...
  end
  public
  def id # ...

ActiveRecord::Base định nghĩa một danh sách dài các phương thức class như là find(), first()... nó cũng khai báo một loạt các instance methods như save(), delete(). Tuy nhiên, chúng cũng chỉ là một phần nhỏ trong ActiveRecord::Base.

ActiveRecord::Validations

Trong phần trên, chúng ta đã sử dụng 2 phương thức valid?() và validates_length_of(), đây là 2 phương thức được khai báo trong module ActiveRecord::Validations

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
  end
end

Vậy chúng ta xem cơ chế làm viêc của ActiveRecord sẽ như thế nào. Đầu tiên, ActiveRecord::Validations sẽ định nghĩa các instance method giống như valid?(), sau đó khi 'ActiveRecord::Validations' được include vào trong ActiveRecord::Base thì mặc đinh ActiveRecord::Base sẽ có các instance method và class method của ActiveRecord::Validations.

alias_method_chain()

Để hiểu rõ hơn về alias_method_chain(), chúng ta xét ví dụ sau

class MyClass
  def greet
    puts "Hello!"
  end
end
MyClass.new.greet
=> Hello!

Mục đích bây giờ của chúng ta là thao tác với hàm MyClass#greet(), với Alias chúng ta có thể làm

module Module
  def alias_method_chain(target, feature)
    aliased_target, punctuation = target.to_s.sub(/([?!=])$/, '' ), $1
    yield(aliased_target, punctuation) if block_given?
    with_method, without_method = "#{aliased_target}_with_#{feature}#{punctuation}" ,
"#{aliased_target}_without_#{feature}#{punctuation}"
    alias_method without_method, target
    alias_method target, with_method
  end
end

Chúng ta hãy xem cách mà alias_method_chain() làm việc. Đầu tiên, chúng sẽ loại bỏ các ký tự đặc biệt ở cuối tên name của phương thức để đẩy nó vào cuối của alias mới. sau đó nó sẽ tính toán tên cho tất cả các aliases, rồi tiếp tục là các phương thức. cuối cùng là việc là việc thiết lập khả năng hoạt động của phương thức.


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í