2010-10-22 34 views
8

Tôi có một chương trình Clojure mà tôi xây dựng dưới dạng tệp JAR sử dụng Maven. Nhúng trong Tệp kê khai JAR là số phiên bản xây dựng, bao gồm dấu thời gian xây dựng.Đặt Clojure "hằng số" tại thời gian chạy

tôi có thể dễ dàng đọc này trong thời gian chạy từ JAR Manifest sử dụng đoạn mã sau:

(defn set-version 
    "Set the version variable to the build number." 
    [] 
    (def version 
    (-> (str "jar:" (-> my.ns.name (.getProtectionDomain) 
            (.getCodeSource) 
            (.getLocation)) 
        "!/META-INF/MANIFEST.MF") 
     (URL.) 
     (.openStream) 
     (Manifest.) 
     (.. getMainAttributes) 
     (.getValue "Build-number")))) 

nhưng tôi đã được cho biết rằng đó là nghiệp chướng xấu để sử dụng def bên defn.

Cách thành ngữ Clojure để đặt hằng số khi chạy là gì? Tôi rõ ràng không có thông tin phiên bản xây dựng để nhúng vào mã của tôi dưới dạng def, nhưng tôi muốn nó được đặt một lần (và cho tất cả) từ chức năng main khi chương trình bắt đầu. Sau đó, nó sẽ có sẵn dưới dạng def cho phần còn lại của mã đang chạy.

CẬP NHẬT: BTW, Clojure phải là một trong những ngôn ngữ tuyệt vời nhất mà tôi đã gặp trong một thời gian dài. Kudos đến Rich Hickey!

Trả lời

7

Tôi vẫn nghĩ cách sạch nhất là sử dụng alter-var-root trong phương thức main của đơn đăng ký của bạn.

(declare version) 

(defn -main 
    [& args] 
    (alter-var-root #'version (constantly (-> ...))) 
    (do-stuff)) 

Nó khai báo Var tại thời gian biên dịch, đặt giá trị gốc của nó trong thời gian chạy một lần, không yêu cầu deref và không bị ràng buộc vào chuỗi chính. Bạn đã không trả lời đề xuất này trong câu hỏi trước của bạn. Bạn đã thử phương pháp này chưa?

+0

Tôi chưa thử, nhưng tôi sẽ làm. Trông nó thật thú vị. Không có hình phạt hiệu suất khi giá trị được đặt. Tôi cũng có thể sử dụng kỹ thuật này để thiết lập các giá trị từ các tùy chọn dòng lệnh - chỉ cần thiết lập một lần. – Ralph

1

Tôi hy vọng tôi không bỏ lỡ điều gì đó lần này.

Nếu phiên bản là hằng số, nó sẽ được xác định một lần và sẽ không bị thay đổi, bạn có thể xóa đơn giản và duy trì (def version ...) một mình. Tôi cho rằng bạn không muốn điều này vì một lý do nào đó.

Nếu bạn muốn thay đổi các biến toàn cầu trong một fn tôi nghĩ rằng cách thành ngữ hơn là sử dụng một số công trình xây dựng đồng thời để lưu trữ các dữ liệu và truy cập và thay đổi nó một cách an toàn Ví dụ:

(def *version* (atom "")) 

(defn set-version! [] (swap! *version* ...)) 
+0

@jneira: "Tôi hy vọng tôi không bỏ lỡ điều gì đó lần này". :-) Tôi không thể làm điều đó bởi vì sau đó nó đánh giá tại thời gian biên dịch (tôi tin) và không có tập tin JAR để đọc được nêu ra. – Ralph

+0

hehe :-P ok và tùy chọn thứ hai? (thứ ba sẽ là chức năng trả về các chuỗi thay vì làm một redef nhưng nó có thể không hiệu quả cho các cuộc gọi khác nhau) – jneira

+0

Tôi đã nghĩ rằng tôi sẽ cần phải làm điều gì đó với các nguyên tử, tôi chỉ nghĩ rằng sẽ là quá mức cần thiết, với chi phí đồng bộ hóa, vv Có lẽ chỉ cần gọi 'def' từ hàm' main' sẽ là OK, ngay cả khi nó được "cau mày". – Ralph

4

Bạn có thể sử dụng liên kết động.

(declare *version*) 

(defn start-my-program [] 
    (binding [*version* (read-version-from-file)] 
    (main)) 

Bây giờ main và mọi chức năng mà cuộc gọi sẽ thấy giá trị *version*.

+0

+1 để nhắc tôi "và mọi chức năng mà nó gọi" là một ràng buộc động, có thể phù hợp với vấn đề – jneira

+0

Tôi thích điều này! Tôi sẽ sử dụng các nguyên tử, nhưng tôi sẽ cần phải khám phá thêm điều này. Các biến (hằng số) tôi cần phải đặt tất cả phải được thiết lập trước khi phần còn lại của chương trình chạy anyway. Bên cạnh số phiên bản, tôi cũng sẽ sử dụng giải pháp để ghi lại các tùy chọn dòng lệnh thu được với Apache commons-cli. – Ralph

+3

Hãy nhớ ranh giới của chuỗi 'ràng buộc'. Xem thêm 'bound-fn'. – kotarak

2

Khi giải pháp của kotarak hoạt động rất tốt, đây là một cách tiếp cận khác: chuyển mã của bạn thành một hàm được ghi nhớ trả về phiên bản. Giống như vậy:

(def get-version 
(memoize 
    (fn [] 
    (-> (str "jar:" (-> my.ns.name (.getProtectionDomain) 
      (.getCodeSource) 
      (.getLocation)) 
     "!/META-INF/MANIFEST.MF") 
    (URL.) 
    (.openStream) 
    (Manifest.) 
    (.. getMainAttributes) 
    (.getValue "Build-number"))))) 
Các vấn đề liên quan