(def매개변수 *web* (make-instance '<앱>)) @route GET "/"(재미없는 색인 () (렌더링 #P"index.tmpl")) @route GET "/hello"(안녕하세요 (&key (|name| "Guest")) (형식 없음 "Hello, ~A" |name|))
모든 것. Caveman2는 처음부터 작성되었습니다.
이것은 주목할만한 점입니다.
닝글을 기반으로 한
데이터베이스 통합 있음
새로운 별도 구성 시스템 사용(Envy)
새로운 라우팅 매크로가 있습니다
가장 자주 묻는 질문 중 하나는 "ningle과 Caveman 중 무엇을 사용해야 합니까? 차이점은 무엇입니까?"였습니다. Caveman과 Ningle이 너무 비슷해서 이런 질문을 너무 자주 받은 것 같아요. 둘 다 "마이크로"라고 불리며 데이터베이스를 지원하지 않습니다.
Caveman2를 사용하면 Caveman은 더 이상 "마이크로" 웹 애플리케이션 프레임워크가 아닙니다. CL-DBI를 지원하며 기본적으로 데이터베이스 연결 관리 기능이 있습니다. 원시인이 성장하기 시작했습니다.
Caveman은 웹 애플리케이션의 공통 부분을 모아 놓은 것입니다. Caveman2에서는 세 가지 규칙을 사용하여 결정을 내립니다.
확장 가능해야 합니다.
실용적이세요.
아무것도 강요하지 마십시오.
원시인처럼 살고 싶어서 여기까지 오셨죠? 이곳은 디즈니랜드는 아니지만 여기서 시작할 수 있습니다. 동굴로 들어가자!
이제 Caveman2를 Quicklisp에서 사용할 수 있습니다.
(ql:퀵로드:caveman2)
(caveman2:make-project #P"/path/to/myapp/" :author "<이름>");-> /path/to/myapp/.gitignore 쓰기; /path/to/myapp/README.markdown 작성; /path/to/myapp/app.lisp 작성; /path/to/myapp/db/schema.sql 작성; /path/to/myapp/shlyfile.lisp 작성; /path/to/myapp/myapp-test.asd 작성; /path/to/myapp/myapp.asd 작성; /path/to/myapp/src/config.lisp 작성; /path/to/myapp/src/db.lisp 작성; /path/to/myapp/src/main.lisp 작성; /path/to/myapp/src/view.lisp 작성; /path/to/myapp/src/web.lisp 작성; /path/to/myapp/static/css/main.css 작성; /path/to/myapp/t/myapp.lisp 작성; /path/to/myapp/templates/_errors/404.html 작성; /path/to/myapp/templates/index.tmpl 작성; /path/to/myapp/templates/layout/default.tmpl 쓰기
이는 애플리케이션 이름이 "myapp"이라고 가정한 예입니다. 서버를 시작하기 전에 먼저 앱을 로드해야 합니다.
(ql:퀵로드:myapp)
애플리케이션에는 웹 애플리케이션을 시작/중지하기 위한 start
및 stop
이라는 함수가 있습니다.
(myapp:시작:포트 8080)
Caveman은 Clack/Lack을 기반으로 하기 때문에 Hunchentoot, Woo 또는 Wookie 등 실행할 서버를 선택할 수 있습니다.
(myapp:시작:서버:hunchentoot:포트 8080) (myapp:시작:서버:fcgi:포트 8080)
로컬 머신에서는 Hunchentoot를 사용하고 프로덕션 환경에서는 Woo를 사용하는 것이 좋습니다.
clackup 명령을 사용하여 애플리케이션을 시작할 수도 있습니다.
$ ros install clack $ which clackup /Users/nitro_idiot/.roswell/bin/clackup $ APP_ENV=development clackup --server :fcgi --port 8080 app.lisp
Caveman2는 경로를 정의하는 두 가지 방법( @route
및 defroute
을 제공합니다. 둘 중 하나를 사용할 수 있습니다.
@route
는 cl-annot을 사용하여 정의된 주석 매크로입니다. 메소드, URL 문자열 및 함수가 필요합니다.
@route GET "/"(재미없는 색인 () ...);; 이름이 없는 경로.@route GET "/welcome"(lambda (&key (|name| "Guest")) (형식 없음 "환영합니다, ~A" |name|))
이는 인수 목록을 제외하면 Caveman1의 @url
과 유사합니다. 필요하지 않은 경우 인수를 지정할 필요가 없습니다.
defroute
는 매크로일 뿐입니다. @route
와 동일한 기능을 제공합니다.
(인덱스 "/" 제거 () ...);; 이름이 없는 경로입니다.(defroute "/welcome" (&key (|name| "Guest")) (형식 없음 "환영합니다, ~A" |name|))
Caveman은 ningle을 기반으로 하기 때문에 Caveman에는 Sinatra와 유사한 라우팅 시스템도 있습니다.
;; GET 요청(기본값)@route GET "/"(람다() ...) (defroute ("/" :method :GET) () ...);; POST 요청@경로 POST "/"(람다() ...) (defroute ("/" :method :POST) () ...);; PUT 요청@경로 PUT "/" (람다() ...) (defroute ("/" :method :PUT) () ...);; DELETE request@route DELETE "/" (람다 () ...) (탈퇴 ("/" :방법 :DELETE) () ...);; OPTIONS request@route OPTIONS "/" (람다 () ...) (defroute ("/" :method :OPTIONS) () ...);; 모든 방법에 대해@route ANY "/" (lambda () ...) (탈퇴 ("/" :방법 :ANY) () ...)
경로 패턴에는 인수에 값을 입력하기 위한 "키워드"가 포함될 수 있습니다.
("/hello/:name" 삭제(&키 이름) (형식은 "Hello, ~A" 이름이 아님))
위 컨트롤러는 "/hello/Eitaro" 또는 "/hello/Tomohiro"에 액세스할 때 호출되며 name
적절하게 "Eitaro" 또는 "Tomohiro"가 됩니다.
(&key name)
은 항상 다른 키를 허용한다는 점을 제외하면 Common Lisp의 람다 목록과 거의 동일합니다.
(defroute "/hello/:name" (&rest 매개변수 &키 이름) ;; ... )
경로 패턴에는 "와일드카드" 매개변수가 포함될 수도 있습니다. splat
사용하여 액세스할 수 있습니다.
(defroute "/say/*/to/*" (&key splat) ; /say/hello/to/world와 일치 (형식 없음 "~A" 표시));=> (hello world)(defroute "/download/*.*" (&key 표시) ; /download/path/to/file.xml과 일치 (nil "~A" 표시 형식)) ;=> (경로/대상/파일 xml)
URL 규칙에 정규 표현식을 사용하려면 :regexp t
작동해야 합니다.
(defroute ("/hello/([w]+)" :regexp t) (&키 캡처) (형식 없음 "Hello, ~A!"(첫 번째 캡처)))
일반적으로 경로는 정의된 순서대로 일치 여부를 테스트하고 일치하는 첫 번째 경로만 호출되며 다음 경로는 무시됩니다. 그러나 경로는 next-route
포함하여 목록에서 일치하는 항목을 계속 테스트할 수 있습니다.
("/guess/:who" 삭제(&키 누구) (if (string= who "Eitaro") "You got me!" (다음 경로))) ("/guess/*" () "놓쳤습니다!")
defroute
의 결과로 다음 형식을 반환할 수 있습니다.
끈
경로명
Clack의 응답 목록(상태, 헤더 및 본문 포함)
(redirect "url")
사용하여 다른 경로로 리디렉션합니다. 두 번째 선택적 인수는 기본적으로 상태 코드 302입니다.
이름으로 경로를 정의한 경우 (url-for route-name &rest params)
사용하여 이름에서 URL을 찾을 수 있습니다.
경로를 찾을 수 없으면 함수에서 오류가 발생합니다.
참조:
add-query-parameters base-url params
대괄호("[" & "]")가 포함된 매개변수 키는 구조화된 매개변수로 구문 분석됩니다. 라우터에서 _parsed
로 구문 분석된 매개변수에 액세스할 수 있습니다.
<양식 작업="/편집"> <input type="name" name="사람[이름]" /> <input type="name" name="사람[email]" /> <input type="이름" name="사람[출생][연도 ]" /> <input type="name" name="person[birth][month]" /> <input type="name" name="person[birth][day]" /></form>
("/edit" 삭제(&key _parsed) (format nil "~S" (cdr (assoc "person" _parsed :test #'string=))));=> "(("name" . "Eitaro") ("email" . "e.arrows@gmail .com") ("생년월일" . (("연도" . 2000) ("월" . 1) ("일" . 1))))";; assoc-utils(ql:quickload :assoc-utils) 사용 ('assoc-utils:aget 가져오기) ("/edit" 삭제(&key _parsed) (형식 nil "~S"(aget _parsed "person")))
빈 키는 여러 값이 있음을 의미합니다.
<양식 작업="/추가"> <input type="text" name="items[][name]" /> <input type="text" name="items[][price]" /> <input type="text" name="items[ ][name]" /> <input type="text" name="items[][price]" /> <input type="submit" value="추가" /></form>
("/add" 제거(&key _parsed) (format nil "~S" (assoc "items" _parsed :test #'string=)));=> "((("name" . "WiiU") ("price" . "30000")) ((" 이름" . "PS4") ("가격" . "69000")))"
Caveman은 Djula를 기본 템플릿 엔진으로 사용합니다.
{%는 "layouts/default.html"을 확장합니다. %} {% 블록 제목 %}사용자 | MyApp{% 엔드블록 %} {% 블록 콘텐츠 %}<div id="main"> <ul> {% 사용자 중 사용자 %}<li><a href="{{ user.url }}">{{ user.name }}</a></li> {% endfor %} </ul></div>{% endblock %}
('myapp.view:render 가져오기) (render #P"users.html"'(:users ((:url "/id/1" :name "nitro_idiot") (:url "/id/2" :name "meymao")) :has-next-page T))
데이터베이스에서 무언가를 가져오거나 Djula를 사용하여 함수를 실행하려면 코드가 실행되도록 렌더링할 인수를 전달할 때 명시적으로 list
호출해야 합니다.
('myapp.view:render 가져오기) (렌더링 #P"users.html"(목록 :users (get-users-from-db)))
이는 JSON API의 예입니다.
("/user.json" 제거(&키 |id|) (let ((사람 (find-person-from-db |id|)));; 사람 => (:|name| "후카마치 에이타로" :|email| "[email protected]")(render- json 사람)));=> {"name":"Eitaro Fukamachi","email":"[email protected]"}
render-json
뼈대 프로젝트의 일부입니다. "src/view.lisp"에서 해당 코드를 찾을 수 있습니다.
"static/" 디렉토리의 이미지, CSS, JS, favicon.ico 및 robots.txt가 기본적으로 제공됩니다.
/images/logo.png => {PROJECT_ROOT}/static/images/logo.png /css/main.css => {PROJECT_ROOT}/static/css/main.css /js/app/index.js => {PROJECT_ROOT}/static/js/app/index.js /robot.txt => {PROJECT_ROOT}/static/robot.txt /favicon.ico => {PROJECT_ROOT}/static/favicon.ico
"PROJECT_ROOT/app.lisp"를 다시 작성하여 이러한 규칙을 변경할 수 있습니다. 자세한 내용은 Clack.Middleware.Static을 참조하세요.
Caveman은 Envy를 구성 전환기로 채택합니다. 이를 통해 여러 구성을 정의하고 환경 변수에 따라 구성 간에 전환할 수 있습니다.
이것은 전형적인 예입니다:
(def패키지:myapp.config (:사용:cl:부러워)) (패키지 내 :myapp.config) (setf (config-env-var) "APP_ENV") (defconfig :공통 `(:application-root ,(asdf:구성 요소 경로 이름 (asdf:find-system :myapp)))) (defconfig |development| `(:debug T:databases((:maindb :sqlite3 :database-name ,(merge-pathnames #P"test.db"*application-root*))))) (defconfig |production| '(:databases((:maindb :mysql :database-name "myapp" :username "whoami" :password "1234") (:workerdb :mysql :데이터베이스 이름 "jobs" :사용자 이름 "whoami" :password "1234")))) (defconfig |staging| `(:debug T,@|production|))
모든 구성은 속성 목록입니다. APP_ENV
설정하여 사용할 구성을 선택할 수 있습니다.
현재 구성에서 값을 얻으려면 원하는 키를 사용하여 myapp.config:config
호출하세요.
('myapp.config:config 가져오기) (setf(osicat:environment-variable "APP_ENV") "개발") (config :debug);=> T
:databases
구성에 추가하면 Caveman은 데이터베이스 지원을 활성화합니다. :databases
데이터베이스 설정의 연관 목록입니다.
(defconfig |production| '(:databases((:maindb :mysql :database-name "myapp" :username "whoami" :password "1234") (:workerdb :mysql :데이터베이스 이름 "jobs" :사용자 이름 "whoami" :password "1234"))))
myapp.db
패키지에 포함된 db
는 위와 같이 구성된 각 데이터베이스에 연결하는 기능이다. 여기에 예가 있습니다.
(사용 패키지 '(:myapp.db :sxql :datafly)) (검색-성인을 거부함 () (연결 있음(db) (모두 검색 (선택:*(출처:사람) (여기서 (:>= :age 20))))))
연결은 Lisp 세션 중에 활성 상태이며 모든 HTTP 요청에서 재사용됩니다.
retrieve-all
검색 및 쿼리 언어는 datafly 및 SxQL에서 나왔습니다. 자세한 내용은 해당 문서 세트를 참조하세요.
HTTP 요청 중에 사용할 수 있는 몇 가지 특수 변수가 있습니다. *request*
과 *response*
요청과 응답을 나타냅니다. Clack에 익숙하다면 이는 Clack.Request 및 Clack.Response의 하위 클래스 인스턴스입니다.
(사용 패키지 : caveman2);; Referer 헤더 값을 가져옵니다.(http-referer *request*);; Content-Type 헤더를 설정합니다.(setf (getf (response-headers *response*) :content-type) "application/json");; HTTP 상태를 설정합니다.(setf (status *response*) 304)
모든 "*.json" 요청에 대해 Content-Type "application/json"을 설정하려면 next-route
사용할 수 있습니다.
("/*.json" 제거() (setf(getf(응답 헤더 *응답*) :콘텐츠 유형) "application/json") (다음 경로)) ("/user.json" 제거() ...) ("/search.json" 제거() ...) (defroute ("/new.json" :method :POST) () ...)
세션 데이터는 사용자별 데이터를 기억하기 위한 것입니다. *session*
세션 데이터를 저장하는 해시 테이블입니다.
이 예에서는 세션에서 :counter
증가시키고 각 방문자에 대해 이를 표시합니다.
("/counter" 삭제 () (형식 없음 "여기에 ~A번 왔습니다." (incf (gethash :counter *session* 0))))
Caveman2는 기본적으로 세션 데이터를 메모리에 저장합니다. 이를 변경하려면 "PROJECT_ROOT/app.lisp"에서 :store
:session
으로 지정하십시오.
이 예에서는 RDBMS를 사용하여 세션 데이터를 저장합니다.
'(:역추적 :출력(getf(config) :오류-로그)) nil)- :session+ (:session+ :store (make-dbi-store :connector (lambda ()+ (적용 #'dbi:connect+ (myapp.db:connection-settings))))) (if (생산p) 무 (람다(앱)
참고: 앱의 :depends-on
:lack-session-store-dbi
추가하는 것을 잊지 마세요. 그것은 Clack/Lack의 일부가 아닙니다.
자세한 내용은 Lack.Session.Store.DBi의 소스 코드를 참조하세요.
부족.세션.스토어.Dbi
('caveman2:throw-code 가져오기) (제거("/auth" :방법 :POST) (&키 |이름| |비밀번호|) ((|이름| |비밀번호|를 승인하지 않은 경우) (throw-code 403)))
404, 500 등에 대한 오류 페이지를 지정하려면 앱의 on-exception
메서드를 정의하세요.
(defmethod on-Exception ((app <web>) (코드 (eql 404))) (선언(앱 코드 무시)) (병합 경로 이름 #P"_errors/404.html" *템플릿 디렉터리*))
Caveman에는 핫 배포 기능이 없지만 Perl 모듈인 Server::Starter를 사용하면 쉽게 사용할 수 있습니다.
$ APP_ENV=production start_server --port 8080 -- clackup --server :fcgi app.lisp
참고: Server::Starter에서는 서버가 특정 fd에 대한 바인딩을 지원해야 합니다. 즉 :fcgi
및 :woo
만 start_server
명령과 함께 작동한다는 의미입니다.
서버를 다시 시작하려면 start_server
프로세스에 HUP 신호( kill -HUP <pid>
)를 보냅니다.
Caveman은 구성의 :error-log
에 지정된 파일에 오류 역추적을 출력합니다.
(defconfig |default| `(:error-log #P"/var/log/apps/myapp_error.log":databases((:maindb :sqlite3 :database-name ,(merge-pathnames #P"myapp.db"* 응용 프로그램 루트*)))))
('cl-who:with-html-output-to-string 가져오기) ("/" 삭제 () (with-html-output-to-string (출력 nil :prologue t) (:html (:head (:title "원시인에 오신 것을 환영합니다!")) (:body "어쩌고 저쩌고."))));=> "<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/ xhtml1/DTD/xhtml1-strict.dtd">; <html><head><title>Caveman에 오신 것을 환영합니다!</title></head><body> 어쩌구 저쩌고. 어쩌구.</body></html>"
CL-WHO 웹사이트
('cl-markup:xhtml 가져오기) ("/" 삭제 () (xhtml (:head (:title "원시인에 오신 것을 환영합니다!")) (:body "어쩌고 저쩌고.")));=> "<?xml version="1.0" 인코딩="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional/ /EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html><head><title>에 오신 것을 환영합니다 원시인!</title></head><body>어쩌고 저쩌고.</body></html>"
CL-마크업 저장소
{네임스페이스 myapp.view} {템플릿 renderIndex}<!DOCTYPE html><html><head> <title>"Caveman에 오신 것을 환영합니다!</title></head><body> 어쩌고저쩌고.</body></html>{/template}
('myapp.config:*템플릿 디렉터리* 가져오기) (클로저 템플릿:컴파일-cl-템플릿(병합 경로 이름 #P"index.tmpl"*템플릿 디렉터리*)) ("/" 삭제 () (myapp.view:렌더-인덱스))
cl-클로저-템플릿
클로저 템플릿 문서
Clack - 웹 애플리케이션 환경.
부족(Lack) - 클랙(Clack)의 핵심.
ningle - Caveman이 기반으로 하는 슈퍼 마이크로 웹 애플리케이션 프레임워크입니다.
Djula - HTML 템플릿 엔진.
CL-DBI - 데이터베이스 독립적인 인터페이스 라이브러리입니다.
SxQL - SQL 빌더 라이브러리.
Envy - 구성 전환기.
Roswell - Common Lisp 구현 관리자.
후카마치 에이타로([email protected])
LLGPL 라이선스에 따라 라이선스가 부여됩니다.