Luminus A Clojure web framework

16059 단어
WhatisLuminus?
Luminus는 경량급 유형의 마이크로 구조를 바탕으로 한다.그것의 목적은 강력하고 확장 가능하며 사용하기 쉬운 플랫폼을 제공하는 것이다.Luminus를 사용하면 다른 방해를 신경 쓰지 않고 응용 프로그램을 개발하는 데 중점을 둘 수 있다.
Whydevelop web applications with Clojure?
Rapiddevelopment-빠른 발전
REPL로 바로 해킹 및 내장형 개발 서버 시작 가능
생산성 - 생산성
JVM이 Clojure와 결합하면 생산성과 성능 사이에서 선택할 필요가 없음
상호 작용 - 상호 작용
다시 컴파일하거나 다시 시작할 필요 없이 변경 사항을 즉시 감지할 수 있습니다
유연성-유연성
당신에게 적합한 구성 요소를 선택하면 프로젝트의 구조상 완전한 통제권이 있습니다
Matureecosystem-성숙한 생태계
기존 Clojure 및 Java 라이브러리 지원
Powerfultools-강력한 도구
Leiningen으로 응용 프로그램을 구축하고 배치하는 것은 매우 쉬우며, Heroku를 포함한 일련의 배치 옵션을 누릴 수 있다
Yourfirstapplication

1.Guestbook Application


이 강좌는 간단한 게시판 프로그램을 만들기 위해 Luminus 동태를 사용하도록 안내합니다.이 메시지는 사용자가 메시지를 남기고 다른 사람이 남긴 메시지를 볼 수 있도록 한다.응용 프로그램은 HTML 템플릿, 기본 데이터베이스 액세스 및 프로젝트의 구조를 표시합니다.
만약 당신이 이미 좋아하는 Clojure 편집기가 없다면, 이 강좌에 따라 LightTable을 사용하는 것을 권장합니다.

2.Installing Leiningen


Luminus 언어를 사용하려면 Leiningen 설치가 필요합니다.Leiningen 설치는 다음 단계를 포함한 간단한 절차입니다.
1. 스크립트 다운로드
2. 실행 가능한 것으로 설정 (예:chmod + x lein)
3. 그것을 당신의 $경로에 놓으세요.(예: ~/bin)
4. leinself-install을 실행하고 설치가 완료될 때까지 기다립니다.
wget https://raw.github.com/technomancy/leiningen/stable/bin/lein
chmod +x lein
mv lein ~/bin
lein self-install

3.Creating a new application


Leiningen이 설치되면 터미널에서 다음 명령을 실행하여 애플리케이션을 초기화할 수 있습니다.
leinnew luminus guestbook +h2
cdguestbook
H2 내장형 데이터베이스를 엔진으로 하는 새 템플릿 프로젝트가 생성됩니다.
이제 다음 절차에 따라 프로젝트를 실행할 수 있습니다.
>leinring server
guestbookstarted successfully...
2013-03-01 19:05:30.389:INFO:oejs.Server:jetty-7.6.1.v20120215
Startedserver on port 3000
2013-03-01 19:05:30.459:INFO:oejs.AbstractConnector:StartedSelectChannelConnect
19:05:30.459:INFO:oejs.AbstractConnector:[email protected]:3000
새 브라우저 창이 뜰 것입니다. 프로그램이 실행 중인 것을 볼 수 있을 것입니다.새 브라우저를 꺼내지 않으려면 다음을 실행할 수 있습니다.
leinring server-headless
다음과 같은 사용자 정의 포트를 사용할 수도 있습니다.
leinring server-headless 8000

4.Anatomy of a Luminus application


새로 만든 응용 프로그램은 다음과 같은 구조를 가지고 있다
Procfile
README.md
project.clj
src
  └ log4j.xml
    guestbook
       └ handler.clj
         layout.clj
         middleware.clj
         util.clj
         repl.clj
         db
           └ core.clj
             schema.clj
          routes
           └ home.clj
test
  └ guestbook
       └ test
           └ handler.clj
resources
  └ templates
       └ about.html
         base.html
         home.html
  └ public
       └ css
           └ screen.css     
         fonts
          └ glyphicons-halflings-regular.eot 
            glyphicons-halflings-regular.svg 
            glyphicons-halflings-regular.ttf
            glyphicons-halflings-regular.woff
         img
         js
         md
          └ docs.md

응용 프로그램의 루트 폴더에 있는 파일의 역할을 살펴보겠습니다.
procfile - Heroku 배포를 촉진하는 데 사용됩니다.
readme.md - 응용 프로그램 문서의 일반적인 보관소.
project.clj - 프로젝트의 구성 및 종속성을 Leiningen에서 관리합니다.

5.The Source Directory


모든 코드는 SRC 폴더 아래에 있습니다.우리의 응용 프로그램을 방명록이라고 부르기 때문에, 이것은 프로젝트의 루트 이름 공간입니다.우리를 위해 만들어진 모든 명명 공간을 살펴보자.
guestbook
handler.clj - 응용 프로그램의 기본 노선을 정의합니다. 이것은 프로그램의 입구점입니다.
layout.clj - 레이아웃 도움말이 있는 이름 공간입니다. 우리가 정의한 모든 웹 페이지는 그들의 경로를 여기에 추가해야 합니다.
middleware.clj - 프로그램 사용자 정의 중간부품을 포함하는 이름 공간
util.clj - 일반적인 액세스 기능에 사용되며 미리 제공되는 MD - HTML 도우미입니다.
repl.clj - REPL에서 응용 프로그램을 시작하고 중지하는 기능 제공
log4j.xml 레코드 구성
guestbook.db
db 이름 공간은 프로그램의 모델을 정의하고 데이터 영구층을 처리하는 데 사용됩니다.
core.clj - 함수 저장 및 데이터베이스 상호작용
schema.clj - 연결 매개 변수와 데이터베이스 테이블 정의에 사용
guestbook.routes
루트 이름 공간은 우리 홈페이지의 루트와 컨트롤러가 있는 위치입니다.authentication(인증)이나 specificworkflows (특정한 작업 절차) 와 같은routes를 더 추가하면, 그들을 위한 이름 공간을 만들어야 합니다.
home.clj - 홈과 프로그램 관련 페이지를 정의하는 이름 공간

6.The Test Directory


이것은 우리 프로그램을 설치하는 테스트에 사용되며, 이미 우리를 위해 두 개의 테스트 샘플을 정의했다.

7.The ResourcesDirectory


이것은 우리 프로그램의 모든 정적 자원을 배치하는 데 쓰인다.이미 폴더 CSS, JavaScript, images, markdown이 정의되어 있습니다.
HTML templates
템플릿 디렉토리는 응용 프로그램 페이지를 나타내는 Selmer 템플릿입니다.
about.html - 페이지
base.html-사이트를 위한 기본 레이아웃
html - 홈페이지

8.Adding Dependencies


위에서 언급한 바와 같이 모든 의존 관계 관리는 프로젝트 업데이트를 통해 이루어진다.clj 파일.루트 폴더에서 만든 응용 프로그램의 항목 파일을 찾을 수 있습니다. 다음과 같습니다.
 
(defproject
  guestbook "0.1.0-SNAPSHOT"

  :url "http://example.com/FIXME"
  :description "FIXME: write description"

  :dependencies
  [[com.h2database/h2 "1.4.178"]
   [ring-server "0.3.1"]
   [environ "0.5.0"]
   [com.taoensso/timbre "3.2.1"]
   [markdown-clj "0.9.44"]
   [korma "0.3.1"]
   [com.taoensso/tower "2.0.2"]
   [selmer "0.6.6"]
   [org.clojure/clojure "1.6.0"]
   [log4j
    "1.2.17"
    :exclusions
    [javax.mail/mail
     javax.jms/jms
     com.sun.jdmk/jmxtools
     com.sun.jmx/jmxri]]
   [compojure "1.1.8"]
   [lib-noir "0.8.3"]]

  :plugins
  [[lein-ring "0.8.7"] [lein-environ "0.5.0"]]

  :ring
  {:handler guestbook.handler/app,
   :init guestbook.handler/init,
   :destroy guestbook.handler/destroy}

  :profiles
  {:uberjar {:aot :all}
   :production
   {:ring
    {:open-browser? false, :stacktraces? false, :auto-reload? false}},
   :dev
   {:dependencies [[ring-mock "0.1.5"] [ring/ring-devel "1.2.2"]],
    :env {:dev true}}}

  :min-lein-version "2.0.0")

보시다시피 프로젝트입니다.clj는 키/value 설명이 포함된 다른 방면의 Clojure 목록일 뿐입니다.사용자 정의 의존 zhixu를 추가하려면 dependenciesvector (의존 벡터) 에 간단하게 추가합니다.

9.Accessingthe Database


우선, 우리는 응용 프로그램을 위한 모델을 만들 것입니다. 이렇게 하면 src/guestbook/db 폴더에 있는 schema를 열 것입니다.clj 파일.여기서 우리는 이미 우리의 데이터베이스 연결에 대해 정의를 내렸다.JDBC 드라이버 JDBCdriver, 프로토콜protocol, 사용자user, 비밀번호password, H2 데이터베이스를 사용하는 데이터베이스 파일을 포함하는 간단한 맵을 정의합니다.
(def db-spec {:classname "org.h2.Driver"
              :subprotocol "h2"
              :subname (str (io/resource-path) db-store)
              :user "sa"
              :password ""
              :naming {:keys clojure.string/upper-case
                       :fields clojure.string/upper-case}})

다음에create-users-table에user표 정의가 있는 함수가 있습니다.이 함수 대신create-guestbook-table 함수를 사용합니다.
(defn create-guestbook-table []
  (sql/db-do-commands
    db-spec
    (sql/create-table-ddl
      :guestbook
      [:id "INTEGER PRIMARY KEY AUTO_INCREMENT"]
      [:timestamp :timestamp]
      [:name "varchar(30)"]
      [:message "varchar(200)"]))
  (sql/db-do-prepared db-spec
      "CREATE INDEX timestamp_index ON guestbook (timestamp)"))

메모표는 주석자의 이름, 메일의 내용과 시간 스탬프 등 모든 필드 설명 정보를 저장합니다.
create-tables 함수를 업데이트하여 호출합니다.
(defn create-tables
  "creates the database tables used by the application"
  []
  (create-guestbook-table))

만든 표로 우리는 우리의 메시지에 읽고 쓰는 기능을 추가할 수 있다.src/guestbook/db/core를 엽니다.clj 파일을 추가하십시오.우리는 이미 사용자 테이블을 처리하는 코드가 있는 것을 보았다.우리는 아래의 코드로 그들을 대체할 것이다.
(ns guestbook.db.core
  (:use korma.core
        [korma.db :only (defdb)])
  (:require [guestbook.db.schema :as schema]))

(defdb db schema/db-spec)

(defentity guestbook)

(defn save-message
  [name message]
  (insert guestbook
          (values {:name name
                   :message message
                   :timestamp (new java.util.Date)})))

(defn get-messages []
  (select guestbook))

위에서 우리는 하나의 실체를 만들어서 우리가 명명 공간을 구축하는 데 메모표를 만드는 것을 대표한다.그리고 함수save-message와 get-messages를 추가하여 그것과 상호작용합니다.

10.Running Code on Startup


핸들handler 이름 공간에는 함수 init가 포함되어 있습니다.프로그램이 시작될 때, 이 함수는 한 번 호출될 것이다.데이터베이스가 초기화되었는지 확인하기 위해 코드를 추가합니다. 필요하면 초기화합니다.
우리는 우선 구조 명칭 공간을 참고해야 한다. initialized를 사용하기 위해서?그리고create-tables의 기능.
(ns guestbook.handler
  (:use ...)
  (:require ...
            [guestbook.db.schema :as schema]))

다음은 init 함수를 업데이트합니다
(defn init
  "init will be called once when
   app is deployed as a servlet on
   an app server such as Tomcat
   put any initialization code here"
  []
  (timbre/set-config!
    [:appenders :rotor]
    {:min-level :info
     :enabled? true
     :async? false ; should be always false for rotor
     :max-message-per-msecs nil
     :fn rotor/appender-fn})

  (timbre/set-config!
    [:shared-appender-config :rotor]
    {:path "guestbook.log" :max-size (* 512 1024) :backlog 10})

  (if (env :selmer-dev) (parser/cache-off!))
  
  ;;initialize the database if needed
  (if-not (schema/initialized?) (schema/create-tables))
  
  (timbre/info "guestbook started successfully"))

프로그램의 init 함수를 바꿨기 때문에 서비스 (ctrl+c) 를 끄고 서비스 (leinring server) 를 다시 시작합니다

11.Creating Pages and HandlingForm Input


우리의 루트는 모두guestbook에 정의되어 있습니다.routes.홈 네임스페이스에서.그것을 열면 데이터베이스에서 정보를 읽는 이동은 논리를 증가시켜야 한다.우선, Referenc가 우리의db 명칭 공간을 늘려야 합니다.
(ns guestbook.routes.home
  (:use ...)
  (:require ...
            [guestbook.db.core :as db]))

다음으로 홈 페이지 컨트롤러를 변경합니다.
(defn home-page [& [name message error]]
  (layout/render "home.html"
                 {:error    error
                  :name     name
                  :message  message
                  :messages (db/get-messages)}))

현재, 우리는 템플릿에 추가 파라미터를 추가할 수 있으며, 파라미터는 데이터베이스에서 나온 정보 목록이다.
우리는 사용자가 새로운 메시지를 발표할 수 있기를 희망하기 때문에handler에 controller theformposts를 추가해야 합니다:
(defn save-message [name message]
  (cond

    (empty? name)
    (home-page name message "Somebody forgot to leave a name")

    (empty? message)
    (home-page name message "Don't you have something to say?")

    :else
    (do
      (db/save-message name message)
      (home-page))))

마지막으로, 우리는 controller에 home-routes 정의에 루트를 추가합니다.
(defroutes home-routes
  (GET "/" [] (home-page))
  (POST "/" [name message] (save-message name message))
  (GET "/about" [] (about-page)))

현재, 우리의 컨트롤러는 이미 setup, 홈을 열었습니다.html 템플릿. 
{% extends "templates/base.html" %}
{% block content %}
 <div class="jumbotron">
    <h1>Welcome to guestbook</h1>
    <p>Time to start building your site!</p>
    <p><a class="btn btn-primary btn-lg" href="http://luminusweb.net">Learn more &raquo;</a></p>
 </div>

 <div class="row-fluid">
    <div class="span8">
    {{content|safe}}
    </div>
 </div>
{% endblock %}

메시지 목록을 반복적으로 표시할 수 있도록 내용을 업데이트합니다.
{% extends "templates/base.html" %}
{% block content %}
 <div class="jumbotron">
    <h1>Welcome to guestbook</h1> 
 </div>

 <div class="row-fluid">
    <div class="span8">    
      <ul>
      {% for item in messages %}
        <li>
          <time>{{item.timestamp|date:"yyyy-MM-dd HH:mm"}}</time> 
          <p>{{item.message}}</p>
          <p> - {{item.name}}</p>      
        </li>
      {% endfor %}
      </ul>
    </div>
 </div>
{% endblock %}

보시다시피 우리는iterator를 사용하여 메시지를 달립니다.모든 메시지는 하나의 맵입니다. 메시지,name,timestamp 키워드를 포함하여name를 통해 얻을 수 있습니다.또한useof the date filter to format the timestamps into a humanreadableform.
다음은 컨트롤러에 발생할 수 있는 오류를 보여주기 위해 error 모듈을 추가해야 합니다.
{% if error %}
<p class="error">{{error}}</p>
{% endif %}

error 모듈이 작동하는지 간단하게 테스트해 보세요.마지막으로, 사용자가 메시지를 제출할 수 있는form을 만듭니다.
<form action="/" method="POST">
    <p>
       Name: 
       <input type="text" name="name" value={{name}}>
    </p>
    <p>
       Message: 
       <textarea rows="4" cols="50" name="message">
           {{message}}
       </textarea>
    </p>
    <input type="submit" value="comment">
</form>

home.html template는 다음과 같습니다.
{% extends "templates/base.html" %}
{% block content %}
 <div class="jumbotron">
    <h1>Welcome to guestbook</h1> 
 </div>

 <div class="row-fluid">
    <div class="span8">    
      <ul>
      {% for item in messages %}
        <li>
          <time>{{item.timestamp|date:"yyyy-MM-dd HH:mm"}}</time> 
          <p>{{item.message}}</p>
          <p> - {{item.name}}</p>      
        </li>
      {% endfor %}
      </ul>
        
      {% if error %}
        <p class="error">{{error}}</p>
      {% endif %}
    </div>
    <div class="span8">
      <form action="/" method="POST">
        <p>
         Name: 
         <input type="text" name="name" value="{{name}}" />
        </p>
        <p>
         Message: 
         <textarea rows="4" cols="50" name="message" />{{message}}</textarea>
        </p>
        <input type="submit" value="comment" />
      </form>        
    </div>    
 </div>
{% endblock %}

마지막으로, 화면을 업데이트합니다.css 파일, 페이지의 아름다움:
body {
	height: 100%;
	padding-top: 70px;  
	font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif;
	line-height: 1.4em;
	background: #eaeaea;
	color: #4d4d4d;
	width: 550px;
	margin: 0 auto;
	-webkit-font-smoothing: antialiased;
	-moz-font-smoothing: antialiased;
	-ms-font-smoothing: antialiased;
	-o-font-smoothing: antialiased;
	font-smoothing: antialiased;
}

input[type=submit] {
	margin: 0;
	padding: 0;
	border: 0;
  line-height: 1.4em;
	background: none;	
	vertical-align: baseline;		
}

input[type=submit], textarea {
	font-size: 24px;
	font-family: inherit;	
	border: 0;
	padding: 6px;
	border: 1px solid #999;
	box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2);
	-moz-box-sizing: border-box;
	-ms-box-sizing: border-box;
	-o-box-sizing: border-box;
	box-sizing: border-box;	
}

input[type=submit]:hover {
	background: rgba(0, 0, 0, 0.15);
	box-shadow: 0 -1px 0 0 rgba(0, 0, 0, 0.3);
}

textarea {
	position: relative;
	line-height: 1em;
	width: 100%;	
}

.error {
  font-weight: bold;
	color: red;  
}

.jumbotron {
	position: relative;
	background: white;
	z-index: 2;
	border-top: 1px dotted #adadad;
}

h1 {
	width: 100%;
	font-size: 70px;
	font-weight: bold;
	text-align: center;		
}

ul {
	margin: 0;
	padding: 0;
	list-style: none;
}

li {
	position: relative;
	font-size: 16px;
	padding: 5px;
	border-bottom: 1px dotted #ccc;
  box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.2),
	            0 25px 50px 0 rgba(0, 0, 0, 0.15);
}

li:last-child {
	border-bottom: none;  
}

li time {
	font-size: 12px;  
	padding-bottom: 20px;			
}

form:before, .error:before {  
	content: '';
	position: absolute;
	top: 0;
	right: 0;
	left: 0;
	height: 15px;	 
	border-bottom: 1px solid #6c615c;
	background: #8d7d77;	
}

form, .error {
	width: 520px;
	padding: 30px;
	margin-bottom: 50px;
	background: #fff;	
	border: 1px solid #ccc;
	position: relative;
	box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.2),
	            0 25px 50px 0 rgba(0, 0, 0, 0.15);
}

form input {
	width: 50%;
	clear: both;
}

페이지를 다시 불러오면 새로운 발견이 있습니다.코멘트를 늘려서 효과가 있는지 확인해 보세요.

12.Packaging the application


포장:
lein ring uberjar
패키지 직접 실행:
java -jartarget/guestbook-0.1.0-SNAPSHOT-standalone.jar
배포할 경우 Apache Tomcat과 같이 다음을 실행합니다.
lein ring uberwar
원하는 곳에 배치할 수 있습니다.
 
 
 
 
 
 
 
 

좋은 웹페이지 즐겨찾기