Computer >> Máy Tính >  >> Lập trình >> Ruby

Linting và tự động định dạng mã Ruby với RuboCop

Linting là kiểm tra tự động mã nguồn để tìm các lỗi về kiểu dáng và lập trình. Việc kiểm tra này được thực hiện bởi một công cụ phân tích mã tĩnh được gọi là linter. Tuy nhiên, trình định dạng mã là một công cụ liên quan đến việc định dạng mã nguồn để nó tuân thủ nghiêm ngặt một bộ quy tắc được định cấu hình trước. Người viết mã thường sẽ báo cáo vi phạm, nhưng việc khắc phục sự cố thường tùy thuộc vào lập trình viên, trong khi trình định dạng mã có xu hướng áp dụng các quy tắc của nó trực tiếp vào mã nguồn, do đó tự động sửa lỗi định dạng.

Nhiệm vụ tạo ra một kiểu mã nhất quán hơn trong một dự án thường đòi hỏi sự ra đời của một công cụ định dạng và kẻ viền riêng biệt, nhưng trong một số trường hợp, một công cụ duy nhất sẽ có khả năng giải quyết cả hai mối quan tâm. Một ví dụ điển hình về cái sau là RuboCop, đây là công cụ mà chúng tôi sẽ xem xét rộng rãi trong bài viết này. Bạn sẽ học cách thiết lập nó trong dự án Ruby của mình và điều chỉnh các tùy chọn cấu hình của nó để đầu ra của nó phù hợp với mong đợi của bạn. Ngoài việc tích hợp nó vào quy trình phát triển địa phương của bạn, bạn cũng sẽ học cách biến nó thành một phần của quy trình tích hợp liên tục của mình.

Cài đặt RuboCop

Cài đặt RuboCop rất đơn giản thông qua RubyGems:

$ gem install rubocop

Kiểm tra phiên bản đã được cài đặt:

$ rubocop --version
1.18.3

Nếu bạn muốn sử dụng Bundler, hãy đặt đoạn mã bên dưới vào Gemfile của bạn và sau đó chạy bundle install . require: false phần nói với Bundler.require không yêu cầu đá quý cụ thể đó trong mã của bạn vì nó sẽ chỉ được sử dụng từ dòng lệnh.

gem 'rubocop', require: false

Kiểm tra phiên bản đã được cài đặt:

$ bundle exec rubocop --version
1.18.3

Chạy RuboCop

Bạn có thể chạy RuboCop bằng cách sử dụng cài đặt mặc định của nó trong dự án của mình bằng cách nhập rubocop (hoặc bundle exec rubocop nếu được cài đặt bằng Bundler). Nếu bạn không chuyển bất kỳ đối số nào cho lệnh, nó sẽ kiểm tra tất cả các tệp nguồn Ruby trong thư mục hiện tại, cũng như tất cả các thư mục con. Ngoài ra, bạn có thể chuyển một danh sách các tệp và thư mục cần được phân tích.

$ bundle exec rubocop
$ bundle exec rubocop src/lib

Không có bất kỳ cấu hình nào, RuboCop thực thi nhiều nguyên tắc được nêu trong Hướng dẫn phong cách Ruby do cộng đồng hướng tới. Sau khi chạy lệnh, bạn có thể gặp một số lỗi (vi phạm). Mỗi hành vi phạm tội được báo cáo đều được trang trí bằng tất cả thông tin cần thiết để giải quyết, chẳng hạn như mô tả về hành vi phạm tội, tệp và số dòng nơi xảy ra.

Linting và tự động định dạng mã Ruby với RuboCop

Ở cuối báo cáo, bạn sẽ thấy một dòng mô tả số lượng tệp đã kiểm tra, tổng số lần vi phạm và bao nhiêu lỗi vi phạm có thể được khắc phục tự động. Nếu bạn nối -a hoặc --auto-correct đối số, RuboCop sẽ cố gắng tự động sửa các vấn đề được tìm thấy trong các tệp nguồn của bạn (những vấn đề có tiền tố là [Correctable] ).

$ bundle exec rubocop -a

Linting và tự động định dạng mã Ruby với RuboCop

Lưu ý cách mỗi lỗi đã sửa hiện có tiền tố là [Corrected] . Bản tóm tắt về số lượng các lỗi vi phạm đã được sửa chữa cũng được trình bày ở cuối báo cáo. Trong ví dụ trên, có một lỗi khác có thể sửa được không được tự động sửa, ngay cả sau khi thêm -a lá cờ. Điều này là do một số chỉnh sửa tự động có thể thay đổi ngữ nghĩa của mã một chút, vì vậy RuboCop coi nó là không an toàn. Nếu bạn cũng muốn tự động sửa những lỗi này, hãy sử dụng -A hoặc --auto-correct-all cờ.

$ bundle exec rubocop -A

Linting và tự động định dạng mã Ruby với RuboCop

Một nguyên tắc chung cần tuân thủ là chạy bộ thử nghiệm của bạn sau khi sử dụng chức năng tự động sửa lỗi để đảm bảo rằng hành vi của mã của bạn không thay đổi đột ngột.

Định cấu hình RuboCop

RuboCop có thể được định cấu hình thông qua .rubocop.yml tệp được đặt ở gốc của dự án của bạn. Nếu bạn muốn sử dụng các kiểm tra giống nhau cho tất cả các dự án, bạn có thể đặt tệp cấu hình chung trong thư mục chính của mình (~/.rubocop.yml ) hoặc thư mục cấu hình XDG (~/.config/rubocop/config.yml ). Tệp cấu hình chung này sẽ được sử dụng nếu tệp cấu hình dự án có phạm vi cục bộ không được tìm thấy trong thư mục hiện tại hoặc các thư mục mẹ kế tiếp.

Cấu hình mặc định cho RuboCop được đặt trong thư mục chính cấu hình của nó (~/.config/rubocop/default.yml ), và tất cả các tệp cấu hình khác kế thừa từ nó. Điều này có nghĩa là khi thiết lập cấu hình dự án của bạn, bạn chỉ cần thực hiện các thay đổi khác với mặc định. Điều này có nghĩa là bật hoặc tắt một số kiểm tra hoặc thay đổi hành vi của chúng nếu chúng chấp nhận bất kỳ thông số nào.

RuboCop đề cập đến từng séc riêng lẻ như cảnh sát và mỗi người chịu trách nhiệm phát hiện một hành vi phạm tội cụ thể. Các cảnh sát có sẵn cũng được nhóm lại thành các bộ phận sau:

  • Cảnh sát tạo kiểu chủ yếu dựa trên Hướng dẫn tạo kiểu Ruby đã nói ở trên và họ kiểm tra tính nhất quán của mã của bạn.
  • Cảnh sát bố trí nắm được các vấn đề liên quan đến định dạng, chẳng hạn như việc sử dụng khoảng trắng.
  • Cảnh sát Lint phát hiện các lỗi có thể xảy ra trong mã của bạn, tương tự như ruby -w , nhưng với một loạt các kiểm tra bổ sung.
  • Cảnh sát chỉ số giải quyết các vấn đề liên quan đến phép đo mã nguồn, chẳng hạn như độ dài lớp và độ dài phương thức.
  • Cảnh sát đặt tên quan tâm đến các quy ước đặt tên.
  • Cảnh sát bảo mật giúp giải quyết các vấn đề bảo mật tiềm ẩn.
  • Cảnh sát Bundler kiểm tra các phương pháp không tốt trong các tệp Bundler (chẳng hạn như Gemfile ).
  • Cảnh sát Gemspec kiểm tra các hành vi xấu trong .gemspec tệp.

Cũng có thể mở rộng RuboCop thông qua các linters và bộ định dạng bổ sung. Bạn có thể tạo các tiện ích mở rộng của riêng mình hoặc tận dụng các tiện ích mở rộng hiện có nếu chúng có liên quan đến dự án của bạn. Ví dụ:một tiện ích mở rộng Rails có sẵn cho mục đích thực thi các phương pháp hay nhất của Rails và các quy ước về mã hóa.

Linting và tự động định dạng mã Ruby với RuboCop

Khi bạn tạo tệp cấu hình của mình lần đầu tiên, bạn sẽ nhận được một loạt thông báo cảnh báo về sự hiện diện của cảnh sát mới đã được thêm vào nhưng chưa được định cấu hình. Điều này là do RuboCop thêm cảnh sát mới trên mỗi bản phát hành và những cảnh sát này được đặt ở trạng thái chờ xử lý đặc biệt cho đến khi chúng được bật hoặc tắt rõ ràng trong cấu hình người dùng. Bạn có thể bật hoặc tắt từng cảnh sát được liệt kê trong thư riêng lẻ hoặc sử dụng đoạn mã bên dưới để bật tất cả cảnh sát mới (được khuyến nghị). Sau đó, các tin nhắn sẽ bị chặn.

# .rubocop.yml
AllCops:
  NewCops: enable

Nếu bạn không muốn loay hoay với các tệp cấu hình và vô số tùy chọn được cung cấp bởi RuboCop, hãy xem xét dự án Tiêu chuẩn. Nó phần lớn là một phiên bản RuboCop được cấu hình sẵn nhằm mục đích thực thi một phong cách nhất quán trong dự án Ruby của bạn mà không cho phép tùy chỉnh bất kỳ quy tắc nào của nó. Cuộc nói chuyện chớp nhoáng nơi nó được công bố lần đầu tiên cung cấp thêm chi tiết về nguồn gốc và động cơ của nó.

Bạn có thể cài đặt nó bằng cách thêm dòng sau vào Gemfile của bạn và sau đó chạy bundle install .

# Gemfile
gem "standard", group: [:development, :test]

Sau đó, bạn có thể thực thi Standard từ dòng lệnh như sau:

$ bundle exec standardrb

Thêm RuboCop vào một dự án hiện có

Hầu hết những người theo chủ nghĩa Ruby không có sự sang trọng khi làm việc trên các dự án greenfield. Phần lớn thời gian phát triển của chúng tôi được dành cho các cơ sở mã kế thừa có thể tạo ra một lượng lớn các hành vi phạm tội mà không thể giải quyết ngay lập tức. May mắn thay, RuboCop có một tính năng hữu ích tạo danh sách cho phép các hành vi vi phạm hiện có, có thể được giải quyết từ từ theo thời gian. Lợi ích là nó cho phép bạn giới thiệu linting cho dự án hiện tại của mình mà không bị tấn công bởi hàng núi lỗi in linting không thể quản lý trong khi gắn cờ bất kỳ vi phạm mới nào trong tương lai.

$ bundle exec rubocop

523 files inspected, 1018 offenses detected

Việc tạo tệp cấu hình cho phép có thể được thực hiện thông qua lệnh dưới đây:

$ bundle exec rubocop --auto-gen-config
Added inheritance from `.rubocop_todo.yml` in `.rubocop.yml`.
Created .rubocop_todo.yml.

--auto-gen-config tùy chọn thu thập tất cả các hành vi vi phạm và số lượng của chúng và tạo ra một .rubocop_todo.yml tệp trong thư mục hiện tại, nơi tất cả các hành vi vi phạm hiện tại được bỏ qua. Cuối cùng, nó gây ra .rubocop.yml kế thừa từ .rubocop_todo.yml để chạy RuboCop trên codebase một lần nữa sẽ không mang lại bất kỳ hành vi vi phạm nào.

$ bundle exec rubocop
523 files inspected, no offenses detected

Trong khi tạo tệp danh sách cho phép, RuboCop sẽ tắt hoàn toàn cảnh sát nếu số lần vi phạm vượt quá một ngưỡng nhất định (15 theo mặc định). Đây thường không phải là điều bạn muốn vì nó ngăn mã mới bị kiểm tra với cảnh sát đó do số lượng vi phạm hiện có. May mắn thay, có thể tăng ngưỡng để cảnh sát không bị vô hiệu hóa ngay cả khi số lượng vi phạm cao.

$ bundle exec rubocop --auto-gen-config --auto-gen-only-exclude --exclude-limit 10000

--auto-gen-only-exclude tùy chọn đảm bảo rằng mỗi cảnh sát trong danh sách cho phép có một Exclude chặn liệt kê tất cả các tệp đã xảy ra vi phạm, thay vì Max , đặt số lượng tệp bị loại trừ tối đa cho một cảnh sát. Đặt --exclude-limit cũng thay đổi số lượng tệp tối đa có thể được thêm vào Exclude chặn cho mỗi cảnh sát. Việc chỉ định một số tùy ý lớn hơn tổng số tệp đang được kiểm tra đảm bảo rằng không có cảnh sát nào sẽ bị vô hiệu hóa hoàn toàn và mọi mã mới được thêm vào tệp hiện có hoặc tệp mới sẽ được kiểm tra tương ứng.

Khắc phục các Vi phạm Hiện tại

Sau khi tạo .rubocop_todo.yml , điều quan trọng là không được quên các vi phạm hiện có mà phải từ từ giải quyết chúng lần lượt. Bạn có thể thực hiện việc này bằng cách xóa tệp khỏi Exclude chặn cảnh sát, sau đó sửa lỗi vi phạm được báo cáo, chạy bộ thử nghiệm của bạn để tránh tạo ra lỗi và cam kết. Khi bạn đã xóa tất cả các tệp khỏi cảnh sát, bạn có thể xóa cảnh sát khỏi tệp theo cách thủ công hoặc tạo lại tệp danh sách cho phép một lần nữa. Đừng quên sử dụng --auto-correct tùy chọn nếu có thể để làm cho quá trình nhanh hơn nhiều.

Áp dụng Hướng dẫn kiểu

RuboCop có thể cấu hình rất tốt, điều này làm cho nó khả thi cho bất kỳ loại dự án nào. Tuy nhiên, có thể mất nhiều thời gian để định cấu hình các quy tắc theo yêu cầu của bạn, đặc biệt nếu bạn không đồng ý với nhiều quy tắc mặc định. Trong những trường hợp như vậy, việc áp dụng một hướng dẫn phong cách hiện có có thể có lợi. Một số công ty đã phát hành hướng dẫn kiểu Ruby của họ để tiêu dùng công khai, chẳng hạn như Shopify và Airbnb. Sử dụng hướng dẫn kiểu ưa thích của bạn trong RuboCop có thể đạt được bằng cách thêm đá quý có liên quan vào Gemfile của bạn :

# Gemfile
gem "rubocop-shopify", require: false

Sau đó, yêu cầu nó trong cấu hình dự án của bạn:

# .rubocop.yml
inherit_gem:
  rubocop-shopify: rubocop.yml

Loại bỏ Lỗi Linting

Mặc dù RuboCop là một công cụ tuyệt vời, nhưng nó có thể mang lại kết quả dương tính giả theo thời gian hoặc đề xuất sửa mã theo cách gây bất lợi cho ý định của lập trình viên. Khi những tình huống như vậy phát sinh, bạn có thể bỏ qua vi phạm bằng một nhận xét trong mã nguồn. Bạn có thể đề cập đến các cảnh sát hoặc phòng ban riêng lẻ cần bị vô hiệu hóa, như được hiển thị bên dưới:

# rubocop:disable Layout/LineLength, Style
[..]
# rubocop:enable Layout/LineLength, Style

Hoặc bạn có thể vô hiệu hóa tất cả cảnh sát đối với một phần của mã trong một lần bị rơi:

# rubocop:disable all
[..]
# rubocop:enable all

Nếu bạn sử dụng chú thích cuối dòng, các cảnh sát được chỉ định sẽ bị vô hiệu hóa riêng trên dòng đó.

for x in (0..10) # rubocop:disable Style/For

Tích hợp Trình chỉnh sửa

Thật tiện lợi khi xem các cảnh báo và lỗi do RuboCop tạo ra khi bạn nhập mã vào trình chỉnh sửa thay vì phải chạy kiểm tra thông qua dòng lệnh mỗi lần. Rất may, tích hợp RuboCop có sẵn trong hầu hết các trình chỉnh sửa mã và IDE phổ biến, chủ yếu thông qua các plugin của bên thứ ba. Trong Visual Studio Code, tất cả những gì bạn cần làm là cài đặt tiện ích mở rộng Ruby này và đặt phần sau vào settings.json người dùng của bạn tệp:

{
  "ruby.lint": {
    "rubocop": true
  }
}

Nếu bạn sử dụng Vim hoặc Neovim, bạn có thể hiển thị chẩn đoán của RuboCop thông qua coc.nvim. Bạn cần cài đặt máy chủ ngôn ngữ Solargraph (gem install solargraph ), theo sau là phần mở rộng coc-solargraph (:CocInstall coc-solargraph ). Sau đó, hãy định cấu hình coc-settings.json của bạn tệp như hình dưới đây:

{
  "coc.preferences.formatOnSaveFiletypes": ["ruby"],
  "solargraph.autoformat": true,
  "solargraph.diagnostics": true,
  "solargraph.formatting": true
}

Linting và tự động định dạng mã Ruby với RuboCop

Thiết lập Móc cam kết trước

Một cách tuyệt vời để đảm bảo rằng tất cả mã Ruby trong một dự án được in và định dạng đúng cách trước khi được kiểm tra trong kiểm soát nguồn là thiết lập một móc cam kết trước Git chạy RuboCop trên mỗi tệp theo giai đoạn. Bài viết này sẽ hướng dẫn bạn cách thiết lập nó với Overcommit, một công cụ để quản lý và định cấu hình các hook cam kết trước của Git, nhưng bạn cũng có thể tích hợp RuboCop với các công cụ khác nếu bạn đã có quy trình làm việc pre-commit.

Đầu tiên, cài đặt Overcommit thông qua RubyGems và sau đó cài đặt nó trong dự án của bạn:

$ gem install overcommit
$ overcommit --install # at the root of your project

Lệnh thứ hai ở trên sẽ tạo một tệp cài đặt repo cụ thể (.overcommit.yml ) trong thư mục hiện tại và sao lưu mọi hook hiện có. Tệp này mở rộng cấu hình mặc định, vì vậy bạn chỉ cần chỉ định cấu hình của mình đối với các cấu hình mặc định. Ví dụ:bạn có thể bật móc cam kết trước RuboCop thông qua đoạn mã sau:

# .overcommit.yml
PreCommit:
  RuboCop:
    enabled: true
    on_warn: fail
    problem_on_unmodified_line: ignore
    command: ['bundle', 'exec', 'rubocop']

on_warn: fail cài đặt khiến Overcommit coi cảnh báo là lỗi, trong khi problem_on_unmodified_line: ignore khiến các cảnh báo và lỗi trên các dòng không được sắp xếp bị bỏ qua. Bạn có thể duyệt qua tất cả các tùy chọn hook có sẵn và phạm vi giá trị được chấp nhận của chúng trên trang GitHub của dự án. Bạn có thể cần chạy overcommit --sign sau khi thay đổi tệp cấu hình của bạn để các thay đổi có hiệu lực.

Linting và tự động định dạng mã Ruby với RuboCop

Đôi khi, nếu bạn muốn xác nhận một tệp không vượt qua tất cả các lần kiểm tra (chẳng hạn như một công việc đang tiến hành), bạn có thể bỏ qua các lần kiểm tra riêng lẻ trên cơ sở từng trường hợp:

$ SKIP=RuboCop git commit -m "WIP: Unfinished work"

Thêm RuboCop vào Quy trình làm việc CI của bạn

Chạy kiểm tra RuboCop trên mỗi yêu cầu kéo là một cách khác để ngăn mã bị định dạng xấu được hợp nhất vào dự án của bạn. Mặc dù bạn có thể thiết lập nó bằng bất kỳ công cụ CI nào, bài viết này sẽ chỉ thảo luận về cách chạy RuboCop thông qua GitHub Actions.

Bước đầu tiên là tạo .github/workflows ở thư mục gốc của dự án của bạn và một rubocop.yml tệp trong thư mục mới. Mở tệp trong trình chỉnh sửa của bạn và cập nhật nó như sau:

# .github/workflows/rubocop.yml
name: Lint code with RuboCop

on: [push, pull_request]

jobs:
  build:
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [macos-latest, ubuntu-latest, windows-latest]

    steps:
    - uses: actions/checkout@v2

    - name: Setup Ruby
      uses: ruby/setup-ruby@v1
      with:
        ruby-version: '3.0'
        bundler-cache: true

    - name: Run RuboCop
      run: bundle exec rubocop

Tệp dòng công việc ở trên mô tả một công việc sẽ được thực thi khi mã được đẩy lên GitHub hoặc khi một yêu cầu kéo được thực hiện đối với bất kỳ nhánh nào. Một job là một chuỗi các bước chạy tuần tự. Công việc cụ thể này sẽ chạy một lần trên các phiên bản Ubuntu, MacOS và Windows mới nhất do GitHub Actions cung cấp (như được định nghĩa bởi runs-onstrategy.matrix ). Bước đầu tiên kiểm tra mã trong kho lưu trữ, trong khi bước tiếp theo thiết lập chuỗi công cụ Ruby và các phụ thuộc, và bước cuối cùng thực thi RuboCop.

Khi bạn đã chỉnh sửa xong tệp, hãy lưu nó, cam kết và đẩy lên GitHub. Sau đó, bạn sẽ nhận được một màn hình nội tuyến về bất kỳ sự cố nào được báo cáo về các yêu cầu đăng ký và kéo tiếp theo.

Linting và tự động định dạng mã Ruby với RuboCop

Bộ định dạng tự động thay thế

Mặc dù RuboCop cung cấp khả năng định dạng tự động toàn diện, điều quan trọng là phải biết các công cụ thay thế trong trường hợp nó không đáp ứng đầy đủ nhu cầu của bạn.

Đẹp hơn

Prettier bắt đầu như một trình định dạng mã được xác nhận cho JavaScript, nhưng hiện tại nó hỗ trợ nhiều ngôn ngữ khác, bao gồm cả Ruby. Việc cài đặt plugin Ruby của nó rất dễ dàng:thêm prettier đá quý vào Gemfile của bạn và sau đó chạy bundle .

# Gemfile
gem 'prettier'

Tại thời điểm này, bạn có thể định dạng mã Ruby của mình bằng Prettier thông qua lệnh sau:

$ bundle exec rbprettier --write '**/*.rb'

Một số quy tắc của Prettier xung đột với RuboCop, vì vậy cần phải tắt kiểm tra định dạng của quy tắc sau để nó không gây trở ngại cho Prettier. May mắn thay, thật dễ dàng để tắt kiểm tra RuboCop xung đột hoặc không cần thiết với Prettier. Tất cả những gì bạn cần làm là kế thừa cấu hình RuboCop của Prettier ở đầu .rubocop.yml trong dự án của bạn tệp:

# .rubocop.yml
inherit_gem:
  prettier: rubocop.yml

Khi bạn chạy RuboCop (với bundle exec rubocop ) kể từ bây giờ, nó sẽ không báo cáo bất kỳ lỗi nào liên quan đến bố cục, mở đường cho Prettier sửa chúng theo các quy tắc riêng của nó. Bạn cũng có thể định cấu hình đầu ra của Prettier thông qua tệp cấu hình của nó, tệp này có thể được chia sẻ giữa mã JavaScript và Ruby trong cùng một dự án.

RubyFmt

RubyFmt là một trình định dạng mã hoàn toàn mới được viết bằng Rust và hiện đang được phát triển tích cực. Giống như Prettier, nó được thiết kế để trở thành một công cụ định dạng chứ không phải một công cụ phân tích mã. Nó vẫn chưa thấy một bản phát hành ổn định, vì vậy bạn có thể nên tiếp tục áp dụng nó ngay bây giờ, nhưng nó chắc chắn là một trong những điều cần chú ý.

Kết luận

Linting và mã định dạng tự động mang lại rất nhiều lợi ích cho cơ sở mã, đặc biệt là trong bối cảnh của một nhóm các nhà phát triển. Ngay cả khi bạn không thích được hướng dẫn cách định dạng mã của mình, bạn cần lưu ý rằng in linting không chỉ dành cho bạn. Nó cũng dành cho những người khác mà bạn cộng tác để mọi người có thể tuân theo các quy ước giống nhau, do đó loại bỏ những hạn chế của việc xử lý nhiều kiểu mã hóa trong cùng một dự án.

Điều quan trọng nữa là không coi đầu ra của máy linter là phúc âm, vì vậy hãy cố gắng cấu hình nó theo cách mang lại nhiều lợi ích nhất cho bạn mà không bị phân tâm khỏi các mục tiêu chính của bạn. Với cài đặt cấu hình mở rộng của RuboCop, đây không phải là vấn đề. Tuy nhiên, nếu bạn thấy rằng việc định cấu hình RuboCop đang chiếm quá nhiều thời gian của mình, bạn có thể sử dụng hướng dẫn kiểu được xác định trước, như đã thảo luận trước đó hoặc áp dụng Tiêu chuẩn cho một giải pháp thay thế không cần cấu hình mà mọi người có thể sử dụng thay vì lo lắng về các chi tiết nhỏ. .

Cảm ơn bạn đã đọc và chúc bạn viết mã vui vẻ!