การโหลดรหัส Clojure โดยใช้(require … :reload)
และ:reload-all
มีปัญหามาก :
หากคุณปรับเปลี่ยนสองเนมสเปซซึ่งขึ้นอยู่กับกันและกันคุณต้องจำไว้ว่าให้โหลดซ้ำในลำดับที่ถูกต้องเพื่อหลีกเลี่ยงข้อผิดพลาดในการรวบรวม
หากคุณลบคำจำกัดความจากไฟล์ต้นฉบับแล้วโหลดซ้ำคำจำกัดความเหล่านั้นยังคงมีอยู่ในหน่วยความจำ หากรหัสอื่นขึ้นอยู่กับคำจำกัดความเหล่านั้นรหัสนั้นจะยังคงทำงานต่อไป แต่จะหยุดพักในครั้งต่อไปที่คุณรีสตาร์ท JVM
หากมีเนมสเปซที่โหลดใหม่defmulti
คุณต้องโหลดdefmethod
นิพจน์ที่เกี่ยวข้องทั้งหมดอีกครั้ง
หากมีเนมสเปซที่โหลดใหม่defprotocol
คุณต้องโหลดเร็กคอร์ดหรือชนิดใด ๆ ที่ใช้โพรโทคอลนั้นและแทนที่อินสแตนซ์ที่มีอยู่ของเร็กคอร์ด / ชนิดเหล่านั้นด้วยอินสแตนซ์ใหม่
หากเนมสเปซที่โหลดใหม่มีมาโครคุณจะต้องโหลดเนมสเปซอื่น ๆ ที่ใช้มาโครเหล่านั้นด้วย
หากโปรแกรมที่กำลังรันอยู่มีฟังก์ชั่นที่ปิดค่าในเนมสเปซที่โหลดใหม่ค่าปิดเหล่านั้นจะไม่ถูกอัพเดต (นี่เป็นเรื่องปกติในเว็บแอปพลิเคชันที่สร้าง "ตัวจัดการสแต็ก" เป็นองค์ประกอบของฟังก์ชั่น)
ไลบรารี clojure.tools.namespace ช่วยปรับปรุงสถานการณ์อย่างมีนัยสำคัญ มันมีฟังก์ชั่นการรีเฟรชที่ง่ายซึ่งจะทำการโหลดซ้ำแบบสมาร์ทตามกราฟการพึ่งพาของเนมสเปซ
myapp.web=> (require '[clojure.tools.namespace.repl :refer [refresh]])
nil
myapp.web=> (refresh)
:reloading (myapp.web)
:ok
น่าเสียดายที่การโหลดครั้งที่สองจะล้มเหลวหากเนมสเปซที่คุณอ้างถึงrefresh
ฟังก์ชั่นเปลี่ยนไป นี่คือความจริงที่ว่า tools.namespace ทำลาย namespace ปัจจุบันก่อนที่จะโหลดรหัสใหม่
myapp.web=> (refresh)
CompilerException java.lang.RuntimeException: Unable to resolve symbol: refresh in this context, compiling:(/private/var/folders/ks/d6qbfg2s6l1bcg6ws_6bq4600000gn/T/form-init819543191440017519.clj:1:1)
คุณสามารถใช้ชื่อ var ที่ผ่านการรับรองเป็นวิธีแก้ปัญหาสำหรับปัญหานี้ แต่โดยส่วนตัวแล้วฉันไม่ต้องการพิมพ์ออกมาในการรีเฟรชแต่ละครั้ง ปัญหาอีกข้อหนึ่งที่กล่าวมาก็คือหลังจากที่โหลดเนมสเปซหลักแล้วฟังก์ชันผู้ช่วย REPL มาตรฐาน (เช่นdoc
และsource
) จะไม่มีการอ้างอิงอีกต่อไป
เพื่อแก้ปัญหาเหล่านี้ฉันต้องการสร้างไฟล์ต้นฉบับจริงสำหรับเนมสเปซผู้ใช้เพื่อให้สามารถโหลดซ้ำได้อย่างน่าเชื่อถือ ฉันใส่ไฟล์ต้นฉบับ~/.lein/src/user.clj
แต่คุณสามารถวางได้ทุกที่ ไฟล์ควรต้องการฟังก์ชั่นการรีเฟรชในการประกาศบน ns ดังนี้:
(ns user
(:require [clojure.tools.namespace.repl :refer [refresh]]))
คุณสามารถตั้งค่าโปรไฟล์ผู้ใช้ Leiningenใน~/.lein/profiles.clj
เพื่อให้สถานที่ที่คุณใส่แฟ้มในจะถูกเพิ่มไปยังเส้นทางการเรียน โปรไฟล์ควรมีลักษณะดังนี้:
{:user {:dependencies [[org.clojure/tools.namespace "0.2.7"]]
:repl-options { :init-ns user }
:source-paths ["/Users/me/.lein/src"]}}
โปรดทราบว่าฉันตั้งค่าเนมสเปซผู้ใช้เป็นจุดเริ่มต้นเมื่อเรียกใช้ REPL สิ่งนี้ช่วยให้มั่นใจว่าฟังก์ชันตัวช่วย REPL ได้รับการอ้างอิงในเนมสเปซผู้ใช้แทนที่จะเป็นเนมสเปซหลักของแอปพลิเคชันของคุณ ด้วยวิธีนี้พวกเขาจะไม่หลงทางเว้นแต่คุณจะแก้ไขไฟล์ต้นฉบับที่เราเพิ่งสร้างขึ้น
หวังว่านี่จะช่วยได้!
(use 'foo.bar :reload-all)
ทำงานได้ดีสำหรับฉันเสมอ นอกจากนี้(load-file)
ไม่ควรจำเป็นถ้าคุณตั้ง classpath ของคุณถูกต้อง "เอฟเฟกต์ที่จำเป็น" ที่คุณไม่ได้รับคืออะไร