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

เชี่ยวชาญ Rails Exception Debugging: ใช้ Backtraces ระบุ Bugs

เมื่อ error เกิดใน Rails application exception และ stack trace ช่วยหาว่า problem เกิดที่ไหน หลังรู้ว่าเกิดอะไรขึ้นที่ไหน เราต้องหาว่า ทำไม มันเกิด บทความนี้เราจะครอบคลุมการใช้ backtrace หา bug ใน Rails application

NoMethodError (undefined method `request_uri' for #):
app/models/product.rb:8:in `download_image!'
app/controllers/products_controller.rb:5:in `create'

ใน example exception นี้ เราได้ NoMethodError พร้อม message undefined method `request_uri' for # เนื่องจาก exception นี้ไม่บอกปัญหาทันที เราต้อง inspect stack trace หาว่าเกิดอะไรขึ้น

app/models/product.rb:8:in `download_image!'
app/controllers/products_controller.rb:5:in `create'

ดู stack trace เรารู้ว่า exception raised จาก method download_image! บน Product model เราจะสืบสวนในโค้ด และทำงานลง stack trace หาว่าอะไรผิด

เปิด model ดู line 8 (ที่ exception raised จาก) เรียก Net::HTTP.get(uri) ดูเหมือน uri ไม่ใช่ object ที่เราคาดหวัง

require 'net/http'
class Product < ApplicationRecord
  after_save :download_image!
  def download_image!
    uri = URI(image_url)
    contents = Net::HTTP.get(uri)
    File.open("public#{local_image_path}", 'wb') do |file|
      file.write contents
    end
  end
  def local_image_path
    "/product_#{id}.png"
  end
end

เนื่องจาก download_image! เป็น after_save callback เรารู้ว่ามัน execute ทันทีหลัง save Product ใหม่

ตัวแปร uri สร้างจาก method ชื่อ image_url บน line 7 เพื่อหาว่ามาจากไหน เราดู stack trace อีกครั้ง ว่า Product#create เรียกจาก ProductsController#create

class ProductsController < ApplicationController
  def create
    @product = Product.new(product_params)
    if @product.save
      redirect_to @product, notice: 'Product was successfully created.'
    else
      render :new
    end
  end
  private
  def product_params
    params.require(:product).permit(:title, :description, :image_url, :price)
  end
end

อ๋อ! ProductsController#create สร้าง product ใหม่ด้วย product_params ซึ่งรวม parameter :image_url ที่เราหา

เรารู้ว่า image_url attribute ใช้สร้าง broken URI หากเราทิ้ง image_url field ว่างเมื่อสร้าง product ใหม่ เราสามารถ reproduce ปัญหาได้สำเร็จ

ในกรณีนี้ การสร้าง URI ด้วย empty string เป็น value ทำให้ได้ URI::Generic object แทน URI::HTTP เพราะมัน determine URL format ไม่ได้ เนื่องจากตัวแรกไม่มี method #request_uri จึง raise NoMethodError จาก Net::HTTP.get

ขึ้นอยู่กับ requirements ของโปรเจกต์ การเพิ่ม validation ให้ field ไม่ว่าง อาจแก้ปัญหาได้ การ validate แค่ image URL ไม่ว่างไม่ได้แก้ปัญหาทั้งหมดของ implementation นี้ (ยังได้ exception เมื่อค่าที่ส่งไม่ใช่ URL) แต่เป็นจุดเริ่มที่ดี

Tracking Down Exceptions ด้วย Stack Trace

Rails logs ให้ debug issues ได้ดี แม้ raised exceptions ไม่ทำ sense ตรงๆ ตอนแรก แต่ careful retracting steps ที่ code ใช้ไปถึง issue มักเป็น great way หาว่าผิดที่ไหน แม้ source ของ problem จะฝังลึกใน app

เรายินดีรู้ความคิดเห็นว่าชอบบทความนี้หรือไม่ มีคำถามอะไร และอยากอ่านเรื่องไหนต่อไป บอกเราที่ @AppSignal ได้เลย