2010-02-15 7 views
5

मैंने देखा है कि क्लोजर-ट्विटर जैसे कुछ पुस्तकालय ओथ प्रमाणीकरण के लिए विशेष वर्र्स (गतिशील बाध्यकारी के लिए लक्षित हैं जो क्षुद्रग्रहों से घिरे हैं) का उपयोग करते हैं। आप अपने प्रमाणीकरण को एक var में सहेजते हैं और फिर (with-oauth myauth ..) का उपयोग करते हैं। मुझे लगता है कि यह इस तरह की समस्या का एक बहुत अच्छा समाधान है, क्योंकि आप आवेदन के प्रत्येक उपयोगकर्ता के लिए auth var को पुन: जोड़ सकते हैं।क्लोजर में अस्थायी रूप से-रिबाइंड-ए-स्पेशल-वर्च मुहावरे का उपयोग कब किया जाना चाहिए?

मैंने एक ईमेल क्लाइंट में एक समान मार्ग लिया है जिसे मैं लिख रहा हूं। मेरे पास सत्र नामक एक विशेष var है जो मैं वर्तमान उपयोगकर्ता के सत्र और उपयोगकर्ता की जानकारी के साथ एक मानचित्र से जुड़ता हूं, और ऐसे विभिन्न महत्वपूर्ण कार्य हैं जो उस var से जानकारी का उपयोग करते हैं। मैंने सत्र के साथ पारित रूपों के एक सेट के संदर्भ में अस्थायी रूप से इसे पुनर्विचार करने के लिए सत्र के साथ एक मैक्रो लिखा था। यह एक सुंदर साफ समाधान (मेरे लिए) साबित हुआ।

तो, मेरा सवाल यह है: क्या मैं इसे 'राइट' कर रहा हूं? क्या यह एक खराब डिजाइन निर्णय है, या यह विशेष वर्रों के इच्छित उपयोगों में से एक है?

उत्तर

7

ऐसा लगता है कि आप इसे ठीक से कर रहे हैं। असल में, कई अंतर्निर्मित/contrib मैक्रोज़ हैं जो समान रूप से काम करते हैं, with-out-str या clojure.contrib.sql/with-connection कहें। उत्तरार्द्ध आज के क्लोजर इंफ्रास्ट्रक्चर का एक प्रमुख हिस्सा है, इसलिए जो भी मुहावरे इसका उपयोग करता है, उसकी जांच बहुत से लोगों द्वारा की जाती है।

महत्वपूर्ण पकड़ लिया ध्यान में रखना है कि धागे आप का शुभारंभ करते हुए एक bindings/with-bindings प्रपत्र के दायरे में नहीं प्रश्न में वार्स के लिए पलटाव मूल्यों के वारिस करते हैं; बल्कि, वे रूट बाइंडिंग देखते हैं। यदि आप अपने बाइंडिंग को वर्कर थ्रेड/एजेंटों को प्रचारित करना चाहते हैं, तो उन्हें स्पष्ट रूप से (फ़ंक्शन तर्क के रूप में कहें) या bound-fn का उपयोग करें।

+0

उत्तर के लिए धन्यवाद। बहुत सूचनाप्रद। में इसे याद रखूंगा। :) – Rayne

+0

आप, ज़ाहिर है, इस चर्चा मैं अचानक याद आया के लिए एक लिंक पोस्ट करने के लिए स्वागत है, हालांकि मुझे लगता है कि ब्रायन साथ आया था और हड़कंप मच गया आप के लिए बातें ... असल में मैं वापस कर रहा हूँ: http://groups.google .com/group/clojure/Browse_thread/thread/a7d020f8c76ecb77/dbd4f016fbb5f2e4 - थ्रेड का शीर्षक "रिंग्स के लिए सत्र" है। रिंग सर्वर-साइड काम करता है, लेकिन मुझे लगता है कि चर्चा अभी भी आपकी मदद कर सकती है। विचारों को परमाणुओं (सत्र प्रति एक सत्र) और कार्यात्मक शुद्धता में सत्र डेटा संग्रहीत करने के बीच विभाजित किया जाता है ... दोनों तरफ से अच्छे तर्क, अपना चयन करें। –

+0

ओह, भी, परमाणुओं के साथ भी संस्करण के लिए उन्हें एक सत्र जो चारों ओर स्पष्ट रूप से पारित हो जाता है में डालता है, लेकिन रिंग सत्र की अंगूठी के कोड * के एक बड़े हिस्से के लिए प्रासंगिक हैं और * वहाँ की उन्हें हर समय चारों ओर पारित किया जा रहा एक भीड़ होने की संभावना है । दूसरी तरफ, एक ई-मेल क्लाइंट के साथ, आप सभी प्रासंगिक जानकारी को केवल एक डेटा संरचना में डाल सकते हैं (संभवतः कई सर्वरों के लिए लॉगिन प्रमाण-पत्र आदि शामिल हैं - लेकिन उनमें से सभी एक ही इंसान से संबंधित हैं होने वाला) जो हमेशा * वहां होगा। यह मेरी आंखों में, वेबपैप से कुछ अलग जानवर बनाता है ... फिर भी, वाईएमएमवी। –

3

हर बार जब आप एक वैश्विक वैर बनाते हैं जिसे आप फिर से बांधने की योजना बनाते हैं, तो आप उस चर को एक्सेस करने वाले प्रत्येक फ़ंक्शन के लिए एक अतिरिक्त निहित तर्क जोड़ रहे हैं। उचित (स्पष्ट) तर्कों के विपरीत, यह छुपा तर्क फ़ंक्शन के हस्ताक्षर में दिखाई नहीं देता है और यह संकेत हो सकता है कि फ़ंक्शन इसका उपयोग कर रहा है। आपका कोड कम "कार्यात्मक" हो जाता है; एक ही तर्क को उसी तर्क के साथ बुलाकर परिणामस्वरूप इन वैश्विक गतिशील युद्धों की वर्तमान स्थिति के आधार पर अलग-अलग रिटर्न मूल्य हो सकते हैं।

वैश्विक वर्रों का लाभ यह है कि आप आसानी से एक डिफ़ॉल्ट मान निर्दिष्ट कर सकते हैं, और यह आपको उस वर्क को हर फंक्शन के पास पास करने के लिए आलसी होने की अनुमति देता है।

नकारात्मकता यह है कि आपका कोड पढ़ने, परीक्षण, उपयोग और डीबग करना कठिन है। और आपका कोड संभावित रूप से अधिक त्रुटि-प्रवण हो जाता है; इससे पहले कि आप इसे इस्तेमाल करने वाले फ़ंक्शन को कॉल करने से पहले var को बांधना या फिर से बांधना भूलना आसान हो, लेकिन session पैरामीटर में गुजरने के लिए भूलना आसान नहीं है, जब यह सही है।

तो आप रहस्य कीड़े के साथ समाप्त होते हैं, और कार्यों के बीच अजीब अंतर्निहित निर्भरताएं समाप्त करते हैं। इस परिदृश्य पर विचार करें:

user> (defn foo [] (when-not (:logged-in *session*) (throw (Exception. "Access denied!")))) 
#'user/foo 
user> (defn bar [] (foo)) 
#'user/bar 
user> (defn quux [] (bar)) 
#'user/quux 
user> (quux) 
; Evaluation aborted. ;; Access denied! 

quux के व्यवहार एक मूल्य होने के सत्र पर परोक्ष निर्भर करता है, लेकिन आपको पता नहीं होता कि जब तक आप quux कॉल हर कार्य के माध्यम से नीचे खोदा, और हर कार्य करते हैं उन कार्यों कहते हैं। *session* के आधार पर नीचे एक समारोह के साथ कॉल श्रृंखला 10 या 20 स्तरों की गहराई की कल्पना करें। मजेदार डिबगिंग है कि।

इसके बजाय यदि आपने (defn foo [session] ...), (defn bar [session] ...), (defn quux [session] ...) था, इसे तुरंत आप के लिए स्पष्ट है कि यदि आप quux फोन, तो आप बेहतर एक सत्र के लिए तैयार होगा होगा।

व्यक्तिगत रूप से, मैं स्पष्ट तर्कों का उपयोग जब तक कि मैं एक मजबूत, समझदार डिफ़ॉल्ट मान है कि कार्यों की टन का इस्तेमाल किया, कि मैं बहुत कम या कभी rebind करने की योजना बनाई थी होगा। (उदाहरण के लिए एसटीडीओयूटी को कुछ भी प्रिंट करना चाहते हैं जो प्रत्येक फंक्शन को एक स्पष्ट तर्क के रूप में पास करने के लिए मूर्खतापूर्ण होगा।)

+0

अच्छा, मुझे लगता है कि इसका मतलब है कि मैं अपने पूरे एप्लिकेशन को फिर से डिजाइन कर दूंगा। जब मैं दो बहुत आत्मविश्वास प्राप्त करता हूं, लेकिन पूरी तरह से असंगत उत्तर देता हूं तो मुझे बस यह पसंद है। : \ उत्तर के लिए धन्यवाद। – Rayne

+1

मैं वहां गया हूं ... मेरे पास वैश्विक सत्र/पैरा/HTTP हेडर/कुकीज़ वर्र्स के साथ एक वेबपैप था जो प्रत्येक अनुरोध पर गतिशील रूप से बाध्य था। जब मैं इसे स्पष्ट पैराम लेने के लिए सभी को फिर से लिखता हूं, तो सब कुछ बेहतर था। जैसे अधिकांश एफएनएस को WHOLE सत्र की आवश्यकता नहीं होती है, उन्हें केवल एक या दो बिट्स की आवश्यकता होती है। आप केवल उन बिट्स को तर्क के रूप में पास कर सकते हैं। मेरे मामले में चारों ओर फेंकने वाले तर्क डेटा की मात्रा बहुत कम थी। कोड थोड़ा अधिक verbose है, लेकिन समझने के लिए बहुत आसान है। यदि आप वास्तव में बहुत अधिक टाइपिंग कर रहे हैं तो आप मैक्रोज़ का उपयोग कर सकते हैं। –

+0

मुझे लगता है कि एक वेबपैड में देखा गया सत्र किसी ई-मेल क्लाइंट में देखे गए सत्र से काफी भिन्न हो सकता है। एक बात के लिए, मुझे लगता है कि एक ई-मेल क्लाइंट ज्यादातर स्थानीय स्टोरेज (शायद एक स्थानीय कॉच डीबी/आरडीबीएमएस कनेक्शन) पर काम करेगा और केवल कुछ कतार में बैठे सामान भेजने पर कनेक्शन बनायेगा या कुछ समय बीत चुका है सर्वर (ओं) से नए मेल के लिए अंतिम अनुरोध। कोई कारण नहीं क्यों लॉगइन क्रेडेंशियल्स एक 'बाध्य-fn' यदि ऐसा है करने के लिए' binding' साथ प्रदान नहीं किया जा सकता है ... वैसे भी, मौलिक डिजाइन मुद्दा है मैं नहीं है कि क्या धागे की स्थानीय –

1

बाइंडिंग फ़ंक्शन परीक्षण कोड के लिए बहुत अच्छे हैं।

मैं अपने परीक्षण कोड में बड़े पैमाने पर rebinding आवरण कार्यों का उपयोग नकली यादृच्छिक संख्या genrator जैसी चीज़ों के लिए, एक निश्चित ब्लॉक आकार आदि का उपयोग करें ... इतना है कि मैं वास्तव में जाना जाता है उत्पादन के खिलाफ एक एन्क्रिप्शन समारोह परीक्षण कर सकते हैं।

(defmacro with-fake-prng [ & exprs ] 
    "replaces the prng with one that produces consisten results" 
    `(binding [com.cryptovide.split/get-prng (fn [] (cycle [1 2 3])) 
      com.cryptovide.modmath/mody 719 
      com.cryptovide.modmath/field-size 10] 
     [email protected])) 

(is (= (with-fake-prng (encrypt-string "asdf")) [23 54 13 63])) 

When using bindings its useful to remember that they only rebind for the current thread so when you fire something off in pmap which uses the thread pool you may loose your bindings. If you have some code that builds a string in parallel like this:

(with-out-str 
    (pmap process-data input)) 

Using that innocent looping \p in front of the map will cause the binding to go away because it will run the process-data function in several threads from the thread-pool.

EDIT: Michał Marczyk points out the bound-fn मैक्रो जो आप थ्रेड का उपयोग करते समय बाइंडिंग को खोने से रोकने के लिए उपयोग कर सकते हैं।

संबंधित मुद्दे