Lisp ile Web Programlamaya Giris Makalesi,
// Bug Researchers Group Adina MnmL Tarafindan Derlenmistir./~*
Kodlarin Yada Makalenin Bozuldugunu Düsünüyorsaniz .TXT Halini Download Etmek Için
Tiklayin.Lisp ile web programlamaya giris niteliginde olan bu yazida, Debian GNU/Linux yüklü bir sisteme gerekli olan programlari nasil kuracagimizi gösterdikten "merhaba dünya" seklinde ufak bir web uygulamasini nasil gelistiricegimizi, ardindan da güçlü makro özellikleri ile neler yapabilecegimize bakacagiz. Bu yazida anlatacaklarimiz, Debian GNU/Linux’un "testing" dagitimi olan Etch isimli dagitim üzerinde denenmistir. Ayni adimlari "stable" olarak nitelenen Debian Sarge ve "unstable" olarak nitelenen SID üzerinde de pek bir sorun çikmadan takip edebileceginizi düsünüyoruz. Unutmadan söyleyelim, bu yazida Lisp dil ailesinin büyük agabeyi, endüstri standardi olan Common Lisp’i kullanacagiz.
Bu yazi hiç Lisp bilmeyenlere Lisp ögreten bir yazi degildir. Ayrica karmasik web uygulamalari ile nasil basa çikacaginizi da göstermemektedir. Sadece Common Lisp ve web programlamaya dair mümkün olan en basit isinma turlarindan biridir.
Kollari Siviyoruz
Lisp ile web uygulamalari gelistirmek için Franz Inc. tarafindan gelistirilen AllegroServe web sunucusunu kullanacagiz. AllegroServe, tamamen Common Lisp ile yazilmis olup, LLGPL ile dagitilmaktadir. Esas olarak Franz’in kendi Lisp ortami olan AllegroCL üzerinde çalismasi için tasarlanan AllegroServe’ü diger Lisp ortamlarinda çalistirabilmek için bir ara katman kullaniliyor. Adi Portable AllegroServe olan bu katman programi, içinde AllegroServe programini da barindiriyor ve beraber dagitiliyor. Debian GNU/Linux paket deposunda bulunan cl-aserve isimli paket de aslinda Portable AllegroServe’ü içeriyor.
Programlarimizi üzerinde gelistirecegimiz ortam olarak SBCL’yi (Steel Bank Common Lisp) seçtik. Tabii ki olmazsa olmaz editörümüz de Emacs. Eger sisteminizde, bu yazida kullanacagimiz, Emacs, SBCL ve SLIME (bir hayli gelismis bir Emacs Lisp gelistirme ortami) yüklü degilse, Emre Sevinç’in " Common Lisp Gelistirme Ortami Kurulumu" baslikli makalesine gözatmanizi öneriyoruz. Ayrica, eger Lisp’e eglenceli bir baslangiç yapmak isterseniz Conrad Barski tarafindan yazilmis ve Seda Çelebican tarafindan Türk diline çevrilmis " Lisp ile TILSIMLI ve Renkli Programlama: Lisperati" baslikli yaziyi okumanizi öneririz.
Artik kuruluma baslayabiliriz. Web uygulamalarizi gelistirebilmemiz için gerekli olan web sunucumuzu, asagidaki komut ile sistemimize kuruyoruz.
# apt-get install cl-aserve
Simdi, IDE-ötesi programimiz Emacs’i açip, M-x slime komutunu verip Emacs ile sbcl arasinda iletisim kuracak ve Lisp kodlarimizi yazarken bize pek çok kolaylik saglayacak olan SLIME’i çalistiriyoruz. Komut satirindan
$ emacs
ve emacs açildiktan sonra, emacs penceresinde
M-x slime
komutlari yeterli olacaktir. (Burada M-x, Meta tusunu basili tutup, x tusuna basmayi ifade ediyor. Meta tusu, eger IBM uyumlu PC klavyesi kullaniyorsaniz genellikle üzerinde Alt yazan tustur.)
SLIME’i ilk kez çalistiriyorsaniz bir süre gerekli SLIME kodlari derlenecek (sadece bir kerelige mahsus) ve sonra da karsiniza Lisp’in ünlü Read-Eval-Print Loop’u (REPL yani Oku-Degerlendir-Bas Döngüsü) çikacaktir.
CL-USER>
Simdi sistemimize AllegroServe’ü yüklemek için asagidaki komutu çalistirmamiz gerekiyor:
CL-USER> (require :aserve)
("URI" "ACL-SOCKET" "ASERVE")
Burada yazdigimiz komut, ilk satirdaki (require :aserve) kismi. Bastaki CL-USER bize su anda hangi paketin içerisinde oldugumuzu gösteriyor. > karakteri de Lisp’in bizden bir seyler girmemizi istedigini söylüyor. Ikinci satir ise, Lisp’in çalistirdigimiz komuta verdigi yanit. Bunu çalistirdigimiz ifadenin geridönüs degeri olarak da düsünebiliriz. Artik AllegroServe programinin bize sagladigi fonksiyonlari kullanabiliriz. Bir sonraki bölüme geçmeden önce Lisp’teki paket kavramindan bahsetmek ve bunun bir tür "namespace" tanimlamak için kullanilan kavramlardan biri oldugunu belirtmek faydali olacaktir. Lisp’teki paket kavramini yazilim karmasikligi ile basa çikmak, islevselligi yalitmak, vs. gibi isler için C#’taki "namespace" ya da Java’daki "package" mekanizmasi gibi düsünebilirsiniz. Daha detayli bilgi için PCL’nin ilgili bölümüne ya da The Complete Idiot’s Guide to Common Lisp Packages belgesine bakabilirsiniz.
Biraz Aserve
Ilk fonksiyonumuz start fonksiyonu. Bu fonksiyon http sunucumuzu baslatmamiza yariyor. Bu fonksiyonu asagidaki gibi çagiriyoruz:
CL-USER> (net.aserve:start :port 3000)
#<NET.ASERVE:WSERVER port 3000 {A3BD9C9}>
Fonksiyonumuz net.aserve isimli pakete ait oldugu ve su anda biz CL-USER paketinin içinde çalistigimiz için, paket ismini fonksiyon isminin önüne yazmamiz ve aralarina : isareti koymamiz gerekiyor. Bu uzun yazis seklinden kaçinmak için use-package fonksiyonunu kullanmamiz yeterli. (Aman dikkat, eger su anda içinde çalisilan paketle use-package ile ihraç (export) edilen paketler ayni isimli fonksiyon, makro veya degisken kullaniyorlarsa, "sembol çakismasi hatasi" gerçeklesir. Bu yüzden use-package dikkatli kullanilmalidir. Burada use-package kullanmamizin nedeni, uzun fonksiyon isimlerinden kaçinmak istememizdir. Common Lisp’teki paket yönetimi ile ilgili olarak Practical Common Lisp’in ilgili bölümüne göz atabilirsiniz.)
CL-USER> (use-package :net.aserve)
T
Artik net.aserve paketindeki -yani AllegroServe’ün- fonksiyonlarina, makrolarina ve global degiskenlerine, isimlerinin önünde paket ismi olmadan erisebilecegiz.
Web sunucumuzu baslatmamiza yarayan start fonksiyonu, hepsi "keyword" parametresi olmak üzere tam 19 parametre aliyor (Common Lisp bir fonksiyona parametre geçirmek için muazzam bir esneklik sunar, detaylar için lütfen Practical Common Lisp’in Functions bölümüne bakiniz). Ancak bu parametrelerin çogu için simdilik varsayilan degerleri degistirmemiz gerekli degil. Biz sadece :port parametresinin degerini 3000 olarak belirledik ve web sunucumuzun 3000 numarali porttan yayin yapacagini söyledik. 3000 sayisinin pek özel bir anlami yok, standart 80 numarali http port numarasini vermememizin sebebi öncelikle bunun "root" kullanici yetkisini gerektirmesi (UNIX ve türevi sistemlerde 1024 numarali portun altindaki port numaralarinin "root" yetkileri gerektirmesi gibi) ve/veya ayni zamanda 80 numarali portunuzun baska programlar tarafindan kullanimda olma olasiligi (Apache web sunucusu, vb.). start fonksiyonu da bize bir WSERVER nesnesi geri gönderdi (Lisp ve nesneye yönelik programlama! :). Fazla detaya inmeden sunu söyleyelim ki, start fonksiyonu basariyla bir WSERVER nesnesi yaratti, yani sunucumuz localhost’umuzun 3000 numarali portunda ziyaretçilerini beklemeye koyuldu.
Eger http://localhost:3000 adresini Firefox (ya da Emacs içinden w3m) gibi bir web tarayici programindan açarsak, çok sevimli bir 404 sayfa bulunamadi sayfasiyla karsilasiyoruz. Bu mesaji size Allegro web sunucusunun verdigini sayfanin altindaki Allegro ve sürüm numarasindan anlayabilirsiniz. (Eger böyle bir sey olmaz da "connection refused" benzeri bir uyari penceresi ile karsilasirsaniz o zaman lütfen adresi dogru yazdiginizi, sunucunun çalistigini ve 3000 numarali portu daha öncede baska bir programin dinlemedigini kontrol edin.) Sayfa bulunamadi seklindeki bir web sayfasi ile karsilasmanizin sebebi web sunucusunda birakin index’i, daha hiçbir sayfayi yayinlamiyor olusumuz. Simdi geleneklere saygi göstererek bir "merhaba dünya" yazisini tarayicimizda göstermek istiyoruz. Bunun için Emacs’taki REPL ortaminda yani Common Lisp komut satirinda
CL-USER> (publish :path "/"
:content-type "text/html"
:function
#’(lambda (req ent)
(with-http-response (req ent)
(with-http-bOdy (req ent)
(format (request-reply-stream req) "merhaba dünya!")))))
#<COMPUTED-ENTITY {9FDFBD1}>
yazdiktan sonra ve simdi http://localhost:3000 adresine gittigimizde bizi
Merhaba Dünya
yazisi karsiliyor. (Ne büyük sürpriz!)
AllegroServe’ü diger web sunucularindan (mesela Apache) ayiran en önemli özelliklerden biri, sadece dosyalari ve dizinleri degil dogrudan bir Lisp fonksiyonunu yayimlayabilmesi. Bu özellik sayesinde sunucumuza "eger kullanici su adrese giderse bu fonksiyonu çalistir" diyebiliyoruz. Lisp’de fonksiyonlar birinci sinif nesne olduklarindan [1], bir adrese gidildiginde çagrilacak fonksiyonu örnekte gösterildigi gibi dinamik olarak da yaratabiliriz.
Fonksiyonlarimizla, URL’leri eslestirmemize yarayan publish fonksiyonuna verdigimiz parametrelere kisaca bir gözatalim.
* :path anahtar sözcügü kullanarak fonksiyona geçirdigimiz deger (:path "/") fonksiyonumuzu eslestirecegimiz adresi belirtiyor. Yukaridaki örnekte "/", yani kök adresi belirttik.
* :content-type çalistiracak fonksiyonun üretecegi MIME tipini belirtiyor ki bu da genellikle "text/html"dir.
* :function Çalistirmak istedigimiz fonksiyon.
Bir adresle eslestirdigimiz fonksiyon iki parametre alan bir fonksiyon olmali. Birincisi http istek (request) nesnesi, ikinci ise publish fonksiyonunun döndürdügü "entity" nesnesi. Bunlarin degerleri, bir http istegi gerçeklestiginde, AllegroServe tarafindan fonksiyonumuza veriliyor.
Gelelim olusturdugumuz fonksiyonun içinde bulunan with-http-response ve with-http-bOdy sembollerine. Bu semboller AllegroServe’de tanimlanmis iki makroyu ifade ediyor. with-http-response makrosu sayesinde, kullaniciya bir yanit (response) hazirladigimizi belirtiyoruz. with-http-response, gerekli olan HTTP baslik (header) degerlerini bize is birakmadan hazir hale getiriyor. Diger sevimli makromuz with-http-bOdy ile artik HTTP cevabimizin içerigini olsuturmaya hazir oldugumuzu belirtiyoruz. Iki makroya da fonksiyonumuza gelen parametreleri vermek zorundayiz.
Fonksiyonumuzda ekrana "merhaba dünya" yazisini yazdiran kisim, format fonksiyonunu çagirdigimiz kisim. format fonksiyonunu C’deki sprintf fonksiyonuna benzetebiliriz (ancak burada belirtmeliyiz ki format çok güçlü bir fonksiyon olup, barindirdigi kurallardan ötürü bir mini dil (DSL, Domain Specific Language) olarak görülmelidir, tipki LOOP gibi. Lisp’teki FORMAT ile ne kadar acayip seyler yapilabilecegini merak edenler Practical Common Lisp’in ilgili bölümüne bakabilirler). Kisaca bir "stream"e, belirli bir sablona göre olusturdugu bir karakter katarini gönderir. Örnegimizde format’a akim olarak kullaniciya yanit vermek için kullandigimiz "stream" nesnesini veriyoruz. Bu nesneye ulasmak için de request-reply-stream fonksiyonunu kullandik.
AllegroServe’ün Apache’nin yaptigi gibi dosyalari ya da dizinleri yayinlamasini istersek, kullanmamiz gereken fonksiyonlar publish-file ve publish-directory fonksiyonlari.
CL-USER> (publish-file :path "/makale.txt"
:file "/home/hb/lisp-web.01.txt")
#<NET.ASERVE::FILE-ENTITY {98CB3A1}>
Artik http://localhost:3000/makale.txt adresinden ev dizinimdeki lisp-web.01.txt dosyasina ulasabilir durumdayim.
Simdi biraz daha gelismis bir "merhaba dünya" uygulamasiyla, HTTP isteginden gelen GET ve POST degiskenlerine nasil ulasacagimiza bakalim. Henüz Lisp ile HTML üretmeyi bilmedigimiz için form sayfasini duragan (statik) bir HTML sayfasi olarak bir dosyaya kaydediyor ve bu dosyayi yayinliyoruz.
Form sayfamiz:
<html>
<head>
<title>Form sayfasi</title>
</head>
<bOdy>
<form action="/handle-form" method="post">
<span>Gezegen:</span>
<input type="text" name="gezegen" size="40"/>
<input type="submit" value="Yolla gitsin"/>
</form>
</bOdy>
CL-USER> (publish-file :path "/form"
:file "/home/hb/form.html")
#<NET.ASERVE::FILE-ENTITY {9068C49}>
Simdi sira bu formu yorumlayacak olan fonksiyonumuzu hazirlamakta.
CL-USER> (defun handle-form (req ent)
(with-http-response (req ent)
(with-http-bOdy (req ent)
(let ((gezegen (request-query-value "gezegen" req))
(req-stream (request-reply-stream req)))
(if (not (equal gezegen ""))
(format req-stream "Selam ~A!" gezegen)
(format req-stream "Ne bileyim nerden geldin!"))))))
HANDLE-FORM
CL-USER> (publish :path "/handle-form"
:content-type "text/html"
:function ’handle-form)
#<COMPUTED-ENTITY {A018F91}>
Bu kez, ilk örnekteki gibi fonksiyonumuzu lambda kullanarak isimsiz degil, defun kullanarak isim vererek yarattik. Daha sonra publish fonksiyonuyla yarattigimiz fonksiyonu olmasi gereken adreste yayina verdik. Bu örnekte, formdan gelen bilgiyi almak için request-query-value fonksiyonunu kullandik. Bu fonksiyonun ilk parametresi formdan gelen degiskenin adi, ikincisi ise request nesnesi.
Gelin HTML üretelim
Bu bölümde Lisp ile HTML üretmek için kullanacagimiz pek ufak bir kütüphane olusturacagiz [2]. HTML dili de Lisp gibi "prefix" gösterime ve iç içe geçen yapilardan olusan yapiya sahip oldugundan bu pek zor bir is olmayacak. Iç içe geçen HTML etiketlerini iç içe geçmis Lisp fonksiyonlari olarak düsünmek gayet dogaldir.
Ilk önce üretecegimiz HTML çiktisinin gidecegi bir "stream" degiskenini olusturmaliyiz. Gerektiginde bu degiskenin degerini degistirerek olusturdugumuz HTML çiktisini standart çiktiya ya da bir dosyaya yazabiliriz.
CL-USER> (defvar *html-stream* nil)
*HTML-STREAM*
Simdi olusturacagimiz çiktilari belirli bir HTML etiketi arasina yerlestirecek olan % makrosunu yaziyoruz. % ismi tamamen bizim seçimimiz. Alternatif olabilecek with-html-tag gibi uzun bir isim yerine, çok kullanilacagini düsündügümüz için % ismini kullandik.
CL-USER> (defmacro % (tag (&rest attrs) &bOdy bOdy)
`(progn
(format *html-stream* "<~(~A~)~A>" ’,tag (process-attrs (list ,@attrs)))
,@bOdy
(format *html-stream* "</~(~A~)>" ’,tag)))
%
% makrosuna bir gözatalim. Ilk satirda defmacro ile % isminde bir makro tanimlamakta oldugumuzu belirtiyoruz. % makrosu, parametre olarak; HTML etiketinin adini, özniteliklerini ve etiketin baslangiç ve bitisi arasinda çalistiracagi bir Lisp kod blogunu aliyor. Ilk format fonksiyonunu çagirdigimizda, < ve > karakterlerinin arasina etiketin adini ve özniteliklerini yaziyoruz. % makrosuna öznitelikleri (özniteligin-adi degeri adi degeri ... ) gibi bir liste halinde veriyoruz. Asagida tanimlayacagimiz process-attrs fonksiyonu bu listeyi HTML sözdizimine uygun bir sekilde isleyip geri döndürüyor. Daha sonra aldigimiz Lisp kodunu çalistirip, kapanis etiketini olusturuyoruz.
Asagida, process-attrs fonksiyonunu nasil tanimladigimizi görebilirsiniz.
CL-USER> (defun process-attrs (attrs)
(if (null (cdr attrs))
""
(if (not (null (second attrs)))
(format nil " ~(~A~)=\\\\\\"~A\\\\\\"~A" (first attrs) (second attrs)
(process-attrs (cddr attrs)))
(process-attrs (cddr attrs)))))
PROCESS-ATTRS
% makrosunun örnek bir kullanimi asagidaki kodda gösteriliyor. Öncelikle let yapisini kullanarak, *html-stream* degiskeninin degerini *standard-output* yapiyoruz. Bunu yapmamizin nedeni, olusacak HTML kodunu ekranda görebilmek. % makrosunu p sembolü ve (:align "left") listesiyle çagirdik ve içinde *html-stream*’e "hello world" karakter katarini bastik. Tam da istedigimiz gibi HTML kodumuz ekranda görüntülendi. Olusan HTML kodunun altindaki satirda bulunan NIL, çalistirdigimiz kodun geri dönüs degeri. Bu deger *html-stream* akimina basilmiyor.
CL-USER> (let ((*html-stream* *standard-output*))
(% p (:align "left")
(format *html-stream* "Merhaba dünya!")))
<p align="left">Merhaba dünya!</p>
NIL
Ikinci makromuz %=, ayni % makrosu gibi çalisacak, ancak bir Lisp blogu alip çalistirmak yerine, bir Lisp nesnesi alip onu ekrana basacak. Yeni makromuzun tanimi asagida.
CL-USER> (defmacro %= (tag (&rest attrs) obj)
`(progn
(format *html-stream* "<~(~A~)~A>" ’,tag (process-attrs (list ,@attrs)))
(princ ,obj *html-stream*)
(format *html-stream* "</~(~A~)>" ’,tag)))
%=
CL-USER> (let ((*html-stream* *standard-output*))
(%= a (:href "http://www.google.com") "Google’a git"))
<a href= "http://www.google.com">Google’a git</a>
NIL
Uzun let yapisindan ve format çagrilarindan kurtulmak için birkaç ufak yardimci fonksiyon tanimlayalim.
CL-USER> (defun pr (obj)
(princ obj *html-stream*))
PR
CL-USER> (defmacro prf (format &rest args)
`(format *html-stream* ,format ,@args))
PRF
pr ve prf, sirasi ile princ ve format fonksiyonlarinin stream degiskenlerini kendiliginden *html-stream*’ye bagliyor. Özellikle prf makrosu bize büyük bir tasarruf saglayacak.
Bir diger makromuz asagida tanimladigimiz with-html-stream makrosu. Parametre olarak bir akim nesnesi ve Lisp kod blogu alan bu makro, *html-stream*’i aldigi "stream" nesnesine bagliyor ve blogu çalistiriyor.
CL-USER> (defmacro with-html-stream ((&optional (stream *standard-output*)) &bOdy bOdy)
`(let ((*html-stream* ,stream))
,@bOdy))
WITH-HTML-STREAM
with-html-stream makrosunu kullanarak, olusturdugu HTML kodlarini string olarak yollayan bir makro yaziyoruz:
CL-USER> (defmacro html-to-string (&bOdy bOdy)
(let ((g-stream (gensym)))
`(with-output-to-string (,g-stream)
(with-html-stream (,g-stream)
,@bOdy))))
HTML-TO-STRING
CL-USER> (html-to-string
(% div (:align "center" :id "mainPane")
(%= span (:class "normalText")
"Naber dünya?")))
"<div align=\\\\\\"center\\\\\\" id=\\\\\\"mainPane\\\\\\"><span class=\\\\\\"normalText\\\\\\">Naber dünya?</span></div>"
Olusturdugumuz bu araçlarla, biraz daha üst seviye olan page makrosunu olusturabiliriz.
CL-USER> (defmacro page ((&key (title "Untitled")) &bOdy bOdy)
`(% html ()
(% head ()
(%= title () ,title))
(% bOdy ()
,@bOdy)))
PAGE
Page makrosu, title parametresini alarak bir HTML belgesini olusturuyor. Burada Lisp makrolarinin çok dogal biçimde sablon görevini üstlendiklerini görebiliyoruz.
CL-USER> (publish :path "/merhaba2"
:content-type "text/html"
:function
#’(lambda (req ent)
(with-http-response (req ent)
(with-http-bOdy (req ent)
(with-html-stream ((request-reply-stream req))
(page (:title "Merhaba Dünya!")
(pr "ikinci kez merhaba!")))))))
#<COMPUTED-ENTITY {A185461}>
Yukaridaki örnekte sayfamizi olusturan kod sadece 2 satir, ama daha öncesinde çok fazla satir yazmak zorunda kaliyoruz. Her tembel programci gibi bunlari yazmamanin bir yolunu aradigimizda imdadimiza yine Lisp makrolari yetisiyor. Asagida tanimladigimiz webfn makrosu, with-* makrolarini yazmaktan kurtulmamizi sagliyor.
CL-USER> (defmacro webfn (&bOdy bOdy)
`#’(lambda (req ent)
(with-http-response (req ent)
(with-http-bOdy (req ent)
(with-html-stream ((request-reply-stream req))
,@bOdy)))))
WEBFN
CL-USER> (publish :path "/merhaba3"
:content-type "text/html"
:function
(webfn
(page (:title "Merhaba")
(% div (:align "center")
(%= p ()
"Birinci paragraf")
(%= p ()
"ikinci paragraf")))))
#<COMPUTED-ENTITY {91DCC41}>
Gördügünüz gibi makrolar sayesinde yazdigimiz kod epey azaldi. Eger "su publish kisimlarini da yazmasak" derseniz, asagidaki makroya bir gözatmanizi tavsiye ederiz.
CL-USER> (defmacro defwebfn (name &bOdy bOdy)
`(publish :path ,(format nil "/~(~A~)" name)
:content-type "text/html"
:function (webfn ,@bOdy)))
DEFWEBFN
CL-USER> (defwebfn welcome-page
(page (:title "Hosgeldiniz")
(% div (:align "center")
(prf "Hosgeldiniz!"))))
#<COMPUTED-ENTITY {A3B99A1}>
defwebfn makrosu webfn ile bir fonksiyon yaratip kendiliginden yayinladi. Simdi http://localhost:3000/welcome-page adresine gidip sonucu görebilirsiniz.
Veritabansiz Olur Mu? Olmaz!
Common Lisp ile MySQL baglantisi yapabilmek için Debian GNU/Linux paket deposundaki cl-sql-mysql paketine ihtiyacimiz var. Asagidaki komutu çalistirarak sistemimize bu paketi yüklüyoruz.
# apt-get install cl-sql-mysql
cl-sql-mysql paketi, cl-sql ve cl-sql-uffi paketlerine bagimli oldugu için apt-get bize "bu paketleri de yükleyeyim mi?" diye soracaktir. Isi ustasina birakip yüklemesine izin veriyor ve Lisp kodu içinden MySQL ile konusmamizi saglayacak kütüphaneyi kuruyoruz.
Simdi Lisp gelistirme ortamina geri dönüp, yeni yükledigimiz bu paketleri Lisp’e dahil ediyoruz:
CL-USER> (require :clsql-mysql)
NIL
cl-sql, veritabani ile nesneler arasinda birebir iliski kurabilen ve SQL sorgulari yerine daha üst seviyede çalisabilmeyi saglayan bir kütüphanedir. Fakat bu özelliklerin nasil kullanilacaginin anlatilmasi ancak baska bir yazinin konusu olabilir. Bu yazida sadece basit SQL sorgularinin Common Lisp ile yapilmasini ve sorgularin sonuçlarinin web uygulamasinda kullanilmasini anlatmakla yetinecegiz.
Örnegimizde hayali bir kitapçi dükkaninin hayali kitaplarini tutacagi bir tabloluk basit bir veritabanini kullanacagiz. Veritabanini, biricik tablosunu ve birkaç kitap kaydini yaratmak için asagidaki SQL sorgusunu kullanabiliriz. Bu asamada MySQL ile bir miktar programlama deneyimine sahip oldugunuzu var sayiyoruz:
CREATE DATABASE bookstore;
CREATE TABLE books (
id INT NOT NULL AUTO_INCREMENT,
title VARCHAR(120) NOT NULL,
author VARCHAR(60) NOT NULL,
PRIMARY KEY (id), UNIQUE(id));
INSERT INTO books (title, author)
VALUES (’I\\\\\\’m sure you\\\\\\’re joking Mr. Feynman’, ’Richard Feynman’);
INSERT INTO books (title, author)
VALUES (’Also Sprach Zaradustra’, ’Freidrich Nietzsche’);
INSERT INTO books (title, author)
VALUES (’The Relativity Theory’, ’Albert Einstein’);
INSERT INTO books (title, author)
VALUES (’ANSI Common Lisp’, ’Paul Graham’);
Veritabanina baglanmak için clsql’nin connect fonksiyonun kullanmamiz gerek. clsql:connect fonksiyonunu asagidaki gibi çagiriyoruz.
CL-USER> (clsql:connect ’("localhost" "bookstore" "root" "parola"))
#<CLSQL-MYSQL:MYSQL-DATABASE localhost/bookstore/root OPEN {A630C41}>
connect fonksiyonuna parametre olarak sirasiyla veritabaninin bulundugu sunucunun alan adini, kullanacagimiz veritabaninin adini, veritabani kullanicisinin adini ve son olrak kullanicinin parolasini içeren bir liste vermemiz gerekiyor. Eger veritabanina baglanmayi basaramazsak, connect fonksiyonu bir hata yaratiyor. (Hata yakalama fonksiyonlari hakkinda bkz. Practical Common Lisp)
Veritabanimizdan veri çekmek için clsql:query fonksiyonunu kullanacagiz. clsql:query fonksiyonu, bir SQL sorgusu alip, eslesen kayitlari ve tablodaki alan adlarini döndürüyor. (Common Lisp’de bir fonksiyon birden fazla deger döndürebilir, bu çok elemanli dizi döndürmekten biraz farkli bir durumdur. Detayli bilgi için bkz.: http://www.lispworks.com/documentation/HyperSpec/bOdy/m_multip.htm ve http://www.gigamonkeys.com/book/the-special-operators.html) Asagida query fonksiyonunun örnek kullanimini görebilirsiniz.
CL-USER> (clsql:query "SELECT title AS ’baslik’ FROM books")
(("I’m sure you’re joking Mr. Feynman") ("Also Sprach Zaradustra")
("The Relativity Theory") ("ANSI Common Lisp"))
("baslik")
Simdi, tablomuzdan kitap listesini çekip, bunu bir HTML tablosu halinde web sunucumuzda yayinlayabiliriz:
CL-USER> (defwebfn book-list
(% page (:title "Book List")
(multiple-value-bind (rows labels) (clsql:query "SELECT * FROM books")
(% table (:border "1")
(% tr ()
(dolist (label labels)
(%= th () label)))
(dolist (row rows)
(% tr ()
(dolist (field row)
(%= td () field))))))))
#<COMPUTED-ENTITY {A6DA9D1}>
Denemek için web tarayiciniz ile http://localhost:3000/book-list adresini ziyaret edebilirsiniz.
Bitirirken
Giris seviyesindeki bu yazida Common Lisp ile Allegro Portable Web Server kullanarak ve Lisp’in makro gelistirme gücünden faydalanarak Internet programlamanin nasil yapilabilecegini çok basitçe gördük. Bu asamada arrtik en temel sekilde Lisp kullanarak HTML üretme ve MySQL gibi popüler bir veritabanina baglanma konusunda belli bir izlenime kavustugunuzu ümit ediyoru.
Common Lisp gibi büyük ve köklü geçmise sahip bir dil ve bu dil ile Internet programlama gibi detayli bir konuya dair bu satirlara sigdiramadigimiz pek çok sey mevcuttur.
Anlattiklarimiz bu tür isleri yapmanin tek yolu degil elbet, alternatif yöntemler, çok daha gelismis UnCommon Web gibi "framework"ler Internet’te sizin tarafinizdan kesfedilmeyi bekliyor.