Awesome print là gem đẹp định dạng đầu ra trong irb & pry dễ đọc hơn.
Ví dụ…
Đây hiển thị hash với awesome_print:

Nhưng nó hoạt động như thế nào?
“Sự thật chỉ tìm thấy ở một nơi: mã nguồn.” ― Robert C. Martin
Hãy xem mã nguồn!
In ấn Awesome
Tôi thích bắt đầu đọc mã với tổng quan cấu trúc project (file & folder), rồi đặt câu hỏi tập trung khám phá.
Câu hỏi đầu tiên:
awesome_print thay đổi đầu ra pry như thế nào?
Đặt giả thuyết:
“Gem này có thể thay thế $stdout để capture đầu ra pry & làm đẹp.”
Nhưng triển khai này không cho tùy chỉnh đầu ra, như pretty_print (thư viện chuẩn Ruby).
Cũng phải parse, thậm chí eval mã, ý kiến không hay!
Tiếp:
Tôi xem gem nạp như thế nào.
Điều này cho entry point vào mã.
Nạp Awesome Print
Để load awesome_print trên pry làm:
require 'awesome_print' AwesomePrint.pry!
Bây giờ tìm pry! định nghĩa ở đâu.
Dùng “search in directory” trong Atom.
Mã:
def pry!
Pry.print = proc { |output, value| output.puts value.ai } if defined?(Pry)
end
Có vẻ Pry cho phép sửa đầu ra bằng set giá trị print.
Vậy trả lời câu hỏi “bắt đầu đâu” 🙂
Muốn học thêm “proc” này? Đọc “Hướng dẫn tối thượng Blocks, Procs & Lambdas”. Chương mẫu từ sách Ruby Deep Dive.
Proc này nhận 2 tham số:
- output
- value
Và gọi 2 phương thức trên đối tượng đó.
Câu hỏi tiếp:
Phương thức ai này là gì?
Đó là phương thức định nghĩa trên module Kernel:
def ai(options = {})
ap = AwesomePrint::Inspector.new(options)
awesome = ap.awesome(self)
if options[:html]
awesome = "</pre>#{awesome}</pre>"
end
awesome = awesome.html_safe if defined? ActiveSupport
awesome
end
alias :awesome_inspect :ai
Vì mọi object include Kernel mặc định, chúng có phương thức ai.
Bây giờ đào sâu hơn xem class Inspector hoạt động ra sao.
Class Inspector
Ngay sau mở inspector.rb thấy hash options khổng lồ trong initialize.
Phần của nó:
@options = {
indent: 4, # Số space indent
index: true, # Hiển thị index mảng
html: false, # Dùng ANSI color thay HTML
multiline: true, # Hiển thị đa dòng
# ...
}
Sau đó code này:
@formatter = AwesomePrint::Formatter.new(self) @indentator = AwesomePrint::Indentator.new(@options[:indent].abs) Thread.current[AP] ||= []
Khởi tạo 2 object xử lý format & indentation.
Cái Thread.current là gì?
Cho truy cập thread hiện tại. Dù không dùng thread app vẫn có thread “main”.
Hằng AP định nghĩa đầu inspector.rb:
AP = :__awesome_print__
Vậy đang xảy ra gì?
Awesome print dùng Thread.current & key __awesome_print__ lưu data chỉ có trên thread hiện tại.
Dùng tránh vấn đề multi-threading.
Định dạng Awesome
Xem mã format output, xảy ra trong class AwesomePrint::Formatter.
Cách hoạt động: inspector (object tạo bởi ai) gọi format.
def unnested(object) @formatter.format(object, printable(object)) end
Phương thức format trên Formatter tìm cách tốt nhất xử lý loại object & gọi phương thức khác dùng metaprogramming.
Phương thức:
def format(object, type = nil)
core_class = cast(object, type)
awesome = if core_class != :self
send(:"awesome_#{core_class}", object) # Core formatters.
else
awesome_self(object, type) # Catch all fallback object.inspect.
end
awesome
end
Để hiểu Formatter cần xem cast:
def cast(object, type) CORE.grep(type)[0] || :self end
Hằng CORE là mảng symbol class Ruby core & :self symbol nghĩa “không tìm thấy” (chỉ ví dụ này).
CORE = [:array, :bigdecimal, :class, :dir, :file, :hash, :method, :rational, :set, :struct, :unboundmethod]
Điều xảy ra:
Nếu object format nằm trong danh sách “core class” nó nhận format chuyên biệt.
Ngược lại, nhận format chung.
Formatter chuyên biệt định nghĩa dưới lib/awesome_print/formatters/ bao gồm Array, Hash & Class.
Ví dụ formatter cho class:
def format
superclass = klass.superclass
if superclass
colorize("#{klass.inspect} < #{superclass}", :class)
else
colorize(klass.inspect, :class)
end
end
Bạn tự viết formatter nếu muốn.
Tóm tắt
Bạn đã học gem Awesome Print, cho hiển thị đối tượng như mảng & hash đẹp.
Hy vọng bạn thích & học được điều mới!
Vui lòng chia sẻ bài viết trên mạng xã hội yêu thích để nhiều người học 🙂