clojure - आरईपीएल में क्लोजर फ़ाइल को फिर से लोड कैसे करें




reload read-eval-print-loop (6)

क्लोजर कोड का उपयोग कर रीलोडिंग कोड (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 नामस्थान के वर्तमान संस्करण को नष्ट कर देता है।

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 नाम का उपयोग कर सकते हैं लेकिन व्यक्तिगत रूप से मैं इसे प्रत्येक रीफ्रेश पर टाइप करना पसंद नहीं करता हूं। उपर्युक्त के साथ एक और समस्या यह है कि मुख्य नामस्थान को पुनः लोड करने के बाद मानक आरईपीएल सहायक कार्यों (जैसे doc और source ) को अब संदर्भित नहीं किया जाता है।

इन मुद्दों को हल करने के लिए मैं उपयोगकर्ता नामस्थान के लिए एक वास्तविक स्रोत फ़ाइल बनाना पसंद करता हूं ताकि इसे विश्वसनीय रूप से पुनः लोड किया जा सके। मैंने स्रोत फ़ाइल को ~/.lein/src/user.clj लेकिन आप कहीं भी जगह ले सकते हैं। फाइल को शीर्ष एनएस घोषणा में रीफ्रेश फ़ंक्शन की आवश्यकता होनी चाहिए:

(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”]}}

ध्यान दें कि मैंने आरईपीएल लॉन्च करते समय उपयोगकर्ता नामस्थान को प्रवेश बिंदु के रूप में सेट किया है। यह सुनिश्चित करता है कि आरईपीएल सहायक कार्यों को आपके एप्लिकेशन के मुख्य नामस्थान की बजाय उपयोगकर्ता नामस्थान में संदर्भित किया जाए। इस तरह वे तब तक खो नहीं जाएंगे जब तक आप हमारे द्वारा बनाई गई स्रोत फ़ाइल को परिवर्तित नहीं करते।

उम्मीद है की यह मदद करेगा!

आरईपीएल को पुनरारंभ किए बिना क्लोजर फ़ाइल में परिभाषित कार्यों को फिर से लोड करने का पसंदीदा तरीका क्या है। अभी, अद्यतन फ़ाइल का उपयोग करने के लिए मुझे यह करना है:

  • src/foo/bar.clj संपादित करें
  • आरईपीएल बंद करें
  • आरईपीएल खोलो
  • (load-file "src/foo/bar.clj")
  • (use 'foo.bar)

इसके अलावा, (use 'foo.bar :reload-all) का उपयोग करने के लिए आवश्यक प्रभाव नहीं होता है, जो कि कार्यों के संशोधित निकायों का मूल्यांकन कर रहा है और नए मूल्यों को वापस कर रहा है, स्रोत के रूप में व्यवहार करने की बजाय बिल्कुल बदल नहीं है।


जैसे ही (use 'foo.bar) आपके लिए काम करता है, इसका मतलब है कि आपके क्लासस्पैट पर foo / bar.clj या foo / bar_init.class है। Bar_init.class bar.clj का एओटी-संकलित संस्करण होगा। यदि आप करते हैं (use 'foo.bar) , तो मुझे बिल्कुल यकीन नहीं है कि क्लोजर क्लाज या दूसरी तरफ से कक्षा को पसंद करता है। यदि यह कक्षा फाइलों को पसंद करेगा और आपके पास दोनों फाइलें हैं, तो यह स्पष्ट है कि clj फ़ाइल को संपादित करना और फिर नामस्थान को फिर से लोड करना कोई प्रभाव नहीं पड़ता है।

बीटीडब्ल्यू: यदि आपका क्लासस्पैट सही तरीके से सेट किया गया है तो आपको use से पहले load-file करने की आवश्यकता नहीं है।

बीटीडब्ल्यू 2: यदि आपको किसी कारण से load-file का उपयोग करने की आवश्यकता है, तो यदि आप फ़ाइल संपादित करते हैं तो आप इसे फिर से कर सकते हैं।


फिर से लोड फ़ाइल का प्रयास करें?

यदि आप एक आईडीई का उपयोग कर रहे हैं, तो आमतौर पर आरईपीएल को कोड-ब्लॉक भेजने के लिए एक कीबोर्ड शॉर्टकट होता है, इस प्रकार प्रभावी कार्यों को प्रभावी ढंग से फिर से परिभाषित करता है।


मैं इसे लाइटटेबल (और भयानक instarepl) में उपयोग करता हूं लेकिन यह अन्य विकास उपकरणों में उपयोग किया जाना चाहिए। मुझे फ़ंक्शन के पुराने परिभाषाओं और पुनः लोड करने के बाद चारों ओर लटकने वाले मल्टीमीथ्स के साथ एक ही समस्या हो रही थी, इसलिए अब नामस्थानों को घोषित करने के बजाय विकास के दौरान:

(ns my.namespace)

मैं इस तरह के नामस्थान घोषित करता हूं:

(clojure.core/let [s 'my.namespace]
                  (clojure.core/remove-ns s)
                  (clojure.core/in-ns s)
                  (clojure.core/require '[clojure.core])
                  (clojure.core/refer 'clojure.core))

बहुत बदसूरत लेकिन जब भी मैं पूरे नामस्थान का पुनर्मूल्यांकन करता हूं (प्रत्येक अभिव्यक्ति के नए instarepl परिणाम प्राप्त करने के लिए लाइटटेबल में सीएमडी-शिफ्ट-एंटर), यह सभी पुरानी परिभाषाओं को दूर करता है और मुझे एक स्वच्छ वातावरण देता है। मैंने इसे शुरू करने से पहले हर कुछ दिनों में पुरानी परिभाषाओं से छीन लिया था और उसने मेरी स्वच्छता को बचा लिया है। :)


सबसे अच्छा जवाब है:

(require 'my.namespace :reload-all)

यह न केवल आपके निर्दिष्ट नेमस्पेस को पुनः लोड करेगा, बल्कि सभी निर्भरता नामस्थानों को फिर से लोड करेगा।


tools.namespace का उपयोग करने का विकल्प भी है, यह बहुत ही कुशल है:

user=> (use '[clojure.tools.namespace.repl :only (refresh)])

user=> (refresh)

:reloading (namespace.app)

:ok




leiningen