+1

Hướng dẫn cho người mới bắt đầu về RuboCop trong Rails

I. RuboCop là gì?

RuboCop là một trình phân tích code tĩnh, nó phân tích code dựa trên các phương pháp hay nhất được các nhà phát triển Ruby trên khắp thế giới tuân theo và được định nghĩa trên hướng dẫn về kiểu Ruby của cộng đồng .

Ngoài việc phân tích code, nó còn cung cấp cho chúng ta tính năng tự động định dạng code và sửa các cảnh báo bên trong code của chúng ta.

Nếu bạn đến từ nền tảng Javascript, bạn có thể đã nghe nói về ESLint.

  • RuboCop là ESLint cho Ruby

Ngoài Ruby, RuboCop cũng cung cấp gem để thực hiện các quy tắc trên các phần mở rộng khác nhau như Rails, Minitest, RSpec, v.v.

II. Tại sao nên sử dụng RuboCop?

Vậy tại sao chúng ta cần RuboCop? Việc sử dụng RuboCop trong các dự án giúp ích được gì?

Dưới đây là một số lý do tại sao các dự án muốn sử dụng RuboCop:

1. Clean Code

Tất cả chúng ta đều muốn viết code rõ ràng tuân theo các phương pháp hay nhất được các nhà phát triển trên khắp thế giới tuân theo. Các phương pháp hay nhất đến từ kinh nghiệm, có thể mất một vài năm để biết về ngôn ngữ và biết các mô hình nên tránh và mô hình tốt để làm theo nếu chúng ta chỉ dựa vào bản thân mình.

Với RuboCop, chúng ta có lợi thế là không cần phải có kinh nghiệm vì các phương pháp hay nhất đã được đóng gói thành quy tắc và chuyển cho chúng ta bên trong gem "rubocop". RuboCop đưa ra các cảnh báo bất cứ khi nào chúng ta vi phạm các quy tắc được định cấu hình cho các phương pháp hay nhất và sau khi khắc phục các vấn đề này, phần lớn code của chúng ta luôn cleandễ hiểu.

2. Giúp quá trình xem xét code dễ dàng

Mục đích chính của việc xem xét code là sửa lỗi logic trong codes hoặc sửa các lỗ hổng bảo mật hoặc thảo luận về con đường mà chúng ta đã thực hiện để phát triển tính năng.

Hãy tưởng tượng một tình huống mà chúng ta đẩy một đoạn code có lỗi đánh máy và người đánh giá phát hiện ra lỗi đó, sau đó nhận xét về nó để sửa vì rõ ràng là không ai muốn đưa đoạn code có lỗi đánh máy đó lên staging hoặc production. Điều này sẽ làm mất đáng kể thời gian phát triển và tiềm ẩn các incident không đáng có.

RuboCop đảm bảo rằng code có vấn đề sẽ không bao giờ được pass khi đẩy pull request.

3. Thực hành tốt nhất là chẳng có gì phù hợp với tất cả

Thông thường, mỗi người sẽ có phong cách code khác nhau, dẫn đến một dự án thường có những template code riêng để hợp nhất code cho cả dự án. Với RuboCop, nó cũng có thể dễ dàng format lại cả phong cách lẫn gợi ý các phưong pháp code tốt, tránh accs đoạn bad code.

III. Thiết lập RuboCop trong Rails

Trong bài viết này, chúng ta sẽ cài đặt gem 'rubocop' chính để thực hiện các quy tắc trong code Ruby cùng với phần mở rộng 'rubocop-rails' cho Rails.

1. Thêm gem vào Gemfile

Thêm phần sau vào Gemfile bên trong nhóm :development, :test

group :development, :test do
  # enforce rails best practice with rubocop
  gem 'rubocop', '~> 1.18.0', require: false
  gem 'rubocop-performance', '~> 1.11.0', require: false
  gem 'rubocop-rails', '~> 2.11.0', require: false
end

LƯU Ý : bạn có thể cập nhật bản mới nhất phù hơpj cho dự án của mình.

Ở trên thêm các gem sau vào Gemfile:

  • rubocop: Đối với Ruby
  • rubocop-performance: Đối với các quy tắc liên quan đến code performance
  • rubocop-rails: Đối với Rails

2. Cài đặt Gem trong Dự án

Cài đặt rubocop trên toàn dự án.

gem install rubocop

Điều này sẽ giúp chúng tôi chạy các lệnh được cung cấp bởi gem 'rubocop' như định dạng tự động, chạy rubocop trong dự án, v.v.

3. Thêm file config

Để kiểm soát (bật / tắt) các quy tắc, chúng ta cần tạo tệp cấu hình cho mỗi tiện ích mở rộng. Nếu không có tệp thì RuboCop sẽ bật các tiện ích mở rộng mặc định. Tôi thích có các tệp cấu hình vì nó mang lại sự linh hoạt cho nhóm.

Hãy tạo các tệp cấu hình cho RuboCop và các phần mở rộng của nó:

  $ cd /path/to/our/project
  $ touch .rubocop.yml
  $ touch .rubocop-performance.yml
  $ touch .rubocop-rails.yml

4. Thêm quy tắc vào file config

Để custom các quy tắc bạn có thể tìm thấy nó tại RuboCop Configuration Files for Rails. Hãy cập nhật các tệp cấu hình và thêm các quy tắc cho Ruby và các phần mở rộng đã cài đặt.

Ruby

# .rubocop.yml

# The behavior of RuboCop can be controlled via the .rubocop.yml
# configuration file. It makes it possible to enable/disable
# certain cops (checks) and to alter their behavior if they accept
# any parameters. The file can be placed either in your home
# directory or in some project directory.
#
# RuboCop will start looking for the configuration file in the directory
# where the inspected file is and continue its way up to the root directory.
#

inherit_from:
  - '.rubocop-performance.yml'
  - '.rubocop-rails.yml'

require:
  - rubocop-performance
  - rubocop-rails

AllCops:
  TargetRubyVersion: 2.7
  TargetRailsVersion: 6.0
  Exclude:
    - '**/db/migrate/*'
    - 'db/schema.rb'
    - '**/Gemfile.lock'
    - '**/Rakefile'
    - '**/rails'
    - '**/vendor/**/*'
    - '**/spec_helper.rb'
    - 'node_modules/**/*'
    - 'bin/*'

###########################################################
###################### RuboCop ############################
###########################################################

# You can find all configuration options for rubocop here: https://docs.rubocop.org/rubocop/cops_bundler.html

###########################################################
####################### Gemspec ###########################
###########################################################

Gemspec/DateAssignment: # (new in 1.10)
  Enabled: true

###########################################################
######################## Layout ###########################
###########################################################

Layout/ClassStructure:
  ExpectedOrder:
    - module_inclusion
    - constants
    - association
    - public_attribute_macros
    - public_delegate
    - macros
    - initializer
    - public_class_methods
    - public_methods
    - protected_attribute_macros
    - protected_methods
    - private_attribute_macros
    - private_delegate
    - private_methods

Layout/EmptyLineAfterMultilineCondition:
  Enabled: true

Layout/EmptyLinesAroundAttributeAccessor:
  Enabled: true

Layout/FirstArrayElementIndentation:
  EnforcedStyle: consistent

Layout/FirstArrayElementLineBreak:
  Enabled: true

Layout/FirstHashElementIndentation:
  EnforcedStyle: consistent

Layout/FirstHashElementLineBreak:
  Enabled: true

Layout/LineEndStringConcatenationIndentation: # (new in 1.18)
  Enabled: true

Layout/LineLength:
  Max: 150
  Exclude:
    - '**/spec/**/*'

Layout/MultilineArrayBraceLayout:
  EnforcedStyle: new_line

Layout/MultilineOperationIndentation:
  EnforcedStyle: indented

Layout/MultilineHashBraceLayout:
  EnforcedStyle: new_line

Layout/MultilineHashKeyLineBreaks:
  Enabled: true

Layout/MultilineMethodCallBraceLayout:
  EnforcedStyle: new_line

Layout/MultilineMethodDefinitionBraceLayout:
  EnforcedStyle: new_line

Layout/SpaceAroundMethodCallOperator:
  Enabled: true

Layout/SpaceBeforeBrackets: # (new in 1.7)
  Enabled: true

Layout/SpaceInLambdaLiteral:
  EnforcedStyle: require_space


###########################################################
######################## Lint #############################
###########################################################

Lint/AmbiguousAssignment: # (new in 1.7)
  Enabled: true

Lint/AmbiguousBlockAssociation:
  Exclude:
    - '**/spec/**/*'

Lint/AssignmentInCondition:
  AllowSafeAssignment: false

Lint/BinaryOperatorWithIdenticalOperands:
  Enabled: true

Lint/DeprecatedConstants: # (new in 1.8)
  Enabled: true

Lint/DeprecatedOpenSSLConstant:
  Enabled: true

Lint/DuplicateBranch: # (new in 1.3)
  Enabled: true

Lint/DuplicateElsifCondition:
  Enabled: true

Lint/DuplicateRegexpCharacterClassElement: # (new in 1.1)
  Enabled: true

Lint/DuplicateRequire:
  Enabled: true

Lint/DuplicateRescueException:
  Enabled: true

Lint/EmptyBlock: # (new in 1.1)
  Enabled: true

Lint/EmptyClass: # (new in 1.3)
  Enabled: true

Lint/EmptyConditionalBody:
  Enabled: true

Lint/EmptyFile:
  Enabled: true

Lint/EmptyInPattern: # (new in 1.16)
  Enabled: true

Lint/FloatComparison:
  Enabled: true

Lint/LambdaWithoutLiteralBlock: # (new in 1.8)
  Enabled: true

Lint/MissingSuper:
  Enabled: true

Lint/MixedRegexpCaptureTypes:
  Enabled: true

Lint/NoReturnInBeginEndBlocks: # (new in 1.2)
  Enabled: true

Lint/NumberConversion:
  Enabled: true

Lint/NumberedParameterAssignment: # (new in 1.9)
  Enabled: true

Lint/OrAssignmentToConstant: # (new in 1.9)
  Enabled: true

Lint/RaiseException:
  Enabled: true

Lint/RedundantDirGlobSort: # (new in 1.8)
  Enabled: true

Lint/SelfAssignment:
  Enabled: true

Lint/SymbolConversion: # (new in 1.9)
  Enabled: true

Lint/ToEnumArguments: # (new in 1.1)
  Enabled: true

Lint/TrailingCommaInAttributeDeclaration:
  Enabled: true

Lint/TripleQuotes: # (new in 1.9)
  Enabled: true

Lint/UnexpectedBlockArity: # (new in 1.5)
  Enabled: true

Lint/UnmodifiedReduceAccumulator: # (new in 1.1)
  Enabled: true

Lint/UnusedBlockArgument:
  IgnoreEmptyBlocks: false

Lint/UnusedMethodArgument:
  IgnoreEmptyMethods: false

Lint/UselessMethodDefinition:
  Enabled: true

###########################################################
######################## Metric ###########################
###########################################################

Metrics/AbcSize:
 Max: 45

Metrics/BlockLength:
  CountComments: false
  Max: 50
  Exclude:
    - '**/spec/**/*'
    - '**/*.rake'
    - '**/factories/**/*'
    - '**/config/routes.rb'

Metrics/ClassLength:
  CountAsOne: ['array', 'hash']
  Max: 150

Metrics/CyclomaticComplexity:
  Max: 10

Metrics/MethodLength:
  CountAsOne: ['array', 'hash']
  Max: 30

Metrics/ModuleLength:
  CountAsOne: ['array', 'hash']
  Max: 250
  Exclude:
    - '**/spec/**/*'

Metrics/PerceivedComplexity:
  Max: 10

###########################################################
######################## Naming ###########################
###########################################################

Naming/InclusiveLanguage: # (new in 1.18)
  Enabled: true

###########################################################
######################## Style ############################
###########################################################

Style/AccessorGrouping:
  Enabled: true

Style/ArgumentsForwarding: # (new in 1.1)
  Enabled: true

Style/ArrayCoercion:
  Enabled: true

Style/AutoResourceCleanup:
  Enabled: true

Style/BisectedAttrAccessor:
  Enabled: true

Style/CaseLikeIf:
  Enabled: true

Style/ClassAndModuleChildren:
  Enabled: false

Style/CollectionCompact: # (new in 1.2)
  Enabled: true

Style/CollectionMethods:
  Enabled: true

Style/CombinableLoops:
  Enabled: true

Style/CommandLiteral:
  EnforcedStyle: percent_x

Style/ConstantVisibility:
  Enabled: true

Style/Documentation:
  Enabled: false

Style/DocumentDynamicEvalDefinition: # (new in 1.1)
  Enabled: true

Style/EndlessMethod: # (new in 1.8)
  Enabled: true

Style/ExplicitBlockArgument:
  Enabled: true

Style/GlobalStdStream:
  Enabled: true

Style/HashConversion: # (new in 1.10)
  Enabled: true

Style/HashEachMethods:
  Enabled: true

Style/HashExcept: # (new in 1.7)
  Enabled: true

Style/HashLikeCase:
  Enabled: true

Style/HashTransformKeys:
  Enabled: true

Style/HashTransformValues:
  Enabled: true

Style/IfWithBooleanLiteralBranches: # (new in 1.9)
  Enabled: true

Style/ImplicitRuntimeError:
  Enabled: true

Style/InlineComment:
  Enabled: true

Style/InPatternThen: # (new in 1.16)
  Enabled: true

Style/IpAddresses:
  Enabled: true

Style/KeywordParametersOrder:
  Enabled: true

Style/MethodCallWithArgsParentheses:
  Enabled: true

Style/MissingElse:
  Enabled: true

Style/MultilineInPatternThen: # (new in 1.16)
  Enabled: true

Style/MultilineMethodSignature:
  Enabled: true

Style/NegatedIfElseCondition: # (new in 1.2)
  Enabled: true

Style/NilLambda: # (new in 1.3)
  Enabled: true

Style/OptionalBooleanParameter:
  Enabled: true

Style/QuotedSymbols: # (new in 1.16)
  Enabled: true

Style/RedundantArgument: # (new in 1.4)
  Enabled: true

Style/RedundantAssignment:
  Enabled: true

Style/RedundantBegin:
  Enabled: true

Style/RedundantFetchBlock:
  Enabled: true

Style/RedundantFileExtensionInRequire:
  Enabled: true

Style/RedundantSelfAssignment:
  Enabled: true

Style/SingleArgumentDig:
  Enabled: true

Style/StringChars: # (new in 1.12)
  Enabled: true

Style/StringConcatenation:
  Enabled: true

Style/SwapValues: # (new in 1.1)
  Enabled: true

Rails

# .rubocop-rails.yml

###########################################################
#################### RuboCop Rails ########################
###########################################################

# You can find all configuration options for rubocop-rails here: https://docs.rubocop.org/rubocop-rails/cops_rails.html

Rails/ActiveRecordCallbacksOrder:
  Enabled: true

Rails/AddColumnIndex: # (new in 2.11)
  Enabled: true

Rails/AfterCommitOverride:
  Enabled: true

Rails/AttributeDefaultBlockValue: # (new in 2.9)
  Enabled: true

Rails/DefaultScope:
  Enabled: true

Rails/EagerEvaluationLogMessage: # (new in 2.11)
  Enabled: true

Rails/ExpandedDateRange: # (new in 2.11)
  Enabled: true

Rails/FindById:
  Enabled: true

Rails/I18nLocaleAssignment: # (new in 2.11)
  Enabled: true

Rails/Inquiry:
  Enabled: true

Rails/MailerName:
  Enabled: true

Rails/MatchRoute:
  Enabled: true

Rails/NegateInclude:
  Enabled: true

Rails/OrderById:
  Enabled: true

Rails/Pluck:
  Enabled: true

Rails/PluckId:
  Enabled: true

Rails/PluckInWhere:
  Enabled: true

Rails/RenderInline:
  Enabled: true

Rails/RenderPlainText:
  Enabled: true

Rails/SaveBang:
  Enabled: true
  AllowImplicitReturn: false

Rails/ShortI18n:
  Enabled: true

Rails/SquishedSQLHeredocs: # (new in 2.8)
  Enabled: true

Rails/TimeZoneAssignment: # (new in 2.10)
  Enabled: true

Rails/UnusedIgnoredColumns: # (new in 2.11)
  Enabled: true

Rails/WhereEquals: # (new in 2.9)
  Enabled: true

Rails/WhereExists:
  Enabled: true

Rails/WhereNot:
  Enabled: true

Performance

.rubocop-performance.yml

###########################################################
#################### RuboCop Performance ##################
###########################################################

# You can find all configuration options for rubocop-performance here: https://docs.rubocop.org/rubocop-performance/

Performance/AncestorsInclude: # (new in 1.7)
  Enabled: true

Performance/BigDecimalWithNumericArgument: # (new in 1.7)
  Enabled: true

Performance/BlockGivenWithExplicitBlock: # (new in 1.9)
  Enabled: true

Performance/CollectionLiteralInLoop: # (new in 1.8)
  Enabled: true

Performance/ConstantRegexp: # (new in 1.9)
  Enabled: true

Performance/MapCompact: # (new in 1.11)
  Enabled: true

Performance/MethodObjectAsBlock: # (new in 1.9)
  Enabled: true

Performance/RedundantEqualityComparisonBlock: # (new in 1.10)
  Enabled: true

Performance/RedundantSortBlock: # (new in 1.7)
  Enabled: true

Performance/RedundantSplitRegexpArgument: # (new in 1.10)
  Enabled: true

Performance/RedundantStringChars: # (new in 1.7)
  Enabled: true

Performance/ReverseFirst: # (new in 1.7)
  Enabled: true

Performance/SortReverse: # (new in 1.7)
  Enabled: true

Performance/Squeeze: # (new in 1.7)
  Enabled: true

Performance/StringInclude: # (new in 1.7)
  Enabled: true

Performance/Sum: # (new in 1.8)
  Enabled: true

5. Chạy RuboCop

Chúng ta có tùy chọn để chạy RuboCop trên:

  • Toàn bộ dự án
  • Các tệp bên trong một thư mục
  • Chỉ trên một tệp duy nhất

Sau khi chạy các lệnh của RuboCop trong dòng lệnh, chúng ta sẽ được trình bày về các vấn đề được tìm thấy trong code của chúng ta bên trong dự án, sau đó chúng ta có thể khắc phục thủ công hoặc cũng có tùy chọn để tự động sửa các vấn đề trong hầu hết các trường hợp.

Toàn bộ dự án

  $ cd /path/to/your/project
  $ rubocop

Các tệp bên trong một thư mục

  $ rubocop app

Tệp đơn

  $ rubocop app/models/user.rb

6. Cảnh báo tự động sửa

RubCop cũng cung cấp tính năng tự động sửa các vấn đề trong code của chúng ta.

Có một số điều cần lưu ý về tính năng tự động sửa:

  • Đối với một số lỗi, không thể thực hiện sửa lỗi tự động.
  • Một số chỉnh sửa tự động có thể có vẫn chưa được thực hiện.
  • Một số chỉnh sửa tự động có thể thay đổi (một chút) ngữ nghĩa của mã, nghĩa là chúng sẽ tạo ra mã gần như tương đương với mã gốc, nhưng không tương đương 100%. Chúng tôi gọi hành vi tự động sửa như vậy là "không an toàn"

Chúng ta có thể chạy tự động sửa bằng lệnh sau:

$ rubocop -a
# or
$ rubocop --auto-correct
# or
$ rubocop -A
# or
$ rubocop --auto-correct-all

7. Các tiện ích mở rộng RuboCop khác

RuboCop cũng có các tùy chọn để triển khai các quy tắc trên các tiện ích mở rộng khác như:

  • rubocop-rspec Đối với Rspec; một khung kiểm tra phổ biến để kiểm tra mã Rails
  • rubocop-rake : Một plugin RuboCop cho Rake
  • rubocop-minitest : Một thư viện thử nghiệm phổ biến khác để thử nghiệm mã Ruby và Rails

IV. Kết luận

RuboCop rất hữu ích trong việc duy trì các phương pháp hay nhất và đó là một trong những gem mà chúng ta nên đưa vào tất cả các thiết lập dự án của mình.

Một điều cần nhớ với Trình phân tích code tĩnh là chúng ta có thể linh hoạt để bật và tắt các quy tắc, do đó chúng ta phải luôn thảo luận với nhóm về những gì cần bao gồm, tại sao nên bao gồm và những gì nên tắt.

Đây là hướng dẫn mà tôi hy vọng tôi đã có khi bắt đầu với tư cách là một ruby dev. Tôi hy vọng bạn thấy nó hữu dụng!

Tài liệu:

https://github.com/rubocop/rubocop

https://github.com/rubocop/rubocop-rails


All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.