DEV Community

Weerasak Chongnguluam
Weerasak Chongnguluam

Posted on • Edited on

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

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

ด้วยความสงสัยว่าตอนสั่ง 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 ที่เราแกะ

Buy Me A Coffee

Top comments (2)

Collapse
 
tnanhpt profile image
Anh Pham

English?

Collapse
 
iporsut profile image
Weerasak Chongnguluam

Maybe in next next post.