Radiance adalah lingkungan aplikasi web, yang seperti kerangka kerja web, tetapi lebih umum, lebih fleksibel. Ini harus memungkinkan Anda menulis situs web pribadi dan umumnya aplikasi yang dapat digunakan dengan mudah dan sedemikian rupa sehingga dapat digunakan pada hampir semua pengaturan tanpa harus menjalani adaptasi khusus.
Radiance dan modul dan aplikasi terkait didistribusikan melalui QuickLisp dalam DIST terpisah. Untuk menginstal Radiance, lakukan:
(ql-dist:install-dist "http://dist.shirakumo.org/shirakumo.txt")
(ql:quickload :radiance)
Dari sana, Anda harus dapat memuat dan menggunakan segala jenis modul pancaran seperti keunguan langsung melalui Quicklisp's quickload .
Anda dapat menemukan tutorial yang memperkenalkan pancaran dan sebagian besar konsep penting, dan mengeksplorasi cara menulis aplikasi web secara umum, di sini. Ini seharusnya memberi Anda perasaan yang baik untuk bagaimana cara melakukan sesuatu, dan memberi Anda petunjuk tentang ke mana harus mencari jika Anda membutuhkan fitur tertentu. Pada bagian terakhir ini juga akan masuk ke pengaturan aktual dan penyebaran instalasi Radiance di server produksi.
Hal paling mendasar yang kemungkinan besar ingin Anda lakukan adalah melayani semacam HTML. Jadi mari kita bekerja untuk itu dan secara bertahap memperpanjangnya. Sebelum kita bisa mulai, kita perlu memulai cahaya.
( ql :quickload :radiance )
( radiance :startup) Jika ini adalah pertama kalinya Anda menyiapkan Radiance, Anda akan mendapatkan catatan tentang itu menggunakan modul r-welcome . Ini juga harus memberi Anda tautan yang dapat Anda buka di browser Anda untuk melihat halaman ucapan kecil. Untuk saat ini kita hanya ingin memasang halaman kecil kita sendiri di sampingnya.
( in-package :rad-user )
(define-page example " /example " ()
( setf (content-type *response* ) " text/plain " )
" Hi! " )Mengunjungi localhost: 8080/contoh sekarang harus hanya menunjukkan "hai". Memang agak membosankan. Jadi mari kita ludahkan beberapa HTML sebagai gantinya. Untuk saat ini, kami akan menggunakan Cl-Who karena sangat sederhana. Quickload pertama, lalu jalankan yang berikut:
(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. " )))))))Sebuah kompilasi dan menyegarkan kemudian dan kami memiliki beberapa gaya font yang terjadi. Selanjutnya kita mungkin ingin menambahkan file CSS ke dalamnya untuk menata dengan benar. Kami dapat melayani CSS menggunakan halaman lain juga, tetapi itu bukan cara terbaik untuk melakukan hal -hal dalam jangka panjang.
Sebaliknya mari kita lihat cara membuat modul, yang akan memungkinkan kita untuk mengatur hal -hal dengan cara yang lebih tertib. Anda dapat membuat file untuk modul secara manual, tetapi untuk sekarang kami akan puas dengan kerangka yang dihasilkan secara otomatis yang dapat diberikan Radiance kepada Anda.
(create-module " example " ) Seharusnya mengembalikan jalan di mana modul berada. Ini harus berisi sistem ASDF, file LISP utama, dan dua folder, static dan template . Cukup mengejutkan, folder static adalah tempat file yang dilayani secara statis, dan template adalah untuk dokumen templat, jika Anda kebetulan menggunakan sistem template.
Mari kita buka example.lisp dan bawa halaman contoh kita darinya.
(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. " ))))))) Halaman diidentifikasi dengan simbol nama. Karena kita sekarang memiliki modul sendiri, dan dengan demikian paket kita sendiri, simbol contoh di atas tidak akan sama dengan yang pernah kita gunakan sebelumnya. Kami hanya harus menghapus halaman di paket rad-user untuk menghindari bentrokan.
(remove-page ' rad-user::example) Pastikan untuk memuat file contoh setiap kali Anda mengubahnya sekarang agar perubahan berlaku. Selanjutnya mari kita buat file CSS sederhana untuk sedikit merapikan segalanya. File akan menjadi example.css ditempatkan di folder static . Berikut contoh CSS jika Anda tidak ingin menulis sendiri.
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 ;
}Selanjutnya kita perlu memodifikasi HTML kami untuk benar -benar menautkan ke lembar gaya. Untuk mendapatkan alamat ke stylesheet, kita harus menggunakan sistem perutean Radiance. Jangan khawatir, itu tidak terlalu merepotkan.
(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. " ))))))) Segarkan halaman, dan voila, sekarang juga punya pizzazz untuk itu. Anda mungkin ingin penjelasan untuk seluruh bisnis uri-to-url . Menjelaskannya secara penuh ditangani oleh bagian -bagian yang mengikuti yang satu ini, tetapi intinya adalah bahwa ia memastikan bahwa tautan ke file statis diselesaikan dengan benar di bawah pengaturan apa pun.
Salah satu konsep paling sentral dalam pancaran adalah URI. URI adalah objek yang terdiri dari daftar domain, nomor port opsional, dan jalur (lihat uri ). Ini pada dasarnya adalah versi yang dilucuti dari URI umum, dan karena itu tidak termasuk skema, kueri, atau bagian fragmen. Perbedaan penting lainnya adalah bahwa URI domains digunakan pada beberapa titik di seluruh kerangka kerja, baik untuk menangkap lokasi dan untuk menangani pencocokan pengiriman.
Perhatikan bahwa URI dapat berubah. Ini penting untuk kinerja, karena modifikasi URI harus terjadi di beberapa bagian yang terletak di jalur kritis. Namun, dalam kasus biasa tidak diharapkan bahwa URI dimodifikasi di luar beberapa fungsi terpilih. Memodifikasi bagian URI dengan cara yang tidak terduga dapat menyebabkan perilaku aneh.
URI memiliki representasi string yang unik dan dapat diserialisasi ke string dan diuraikan kembali ke objek URI penuh lagi. URI juga dapat dibuang ke file FASL sebagai literal, jadi mengeluarkannya dari makro tidak masalah. Sintaks untuk URI adalah sebagai berikut:
URI ::= DOMAINS? (':' PORT)? '/' PATH?
DOMAINS ::= DOMAIN ('.' DOMAIN)*
DOMAIN ::= ('a'..'Z' | '0'..'9' | '-')
PORT ::= ('0'..'9'){1, 5}
PATH ::= .*
Anda dapat menggunakan uri-to-url untuk mengubah URI menjadi URL konkret. Pembalikan, pengkodean, dan pemformatan yang tepat dari semua bagian ditangani untuk Anda secara otomatis di sana.
Lihat 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 .
Untuk merangkum data yang dikirim ke dan dari, kami memiliki gagasan objek permintaan ( request ) dan respons ( response ). Objek permintaan memegang URI yang mewakili lokasi mana permintaan tersebut, dan semua data yang terkandung dalam variabel http muatan seperti pos, get, header, dan cookie. Objek respons memegang kode pengembalian, header, cookie, dan data tubuh yang sebenarnya.
Selama pemrosesan permintaan, kedua objek ini harus selalu ada dan terikat pada variabel *request* dan *response* . Mereka merangkum banyak informasi yang sangat vital yang diperlukan untuk menghasilkan halaman dinamis. Selain itu, permintaan tersebut berisi tabel data buram di mana Anda dapat menyimpan data sewenang -wenang. Ini berguna ketika Anda perlu bertukar informasi antara masing -masing bagian sistem yang dapat dicapai selama eksekusi permintaan.
Permintaan tidak harus datang dari server HTTP. Untuk menguji hal -hal, Anda juga dapat membangun permintaan sendiri dan mengirimkannya secara terprogram. Apa pun masalahnya, antarmuka utama untuk mengirim permintaan disebut request . Ini akan membangun objek permintaan dan respons untuk Anda dan menangani URI dengan tepat. Jika Anda ingin melakukannya sendiri dan benar-benar hanya mengirimkan objek permintaan lengkap, Anda dapat menggunakan execute-request .
Untuk penanganan permintaan yang sebenarnya, lihat Dispatcher, Halaman, dan Titik Akhir API.
Lihat *request* , *response* , *default-external-format* , *default-content-type* , request , uri , http-method , body-stream , headers , post-data , get-data cookies cookies , user-agent , referer , domain headers remote , data , issue-time , response , data , content-type return-code , external-format cookie , value name path domain , expires , http-only , secure , cookie-header , cookie , get-var , post-var , post/get , header , file , redirect , serve-file , request-run-time , *debugger* , handle-condition , render-error-page , execute-request , set-data , request
Sebelum permintaan dapat dikirim, ia melewati sesuatu yang disebut sistem perutean. Tidak seperti dalam kerangka kerja lain, di mana 'rute' menunjuk apa yang menangani permintaan, dalam rute rute ( route ) adalah bentuk penerjemah URI. Bagian sistem ini adalah apa yang bertanggung jawab untuk menciptakan dan menjunjung tinggi dua "alam semesta", internal dan yang eksternal.
Semesta internal adalah satu -satunya aplikasi web yang sebenarnya. Alam semesta eksternal adalah yang server HTTP dan pengguna situs web tinggal. Perbedaan ini diperlukan untuk memungkinkan Anda, satu sisi, menulis aplikasi web tanpa harus khawatir tentang seperti apa potensi pengaturan pada server mungkin pada suatu titik. Anda tidak perlu khawatir tentang domain, port, pengaturan jalur apa yang mungkin diperlukan untuk menjalankan aplikasi Anda. Di sisi lain, ini memungkinkan Anda, sebagai webadmin, untuk menyesuaikan dan menjalankan sistem dengan keinginan Anda yang tepat tanpa takut melanggar sesuatu.
Ini semua difasilitasi oleh rute, yang ada dua jenis: pemetaan, dan rute pembalikan. Rute pemetaan bertanggung jawab untuk mengubah URI dari alam semesta eksternal menjadi salah satu alam semesta internal. Biasanya ini melibatkan pemotongan domain tingkat atas dan mungkin melakukan pemetaan subdomain. Rute pembalikan melakukan yang sebaliknya- mereka beralih dari alam semesta internal ke eksternal. Ini diperlukan untuk membuat tautan di halaman yang dilayani Anda merujuk pada sumber daya yang sebenarnya dapat diakses dari luar. Biasanya ini melibatkan membalikkan pemetaan subdomain dan menambahkan domain tingkat atas lagi.
Rute dapat melakukan pekerjaan sewenang -wenang. Pada tingkat paling dasar, mereka hanyalah fungsi yang memodifikasi URI dengan cara tertentu. Ini memungkinkan Anda untuk membuat sistem yang sangat fleksibel yang seharusnya cukup kuat untuk mengakomodasi semua kebutuhan Anda sebagai administrator. Sebagai penulis aplikasi, Anda hanya perlu memastikan untuk menggunakan external-uri atau uri-to-url pada semua tautan yang Anda masukkan ke halaman Anda.
Lihat route , name , direction , priority , translator , route , remove-route , list-routes , define-route , define-matching-route , define-target-route , define-string-route , internal-uri , external-uri
Akhirnya kami sampai pada bagian yang benar -benar menghasilkan konten untuk permintaan. Dispatcher URI adalah subclass dari URI yang juga membawa nama, fungsi, dan prioritas. Daftar langsung yang disortir prioritas, yang diproses setiap kali permintaan tiba. URI permintaan dicocokkan dengan masing -masing operator. Fungsi operator pertama yang cocok kemudian dieksekusi.
Dan itu saja. Fungsi Dispatcher bertanggung jawab untuk mengatur nilai yang diperlukan dalam objek respons untuk mengirimkan konten halaman. Untuk melakukan ini, ia dapat secara langsung mengatur bidang data objek respons, atau Anda dapat mengembalikan nilai yang sesuai dari fungsi. Radiance hanya menerima empat jenis nilai: stream , pathname , string , dan (array (unsigned-byte 8)) .
Jika operator URI tidak memiliki nomor prioritas eksplisit, prioritasnya di atas orang lain ditentukan oleh kekhususan URI. Lihat fungsi penyortiran URI uri> untuk penjelasan tentang bagaimana tepatnya ini dihitung.
Lihat uri-dispatcher , name , dispatch-function , priority , uri-dispatcher , remove-uri-dispatcher , list-uri-dispatchers , uri-dispatcher> , define-uri-dispatcher , dispatch
Halaman adalah apa yang mungkin Anda gunakan untuk menentukan fungsi porsi konten Anda yang sebenarnya. Namun, sebuah halaman hanyalah orang-orang URI dengan beberapa fungsi tambahan dalam makro definisi yang membuat segalanya lebih mudah bagi Anda. Terutama adalah opsi yang dapat diperluas, yang dapat Anda temukan penjelasan di bawah ini.
Ada beberapa halaman default yang diatur oleh Radiance itu sendiri. Pertama ada halaman favicon dan robots , yang hanya melayani file masing -masing dari static/ direktori Radiance. Anda mungkin ingin menyediakan halaman Anda sendiri untuk itu atau memperbarui file di server produksi Anda.
Lalu ada halaman static , yang bertanggung jawab untuk melayani konten statis untuk semua aplikasi dan modul Web. Itu harus aktif pada domain apa pun dan selalu di jalur /static/... di mana ... harus memiliki bentuk di mana direktori pertama adalah nama modul, dan sisanya adalah jalur dalam static/ direktori modul itu. Ini memungkinkan Anda untuk selalu dapat merujuk ke file statis seperti CSS, JS, dan gambar melalui jalur umum.
Akhirnya ada halaman api , yang bertanggung jawab untuk menangani pengiriman titik akhir API, yang dijelaskan di bagian berikut. Halaman tersebut bertindak mirip dengan statis dengan menangkap /api/... Jalur di semua domain.
Lihat page , remove-page , define-page
Radiance memberikan dukungan terintegrasi untuk definisi API REST. Ini bukan hanya fitur yang ditempelkan, tetapi lebih karena sebagian besar aplikasi modern ingin memberikan semacam API, dan karena Radiance menyarankan cara menulis aplikasi Anda yang melibatkan titik akhir API.
Secara konseptual, titik akhir API adalah fungsi yang dapat dipanggil melalui permintaan browser. Respons mereka kemudian diserialisasi ke format yang dapat dibaca oleh pemohon, apa pun itu. Namun penting untuk diingat adalah bahwa titik akhir API harus dapat digunakan oleh pengguna dan program. Radiance mendorong ini karena biasanya tindakan apa pun yang dapat dilakukan secara terprogram melalui API juga harus dilakukan oleh pengguna dengan cara tertentu. Untuk menghindari duplikasi, keduanya dapat digabungkan.
Dengan demikian, biasanya segala jenis tindakan modifikasi data harus disediakan melalui titik akhir API yang bereaksi sedikit berbeda tergantung pada apakah pengguna atau aplikasi memintanya. Dalam kasus pengguna, biasanya harus mengarahkan kembali ke halaman yang sesuai, dan dalam hal aplikasi itu harus memberikan muatan data dalam format yang dapat dibaca.
Bagian pertama dari semua ini adalah sistem format API, yang bertanggung jawab untuk serialing data ke beberapa format tertentu. Secara default hanya format berbasis S-ekspresi yang disediakan, tetapi kontrib untuk mendapatkan output JSON dapat dengan mudah dimuat.
Bagian kedua adalah spesifikasi parameter browser Post/GET. Jika parameter itu berisi string yang tepat "true" , maka permintaan API diperlakukan dari pengguna, dan dengan demikian pengalihan daripada muatan data harus dikeluarkan.
Aplikasi Anda harus memanfaatkan hal -hal itu untuk menyediakan API yang terintegrasi dengan benar. Sekarang, definisi titik akhir yang sebenarnya terdiri dari nama, fungsi mentah, daftar lambda yang menggambarkan argumen fungsi, dan fungsi parsing permintaan. Biasanya untuk argumen Anda, hanya argumen yang diperlukan dan opsional masuk akal. Lagi pula, permintaan HTTP hanya memiliki "argumen kata kunci" yang dapat diberikan, dan itu bisa hadir atau hilang.
Nama titik akhir API juga berfungsi sebagai pengidentifikasi yang memberi tahu Anda di mana Anda dapat mencapainya. Titik akhir API hidup di /api/ jalur, diikuti dengan nama titik akhir. Dengan demikian, Anda bertanggung jawab untuk mengawali titik akhir Anda dengan nama modul atau aplikasi Anda untuk menghindari secara tidak sengaja tersandung titik akhir lainnya. Ini tidak seperti di Dispatcher URI, karena titik akhir API harus cocok dengan tepat dan tidak mengizinkan ambiguitas atau pemrosesan jalur. Dengan demikian setiap titik akhir harus memiliki jalur yang unik, yang juga dapat segera berfungsi sebagai namanya.
Fungsi mentah adalah fungsi yang disediakan API untuk antarmuka. Ini bertanggung jawab untuk melakukan tindakan yang diminta dan mengembalikan data yang sesuai seperti yang dijelaskan di atas. Untuk mengembalikan data API yang diformat, lihat api-output . Untuk mengarahkan kembali dalam kasus permintaan browser, lihat redirect .
Akhirnya, fungsi parsing permintaan bertanggung jawab untuk mengambil objek permintaan, mengekstraksi argumen yang dibutuhkan fungsi darinya, dan akhirnya memanggil fungsi itu dengan argumen yang sesuai- jika memungkinkan. Fungsi parsing dapat menandakan kesalahan api-argument-missing jika argumen yang diperlukan tidak ada. Argumen berlebihan harus diabaikan.
Anda juga dapat secara terprogram memanggil titik akhir API menggunakan call-api , atau mensimulasikan panggilan permintaan dengan call-api-request , tanpa harus melalui seluruh mekanisme pengiriman URI.
Demikian pula dengan halaman, definisi titik akhir API juga menerima opsi yang dapat diperluas yang membuat definisi lebih sederhana. Lihat bagian berikut untuk penjelasan opsi.
See api , *default-api-format* , *serialize-fallback* , api-format , remove-api-format , list-api-formats , define-api-format , api-output , api-serialize , api-endpoint , remove-api-endpoint , list-api-endpoints , api-endpoint , name , handler , argslist , request-handler , call-api-request , call-api , define-api
Opsi adalah cara untuk menyediakan makro definisi yang dapat diperluas. Ini berguna ketika kerangka kerja memberikan cara umum untuk mendefinisikan sesuatu, tetapi bagian lain mungkin ingin memberikan ekstensi untuk itu untuk membuat operasi umum lebih pendek. Misalnya, tugas umum adalah membatasi halaman akhir halaman atau API untuk orang -orang yang memiliki kredensial akses yang diperlukan.
Untuk memfasilitasi ini, Radiance menyediakan mekanisme opsi yang agak umum. Opsi dibagi dengan jenis opsi yang menunjuk ke mana makro definisi milik. Radiance menyediakan tipe opsi api dan page di luar kotak.
Setiap opsi memiliki kata kunci untuk nama dan fungsi expander yang harus menerima sejumlah argumen, tergantung pada jenis opsi. Selalu disediakan sebagai argumen adalah nama dari hal yang didefinisikan, daftar bentuk tubuh dari definisi, dan nilai akhir, opsional, yang disediakan untuk opsi dalam daftar opsi, jika disebutkan sama sekali. Fungsi ekspansi ini kemudian bertanggung jawab untuk mengubah bentuk tubuh makro definisi dalam beberapa cara. Ini juga dapat memancarkan bentuk kedua yang ditempatkan di luar definisi itu sendiri, untuk memungkinkan pengaturan lingkungan dengan cara tertentu.
Lihat option , option-type , name , expander , option , remove-option , list-options , define-option , expand-options
Konsep modul sangat penting untuk pancaran. Ini berfungsi sebagai representasi dari "bagian" dari keseluruhan. Pada tingkat teknis, modul adalah paket yang memiliki metadata khusus yang terpasang padanya. Ini disediakan oleh sistem modularize dan digunakan untuk memfasilitasi kait dan pemicu, antarmuka, dan pelacakan beberapa informasi lainnya.
Apa artinya ini bagi Anda adalah bahwa alih-alih defpackage standar Anda harus menggunakan formulir define-module untuk menentukan paket utama Anda. Sintaksnya sama dengan defpackage , tetapi mencakup beberapa opsi tambahan seperti :domain , yang memungkinkan Anda untuk menentukan domain utama di mana modul ini harus beroperasi (jika ada).
Sistem modul juga memungkinkan pengikat sistem ASDF ke modul. Jika itu dilakukan, maka sistem ASDF menjadi "modul virtual". Untuk melakukan ini, Anda harus menambahkan tiga opsi ke definisi sistem Anda:
:defsystem-depends-on (:radiance)
:class "radiance:virtual-module"
:module-name "MY-MODULE"
Ini memungkinkan Radiance untuk mengidentifikasi dan mengaitkan informasi sistem ASDF ke modul Anda. Untuk pembuatan otomatis dari sistem yang diperlukan dan definisi modul untuk modul baru, lihat create-module .
See 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 , module-dependencies , module-required-interfaces , module-required-systems , module-pages , module-api-endpoints , describe-module , find-modules-directory , *modules-directory* , create-module
Salah satu mekanisme yang disediakan Radiance untuk memungkinkan mengintegrasikan modul satu sama lain adalah pengait. Hooks memungkinkan Anda untuk menjalankan fungsi sewenang -wenang sebagai respons terhadap beberapa jenis peristiwa. Misalnya, perangkat lunak forum mungkin mengatur kait yang dipicu setiap kali posting baru dibuat. Ekstensi kemudian dapat menentukan pemicu pada kait yang melakukan tugas tambahan.
Kait dapat memiliki jumlah pemicu yang sewenang -wenang yang didefinisikan di atasnya, tetapi Anda harus memastikan bahwa pemicu tidak memakan waktu terlalu lama, karena memicu kait adalah operasi pemblokiran yang tidak akan selesai sampai semua pemicu selesai. Dengan demikian, operasi pemicu jangka panjang mungkin menunda respons permintaan terlalu lama.
Terkadang kait harus berfungsi lebih seperti sakelar, di mana mereka dapat "aktif" untuk waktu yang lama, sampai mereka dimatikan "lagi" nanti. Jika pemicu baru didefinisikan selama waktu itu, mereka harus dipanggil secara otomatis. Inilah yang difasilitasi oleh define-hook-switch . Itu menghasilkan dua kait. Setelah yang pertama dipicu, pemicu apa pun yang didefinisikan di kemudian hari disebut secara otomatis sampai kait kedua dipicu. Ini memungkinkan pemicu pada kait seperti server-start berfungsi dengan baik bahkan jika pemicu hanya ditentukan setelah server sudah dimulai.
Lihat list-hooks , define-hook , remove-hook , define-trigger , remove-trigger , trigger , define-hook-switch
Untuk menghindari menjadi monolitik, dan untuk memungkinkan backend yang dapat diperluas, Radiance mencakup sistem antarmuka. Dalam arti yang paling umum, suatu antarmuka memberikan janji tentang bagaimana beberapa fungsi, makro, variabel, dll. Harus berfungsi, tetapi tidak benar -benar mengimplementasikannya. Fungsionalitas aktual yang membuat semua yang antarmuka menguraikan pekerjaan didorong ke implementasi. Ini memungkinkan pengguna untuk mengkode antarmuka dan memanfaatkan fungsionalitas yang disediakan, tanpa mengikat diri mereka sendiri ke backend tertentu.
Sebagai contoh konkret, katakanlah ada antarmuka untuk database. Ini masuk akal, karena ada banyak jenis database, yang semuanya menawarkan banyak cara interaksi yang berbeda, tetapi masih semua juga menawarkan beberapa operasi yang sangat umum: menyimpan data, mengambil data, dan memodifikasi data. Dengan demikian kami membuat antarmuka yang menawarkan operasi umum ini. Maka tergantung pada implementasi untuk jenis database tertentu untuk membuat operasi yang sebenarnya berhasil. Sebagai penulis aplikasi, Anda kemudian dapat menggunakan antarmuka basis data, dan dengan itu, buat aplikasi Anda secara otomatis bekerja dengan banyak database yang berbeda.
Selain memberikan keuntungan kepada penulis aplikasi, decoupling yang diberikan antarmuka juga berarti bahwa administrator sistem dapat menulis implementasi mereka sendiri dengan relatif mudah, jika persyaratan khusus mereka tidak dipenuhi oleh implementasi yang ada. Berkat ketidaknyamanan antarmuka, implementasi dapat memberikan jembatan untuk sesuatu yang berjalan dalam proses LISP, dan sesuatu yang sepenuhnya eksternal. Ini membuat banyak pilihan terbuka bagi administrator sistem produksi untuk memungkinkan mereka memilih dengan tepat apa yang mereka butuhkan.
Dalam praktiknya, antarmuka adalah jenis modul khusus, dan dengan demikian jenis paket khusus. Sebagai bagian dari definisi mereka, mereka menyertakan serangkaian definisi untuk ikatan lain seperti fungsi, variabel, dll. Karena itu adalah paket, sebagai pengguna Anda dapat menggunakan komponen antarmuka seperti Anda akan menggunakan hal lain dalam paket lain. Tidak ada perbedaan. Sebagai penulis implementasi, Anda kemudian cukup mendefinisikan kembali semua definisi yang diuraikan antarmuka.
Untuk benar -benar memuat modul yang menggunakan antarmuka, implementasi untuk antarmuka harus dimuat sebelumnya. Kalau tidak, makro tidak bisa berfungsi dengan baik. Dengan demikian, untuk mengizinkan tergantung pada antarmuka dalam definisi sistem ASDF Anda tanpa harus merujuk pada implementasi tertentu, Radiance menyediakan ekstensi ASDF. Ekstensi ini memungkinkan untuk menambahkan daftar seperti (:interface :foo) ke Anda :depends-on . Radiance kemudian akan menyelesaikan antarmuka ke implementasi konkretnya ketika modul dimuat.
Radiance menyediakan banyak antarmuka standar. Masing-masing antarmuka tersebut memiliki setidaknya satu implementasi standar yang disediakan oleh Radiance-Contribs. Antarmuka adalah:
adminauthbancachedatabaseloggermailprofilerateserversessionuserAntarmuka dijelaskan secara mendalam di bawah ini.
Lihat interface , interface-p , implementation , implements , reset-interface , define-interface-extension , load-implementation find-implementation , define-interface , define-implement-trigger
Untuk mengizinkan menjalankan beberapa contoh pancaran dengan pengaturan yang berbeda pada mesin yang sama, Radiance memberikan apa yang disebut sistem lingkungan. Lingkungan pada dasarnya adalah himpunan file konfigurasi dan runtime untuk Radiance itu sendiri dan semua modul yang dimuat. Konfigurasi Radiance juga mencakup pemetaan antarmuka untuk implementasi yang dipilih dan dengan demikian memutuskan apa yang harus dipetik jika antarmuka diminta.
Lingkungan khusus yang digunakan dipilih paling lambat ketika startup dipanggil, dan paling awal ketika modul dimuat. Dalam kasus terakhir, restart interaktif disediakan untuk memungkinkan Anda memilih lingkungan. Ini diperlukan, karena jika tidak, Radiance tidak akan dapat menyelesaikan pemetaan antarmuka.
Sebagai bagian dari sistem lingkungan, Radiance memberi Anda sistem konfigurasi yang dapat Anda-dan mungkin harus-digunakan untuk aplikasi Anda. Ini memastikan bahwa pengaturannya multiplexed dengan benar untuk setiap lingkungan, dan bahwa pengaturan selalu persisten. Ini juga menggunakan format penyimpanan yang dapat dibaca manusia, sehingga file dapat dibaca dan dimodifikasi tanpa memerlukan alat khusus apa pun.
Lihat di mana-mana untuk penanganan dan penggunaan-instruksi penyimpanan konfigurasi yang sebenarnya. Cukup perhatikan bahwa alih -alih fungsi value , Radiance menyediakan fungsi config .
Selain file konfigurasi, lingkungan juga menyediakan lokasi penyimpanan yang konsisten untuk file data runtime, seperti unggahan pengguna, file cache, dan sebagainya. Anda dapat mengambil lokasi ini dengan menggunakan environment-module-directory dan environment-module-pathname . Saat menyimpan unggahan dan cache, modul harus menggunakan jalur ini untuk menyajikan antarmuka yang konsisten kepada administrator.
Pada sistem yang digunakan, mungkin diinginkan untuk mengubah lokasi jalur penyimpanan lingkungan, dalam hal ini administrator didorong untuk memasok metode baru pada environment-directory dan environment-module-directory untuk menyesuaikan perilaku seperti yang diinginkan. Lihat juga string dokumentasi terkait untuk perincian lebih lanjut dan tindakan default.
Lingkungan juga memungkinkan penanggulangan administrator. Menggunakan :static dan :template untuk environment-module-directory memberi Anda jalur untuk menyimpan file yang harus mengganti templat standar modul dan file statis. Jalur dalam direktori masing -masing harus cocok dengan file sumber modul sendiri. Perhatikan bahwa file statis dan template yang sebenarnya dimanfaatkan oleh modul yang di-cache pada waktu pemuatan modul, dan dengan demikian tidak akan berubah kecuali gambar LISP dimulai kembali, atau file sumber modul dimuat ulang.
Lihat 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
Terkadang sistem berkembang dengan cara yang tidak kompatibel ke belakang. Dalam hal ini, untuk pengaturan yang ada untuk terus berfungsi dengan versi baru, migrasi data runtime diperlukan. Radiance menawarkan sistem untuk mengotomatiskan proses ini dan memungkinkan peningkatan yang lancar.
Migrasi antara versi harus terjadi secara otomatis selama urutan startup Radiance. Sebagai administrator atau penulis, Anda tidak perlu melakukan langkah -langkah tambahan untuk migrasi terjadi. Namun, sebagai penulis modul, Anda secara alami harus menyediakan kode untuk melakukan langkah -langkah migrasi data yang diperlukan untuk modul Anda.
Agar suatu modul dapat migrata, perlu dimuat oleh sistem ASDF yang memiliki spesifikasi versi. Versi harus mengikuti skema angka putus -putus standar, dengan hash versi opsional yang dapat ditambahkan di akhir. Anda kemudian dapat mendefinisikan langkah-langkah migrasi antara versi individu dengan menggunakan define-version-migration . Setelah didefinisikan, Radiance akan secara otomatis mengambil versi konkret dan melakukan migrasi yang diperlukan secara berurutan untuk mencapai versi target saat ini. Untuk informasi lebih lanjut tentang prosedur yang tepat dan apa yang dapat Anda lakukan, lihat migrate dan migrate-versions .
Lihat last-known-system-version , migrate-versions , define-version-migration , ready-dependency-for-migration , ensure-dependencies-ready , versions , migrate
Akhirnya, Radiance menyediakan startup standar dan urutan shutdown yang harus memastikan segala sesuatunya diatur dengan benar dan disiapkan, dan setelah itu dibersihkan dengan baik lagi. Sebagian besar dari urutan itu hanya memastikan bahwa kait tertentu dipanggil dalam urutan yang tepat dan pada waktu yang tepat.
Meskipun Anda dapat memulai server secara manual dengan menggunakan fungsi antarmuka yang sesuai, Anda seharusnya tidak mengharapkan aplikasi berjalan dengan benar jika Anda melakukannya dengan cara itu. Banyak dari mereka akan mengharapkan kait tertentu dipanggil agar dapat bekerja dengan baik. Inilah sebabnya mengapa Anda harus selalu, kecuali Anda tahu persis apa yang Anda lakukan, gunakan startup dan shutdown untuk mengelola instance Radiance. Dokumentasi dari dua fungsi harus menjelaskan dengan tepat kait mana yang dipicu dan dalam urutan mana. Implementasi dapat memberikan definisi tambahan yang tidak ditentukan pada simbol dalam paket antarmuka, selama simbol tersebut tidak diekspor.
Lihat *startup-time* , uptime , server-start , server-ready server-stop , server-shutdown , startup , startup-done , shutdown , shutdown-done , started-p
Antarmuka ini didistribusikan dengan pancaran dan merupakan bagian dari paket inti. Namun, perpustakaan dapat menyediakan antarmuka tambahan. Untuk implementasi antarmuka standar, relaksasi kendala definisi antarmuka berikut diizinkan:
Daftar lambda yang berisi &key dapat diperluas dengan lebih lanjut, argumen kata kunci yang bergantung pada implementasi. Daftar lambda yang berisi &optional tetapi tidak &key atau &rest dapat diperpanjang dengan argumen opsional lebih lanjut. Daftar lambda yang hanya berisi argumen yang diperlukan dapat diperpanjang dengan argumen opsional atau kata kunci lebih lanjut.
Antarmuka ini menyediakan halaman administrasi. Ini harus digunakan untuk segala jenis pengaturan yang dapat dikonfigurasi pengguna, atau tampilan informasi sistem. Perhatikan bahwa meskipun disebut "administrasi", ini tidak dimaksudkan semata -mata untuk administrator sistem. Halaman harus dapat digunakan untuk pengguna mana pun.
Halaman administrasi harus dapat menampilkan menu yang dikategorikan, dan panel. Panel disediakan oleh modul lain dan dapat ditambahkan melalui admin:define-panel . Panel yang memberikan akses ke operasi sensitif harus dibatasi dengan tepat melalui :access dan izin non-default. Lihat antarmuka pengguna untuk penjelasan tentang izin.
Untuk menautkan ke halaman administrasi, atau panel tertentu di atasnya, gunakan jenis sumber daya page .
Lihat admin:list-panels , admin:remove-panel , admin:define-panel , admin:panel
Antarmuka otentikasi bertanggung jawab untuk mengikat pengguna ke permintaan. Karena alasan ini, ia harus memberikan beberapa cara yang dengannya pengguna dapat mengotentikasi diri terhadap sistem. Bagaimana hal ini dilakukan dengan tepat untuk implementasi. Namun implementasi harus menyediakan halaman di mana proses otentikasi dimulai. 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: