Radiance هي بيئة تطبيق ويب ، والتي تشبه إطار عمل الويب ، ولكنها أكثر عمومية وأكثر مرونة. يجب أن يتيح لك كتابة مواقع الويب الشخصية والتطبيقات القابلة للنشر بشكل عام بسهولة وبهذه الطريقة التي يمكن استخدامها في أي إعداد عمليًا دون الحاجة إلى التعديلات الخاصة.
يتم توزيع الإشراق والوحدات والتطبيقات المرتبطة به عبر QuickLisp في dist منفصلة. لتثبيت Radiance ، افعل:
(ql-dist:install-dist "http://dist.shirakumo.org/shirakumo.txt")
(ql:quickload :radiance)
من هناك ، يجب أن تكون قادرًا على تحميل واستخدام أي نوع من الوحدات النمطية للإشعاع مثل Purplish مباشرة عبر quickload QuickLisp السريع.
يمكنك العثور على برنامج تعليمي يقدم Radiance ومعظم المفاهيم المهمة ، ويستكشف كيفية كتابة تطبيق الويب بشكل عام ، هنا. يجب أن يمنحك شعورًا جيدًا لكيفية القيام بالأشياء ، ويمنحك مؤشرات حول مكان البحث إذا كنت بحاجة إلى ميزة معينة. في الجزء الأخير ، ستذهب أيضًا إلى الإعداد الفعلي ونشر تثبيت Radiance على خادم الإنتاج.
الشيء الأساسي الذي تريد القيام به على الأرجح هو خدمة نوع من HTML. لذلك دعونا نعمل من أجل ذلك وتوسيعه تدريجياً. قبل أن نتمكن من البدء ، نحتاج إلى بدء الإشراق.
( ql :quickload :radiance )
( radiance :startup) إذا كانت هذه هي المرة الأولى التي تقوم فيها بإعداد الإشعاع ، فستحصل على ملاحظة حوله باستخدام وحدة r-welcome . يجب أن يعطيك أيضًا رابطًا يمكنك فتحه في متصفحك لرؤية صفحة تحية صغيرة. في الوقت الحالي ، سنرغب فقط في طرح صفحتنا الصغيرة إلى جانبها.
( in-package :rad-user )
(define-page example " /example " ()
( setf (content-type *response* ) " text/plain " )
" Hi! " )زيارة LocalHost: 8080/مثال يجب الآن إظهار "مرحبًا". مملة إلى حد ما حقا. لذلك دعونا نبصق بعض HTML بدلاً من ذلك. في الوقت الحالي ، سنستخدم CL-Who لأنه بسيط للغاية. أول تحميل سريع ، ثم قم بتشغيل ما يلي:
(define-page example " /example " ()
( cl-who :with-html-output-to-string (o)
( cl-who :htm
( :html
( :head ( :title " Example Page " ))
( :body ( :header ( :h1 " Couldn't Be Simpler. " ))
( :main ( :p " Trust me on this one. " )))))))إعادة ترجمة وتحديث في وقت لاحق ولدينا بعض تصميم الخطوط. بعد ذلك ، ربما نريد إضافة ملف CSS إليه لتصميمه بشكل صحيح. يمكن أن نخدم CSS باستخدام صفحة أخرى أيضًا ، لكن هذه ليست أفضل طريقة للقيام بالأشياء على المدى الطويل.
دعنا نلقي نظرة على كيفية إنشاء وحدة نمطية ، والتي ستسمح لنا بتنظيم الأشياء بطريقة أكثر منظمة. يمكنك إنشاء الملفات لوحدة نمطية يدويًا ، ولكن في الوقت الحالي ، سنستقر مع هيكل عظمي تم إنشاؤه تلقائيًا يمكن أن يوفره لك Radiance.
(create-module " example " ) يجب أن يعيد لك مسارًا تكمن فيه الوحدة النمطية. يجب أن يحتوي على نظام ASDF ، ملف LISP الرئيسي ، ومجلدان ، static template . من المثير للدهشة أن المجلد static هو المكان الذي تسير فيه الملفات التي يتم تقديمها بشكل ثابت ، template مخصص لمستندات القالب ، إذا كنت تستخدم نظام قالب.
دعنا نفتح example.lisp .
(define-page example " /example " ()
( cl-who :with-html-output-to-string (o)
( cl-who :htm
( :html
( :head ( :title " Example Page " ))
( :body ( :header ( :h1 " Couldn't Be Simpler. " ))
( :main ( :p " Trust me on this one. " ))))))) يتم تحديد الصفحات بواسطة رمز الاسم. نظرًا لأن لدينا الآن الوحدة النمطية الخاصة بنا ، وبالتالي الحزمة الخاصة بنا ، فإن رمز المثال أعلاه لن يكون هو نفسه الرمز الذي استخدمناه من قبل. سيتعين علينا فقط إزالة الصفحة في حزمة rad-user لتجنب الصدام.
(remove-page ' rad-user::example) تأكد من تحميل ملف المثال كلما قمت بتغييره الآن حتى تسري التغييرات. بعد ذلك ، دعنا ننشئ ملفًا CSS بسيطًا لتوبيخ الأشياء قليلاً. سيتم وضع الملف example.css في المجلد static . إليك عينة CSS إذا كنت لا تريد كتابة بنفسك.
body {
font-family : sans-serif;
font-size : 12 pt ;
background : # EEE ;
}
header {
text-align : center;
}
main {
width : 800 px ;
margin : 0 auto 0 auto;
background : # FFF ;
padding : 10 px ;
border : 1 px solid # BBB ;
border-radius : 5 px ;
}بعد ذلك ، نحتاج إلى تعديل HTML الخاص بنا لربط ورقة الأنماط فعليًا. من أجل الحصول على العنوان إلى ورقة الأنماط ، سيتعين علينا الاستفادة من نظام توجيه Radiance. لا تقلق ، إنها ليست مشكلة كبيرة.
(define-page example " /example " ()
( cl-who :with-html-output-to-string (o)
( cl-who :htm
( :html
( :head ( :title " Example Page " )
( :link :rel " stylesheet " :type " text/css "
:href (uri-to-url " /static/example/example.css " :representation :external )))
( :body ( :header ( :h1 " Couldn't Be Simpler. " ))
( :main ( :p " Trust me on this one. " ))))))) قم بتحديث الصفحة ، وفويلا ، والآن أصبح هناك بعض البيتزاز إليها أيضًا. من المحتمل أن ترغب في تفسير لأعمال uri-to-url بأكملها. تتم معالجة شرحها بالكامل من قبل الأقسام التي تلي هذا ، ولكن جوهره هو أنه يضمن حل الارتباط بالملف الثابت بشكل صحيح تحت أي إعداد.
واحدة من أكثر المفاهيم المركزية في الإشراق هي مفهوم URI. URI هو كائن يتكون من قائمة من المجالات ورقم منفذ اختياري ومسار (انظر uri ). إنها في الأساس نسخة مجردة من URI العامة ، وبالتالي لا تتضمن مخططًا أو استعلامًا أو جزءًا. هناك اختلاف مهم آخر هو أن domains URIs تستخدم في عدة نقاط خلال الإطار ، سواء لالتقاط المواقع والتعامل مع مطابقة الإرسال.
لاحظ أن URIs قابلة للتغيير. هذا مهم للأداء ، حيث يجب أن تحدث تعديلات URI في عدة أجزاء تقع على المسار الحرج. ومع ذلك ، في الحالة المعتادة ، ليس من المتوقع تعديل URIs خارج عدد قليل من الوظائف المختارة. قد يؤدي تعديل أجزاء URI بطرق غير متوقعة إلى سلوك غريب.
يمتلك URIS تمثيل سلسلة فريد من نوعه ويمكن أن يتم تسلسله للسلسلة وتوحله مرة أخرى إلى كائن URI كامل مرة أخرى. يمكن أيضًا إلقاء URIS على ملفات FASL كحرفية ، لذا فإن انبعاثها من وحدات الماكرو جيدة. بناء الجملة لـ URI كما يلي:
URI ::= DOMAINS? (':' PORT)? '/' PATH?
DOMAINS ::= DOMAIN ('.' DOMAIN)*
DOMAIN ::= ('a'..'Z' | '0'..'9' | '-')
PORT ::= ('0'..'9'){1, 5}
PATH ::= .*
يمكنك استخدام uri-to-url لتحويل URI إلى عنوان URL الخرساني. يتم التعامل مع الانعكاس والترميز والتنسيق المناسب لجميع الأجزاء من أجلك تلقائيًا هناك.
انظر uri ، domains ، port ، path ، matcher ، uri-string ، make-uri ، make-url ، ensure-uri ، copy-uri ، parse-uri ، uri< ، uri> ، uri= ، uri-matches ، merge-uris ، represent-uri ، uri-to-url .
من أجل تغليف البيانات التي يتم إرسالها من وإلى ، لدينا فكرة كائن طلب ( request ) والاستجابة ( response ). يحمل كائن الطلب URI الذي يمثل الموقع الذي يذهب إليه الطلب ، وجميع البيانات الواردة في حمولة HTTP مثل متغيرات Post و Get و Header و Cookie. يحمل كائن الاستجابة رمز الإرجاع والرؤوس وملفات تعريف الارتباط وبيانات الجسم الفعلية.
أثناء معالجة الطلب ، يجب أن يكون هذان الكائنان دائمًا حاضرين ومرتبطين بمتغيرات *request* و *response* . إنها تغلف الكثير من المعلومات الحيوية للغاية التي تكون ضرورية لإنشاء صفحات ديناميكية. بالإضافة إلى ذلك ، يحتوي الطلب على جدول data غير شفاف يمكنك فيه تخزين البيانات التعسفية. هذا مفيد عندما تحتاج إلى تبادل أجزاء من المعلومات بين الأجزاء الفردية من النظام التي يمكن الوصول إليها أثناء تنفيذ الطلب.
لا يجب أن تأتي الطلبات بالضرورة من خادم HTTP. من أجل اختبار الأشياء ، يمكنك أيضًا إنشاء طلب بنفسك وإرساله برمجيًا. مهما كانت الحالة ، تسمى الواجهة الأساسية لإرسال طلب request . سيؤدي ذلك إلى بناء كائن طلب وكائن استجابة لك والتعامل مع URI بشكل مناسب. إذا كنت ترغب في القيام بذلك بنفسك وفقط إرسال كائن طلب كامل ، فيمكنك استخدام execute-request .
لمعرفة المعالجة الفعلية للطلب ، راجع المرسلون والصفحات ونقاط نهاية واجهة برمجة التطبيقات.
انظر *request* ، *response* ، *default-external-format* ، *default-content-type* domain request ، uri ، http-method response body-stream cookie referer headers post-data data get-data name cookies ، user-agent issue-time value external-format headers ، cookies ، data remote return-code content-type domain ، path ، expires ، http-only ، secure ، cookie file cookie-header ، serve-file redirect header get-var post-var handle-condition render-error-page execute-request request set-data request-run-time *debugger* post/get
قبل أن يتم إرسال الطلب ، يمر عبر شيء يسمى نظام التوجيه. على عكس الأطر الأخرى ، حيث تحدد "الطرق" التي تتعامل مع الطلب ، في Radiance A Route ( route ) هو شكل من أشكال مترجم URI. هذا الجزء من النظام هو ما هو مسؤول عن إنشاء ودعم اثنين من "الأكوان" ، جزء داخلي وخارجي.
الكون الداخلي هو تطبيق تطبيقات الويب الفعلي الواحد. الكون الخارجي هو الخادم HTTP ومستخدم موقع الويب الذي يعيش فيه. هذا التمييز ضروري من أجل السماح لك ، بيد واحدة ، كتابة تطبيقات الويب دون الحاجة إلى القلق بشأن ما قد يبدو عليه الإعداد المحتمل على الخادم في مرحلة ما. لا داعي للقلق بشأن نوع المجال والمنفذ ، قد يكون إعداد المسار ضروريًا لتشغيل التطبيق الخاص بك. من ناحية أخرى ، فإنه يتيح لك ، باعتباره WebAdmin ، تخصيص وتشغيل النظام إلى رغباتك الدقيقة دون خوف من كسر الأشياء.
يتم تسهيل كل هذا من خلال الطرق ، والتي يوجد منها نوعان: رسم الخرائط ، وطرق الانعكاس. طرق التعيين هي المسؤولة عن تحويل URI من الكون الخارجي إلى أحد الكون الداخلي. عادةً ما يتضمن هذا قطع المجال العلوي وربما القيام برسم خرائط للنطاقات الفرعية. طرق الانعكاس تفعل عكس ذلك- يذهبون من الكون الداخلي إلى الخارجي. هذا ضروري من أجل جعل روابط في صفحاتك الخدمية تشير إلى الموارد التي يمكن الوصول إليها بالفعل من الخارج. عادةً ما يتضمن هذا عكس رسم خرائط النطاق الفرعي وإضافة مجال المستوى الأعلى مرة أخرى.
يمكن أن تؤدي الطرق إلى عمل تعسفي. على المستوى الأساسي ، فهي مجرد وظائف تعدل URI بطريقة ما. يتيح لك ذلك إنشاء نظام مرن للغاية يجب أن يكون قويًا بما يكفي لاستيعاب جميع احتياجاتك كمسؤول. ككاتب تطبيق ، تحتاج فقط إلى التأكد من استخدام external-uri أو uri-to-url على جميع الروابط التي تضعها في صفحاتك.
انظر route internal-uri name ، direction ، priority ، translator ، route ، remove-route define-route ، list-routes ، define-target-route define-string-route ، define-matching-route external-uri
أخيرًا ، وصلنا إلى الجزء الذي يولد بالفعل محتوى لطلب. مرسلو URI هم فئة فرعية من URI تحمل أيضًا اسمًا ووظيفة وأولوية. Live في قائمة تورم ذات أولوية ، تتم معالجتها عند وصول الطلب. تتم مطابقة URI للطلب ضد كل مرسل. ثم يتم تنفيذ وظيفة المرسل الأول الذي يتطابق.
وهذا كل شيء. وظيفة المرسل هي المسؤولة عن تعيين القيم اللازمة في كائن الاستجابة لتسليم محتوى الصفحة. من أجل القيام بذلك ، يمكن إما تعيين حقل data لكائن الاستجابة مباشرة ، أو يمكنك إرجاع قيمة مناسبة من الوظيفة. يقبل Radiance فقط أربعة أنواع من القيم: stream ، pathname ، string ، و (array (unsigned-byte 8)) .
إذا لم يكن لدى مرسل URI رقم أولوية صريح ، يتم تحديد أولويته على الآخرين بخصوصية URI. راجع وظيفة فرز URI uri> للحصول على شرح حول كيفية حساب ذلك بالضبط.
انظر uri-dispatcher ، name ، dispatch-function ، priority ، uri-dispatcher ، remove-uri-dispatcher list-uri-dispatchers ، uri-dispatcher> ، define-uri-dispatcher ، dispatch
الصفحات هي ما ستستخدمه على الأرجح لتحديد وظائف خدمة المحتوى الفعلي. ومع ذلك ، فإن الصفحة هي مجرد مرمى URI مع بعض الوظائف الإضافية في الماكرو تعريف الذي يجعل الأمور أسهل عليك. والأهم من ذلك هي الخيارات القابلة للتمديد ، والتي يمكنك العثور على شرح أدناه.
هناك بعض الصفحات الافتراضية التي تم إعدادها بواسطة Radiance نفسها. أولاً ، هناك صفحات favicon and robots ، والتي تخدم ببساطة الملفات المعنية من الدليل static/ الدليل Radiance. ربما ستحتاج إما إلى توفير صفحاتك الخاصة لذلك أو تحديث الملفات على خادم الإنتاج الخاص بك.
ثم هناك الصفحة static ، المسؤولة عن تقديم محتويات ثابتة لجميع تطبيقات ويب ويب. يجب أن يكون نشطًا في أي مجال ودائمًا على المسار /static/... حيث ... يجب أن يكون له نموذج حيث يكون الدليل الأول هو اسم الوحدة النمطية ، والباقي هو مسار داخل الدليل static/ الدليل. يتيح لك ذلك دائمًا أن تكون قادرًا على الرجوع إلى الملفات الثابتة مثل CSS و JS والصور من خلال مسار مشترك.
أخيرًا ، هناك صفحة api ، وهي مسؤولة عن التعامل مع إرسال نقاط نهاية API ، والتي يتم شرحها في القسم التالي. تعمل الصفحة بشكل مشابه للثابت عن طريق التقاط مسار /api/... على جميع المجالات.
انظر page ، remove-page ، define-page
يوفر Radiance دعمًا متكاملًا لتعريف API REST. هذه ليست مجرد ميزة مخصصة ، ولكن لأن معظم التطبيقات الحديثة ترغب في توفير واجهة برمجة تطبيقات من نوع ما ، ولأن Radiance تنصح طريقة معينة لكتابة تطبيقاتك التي تتضمن بالضرورة نقاط نهاية API.
من الناحية النظرية ، فإن نقاط نهاية API هي وظائف قابلة للاتصال من خلال طلب المتصفح. ثم يتم استجابةهم إلى تنسيق يمكن قراءته من قبل الطالب ، مهما كان ذلك. من المهم أن نتذكر أن نقاط نهاية API يجب أن تكون قابلة للاستخدام من قبل كل من المستخدمين والبرامج. يشجع Radiance هذا لأنه عادة ما يجب أن يقوم المستخدم أيضًا بأي نوع من الإجراءات التي يمكن تنفيذها برمجيًا من خلال واجهة برمجة التطبيقات (API) بطريقة ما. من أجل تجنب الازدواجية ، يمكن خلط الاثنين.
على هذا النحو ، عادة ما يتم توفير أي نوع من إجراء تعديل البيانات من خلال نقطة نهاية API التي تتفاعل بشكل مختلف قليلاً اعتمادًا على ما إذا كان المستخدم أو التطبيق يطلب منه. في حالة المستخدم ، يجب عادةً إعادة توجيه مرة أخرى إلى صفحة مناسبة ، وفي حالة التطبيق ، يجب أن يوفر حمولة بيانات بتنسيق قابل للقراءة.
الجزء الأول من كل هذا هو نظام تنسيق API ، وهو مسؤول عن تسلسل البيانات إلى بعض التنسيقات المحددة. بشكل افتراضي ، يتم توفير تنسيق يعتمد على S-Expression فقط ، ولكن يمكن بسهولة تحميل إخراج JSON.
الجزء الثاني هو مواصفات browser Post/GET Post/GET. إذا كانت هذه المعلمة تحتوي على السلسلة الدقيقة "true" ، فسيتم التعامل مع طلب API على أنه قادم من مستخدم ، وبالتالي يجب إخراج حمولة البيانات بدلاً من حمولة البيانات.
يجب أن يستفيد تطبيقك من هذه الأشياء من أجل توفير واجهة برمجة تطبيقات متكاملة بشكل صحيح. الآن ، يتكون تعريف نقطة النهاية الفعلية من اسم ، وظيفة خام ، قائمة Lambda التي تصف وسيطات الوظيفة ، ووظيفة تحليل الطلب. عادةً بالنسبة للوسائط الخاصة بك ، فإن الوسائط المطلوبة والاختيارية فقط منطقية. بعد كل شيء ، يحتوي طلب HTTP فقط على "وسيطات الكلمات الرئيسية" التي يمكن أن توفرها ، ويمكن أن تكون هذه إما حاضرة أو مفقودة.
يعمل اسم نقطة نهاية API أيضًا كمعرف يخبرك بالمكان الذي يمكنك الوصول إليه. API Endpoints Live على /api/ PATH ، تليها اسم نقطة النهاية. على هذا النحو ، فأنت مسؤول عن بادئة نقاط النهاية الخاصة بك باسم الوحدة النمطية أو التطبيق من أجل تجنب التعثر عن طريق الخطأ على نقاط النهاية الأخرى. هذا على عكس مرسلات URI ، لأن نقاط نهاية API يجب أن تتطابق تمامًا ولا تسمح بأي غموض أو معالجة المسار. وبالتالي ، يجب أن يكون لكل نقطة نهاية مسار فريد ، والذي يمكن أن يكون على الفور بمثابة الاسم.
الوظيفة الخام هي الوظيفة التي توفرها واجهة برمجة التطبيقات واجهة ل. وهي مسؤولة عن تنفيذ الإجراء المطلوب وإعادة البيانات المناسبة كما هو موضح أعلاه. لإرجاع بيانات API المنسقة ، انظر api-output . لإعادة التوجيه في حالة طلب المتصفح ، انظر redirect .
أخيرًا ، تكون وظيفة تحليل الطلب مسؤولة عن أخذ كائن طلب ، واستخراج الوسائط التي تحتاجها الوظيفة منه ، وأخيراً استدعاء هذه الوظيفة مع الوسائط المناسبة- إذا كان ذلك ممكنًا. قد تشير وظيفة التحليل إلى خطأ api-argument-missing إذا كانت الوسيطة المطلوبة مفقودة. يجب تجاهل الحجج الزائدة.
يمكنك أيضًا الاتصال برنامجياً على نقطة نهاية API باستخدام call-api ، أو محاكاة استدعاء طلب باستخدام call-api-request ، دون الاضطرار إلى الذهاب إلى آلية إرسال URI بأكملها.
على غرار الصفحات ، تقبل تعريفات نقطة نهاية API أيضًا خيارات قابلة للتمديد تجعل التعريف أكثر بساطة. راجع القسم التالي للحصول على شرح للخيارات.
انظر api ، *default-api-format* ، *serialize-fallback* ، api-format ، remove-api-format name define-api-format list-api-formats ، remove-api-endpoint ، api-endpoint api-output ، api-endpoint list-api-endpoints api-serialize handler call-api-request ، argslist request-handler call-api define-api
الخيارات هي وسيلة لتوفير ماكرو تعريف قابل للتوسيع. يكون هذا مفيدًا عندما يوفر الإطار طريقة شائعة لتحديد شيء ما ، ولكن قد ترغب أجزاء أخرى في توفير ملحقات لذلك لجعل العمليات المشتركة أقصر. على سبيل المثال ، تتمثل المهمة المشتركة في تقييد نقطة نهاية API للأشخاص الذين لديهم بيانات اعتماد الوصول المطلوبة.
من أجل تسهيل ذلك ، يوفر Radiance آلية خيارات عامة إلى حد ما. يتم تقسيم الخيارات حسب نوع الخيار الذي يحدد التعريف الماكرو الذي ينتمي إليه الخيار. يوفر Radiance أنواع خيارات api وأنواع خيارات page خارج المربع.
يحتوي كل خيار على كلمة رئيسية لاسم ودالة موسعة يجب أن تقبل عددًا من الوسائط ، اعتمادًا على نوع الخيار. يتم توفيرها دائمًا كوسائط هي اسم الشيء الذي يتم تعريفه ، وقائمة أشكال الجسم للتعريف ، والقيمة النهائية ، الاختيارية ، التي تم توفيرها للخيار في قائمة الخيارات ، إذا تم ذكرها على الإطلاق. ثم تكون وظيفة التوسع هذه مسؤولة عن تحويل أشكال الجسم للماكرو تعريفًا بطريقة ما. يمكن أن ينبعث أيضًا من النموذج الثاني الذي يتم وضعه خارج التعريف نفسه ، من أجل السماح بإعداد البيئة بطريقة ما.
انظر option ، option-type ، name ، expander ، option ، remove-option ، list-options ، define-option ، expand-options
مفهوم الوحدة النمطية ضروري للإشراق. إنه بمثابة تمثيل "جزء" من الكل. على المستوى الفني ، الوحدة النمطية عبارة عن حزمة تحتوي على بيانات تعريف خاصة بها. يتم توفيره بواسطة نظام modularize ويستخدم لتسهيل السنانير والمشغلات ، والواجهات ، وتتبع بعض الأجزاء الأخرى من المعلومات.
ما يعنيه هذا بالنسبة لك هو أنه بدلاً من defpackage قياسية ، يجب عليك استخدام نموذج define-module لتحديد الحزمة الأساسية الخاصة بك. بناء الجملة هو نفسه defpackage ، ولكنه يتضمن بعض الخيارات الإضافية مثل :domain ، والذي يتيح لك تحديد المجال الأساسي الذي يجب أن تعمل عليه هذه الوحدة (إن وجدت).
يسمح نظام الوحدة أيضًا بربط نظام ASDF بوحدة نمطية. إذا تم ذلك ، يصبح نظام ASDF "وحدة افتراضية". من أجل القيام بذلك ، يجب عليك إضافة ثلاثة خيارات إلى تعريف النظام الخاص بك:
:defsystem-depends-on (:radiance)
:class "radiance:virtual-module"
:module-name "MY-MODULE"
يتيح هذا Radiance تحديد وربط معلومات نظام ASDF إلى الوحدة النمطية الخاصة بك. لإنشاء تلقائي للنظام والوحدات النمطية اللازمة لوحدة جديدة ، انظر create-module .
راجع virtual-module ، virtual-module-name ، اسم define-module ، define-module-extension الحذف ، delete-module module module-p ، module-storage ، module-storage-remove ، وحدة التحكم module-identifier ، module-name current-module ، module-domain ، module-permissions required ، module-dependencies ، module-required-interfaces ، module-required-systems module-pages ، module-api-endpoints describe-module find-modules-directory ، *modules-directory* create-module
إحدى الآليات التي توفرها Radiance للسماح بدمج الوحدات النمطية في بعضها البعض هي السنانير. تتيح لك الخطافات تشغيل وظيفة تعسفية استجابة لنوع من الحدث. على سبيل المثال ، قد يقوم برنامج المنتدى بإعداد خطاف يتم تشغيله كلما تم إنشاء منشور جديد. يمكن للتمديد بعد ذلك تحديد المشغل على هذا الخطاف الذي يؤدي مهام إضافية.
يمكن أن يكون للخطاف عددًا تعسفيًا من المشغلات المحددة عليه ، ولكن يجب عليك التأكد من أن الزناد لا يستغرق وقتًا طويلاً ، لأن تشغيل الخطاف هو عملية حظر لن تنتهي حتى تكتمل جميع المشغلات. على هذا النحو ، قد تؤخر عملية الزناد طويلة الأمد استجابة الطلب لفترة طويلة جدًا.
في بعض الأحيان ، يجب أن تعمل السنانير أكثر مثل المفاتيح ، حيث يمكن أن تكون "على" لفترة طويلة ، حتى يتم إيقافها "مرة أخرى" مرة أخرى. إذا تم تعريف المشغلات الجديدة خلال ذلك الوقت ، فيجب أن يتم استدعاؤها تلقائيًا. هذا هو ما يسهل define-hook-switch . ينتج اثنين من السنانير. بمجرد أن يتم تشغيل أول واحد ، يتم استدعاء أي مشغل يتم تعريفه عليه لاحقًا تلقائيًا حتى يتم تشغيل الخطاف الثاني. يتيح ذلك المشغلات على السنانير مثل server-start للعمل بشكل صحيح حتى لو تم تعريف المشغل فقط بعد بدء الخادم بالفعل.
انظر list-hooks ، define-hook ، remove-hook ، define-trigger ، remove-trigger ، trigger ، define-hook-switch
من أجل تجنب أن تصبح متجانسة ، ومن أجل السماح للخلفية القابلة للتمديد ، يتضمن Radiance نظام واجهة. بالمعنى الأكثر عمومية ، توفر الواجهة وعدًا حول كيفية عمل بعض الوظائف أو الماكرو والمتغيرات ، وما إلى ذلك ، ولكنها لا تنفذها بالفعل. يتم تشغيل الوظيفة الفعلية التي تجعل كل ما تطرحه الخطوط العريضة للواجهة إلى التنفيذ. يتيح ذلك للمستخدمين رمزًا مقابل واجهة والاستفادة من وظائفها المقدمة ، دون ربط أنفسهم بأي واجهات خلفية معينة.
للحصول على مثال ملموس ، دعنا نقول أن هناك واجهة لقاعدة بيانات. هذا أمر منطقي ، نظرًا لوجود العديد من أنواع قواعد البيانات المختلفة ، والتي توفر جميعها طرقًا مختلفة للتفاعل ، ولكن لا تزال جميعها توفر أيضًا بعض العمليات الشائعة جدًا: تخزين البيانات واسترداد البيانات وتعديل البيانات. وبالتالي نقوم بإنشاء واجهة توفر هذه العمليات الشائعة. بعد ذلك يصل إلى تنفيذ نوع معين من قاعدة البيانات لجعل العمليات الفعلية تعمل. ككاتب تطبيق ، يمكنك بعد ذلك الاستفادة من واجهة قاعدة البيانات ، ومعها ، تجعل التطبيق الخاص بك يعمل تلقائيًا مع الكثير من قواعد البيانات المختلفة.
بصرف النظر عن إعطاء كتاب التطبيق ميزة ، فإن الفصل بين الواجهات التي توفرها الواجهات تعني أيضًا أن مسؤول النظام يمكنه كتابة تنفيذهم بسهولة ، إذا لم يتم الوفاء بمتطلباتها الخاصة بواسطة التطبيقات الحالية. بفضل غموض الواجهات ، يمكن للتنفيذ أن يوفر جسرًا لشيء يعمل في عملية LISP ، وشيء خارجي تمامًا. هذا يترك الكثير من الاختيار مفتوحًا لمسؤول نظام الإنتاج للسماح لهم باختيار ما يحتاجونه بالضبط.
في الممارسة العملية ، الواجهات هي أنواع خاصة من الوحدات ، وبالتالي أنواع خاصة من الحزم. كجزء من تعريفها ، تتضمن سلسلة من التعريفات للربط الأخرى مثل الوظائف والمتغيرات ، وما إلى ذلك. نظرًا لأنها حزمة ، كمستخدم يمكنك استخدام مكونات الواجهة تمامًا مثلما كنت تستخدم أي شيء آخر في أي حزمة أخرى. لا يوجد فرق. ككاتب تنفيذ ، يمكنك ببساطة إعادة تعريف جميع التعريفات التي تحددها الواجهة.
من أجل تحميل وحدة نمطية تستخدم واجهة فعليًا ، يجب تحميل تطبيق الواجهة مسبقًا. خلاف ذلك ، لا يمكن أن تعمل وحدات الماكرو بشكل صحيح. وبالتالي ، من أجل السماح اعتمادًا على واجهات في تعريف نظام ASDF الخاص بك دون الحاجة إلى الرجوع إلى تطبيق محدد ، يوفر Radiance امتداد ASDF. هذا الامتداد يجعل من الممكن إضافة قائمة مثل (:interface :foo) إلى قائمة :depends-on قائمة. سيقوم Radiance بعد ذلك بحل الواجهة إلى تطبيق خرساني عند تحميل الوحدة النمطية.
يوفر Radiance مجموعة من الواجهات القياسية. يحتوي كل من هذه الواجهات على تطبيق قياسي واحد على الأقل توفره Radiance-Contribs. الواجهات هي:
adminauthbancachedatabaseloggermailprofilerateserversessionuserيتم وصف الواجهات في أعماق أدناه.
راجع interface ، interface-p ، implementation ، implements ، reset-interface ، define-interface-extension ، find-implementation ، load-implementation ، define-interface ، define-implement-trigger
من أجل السماح بتشغيل مثيلات متعددة من الإشعاع مع إعدادات مختلفة على نفس الجهاز ، يوفر Radiance ما يسميه نظام البيئة. البيئة هي في الأساس مجموعة من ملفات التكوين ووقت التشغيل للإرهاق نفسه وجميع الوحدات النمطية المحملة. يتضمن تكوين Radiance أيضًا تعيين الواجهة للتنفيذ المختار ، وبالتالي يقرر ما يجب اختياره إذا تم طلب واجهة.
يتم اختيار البيئة المعينة التي يتم استخدامها على أبعد تقدير عند استدعاء startup ، والأقرب عند تحميل الوحدة النمطية. في الحالة الأخيرة ، يتم توفير عمليات إعادة التشغيل التفاعلية للسماح لك باختيار بيئة. هذا ضروري ، لأن Radiance لن يتمكن من حل رسم خرائط الواجهة.
كجزء من نظام البيئة ، يوفر لك Radiance نظام تكوين يمكنك استخدامه-وربما يجب استخدامه لتطبيقك. إنه يضمن أن الإعدادات تعدد الإرسال بشكل صحيح لكل بيئة ، وأن الإعدادات ثابتة دائمًا. كما أنه يستخدم تنسيق تخزين قابل للقراءة البشرية ، بحيث يمكن قراءة الملفات وتعديلها دون الحاجة إلى أي أدوات خاصة.
انظر في كل مكان للاطلاع على المعالجة الفعلية واستخدامها لتخزين التكوين. لاحظ فقط أنه بدلاً من وظائف value ، يوفر Radiance وظائف config .
بصرف النظر عن ملفات التكوين ، توفر البيئة أيضًا مواقع تخزين متسقة لملفات بيانات وقت التشغيل ، مثل تحميل المستخدم وملفات ذاكرة التخزين المؤقت وما إلى ذلك. يمكنك استرداد هذا الموقع باستخدام environment-module-directory environment-module-pathname . عند تخزين التحميلات واخبارات ذاكرة التخزين المؤقت ، يجب أن تستخدم الوحدة النمطية هذه المسارات لتقديم واجهة متسقة للمسؤول.
في نظام تم نشره ، قد يكون من المرغوب فيه تغيير موقع مسارات تخزين البيئة ، وفي هذه الحالة يتم تشجيع المسؤول على توفير طرق جديدة في environment-directory والبيئة environment-module-directory لتخصيص السلوك حسب الرغبة. انظر أيضًا سلاسل الوثائق المرتبطة لمزيد من التفاصيل والإجراءات الافتراضية.
البيئة تسمح أيضا تجاوزات المسؤول. يمنحك أنواع :static و :template environment-module-directory المسار لتخزين الملفات التي يجب أن تتجاوز القالب القياسي للوحدة والملفات الثابتة. يجب أن تتطابق المسارات الموجودة في الدلائل المعنية مع مسارات ملفات المصدر الخاصة بالوحدة. لاحظ أن ملفات القالب الثابتة والقوالب تستخدمها الوحدة التي تستخدمها بالفعل في وقت تحميل الوحدة النمطية ، وبالتالي لن تتغير ما لم تتم إعادة تشغيل صورة LISP ، أو يتم إعادة تحميل ملفات مصدر الوحدة النمطية.
راجع environment-change ، environment ، environment-directory ، environment-module-directory ، environment-module-pathname ، والبيئة check-environment ، و mconfig-pathname ، و mconfig-storage ، mconfig ، و defaulted-mconfig ، و config ، و defaulted-config ، template-file ، @template ، static-file ، @static
في بعض الأحيان تتطور الأنظمة بطرق غير متوافقة مع الوراء. في هذه الحالة ، لكي تستمر الإعدادات الحالية في العمل مع الإصدار الجديد ، من الضروري ترحيل بيانات وقت التشغيل. يوفر Radiance نظامًا لأتمتة هذه العملية والسماح بترقية سلسة.
يجب أن تحدث الترحيل بين الإصدارات تلقائيًا أثناء تسلسل بدء تشغيل Radiance. بصفتك مسؤولًا أو مؤلفًا ، يجب ألا تحتاج إلى إجراء أي خطوات إضافية لتحدث الترحيل. ومع ذلك ، بصفتك مؤلفًا للوحدة ، سيتعين عليك بطبيعة الحال توفير الكود لتنفيذ خطوات ترحيل البيانات اللازمة للوحدة النمطية الخاصة بك.
من أجل أن تكون الوحدة النمطية قابلة للترحيل ، يجب تحميلها بواسطة نظام ASDF يحتوي على مواصفات الإصدار. يجب أن يتبع الإصدار مخطط الأرقام المنقط القياسي ، مع وجود إصدار اختياري يمكن إضافته في النهاية. يمكنك بعد ذلك تحديد خطوات الترحيل بين الإصدارات الفردية باستخدام define-version-migration . بمجرد تحديدها ، ستقوم Radiance تلقائيًا بالتقاط إصدارات ملموسة وتنفيذ عمليات الترحيل اللازمة بالتسلسل للوصول إلى الإصدار الهدف الحالي. لمزيد من المعلومات حول الإجراء الدقيق وما يمكنك القيام به ، راجع migrate migrate-versions .
راجع last-known-system-version ، migrate-versions define-version-migration ، ready-dependency-for-migration ، ensure-dependencies-ready versions ، والترحيل ، migrate
أخيرًا ، يوفر Radiance تسلسلًا قياسيًا لبدء التشغيل والإغلاق الذي يجب أن يضمن إعداد الأشياء بشكل صحيح واستعادتها ، وبعد ذلك تم تنظيفها بشكل جيد مرة أخرى. جزء كبير من هذا التسلسل هو مجرد ضمان استدعاء خطافات معينة بالترتيب الصحيح وفي الأوقات المناسبة.
على الرغم من أنه يمكنك بدء تشغيل خادم يدويًا باستخدام وظيفة الواجهة المناسبة ، إلا أنه لا يجب أن تتوقع تشغيل التطبيقات بشكل صحيح إذا قمت بذلك بهذه الطريقة. يتوقع الكثير منهم استدعاء خطافات معينة من أجل العمل بشكل صحيح. هذا هو السبب في أنه يجب عليك دائمًا ، إلا إذا كنت تعرف بالضبط ما تفعله ، استخدم startup shutdown لإدارة مثيل Radiance. يجب أن تفسر توثيق وظيفتين بالضبط الخطافات التي يتم تشغيلها وبأي ترتيب. قد يوفر التنفيذ تعريفات إضافية غير محددة على الرموز في حزمة الواجهة ، طالما لم يتم تصدير الرموز المذكورة.
راجع *startup-time* ، uptime ، server-start ، server-ready ، server-stop ، server-shutdown ، startup ، shutdown startup-done ، shutdown-done ، started-p
يتم توزيع هذه الواجهات بإشراق وهي جزء من الحزمة الأساسية. قد توفر المكتبات واجهات إضافية. لتطبيقات الواجهات القياسية ، يُسمح بالاسترخاء التالي لقيود تعريف الواجهة:
يمكن تمديد قوانين Lambda التي تحتوي على وسيطات &key من خلال المزيد من وسيطات الكلمات الرئيسية المعادلة للتنفيذ. قد يتم تمديد قوانين Lambda التي تحتوي على &optional ولكن NO &key أو &rest بواسطة وسيطات اختيارية أخرى. قد يتم تمديد قائدات Lambda التي تحتوي على وسائط مطلوبة فقط بواسطة وسيطات اختيارية أو رئيسية أخرى.
توفر هذه الواجهة صفحة الإدارة. يجب استخدامه لأي نوع من الإعدادات القابلة للتكوين للمستخدم ، أو عرض معلومات النظام. لاحظ أنه على الرغم من أنها تسمى "الإدارة" ، فإن هذا ليس مخصصًا فقط لمسؤولي النظام. يجب أن تكون الصفحة قابلة للاستخدام لأي مستخدم.
مطلوب صفحة الإدارة لتكون قادرة على عرض قائمة مصنفة ولوحة. يتم توفير اللوحات بواسطة وحدات أخرى ويمكن إضافتها من خلال admin:define-panel . يجب تقييد اللوحات التي تتيح الوصول إلى العمليات الحساسة بشكل مناسب من خلال خيار :access وإذن غير مرفوع. راجع واجهة المستخدم للحصول على شرح على الأذونات.
من أجل الارتباط بصفحة الإدارة ، أو لوحة محددة عليها ، استخدم نوع مورد page .
انظر admin:list-panels ، admin:remove-panel ، admin:define-panel ، admin:panel
واجهة المصادقة مسؤولة عن ربط المستخدم بالطلب. لهذا السبب ، يجب أن يوفر بطريقة ما يمكن للمستخدم مصادقة نفسه ضد النظام. كيف يتم ذلك بالضبط هو الأمر متروك للتنفيذ. ومع ذلك ، يجب أن يوفر التنفيذ صفحة تبدأ عليها عملية المصادقة. You can get a URI to it through the page resource and passing "login" as argument.
You can test for the user currently tied to the request by auth:current . This may also return NIL , in which case the user should be interpreted as being "anonymous" . See the user interface for more information.
See auth:*login-timeout* , auth:page , auth:current , auth:associate
This interface provides for IP-banning. It must prevent any client connecting through a banned IP from seeing the content of the actual page they're requesting. Bans can be lifted manually or automatically after a timeout. The implementation may or may not exert additional effort to track users across IPs.
See ban:jail , ban:list , ban:jail-time , ban:release
The cache interface provides for a generic caching mechanism with a customisable invalidation test. You can explicitly renew the cache by cache:renew . To define a cached block, simply use cache:with-cache , which will cause the cached value of the body to be returned if the test form evaluates to true.
The exact manner by which the cached value is stored is up to the implementation and cache:get or cache:with-cache may coerce the cached value to a string or byte array. The implementation may support any number of types of values to cache, but must in the very least support strings and byte arrays.
The name for a cached value must be a symbol whose name and package name do not contain any of the following characters: <>:"/|?*. The variant for a cached value must be an object that can be discriminated by its printed (as by princ ) representation. The same character constraints as for the name apply.
See cache:get , cache:renew , cache:with-cache
This interface provides you with a data persistence layer, usually called a database. This does not have to be a relational database, but may be one. In order to preserve implementation variance, only basic database operations are supported (no joins, triggers, etc). Data types are also restricted to integers, floats, and strings. Despite these constraints, the database interface is sufficiently useful for most applications.
Note that particular terminology is used to distance from traditional RDBMS terms: a schema is called a "structure". A table is called a "collection". A row is called a "record".
Performing database operations before the database is connected results in undefined behaviour. Thus, you should put your collection creation forms ( db:create ) within a trigger on db:connected . Radiance ensures that the database is connected while Radiance is running, so using the database interface in any page, api, or uri dispatcher definitions is completely fine.
The functions for actually performing data storage are, intuitively enough, called db:insert , db:remove , db:update , db:select , and db:iterate . The behaviour thereof should be pretty much what you'd expect. See the respective docstrings for a close inspection. Also see the docstring of db:create for a lengthy explanation on how to create a collection and what kind of restrictions are imposed.
The database must ensure that once a data manipulation operation has completed, the changes caused by it will be persisted across a restart of Radiance, the lisp image, or the machine, even in the case of an unforeseen crash.
See database:condition , database:connection-failed , database:connection-already-open , database:collection-condition , database:invalid-collection , database:collection-already-exists , database:invalid-field , database:id , database:ensure-id , database:connect , database:disconnect , database:connected-p , database:collections , database:collection-exists-p , database:create , database:structure , database:empty , database:drop , database:iterate , database:select , database:count , database:insert , database:remove , database:update , database:with-transaction , database:query , database:connected , database:disconnected
This interface provides primitive logging functions so that you can log messages about relevant happenings in the system. The actual configuration of what gets logged where and how is up to the implementation and the administrator of the system.
See logger:log , logger:trace , logger:debug , logger:info , logger:warn , logger:error , logger:severe , logger:fatal
With the mail interface you get a very minimal facility to send emails. A variety of components might need email access, in order to reach users outside of the website itself. The configuration of the way the emails are sent --remote server, local sendmail, etc.-- is implementation dependant.
The mail:send hook provided by the interface allows you to react to outgoing emails before they are sent.
See mail:send
The profile interface provides extensions to the user interface that are commonly used in applications that want users to have some kind of presence. As part of this, the interface must provide for a page on which a user's "profile" can be displayed. The profile must show panels of some kind. The panels are provided by other modules and can be added by profile:define-panel .
You can get a URI pointing to the profile page of a user through the page resource type.
The interface also provides access to an "avatar image" to visually identify the user ( profile:avatar ), a customisable name that the user can change ( profile:name ), and field types to what kind of data is contained in a user's field and whether it should be public information or not ( profile:fields profile:add-field profile:remove-field ).
See profile:page , profile:avatar , profile:name , profile:fields , profile:add-field , profile:remove-field , profile:list-panels , profile:remove-panel , profile:define-panel , profile:panel
This interface provides for a rate limitation mechanism to prevent spamming or overly eager access to potentially sensitive or costly resources. This happens in two steps. First, the behaviour of the rate limitation is defined for a particular resource by rate:define-limit . Then the resource is protected through the rate:with-limitation macro. If the access to the block by a certain user is too frequent, the block is not called, and the code in the limit definition is evaluated instead.
Note that rate limitation is per-client, -user, or -session depending on the implementation, but certainly not global.
See rate:define-limit , rate:left , rate:with-limitation
This and the logger interface are the only interfaces Radiance requires an implementation for in order to start. It is responsible for accepting and replying to HTTP requests in some manner. The implementation must accept requests and relay them to the Radiance request function, and then relay the returned response back to the requester.
Note that the actual arguments that specify the listener behaviour are implementation-dependant, as is configuration thereof. However, if applicable, the implementation must provide for a standard listener that is accessible on localhost on the port configured in (mconfig :radiance :port) and is started when radiance:startup is called.
See server:start , server:stop , server:listeners , server:started , server:stopped
The session interface provides for tracking a client over the course of multiple requests. It however cannot guarantee to track clients perfectly, as they may do several things in order to cloak or mask themselves or falsify information. Still, for most users, the session tracking should work fine enough.
The session interface is usually used by other interfaces or lower-lying libraries in order to provide persistence of information such as user authentication.
See session:*default-timeout* , session:session , session:= , session:start , session:get , session:list , session:id , session:field , session:timeout , session:end , session:active-p , session:create
This interface provides for persistent user objects and a permissions system. It does not take care of authentication, identification, tracking, or anything of the sort. It merely provides a user object upon which to build and with which permissions can be managed.
See user:user for a description of permissions and their behaviour.
See user:condition , user:not-found , user:user , user:= , user:list , user:get , user:id , user:username , user:fields , user:field , user:remove-field , user:remove , user:check , user:grant , user:revoke , user:add-default-permissions , user:create , user:remove , user:action , user:ready , user:unready
This is an extension of the database interface. Any module implementing this interface must also implement the database interface. This interface provides some extensions to allow more expressive database operations that are only directly supported by relational database systems.
See relational-database:join , relational-database:sql
*environment-root* has been removed as per issue #28 and fix #29. It has instead been replaced by a more generic mechanism for environment directories, incorporated by the function environment-directory . If you previously customised *environment-root* , please now change environment-directory instead, as described in §1.11.template-file and static-file .user:id identifier for each user object, allowing you to reference users in databases and records more efficiently.:unique on db:select and db:iterate . If you'd like to support the continued development of Radiance, please consider becoming a backer on Patreon: