DEV Community

loading...

ตัวอย่างการค้นหาและทำความเข้าใจโค้ด ที่ไม่เคยรู้มาก่อน

iporsut profile image Weerasak Chongnguluam ・1 min read

เมื่อเราอยากรู้ว่ามันทำงานได้ยังไง ก็แงะโค้ดมันเลย จะพาดูว่าปกติผมแงะโค้ดเพื่อทำความเข้าใจจุดที่สงสัยยังไง

ด้วยความสงสัยว่าตอนสั่ง mix run หรือ mix test มันไปโหลด config/runtime.exs ตอนไหนเลยแงะโค้ดของ mix ดูจนได้คำตอบ

เวลาสั่ง mix run นั่นคือมันจะไปเรียก task ที่ชื่อว่า run ซึ่งเป็น task ที่ติดมากับตอนติดตั้ง elixir อยู่แล้ว ตัวโค้ดอยู่ที่ elixir/lib/mix/lib/mix/tasks/run.ex (elixir คือ directory ที่เราลง elixir เอาไว้ ต่างกันไปขึ้นอยู่กับว่าติดตั้ง elixir ด้วยอะไร บน OS ไหน)

กลไกของ mix task คือ module สำหรับ task ต้องมี function run ก็ไปแงะแถวนั้น ตอนแรกก็ยังไม่เห็นว่ามีส่วนไหนที่เกี่ยวข้องกับการโหลดไฟล์ config/runtime.exs

สิ่งที่พอจะทำได้คือลองค้นว่ามีไฟล์ไหนใน path elixir/lib/mix/lib/mix ที่อ้างถึง config/runtime.exs บ้าง

ค้นดูแล้วก็เจอไฟล์ที่น่าสงสัยตามรูป

Alt Text

tasks/app.config.ex น่าสงสัยสุดเพราะบรรทัดที่ค้นเจอบอกเลยว่า

9: This is done by loading config/runtime.exs if one exists.

พอเปิดโค้ดในไฟล์ดูแล้วก็เจอโค้ดแบบนี้

    config = Mix.Project.config()
    runtime = config[:config_path] |> Path.dirname() |> Path.join("runtime.exs")

    if File.exists?(runtime) do
      Mix.Tasks.Loadconfig.load_runtime(runtime)
    end
Enter fullscreen mode Exit fullscreen mode

ชัดเลยว่า task นี้มันจะทำหน้าที่โหลดไฟล์ config/runtime.exs ถ้ามีไฟล์นี้อยู่ ส่วนโค้ดที่โหลดจริงๆอยู่ในฟังก์ชัน Mix.Tasks.Loadconfig.load_runtime ลองเปิดดูเจอโค้ดประมาณนี้

  @doc false
  # Loads runtime configuration, they do not support imports, and are deep merged.
  def load_runtime(file) do
    config = Config.Reader.read!(file, env: Mix.env(), target: Mix.target(), imports: :disabled)
    Mix.ProjectStack.loaded_config(persist_apps(hydrate_apps(config), file), [])
    config
  end
Enter fullscreen mode Exit fullscreen mode

เอาละเราคงไม่ขุดลึกไปกว่านี้ ณ จุดนี้เรารู้แล้วว่า task app.config ทำหน้าที่โหลด config/runtime.exs แต่ว่าตอนเราใช้งานเราเรียก mix run กับ mix test อ่ะไม่ได้เรียก mix app.config มันต้องมีอะไรเชื่อมโยงกัน

เราค้นหาต่อโดยลองค้นว่ามีไฟล์ในที่มี app.config อยู่ในไฟล์บ้าง แล้วก็เจอแบบนี้

Alt Text

และแล้วเราก็เจอว่าไฟล์ tasks/app.start.ex นั่นไง พอเปิดดูโค้ดรอบๆบรรทัดนั้นก็เจอแบบนี้

@impl true
  def run(args) do
    Mix.Project.get!()
    Mix.Task.run("app.config", args)
Enter fullscreen mode Exit fullscreen mode

ชัดเลยว่าทุกครั้งที่ task app.start ถูกเรียกนั้นมันจะเรียก app.config ให้ทำงานด้วย เราคงไม่ต้องไปแงะ Mix.Task.run แล้วเพราะชื่อก็ค่อนข้างชัดแล้วว่าเรียกแล้วมันจะไปสั่ง app.config ให้ทำงาน

ต่อไปก็หาต่อว่าแล้วใครมันเรียก app.start บ้างเหมือนเดิมค้น app.start ดูว่าเจอที่ไฟล์ไหนบ้าง แล้วก็เจอแบบนี้

Alt Text

นั่นไงและแล้วเราก็เจอว่า tasks/run.ex กับ tasks/test.ex มีการเรียก app.start ให้ทำงาน ลองเปิดดูโค้ดของทั้ง task run กับ test พบว่ามันไปเรียก app.start เวลาสอง task นี้ทำงานจริงๆด้วย

สรุปคือ run และ test จะเรียก app.start แล้ว app.start เรียก app.config แล้ว app.config โหลด config/runtime.exs อีกที

จริงๆที่เขียนมาประเด็นไม่ได้อยู่ที่ว่าจะรู้ไปทำไปหรอก ใช้ mix ไม่ต้องรู้ก็ได้ว่ามันโหลด runtime.ex ยังไง แต่ประเด็นคือถ้าเราสงสัยการทำงาน เรามีโค้ดมันอยู่ ไม่ต้องกลัวที่จะขุดเพื่อจะเรียนรู้มัน เครื่องมือสำคัญในการลงลุยอ่านโค้ดในแต่ละครั้งคือ

  • Document เพราะเราต้องรู้ก่อนว่าสิ่งต่างๆที่เราลงไปขุดหลักๆมันไว้ทำอะไร มีองค์ประกอบอะไรบ้าง เช่นตอนขุดก็ต้องอ่าน document ของ mix ว่ามันมีหลักการยังไง แบ่ง task ยังไง โค้ดของ task อยู่ที่ไหน
  • Text search tool อันนี้สำคัญก็คือเอาไว้ค้นหาไฟล์ที่มีข้อความ หรือส่วนของโค้ดที่เราสนใจ ว่ามันถูกอ้างอิงหรือเรียกใช้ที่จุดไหน
  • Editor แน่นอนเจอไฟล์แล้วเราก็ต้องเปิดดูโค้ดโดยรอบมัน หาความเชื่อมโยงต่ออีกที
  • Paper and Pen กระดาษและปากกา เอาไว้จดโน้ต เอาไว้วาดรูปเพื่อเชื่อมโยงความสัมพันธ์ ของโค้ดแต่ละไฟล์ แต่ละ module ที่เราแกะ

Discussion

pic
Editor guide
Collapse
tnanhpt profile image
Anh Pham

English?

Collapse
iporsut profile image
Weerasak Chongnguluam Author

Maybe in next next post.