Computer >> คอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Ruby

รหัสทับทิม Linting

Linting เป็นกระบวนการของการวิเคราะห์โค้ดแบบคงที่เพื่อค้นหาปัญหาที่อาจเกิดขึ้น

ในกรณีนี้ สิ่งที่ก่อให้เกิดปัญหาอาจแตกต่างกันไปตามภาษาโปรแกรม หรือแม้แต่ข้ามโปรเจ็กต์ในภาษาเดียวกัน ฉันจะใส่ปัญหาเหล่านี้ไว้ในหมวดหมู่ต่างๆ สองสามหมวดหมู่:

  • แบบเป็นโปรแกรม
  • ความปลอดภัย
  • โวหาร
  • ประสิทธิภาพ

มาดูตัวอย่างบางส่วนกัน

ปัญหาโวหาร

ไม่มีวิธีการจัดรูปแบบโค้ดที่ถูกต้องตามความเป็นจริง เนื่องจากเป็นเรื่องเกี่ยวกับความชอบของผู้อ่านเท่านั้น กุญแจสำคัญคือความสม่ำเสมอ ประเด็นทั่วไปของการอภิปรายได้แก่:

  1. เครื่องหมายคำพูดคู่เทียบกับเครื่องหมายคำพูดเดี่ยว
  2. แท็บเทียบกับช่องว่าง
  3. ความยาวเส้นสูงสุด
  4. การเยื้องของการโทรหลายสายดังที่แสดงด้านล่าง:
# always single-line
foo(:a, :b, :c)
 
# aligned with first argument
foo(:a,
    :b,
    :c
)
 
# aligned with function name
foo(
  :a,
  :b
)

สิ่งเหล่านี้ล้วนแล้วแต่เป็นความเห็นส่วนตัว แต่มักจะเป็นประโยชน์ที่จะตกลงเรื่องมาตรฐานสำหรับแต่ละโปรเจ็กต์ เพื่อให้โค้ดเบสทั้งหมดสอดคล้องกัน

ปัญหาเชิงโปรแกรม

ฉันรวมปัญหาเช่น:

  • เนื้อหาวิธีการที่ยาวมาก ซึ่งนำไปสู่ปัญหาเรื่องความสามารถในการอ่านและการบำรุงรักษา
  • Cyclomatic Complexity เมตริกที่ใช้กันทั่วไปในการวัดความซับซ้อนของโค้ด
  • การมอบหมายงานภายในเงื่อนไข เป็นไปได้มากว่าถ้าคุณพิมพ์ if x = true คุณหมายถึง if x == true . และแม้ว่าคุณจะหมายถึงงานที่ได้รับมอบหมาย แต่ก็ยังเป็นแนวทางที่ไม่สัญชาตญาณ

ปัญหาด้านความปลอดภัย

ฟังก์ชันหรือแนวทางปฏิบัติบางอย่างอาจมีปัญหาด้านความปลอดภัยที่นักพัฒนาอาจไม่ทราบ

ตัวอย่างเช่น ใน Ruby Kernel#open เป็นฟังก์ชันที่ยืดหยุ่นซึ่งช่วยให้เปิดไฟล์หรือ URL ภายนอกได้ แต่ยังอนุญาตให้เข้าถึงระบบไฟล์ตามอำเภอใจด้วยการโทรแปลก ๆ เช่น open("| ls") ดังนั้นจึงควรเตือนนักพัฒนาเกี่ยวกับเรื่องนี้เพื่อให้สามารถใช้แนวทางที่ปลอดภัยยิ่งขึ้น (File#open , IO.popen , URI.parse#open ) หรือตัดสินใจให้แน่ชัดว่าจะต้องรับความเสี่ยงจากพฤติกรรมนั้นเอง

ปัญหาด้านประสิทธิภาพ

มีรายละเอียดมากมายเกี่ยวกับการทำงานภายในของ Ruby ที่ทำให้ตัวเลือกบางอย่างมีประสิทธิภาพมากกว่าตัวเลือกอื่นๆ ขึ้นอยู่กับบริบท

ข้อมูลเล็กๆ น้อยๆ ที่เตือนเราเกี่ยวกับสิ่งเหล่านี้ช่วยให้เราเรียนรู้ไปพร้อมๆ กับการปรับรายละเอียดบางอย่างของโปรแกรมของเราให้เหมาะสม

ตัวอย่างเช่น Ruby 2.5 แนะนำ String#delete_suffix ซึ่งลบสตริงย่อยออกจากส่วนท้ายของสตริง สองบรรทัดนี้เทียบเท่ากัน แต่บรรทัดหนึ่งมีประสิทธิภาพมากกว่าเนื่องจากไม่พึ่งพา regexmatch สตริงทั่วไป:

str = 'string_with_suffix'
 
# bad
str.gsub(/suffix\z/, '')
 
# good
str.delete_suffix('suffix')

แก้ไขอัตโนมัติ

สิ่งสำคัญของ linters คือความสามารถในการแก้ไขปัญหาที่พบบางส่วนหรือทั้งหมดโดยอัตโนมัติ ด้านการจัดรูปแบบ เช่น ความยาวของเส้นจะเป็นไปโดยอัตโนมัติอย่างง่ายดาย ดังนั้นจึงควรที่จะขจัดภาระนั้นออกจากนักพัฒนา ปัญหาอื่นๆ อาจเป็นเรื่องส่วนตัวหรือต้องการการแทรกแซงจากมนุษย์ เช่น asrefactoring วิธีการขนาดใหญ่ ในกรณีเหล่านี้ ระบบอัตโนมัติจะไม่สามารถทำได้

การประชุมหรือการกำหนดค่า

มักจะมีการถกเถียงกันอย่างหนักภายในชุมชนหรือโครงการที่กฎเกณฑ์ต่างๆ เหมาะสม

วิธีแก้ปัญหาแบบดั้งเดิมคือการอนุญาตให้แต่ละทีมแก้ปัญหาการโต้วาทีภายในสมาชิกโดยอนุญาตให้พวกเขากำหนดค่ากฎผ้าสำลีตามรสนิยมของตนเอง อย่างไรก็ตาม ในช่วงไม่กี่ปีที่ผ่านมา มีการผลักดันหลายภาษาเพื่อสร้างมาตรฐานให้เป็นแบบแผนเดียว แม้ว่าจะไม่ใช่ การบังคับใช้ในทุกที่ แนวคิดทั่วไปคือการลบโอเวอร์เฮดตามหัวข้อที่นักพัฒนาซอฟต์แวร์มีเมื่อใส่สไตล์โค้ด แทนที่จะพูดถึงความยาวบรรทัดที่ดีที่สุด ทุกคนก็แค่ใช้กฎที่ตกลงกันโดยชุมชน

ใน Ruby ค่านี้จะแปลเป็นสอง linters ที่มีอยู่:RuboCop ซึ่งอนุญาตให้กำหนดค่าแบบเต็มและ StandardRB ซึ่งใช้แนวทางตรงข้ามและกำหนดมาตรฐานทั่วไป

รูโบคอป

ใช้แนวทางปกติในการจัดทำชุดกฎเกณฑ์ ซึ่งแต่ละข้อจะมองหาปัญหาเฉพาะ นักพัฒนาสามารถปิดใช้งานหรือปรับแต่งกฎบางอย่างภายในโครงการของตนเองได้:

# configure max allowed line length
Layout/LineLength:
  Max: 80
 
# disable cyclomatic complexity
Metrics/CyclomaticComplexity:
  Enabled: false

มีค่าเริ่มต้นที่เหมาะสมอยู่แล้ว ดังนั้นการกำหนดค่าจึงจำเป็นสำหรับกฎเฉพาะที่คุณต้องการเปลี่ยนแปลงเท่านั้น

กำลังเรียกใช้ bundle exec rubocop จะทำให้ RuboCop วิเคราะห์ codebase ทั้งหมดและแสดงรายการปัญหาทั้งหมดที่พบ:

# test.rb
def badName
  if something
    return "inner result"
  end
 
  "outer result"
end
$ bundle exec rubocop
Inspecting 1 file
C
 
Offenses:
 
test.rb:1:1: C: [Correctable] Style/FrozenStringLiteralComment: Missing frozen string literal comment.
def badName
^
test.rb:1:5: C: Naming/MethodName: Use snake_case for method names.
def badName
    ^^^^^^^
test.rb:2:3: C: [Correctable] Style/IfUnlessModifier: Favor modifier if usage when having a single-line body. Another good alternative is the usage of control flow &&/||.
  if something
  ^^
test.rb:3:12: C: [Correctable] Style/StringLiterals: Prefer single-quoted strings when you don't need string interpolation or special symbols.
    return "inner result"
           ^^^^^^^^^^^^^^
test.rb:6:3: C: [Correctable] Style/StringLiterals: Prefer single-quoted strings when you don't need string interpolation or special symbols.
  "outer result"
  ^^^^^^^^^^^^^^
 
1 file inspected, 5 offenses detected, 4 offenses auto-correctable

จากนั้นคุณสามารถเรียกใช้ bundle exec rubocop --auto-correct และปัญหาส่วนใหญ่ของคุณจะได้รับการแก้ไขตามการกำหนดค่าของคุณ

กำลังตั้งค่า bundle exec rubocop เป็นส่วนหนึ่งของไปป์ไลน์ CI ของคุณเพื่อให้แน่ใจว่าไม่มีโค้ดเก็ตผ่านหากไม่ปฏิบัติตามกฎการขัดถูก่อน

StandardRB

โปรเจ็กต์ล่าสุดที่ใช้ RuboCop จริง ๆ แล้ว เป้าหมายหลักของ StandardRB ไม่ใช่การสร้าง linter ที่แยกจากกันโดยสิ้นเชิง แต่บรรลุมาตรฐานที่ทุกคนสามารถใช้แทนการโต้เถียงได้

การพูดคุยแบบสายฟ้าแลบเมื่อมีการประกาศครั้งแรกนั้นค่อนข้างชัดเจนเกี่ยวกับการสร้างแรงจูงใจ:หากผู้คนใช้เวลาน้อยลงในการโต้เถียงเกี่ยวกับรายละเอียดวากยสัมพันธ์และทำตามการตัดสินใจของข้อตกลงระดับชุมชน เราทุกคนสามารถใช้เวลามากขึ้นในการทำสิ่งที่สำคัญจริงๆ:การสร้างผลิตภัณฑ์และห้องสมุดที่ยอดเยี่ยม

เนื่องจากมีการใช้ RuboCop ด้านล่าง ผลลัพธ์ที่คุณได้รับจึงอยู่ในรูปแบบเดียวกัน ข้อแตกต่างเพียงอย่างเดียวคือคุณไม่ได้รับอนุญาตให้ปรับแต่งกฎใดๆ

StandardRB เพิ่งถึง 1.0.0 ซึ่งหมายความว่าการอภิปรายส่วนใหญ่เกี่ยวกับกฎที่จะใช้ได้เกิดขึ้นแล้วในหน้าปัญหาของพวกเขา หากคุณสนใจหรือไม่เห็นด้วยกับกฎใดกฎหนึ่ง มีโอกาสที่คุณจะเห็นการสนทนาที่เกี่ยวข้องในนั้น

อย่างไรก็ตาม ในท้ายที่สุด คุณสามารถมั่นใจได้ว่าได้มีการพูดคุยกันในเชิงลึก เป็นไปไม่ได้ที่ทั้งชุมชนจะเห็นด้วย 100% ในทุกประเด็น ปรัชญาของแนวทางนี้คือผู้คนมีความยืดหยุ่น ไม่เห็นด้วย และตัดสินใจได้

ความคิดสุดท้าย

หลังจากใช้เวลามากกว่าที่ฉันภาคภูมิใจในการค้นหากฎการนินทาในโปรเจ็กต์ที่ผ่านมา ฉันเห็นคุณค่าในแนวทางของ StandardRB อย่างแน่นอน และฉันขอแนะนำให้ใช้เมื่อมีโอกาส

การรักษาผลประโยชน์ของความสม่ำเสมอทั้งหมดไว้พร้อมๆ กับขจัดส่วนเกินของการอภิปราย ควบคู่ไปกับการแก้ไขอัตโนมัติสำหรับกฎส่วนใหญ่ช่วยให้เราส่งมอบซอฟต์แวร์ที่ดีขึ้นอย่างมีประสิทธิภาพมากขึ้น และมุ่งเน้นไปที่สิ่งที่สำคัญจริงๆ

ภาษาอื่นกำลังใช้ตัวจัดรูปแบบโค้ดที่กำหนดค่าได้ต่ำที่คล้ายคลึงกัน mix formatter ใน Elixir และ rustfmt ใน Rust ทั้งคู่อนุญาตให้กำหนดค่าบางอย่างได้ แต่ชุมชนมีความพร้อมอย่างน่าประหลาดใจโดยรักษาให้อยู่ในมาตรฐาน

จากที่กล่าวมา RuboCop ยังคงเป็นตัวเลือกที่ถูกต้องสมบูรณ์หากคุณไม่เห็นด้วยกับความรู้สึกนี้อย่างแดกดัน

ป.ล. หากคุณต้องการอ่านโพสต์ Ruby Magic ทันทีที่ออกจากสื่อ สมัครรับจดหมายข่าว Ruby Magic ของเราและไม่พลาดแม้แต่โพสต์เดียว!