Upgrade to Pro — share decks privately, control downloads, hide ads and more …

RuboCop in 2021: Stable and Beyond

023b04c98f39cc041293d780352432ff?s=47 Koichi ITO
September 09, 2021

RuboCop in 2021: Stable and Beyond

RubyKaigi Takeout 2021
https://rubykaigi.org/2021-takeout

023b04c98f39cc041293d780352432ff?s=128

Koichi ITO

September 09, 2021
Tweet

Transcript

  1. 3VCP$PQJO 4UBCMFBOE#FZPOE ,PJDIJ*50&4. *OD 3VCZ,BJHJ5BLFPVU Next step toward RuboCop 2.0

     5IV
  2. !LPJD w 044QSPHSBNNFS w 3VCP$PQDPSFUFBN w )FBENBJOUBJOFSPG3VCP$PQ3BJMT  3VCP$PQ1FSGPSNBODF BOE

    3VCP$PQ.JOJUFTU w &OHJOFFSJOH.BOBHFSBOE %JTUJOHVJTIFE&OHJOFFSPG&4. *OD
  3. 3VCP$PQDPSFEFWFMPQFST +0900 UTC+0200 +0300 +0800 -0400 .PSFUIBODPOUSJCVUPSTJOZFBST

  4. 3VCP$PQDPSFEFWFMPQFST +0900 UTC+0200 +0300 +0800 -0400 .PSFUIBODPOUSJCVUPSTJOZFBST "VUIPS

  5. 3VCP$PQDPSFEFWFMPQFST +0900 UTC+0200 +0300 +0800 -0400 .PSFUIBODPOUSJCVUPSTJOZFBST .F

  6. .BJOUBJO3VCP$PQ)2&WFSZ%BZ IUUQTHJUIVCDPNLPJD PSHSVCPDPQ

  7. .F

  8. (JU)VC4QPOTPST

  9. &4. *OD

  10. %BZ SVCZLBJHJ#  %BZ SVCZLBJHJ#  4QFBLFSTGSPN&4. *OD

  11. None
  12. None
  13. -BTUSFMFBTFCFGPSF  🤔

  14. /VNCFSPGEPXOMPBET         

                   
  15.          

                   /VNCFSPGEPXOMPBET
  16. #FOFpUTPGVQHSBEJOHUP UIF3VCP$PQW

  17. 3VCP$PQJO 4UBCMFBOE#FZPOE ,PJDIJ*50&4. *OD 3VCZ,BJHJ5BLFPVU Next step toward RuboCop 2.0

     5IV
  18. 4UBCMF #FZPOE

  19. 4UBCMF

  20. 5IFXPSMEBGUFS3VCP$PQ 1BSU

  21. w WYDBVTFECSFBLJOHDIBOHFTBOE VOLOPXOXBSOJOHTEVFUPTVEEFO SFOBNJOHDPQOBNFTBOEOFXSVMFT w WYEPFTOPUDIBOHFDPQOBNFT w "MMPGOFXDPQTBSFQFOEJOHCZ EFGBVMU WIBTJOUFSGBDFDPNQBUJCJMJUZ

    CFUXFFOSFMFBTFT
  22. w %POUBEEOFXDPEJOHSVMFTBT 3VCP$PQEPFTOULOPXXIFUIFS OFXDPQTXJMMNBUDIZPVSUBTUF w /FXDPQTPDDBTJPOBMMZIBWFCVHT💦 3FEVDFVQHSBEFQBJO

  23. w %POUBEEOFXDPEJOHSVMFTBT 3VCP$PQEPFTOULOPXXIFUIFS OFXDPQTXJMMNBUDIZPVSUBTUF w /FXDPQTPDDBTJPOBMMZIBWFCVHT💦 3FEVDFVQHSBEFQBJO 👉:PVDBODIPPTFXIFUIFSFOBCMFEPS EJTBCMFEGPSWNJOPSWFSTJPOVQHSBEF

  24. /FXDPQTBSFQFOEJOHCZEFGBVMUVOUJM gem 'rubocop', '~> 1.19', require: false gem 'rubocop', '~>

    0.78.0', require: false w/POFFEUPTQFDJGZUIFQBUDI WFSTJPOJO(FNpMFXIFOVTJOH~> WY WY
  25. *GZPVBSFFBSMZBEPQUFS ZPVDBOFOBCMFQFOEJOHDPQT &OBCMFOFXDPQT % bundle exec rubocop The following cops

    were added to RuboCop, but are not configured. Please set Enabled to either `true` or `false` in your `.rubocop.yml` file. Please also note that you can opt-in to new cops by default by adding this to your config: AllCops: NewCops: enable Gemspec/DateAssignment: # (new in 1.10) Enabled: true Layout/LineEndStringConcatenationIndentation: # (new in 1.18) Enabled: true Layout/SpaceBeforeBrackets: # (new in 1.7) Enabled: true Lint/AmbiguousAssignment: # (new in 1.7) Enabled: true
  26. w 4FUEnabledUPFJUIFStruePSfalseJOZPVS SVCPDPQZNM w :PVDBODIPPTFFOBCMFOFXDPQTCZ EFGBVMUJGZPVXBOUJUNewCops: enable UPSVCPDPQZNM w 0S

    EJTBCMFOFXDPQTCZEFGBVMUJGZPV EPOUXBOUJUNewCops: disable &OBCMFOFXDPQT
  27. w /FXGFBUVSFTXJUICBDLXBSE DPNQBUJCJMJUZ w 3VCZOFXTZOUBYTVQQPSU w #FUUFSQFSGPSNBODF w #VHpYFT 8IBUZPVDBOHFUXJUIVQHSBEF💎

  28. w"EEFE DPQBVUP DPSSFDUJPOTTJODF w'PSFYBNQMF NPTUPGlayout EFQBSUNFOUDPQTDBOOPX BVUPDPSSFDUPGGFOTFT .PSFBVUPDPSSFDUJPOT

  29. )PXUPJOWFTUJHBUF % cd path/to/rubocop % bin/console RuboCop::Cop::Registry.global. with_department(:Layout). reject(&:support_autocorrect?)

  30. )PXUPJOWFTUJHBUF % cd path/to/rubocop % bin/console RuboCop::Cop::Registry.global. with_department(:Layout). reject(&:support_autocorrect?) =>

    [RuboCop::Cop::Layout::EndOfLine]
  31. 8IZOPUTVQQPSUFE % cd path/to/rubocop % bin/console RuboCop::Cop::Registry.global. with_department(:Layout). reject(&:support_autocorrect?) =>

    [RuboCop::Cop::Layout::EndOfLine] *EPOUIBWF8JOEPXT🤦
  32. w4BGFBVUPDPSSFDUJPOUIBUEPOPUDIBOHF TFNBOUJDT FHLayoutEFQBSUNFOU  w6OTBGFBVUPDPSSFDUJPOUIBUNBZDIBOHF TFNBOUJDTPSMPHJD FHUZQFPGSFUVSOWBMVF  w$BOOPUCFBVUPDPSSFDUJPO WBSJBCMFOBNF

     EPDVNFOU BOEFUD 5ZQFPGBVUPDPSSFDUJPOT
  33. w-APQUJPOBQQMJFTTBGFBOEVOTBGFBVUP DPSSFDUJPO FHStyle/FrozenStringLiteralComment  w5IJTPQUJPOJTUIFTBNF-aCFIBWJPSPG 3VCP$PQBOEMPXFS w-JLFgit branch -dWT-D <'"2>-A,

    --auto-correct-all
  34. 3VCZOFXTZOUBY w1BUUFSONBUDIJOH /VNCFSFEQBSBNFUFST &OEMFTT NFUIPEEFpOJUJPO BOEFUD w"TBUSJWJBGPSDPQEFWFMPQFST UIFPOFMJOFQBUUFSO NBUDIJOHTZOUBY"45EJGGFSTCFUXFFOBOE In

    Ruby 2.7(Parser::Ruby27) SVCZQBSTFFJOGPP NBUDIQBUUFSO JOU  NBUDIWBSGPP  SVCZQBSTFFGPP NBUDIQBUUFSO JOU  NBUDIWBSGPP  SVCZQBSTFFJOGPP NBUDIQBUUFSOQ JOU  NBUDIWBSGPP In Ruby 3.0(Parser::Ruby30) ruby-parseDPNNBOEJTQSPWJEFECZ1BSTFSHFN
  35. w--parallelPQUJPOGPSQBSBMMFM QSPDFTTJOHVTJOH1BSBMMFMHFN w.PEFSO1$TIBWFCFUUFS1$SFTPVSDFT NVMUJDPSFT IVHFNFNPSZ BOEFUD  w#ZEFGBVMU QBSBMMFMQSPDFTTJOHJT FOBCMFEFYDFQUGPS8JOEPXTBOE+3VCZ

    1FSGPSNBODF
  36. SVCPDPQSVCPDPQ 1BSBMMFMTUBUJDBOBMZTJTCZEFGBVMU

  37. None
  38. DPSF$16T )ZQFS5ISFBEJOH TFD      OPQBSBMMFM QBSBMMFM

    w8IFO3VCP$PQ JOTQFDUTBMPUPG pMFT JUXJMMCF FGGFDUJWF wFHTFDTFD BCPVUpMFT
  39. 3BOLJOHDPQTCZQFSGPSNBODF NFNPSZ@QSPpMFS BOE4UBDL1SPG DPOUSJCVUFUP SFNBSLBCMZTMPX DPQTQFSGPSNBODF JNQSPWFNFOU

  40. w3VCZTUBUJDBOBMZTJTXJUIPVUTUBUJDUZQJOH TUSVHHMJOHBHBJOTUGBMTFQPTJUJWFTBOE GBMTFOFHBUJWFT w"VUPDPSSFDUFEDPEFNBZUSJHHFS VOFYQFDUFECFIBWJPST TPJGZPVDPNF BDSPTTBDBTFUIBUZPVUIJOLJTTUSBOHF  TUPQBOESFWJFXJUBHBJO #VHpYFT

  41. SVCPDPQSVCPDPQ $IBOHFFOGPSDFETUZMFUPDPOEJUJPOBMTGPS4UZMF"OE0S

  42. #VHpYFT w*OUIFQBTU 3VCP$PQTVHHFTUFEUPDIBOHF expr and returnUPexpr && returnCZEFGBVMU w*UMPPLFEXFJSE TP*DIBOHFEUIFDPOpHVSBUJPO

    UPBDDFQUexpr and returnCZEFGBVMU 6TF `and return` % ruby -e \ 'p false || true and return' # => true # p(false || true) and return % ruby -e \ 'p false || true && return' # noop # p(false || (true && return)) 6TF `&& return`
  43. w /FXGFBUVSFTXJUICBDLXBSE DPNQBUJCJMJUZ w 3VCZOFXTZOUBYTVQQPSU w #FUUFSQFSGPSNBODF w #VHpYFT 8IBUZPVDBOHFUXJUIVQHSBEF💎<BHBJO>

  44. 3VCP$PQJTBTUBUJDBOBMZTJT UPPM*GZPVXBOUUPXSJUFB QBUDIJO3VCP$PQ ZPVTIPVME LFFQJONJOEUIBUEZOBNJD BOBMZTJTJTOPUBWBJMBCMF

  45. 3FTUSJDUJPOPGTUBUJDBOBMZTJT XJUIPVUTUBUJDUZQJOH 1BSU

  46. w 3VCZIBTOPTUBUJDUZQJOH w /PUZQFJOGPSNBUJPOGPSWBSJBCMFT BSHVNFOUT  SFUVSOWBMVFT XJUIPVU3#4  w

    Safe: falseJTTFUJODBTFTXIFSFGBMTF QPTJUJWFTDBOOPUCFQSFWFOUFE w "WPJESafe: falseBTNVDIBTQPTTJCMF BOE EFTJHODPQTDBSFGVMMZOPUUPDBVTFGBMTF QPTJUJWFT 3VCP$PQJTBTUBUJDBOBMZ[FS
  47. SBJMTSBJMT 5XFBLVOSFBDIBCMFBTTFSUJPOUFTUTJOUIFCMPDLPGABTTFSU@SBJTFTA

  48. None
  49. SVCPDPQSVCPDPQNJOJUFTU <'JY>"EEOFX.JOJUFTU6OSFBDIBCMF"TTFSUJPODPQ

  50. &YBNQMF IUUQTEPDTSVCPDPQPSHSVCPDPQNJOJUFTUDPQT@NJOJUFTUIUNMNJOJUFTUVOSFBDIBCMFBTTFSUJPO

  51. )PXEJE*SFTPMWFUIFJTTVF UIBU3VCP$PQJTOPUBCMFUP EFUFDUUIFMPDBUJPOXIFSF FYDFQUJPOTPDDVSSFE 🤔

  52. w "TTFSUJPONFUIPEOPUVTFEJO assert_raises w *OPUIFSXPSET HPPEDBTF 5SVFOFHBUJWF EFpOJUFMZ assert_raises FooError

    do do_something('foo', obj.bar) end "TTFSUJPONFUIPEOPUVTFE
  53. assert_raises FooError do obj.foo assert_equal('foo', obj.bar) end w "OBTTFSUJPONFUIPE FHassert_equal

    JT DBMMFEBUUIFCPUUPNPGassert_raisesCMPDL w 5IFSFGPSF OPUBTTFSUFEJUCFDBVTFBO FYDFQUJPOTIPVMEIBWFPDDVSSFE TPNFXIFSFCFGPSF 5SVFQPTJUJWF EFpOJUFMZ assert_equalJTOFWFSDBMMFE assert_raises FooError do obj.foo assert_equal('foo', obj.bar) end %PFTfooSBJTFBOFYDFQUJPO 0S EPFTbarSBJTFBOFYDFQUJPO assert_equalJTOFWFSDBMMFECFDBVTFUIFNFUIPEJTFWBMVBUFEMBTU
  54. w GBMTFQPTJUJWFJGOPFYDFQUJPO PDDVSTJOobj.foo w USVFQPTJUJWFJGBOFYDFQUJPO PDDVSTJOobj.foo $POTFSWBUJWFEFTJHO assert_raises FooError do

    assert_equal('foo', obj.foo) obj.bar end *GJUEPFTOUSBJTFTIFSF JUTBGBMTFQPTJUJWF *GJUSBJTFTIFSF JUTBUSVFQPTJUJWF RuboCop don't know if it's a false positive or not
  55. w *GUIFMBTUNFUIPEDBMMFEJTBOBTTFSUJPO NFUIPE UIFSFTIPVMECFBOFYDFQUJPOJO UIFQSFWJPVTNFUIPEDBMM /FWFSSFBDIFE  w 'BMTFOFHBUJWFTBDDFQUFE w

    1SFWFOUCSFBLJOHDPEFCZGBMTFQPTJUJWFT w /PUFTUDPEFQSPUFDUTUIFUFTUDPEF %FTJHOUPBWPJEGBMTFQPTJUJWFT
  56. w *UTJNQPSUBOUUPSVO3VCP$PQUIBUBDUVBMMZVTFT .JOJUFTU w *SBOJUJOUIFSBJMTSBJMTSFQPTJUPSZBOEGPVOE GBMTFQPTJUJWFT w "EESVCPDPQNJOJUFTUUPSBJMTSBJMT(FNpMFBOE TQFDJGZpathPSgithubPQUJPO w

    3VOrubocop --only Minitest/UnreachableAssertion %PHGPPEJOHXJUISFBMXPSMEDPEF
  57. None
  58. Support OSS community

  59. 4UBCMF #FZPOE

  60. #FZPOE

  61. #SFBLJOHDIBOHFTQMBOOFE GPS3VCP$PQ 1BSU

  62. w 6TFBaseDMBTTJOTUFBEPGCopDMBTT w .PTUPGHFNTNBJOUBJOFECZSVCPDPQPSH TVDIBT3VCP$PQ1FSGPSNBODF 3BJMT  34QFD .JOJUFTU 3BLFIBWFCFFONJHSBUFE

    w "EFQSFDBUFEXBSOJOHNBZCFEJTQMBZFE BGUFS3VCP$PQDPSFJUTFMGIBTCFFOBMM NJHSBUFE W"1*GPSDPQEFWFMPQFST
  63. < Base FYUFOE"VUP$PSSFDUPS EPcDPSSFDUPSc

  64. w $POTJEFSEFpOJOH RESTRICT_ON_SENDDPOTUBOUXIFO EFpOJOHon_sendIBOEMFS <5JQ>1FSGPSNBODFJNQSPWFNFOU # `on_send` is called only

    when `foo` was called class ExampleCop < Base RESTRICT_ON_SEND = %i[foo].freeze def on_send(node) = add_offense(node) end
  65. w $PQOBNFTBOEDPOpHVSBUJPO PQUJPOOBNFTJOSVCPDPQZNMBOE UIFDPNNBOEMJOFPQUJPOTDBOOPU CFDIBOHFEFBTJMZJOW w 5IPTFOBNFTXJMMCFDIBOHFEJO 7 /BNJOHNBUUFST

  66. w $VSSFOUMZOPXBSOJOHJTEJTQMBZFE TPGUEFQSFDBUJPO  w 8JMMEJTQMBZBXBSOJOHBUTPNF QPJOUJOW w 5IPTF"1*XJMMCFDIBOHFE PSSFNPWFE

    JOW %FQSFDBUFE"1*GPSSEQBSUZHFNT
  67. w TargetRailsVersionBOETJNJMBS"1* SFNBJOTJO3VCP$PQDPSFBGUFS3BJMT EFQBSUNFOUFYUSBDUFE w 3FNPWJOHTargetRailsVersion BOE TJNJMBS"1*SFGFSFODFECZ3VCP$PQ 3BJMTJTBCSFBLJOHDIBOHF TP*XJMM

    UBLFJUDBSFGVMMZ 3FNPWF3BJMTEFQFOEFODJFTGSPNDPSF
  68. w $PQTXIPBSFOPUEJTBCMFEJOZPVS SVCPDPQZNMXJMMCFFOBCMFECZ EFGBVMUTJODF3VCP$PQ w 5IFSFBSFBCPVUOFXDPQTJO 3VCP$PQ w *UXJMMJODSFBTFJOUIFGVUVSF 1FOEJOHDPQTXJMMCFFOBCMFECZEFGBVMU

  69. IUUQTHJUIVCDPNSVCPDPQSVCPDPQQVMMJTTVFDPNNFOU 8IFOSFMFBTF 5#) OPQMBOT😅

  70. *EFBTGPSGVUVSFPG3VCP$PQ 1BSU

  71. "Default rules are too strict, but customization is hard..." 'FFECBDLGSPNVTFST

  72. "Default rules are too strict, but customization is hard..." 'FFECBDLGSPNVTFST

    *EPOUIBWFUIFCFTUBOTXFS  CVU*MMUBMLJEFBTIFSF
  73. w4UBSUXJUIUIFEFGBVMUEJTBCMFE BOEFOBCMFDPQTUFQCZTUFQ w6TFSVCPDPQ@UPEPZNM w3JEFPOSEQBSUZQSFTFU w$PNCJOFUIFN 0QUJPOTBWBJMBCMF

  74. "QQMJDBUJPOTUSBUFHZ %JTBCMFE#Z%FGBVMUUSVF &OBCMFE#Z%FGBVMUUSVF %FGBVMUDPOpHVSBUJPO 5IJSEQBSUZQSFTFU 3VMFTUSFOHUIBOEDPNQMFYJUZ 5JNFUPBQQMZ3VCP$PQ $VTUPNDPOpHVSBUJPO 8IFUIFSUPDPOpHVSFPSOPU 8IFUIFSUPDPOpHVSFPSOPU

    1SPCBCMZPOMZGPS3VCP$PQUFTUJOH $POpHVSFMBUFS 5IFDPOpHVSBUJPOZPVMJLF ⭐ ⭐ ⭐ ⭐)PXUPHFUTUBSUFE .BZCFUIFSFJTBQSFTFUUIBU JTDMPTFUPZPVSUBTUF 3VMFTFUUPBQQMZ
  75. "QQMJDBUJPOTUSBUFHZ %JTBCMFE#Z%FGBVMUUSVF &OBCMFE#Z%FGBVMUUSVF %FGBVMUDPOpHVSBUJPO 5IJSEQBSUZQSFTFU Today's topic 3VMFTUSFOHUIBOEDPNQMFYJUZ 5JNFUPBQQMZ3VCP$PQ $VTUPNDPOpHVSBUJPO

    8IFUIFSUPDPOpHVSFPSOPU 8IFUIFSUPDPOpHVSFPSOPU 1SPCBCMZPOMZGPS3VCP$PQUFTUJOH $POpHVSFMBUFS 5IFDPOpHVSBUJPOZPVMJLF ⭐ ⭐ ⭐ ⭐)PXUPHFUTUBSUFE .BZCFUIFSFJTBQSFTFUUIBU JTDMPTFUPZPVSUBTUF 3VMFTFUUPBQQMZ
  76. w"QQMZSVMFTFBDIUJNFZPV FODPVOUFSBOPGGFOTF %3: %JTBCMFE#Z%FGBVMUUSVF

  77. 3JEFPO5IJSEQBSUZQSFTFU

  78. 3VOUJNFFOHJOFBOE DPSFDPQT $POpHVSBUJPO $POpHVSBUJPO 3VCP$PQDPSF SVCPDPQZNM SBJMTSBJMTSFQP &YUFOEBCMFDPOpHVSBUJPO $POpHVSBUJPO SVCPDPQSBJMT@DPOpH

    HFN SVCPDPQZNM BQQMJDBUJPO 4UBOEBSEHFN UFTUEPVCMFTUBOEBSE You can use existing 3rd party configurations Configurable No configuration
  79. 3VOUJNFFOHJOFBOE DPSFDPQT $POpHVSBUJPO 1SFTFUDPOpHVSBUJPO 3VCP$PQDPSF SVCPDPQZNM SBJMTSBJMTSFQP 3JEFPOSEQBSUZQSFTFUXBZ $POpHVSBUJPO SVCPDPQSBJMT@DPOpH

    HFN SVCPDPQZNM BQQMJDBUJPO 4UBOEBSEHFN UFTUEPVCMFTUBOEBSE You can use existing 3rd party preset or your config Configurable No configuration
  80. IUUQTEPDTSVCPDPQPSHSVCPDPQFYUFOTJPOTIUNMDPQFYUFOTJPOT

  81. IUUQTHJUIVCDPNHJUIVCSVCPDPQHJUIVC

  82. 0OFPGJEFBTGPS GVUVSF3VCP$PQ

  83. .JOJNVNQSFTFU %JTBCMFE#Z%FGBVMUUSVF &OBCMFE#Z%FGBVMUUSVF %FGBVMUDPOpHVSBUJPO 5IJSEQBSUZQSFTFU 3VMFTUSFOHUI CVUDPNQMFY 5JNFUPJOUSPEVDFBQSPKFDU $VTUPNDPOpHVSBUJPO 8IFUIFSUPDPOpHVSFPSOPU

    8IFUIFSUPDPOpHVSFPSOPU 1SPCBCMZPOMZGPS3VCP$PQUFTUJOH $POpHVSFMBUFS 5IFDPOpHVSBUJPOZPVMJLF ⭐ ⭐ ⭐ ⭐)PXUPHFUTUBSUFE .BZCFUIFSFJTBQSFTFUUIBU JTDMPTFUPZPVSUBTUF 3VMFTFUUPBQQMZ JEFB .JOJNVNQSFTFUGSPNP⒏DJBM
  84. SVCPDPQSVCPDPQ 1SPQPTBMUPEJTDVTT4VHHFTUJPONPEFDPQT

  85. IUUQTHJUIVCDPNSVCPDPQSVCPDPQJTTVFTJTTVFDPNNFOU

  86. None
  87. w&WFSZ3VCZQSPHSBNNFSTIBWF QSFGFSFODFT EJWFSTJUZJTHPPE  w*O3VCZ TUZMFQSFGFSFODFTUFOEUP WBSZGSPNQFSTPOUPQFSTPO w"QQBSFOUMZFWFO3VCZDPNNJUUFST IBWFEJGGFSFOUUBTUFT😅 3VCZJTBUSVMZFYQSFTTJWFMBOHVBHF

  88. 'JYJOHUIFQSFTFUTFFNT EJGpDVMU 🤔 To be continued…

  89. 5IF3VCZDPODFQU 4NBSUFS 'BTUFS

  90. QPDLFSVCPDPQUZQFE $PQJEFBT

  91. w SVCPDPQUZQFEJTB1P$UIBU DPNCJOFT4UFFQTTUBUJDUZQFDIFDLFS w 6TJOHUZQFJOGPSNBUJPOXJMMTPMWF TPNFGBMTFQPTJUJWFT w 3VCP$PQXJMMCFBCMFUPQFSGPSN TNBSUFSTUBUJDBOBMZTJT $BOUZQFJOGPSNBUJPOCFBHBNFDIBOHFS

  92. SVCPDPQSVCPDPQ 1FSGPSNBODFJNQSPWFNFOUTGPSCFUUFSFEJUPSJOUFHSBUJPO

  93. w #PPUTOBQXBTQSPQPTFEJOUIFQBTU  CVUXPSUISFWJTJUFE    w *OUFHSBUFEBFNPOTMJLFSVCPDPQ EBFNPO

    BOEDPMMBCPSBUFXJUI-41  4PMBSHSBQI  w *UTIPVMEDPOUSJCVUFUPUIF3VCZ DPODFQU4NBSUFS 'BTUFS 1FSGPSNBODFJNQSPWFNFOUJEFBT
  94. None
  95. .BOZPGZPVVTFT3VCP$PQ  JUTBHSFBUUPPM CVUXFDBO JNQSPWFJUBMPUJOUIFGVUVSF .BU[TBJE &VSVLP,FZOPUF #FZPOE3VCZ

  96. -41 3#* 3#4 3VCP$PQ 4PMBSHSBQI 4PSCFU 4UFFQ 5ZQF1SPG &MFNFOUTPG&P% 💨

    BOENPSF
  97. 3VCZTUBUJDBOBMZTJTUPPMT XJMMDPOUJOVFUPJNQSPWF rubocop.org