12/08/2018, 16:59

Creating Configuration Objects in Ruby

Simple Configuration Objects Nhiều Application đơn giản thì thường không cần 1 hệ thống config phức tạp cho nên có thể dùng 1 đối tượng config đơn giản chẳng hạn như 1 hash và định dạng file YAML. Kho lưu trữ key - Value là khái niệm đơn giản nhất về đối tượng cấu hình cần phải là gì. Và Hash của ...

Simple Configuration Objects

Nhiều Application đơn giản thì thường không cần 1 hệ thống config phức tạp cho nên có thể dùng 1 đối tượng config đơn giản chẳng hạn như 1 hash và định dạng file YAML. Kho lưu trữ key - Value là khái niệm đơn giản nhất về đối tượng cấu hình cần phải là gì. Và Hash của Ruby là khối xây dựng cơ bản cho tất cả các triển khai đơn giản của nó - cho dù đó là một đối tượng hash đơn lẻ hoặc một đối tượng hash với methods.

config = {}

config[:my_key] = :value
config[:my_key]
# => :value

Một cách hơi phức tạp hơn là một phương pháp tiếp cận hướng đối tượng tự động định nghĩa các phương pháp để hoạt động như các phím và các giá trị trả về của chúng như các giá trị. Một đối tượng cấu hình Ruby phổ biến có mặt giữa giữa hai phương pháp này là đối tượng OpenStruct. Nó sử dụng một hash nội bộ nhưng cũng xác định các phương pháp cho mỗi mục bạn thiết lập để chúng có thể được lấy một trong hai cách.

require 'ostruct'

config = OpenStruct.new

config.my_key = :value
config.my_key
# => :value
config[:my_key]
# => :value

Một Hash Ruby sẽ trả về nil như mặc định cho một khoá được sử dụng cho tra cứu nhưng chưa được thiết lập. Điều này không sao đối với các dự án thô sơ và thiết kế đơn giản, nhưng nó không phải là cách OOP, và đôi khi bạn sẽ muốn một cái gì đó khác như mặc định. Hash có một phương pháp default_proc trên đó, từ đó bạn có thể định nghĩa hành vi mặc định cho giá trị trả về cũng như gán cho các khóa không xác định. Nếu bạn muốn một hash để tạo khoá lồng nhau sâu trong một lần, bạn có thể có bảng băm bên trong đệ quy trả về một băm nội trong trống.

module MyProject
  def self._hash_proc
    ->hsh,key{
      hsh[key] = {}.tap do |h|
        h.default_proc = _hash_proc()
      end
    }
  end

  def self.config
    @config ||= begin
      hsh = Hash.new
      hsh.default_proc = _hash_proc()
      hsh
    end
  end
end


MyProject.config
# => {}

MyProject.config[:apple][:banana][:cherry] = 3

MyProject.config
# => {:apple=>{:banana=>{:cherry=>3}}}

Nhưng trong trường hợp này, chúng ta chỉ trao đổi một số cho một hash trống rỗng mà không đạt được các tiêu chuẩn lập trình hướng đối tượng. Đó không phải là điều xấu, và nó là tuyệt vời cho các dự án nhỏ hoặc đơn giản. Tuy nhiên, giá trị mặc định ở đây không có ý nghĩa đối với chúng ta khi nó chưa được thiết lập, vì vậy chúng ta phải xây dựng trong guards và hành vi mặc định để làm việc xung quanh kiểu mô hình này. Rails cung cấp một loại thay thế của băm bạn có thể sử dụng được gọi là HashWithIndifferentAccess, mà sẽ cho phép bạn sử dụng cả một chuỗi hoặc một biểu tượng như cùng một chìa khóa cho một giá trị.

config = HashWithIndifferentAccess.new

config["asdf"] = 4

config[:asdf]
# => 4

Better Configuration Objects

Nếu bạn sử dụng Rails, bạn sẽ rất vui khi biết rằng họ có một hệ thống cho các đối tượng cấu hình có sẵn cho bạn để sử dụng - và nó khá đơn giản.

class DatabaseConfig
  include ActiveSupport::Configurable

  config_accessor :database_api do
    DummyDatabaseAPI.new
  end
end

Trong đoạn mã ở trên, config_accessor tạo ra các phương thức cấu hình thích hợp trên lớp DatabaseConfig, và chúng sẽ cung cấp một cá thể DummyDatabaseAPI làm đối tượng giá trị mặc định. Điều này là tốt đẹp bởi vì chúng ta có thể định nghĩa một số hành vi mặc định khi một cơ sở dữ liệu thích hợp đã không được cấu hình và thiết lập nào được nêu ra. Và để cập nhật cơ sở dữ liệu API trên thể hiện đối tượng DatabaseConfig, chúng ta chỉ cần gọi phương thức setter database_api =. Được rõ ràng với phương pháp cấu hình các phím và trả lại đối tượng vịt gõ là thực hành tốt và nên làm cho các cơ sở mã thú vị hơn để làm việc với trong tương lai. Trong bài viết trước, chúng tôi đã giới thiệu "Tạo các công cụ dòng lệnh mạnh mẽ trong Ruby". Nó có một số công cụ, chẳng hạn như slop, có tùy chọn đầu vào dòng lệnh và cho chúng ta một đối tượng cấu hình bao gồm các giá trị mặc định cho bất cứ điều gì không được thiết lập thông qua dòng lệnh. Điều này rất hữu ích khi các cấu hình đon giản.

0