Browse Source

a lot of comments added; minor refactoring

master
Christian Mueller 14 years ago
parent
commit
3308c64c5a
  1. 10
      src/NoteHub/settings.clj
  2. 2
      src/NoteHub/storage.clj
  3. 35
      src/NoteHub/views/common.clj
  4. 30
      src/NoteHub/views/pages.clj
  5. 4
      test/NoteHub/test/storage.clj
  6. 4
      test/NoteHub/test/views/pages.clj

10
src/NoteHub/settings.clj

@ -3,9 +3,8 @@
(:refer-clojure :exclude [replace reverse]) (:refer-clojure :exclude [replace reverse])
(:use [clojure.string])) (:use [clojure.string]))
; Loads and parses the settings file; returns a key-value map. ; Loads and parses any file with each line consisting a key and
; Assumes, that all string of the setings file are in format: ; a value separated by a "=", and returns a corresponding key-value map.
; key = value
(defn- get-pairs-map [file] (defn- get-pairs-map [file]
(let [file-content (slurp file) (let [file-content (slurp file)
pairs (map #(map trim (split % #"=" 2)) pairs (map #(map trim (split % #"=" 2))
@ -13,9 +12,11 @@
(apply hash-map (apply hash-map
(mapcat #(list (keyword (first %)) (second %)) pairs)))) (mapcat #(list (keyword (first %)) (second %)) pairs))))
; Loads the setting file to a map
(def settings-map (def settings-map
(get-pairs-map "settings")) (get-pairs-map "settings"))
; Loads the messages file to a map
(def messages-map (def messages-map
(get-pairs-map "messages")) (get-pairs-map "messages"))
@ -27,7 +28,8 @@
"Takes a settings key, a converter function and a default value, and returns a corresponding "Takes a settings key, a converter function and a default value, and returns a corresponding
setting value. The default value is returned back when no setting value was found. setting value. The default value is returned back when no setting value was found.
The converter function can be provided to convert the setting from string to a needed type. The converter function can be provided to convert the setting from string to a needed type.
Every key should be a keyword, e.g. (get-setting :page-width)." This function is not applied to the specified default value!
Every specified key should be a keyword, e.g. (get-setting :page-width)."
[key & more] [key & more]
(let [converter (first more) (let [converter (first more)
default (second more) default (second more)

2
src/NoteHub/storage.clj

@ -32,7 +32,7 @@
(redis/hincrby db views key 1) (redis/hincrby db views key 1)
text)))) text))))
(defn get-views (defn get-note-views
"Returns the number of views for the specified date and note title" "Returns the number of views for the specified date and note title"
[date title] [date title]
(redis/hget db views (build-key date title))) (redis/hget db views (build-key date title)))

35
src/NoteHub/views/common.clj

@ -5,9 +5,15 @@
[noir.core :only [defpartial]] [noir.core :only [defpartial]]
[noir.options :only [dev-mode?]] [noir.options :only [dev-mode?]]
[hiccup.util :only [escape-html]] [hiccup.util :only [escape-html]]
[hiccup.core]
[hiccup.page :only [include-js html5]] [hiccup.page :only [include-js html5]]
[hiccup.element :only [javascript-tag]])) [hiccup.element :only [javascript-tag]]))
(defn url
"Creates a local url from the given substrings"
[& args]
(apply str (interpose "/" (cons "" args))))
; Creates the main html layout ; Creates the main html layout
(defpartial generate-layout (defpartial generate-layout
[params title & content] [params title & content]
@ -16,28 +22,37 @@
(html5 (html5
[:head [:head
[:title (print-str (get-message :name) "—" title)] [:title (print-str (get-message :name) "—" title)]
; generating a link to google's webfonts
[:link {:href [:link {:href
(clojure.string/replace (clojure.string/replace
(str "http://fonts.googleapis.com/css?family=" (str "http://fonts.googleapis.com/css?family="
(apply str (apply
(interpose "|" (concat ["PT+Serif:700" "Noticia+Text:700"] str
(vals (select-keys params (interpose "|"
[:header-font :text-font]))))) ; ugly thing, but it cannot be avoided since these
; fonts have to be loaded (independently of CSS)
(concat ["PT+Serif:700" "Noticia+Text:700"]
(vals (select-keys params
[:header-font :text-font])))))
"&subset=latin,cyrillic") " " "+") "&subset=latin,cyrillic") " " "+")
:rel "stylesheet" :rel "stylesheet"
:type "text/css"}] :type "text/css"}]
; generating the global CSS
[:style {:type "text/css"} (global-css params)] [:style {:type "text/css"} (global-css params)]
; google analytics code should appear in prod mode only
(if-not dev-mode? (include-js "/js/google-analytics.js"))] (if-not dev-mode? (include-js "/js/google-analytics.js"))]
(if (params :js) [:body content
[:body content ; we only need JS during a new note creation, so don't render it otherwise
(javascript-tag "var CLOSURE_NO_DEPS = true;") (when (params :js)
(include-js "https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js") (html
(include-js "/cljs/main.js")] (javascript-tag "var CLOSURE_NO_DEPS = true;")
[:body content])))) (include-js "https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js")
(include-js "/cljs/main.js")))])))
(defn layout (defn layout
"Generates the main html layout" "Generates the main html layout"
[& args] [& args]
; if some parameter weren't added we provide an empty map
(if (map? (first args)) (if (map? (first args))
(apply generate-layout args) (apply generate-layout args)
(apply generate-layout {} args))) (apply generate-layout {} args)))

30
src/NoteHub/views/pages.clj

@ -24,13 +24,14 @@
(defn md-to-html [md-text] (defn md-to-html [md-text]
(.markdownToHtml (PegDownProcessor.) md-text)) (.markdownToHtml (PegDownProcessor.) md-text))
; Creates a random session number ; Creates a random session token
(defn- get-flash-key [] (defn- get-flash-key []
(let [k (encrypt (str (rand-int Integer/MAX_VALUE)))] (let [k (encrypt (str (rand-int Integer/MAX_VALUE)))]
(do (flash-put! k true) (do (flash-put! k true)
(print-str k)))) (print-str k))))
; Sets a custom message for each corresponding HTTP status ; Sets a custom message for each needed HTTP status.
; The message to be assigned is extracted with a dynamically generated key
(doseq [code [400 404 500]] (doseq [code [400 404 500]]
(set-page! code (set-page! code
(let [message (get-message (keyword (str "status-" code)))] (let [message (get-message (keyword (str "status-" code)))]
@ -41,7 +42,7 @@
(defn- response [code] (defn- response [code]
(status code (get-page code))) (status code (get-page code)))
; Converts given markdwon to html and wraps with layout ; Converts given markdown to html and wraps with the main layout
(defn- wrap [params md-text] (defn- wrap [params md-text]
(if md-text (if md-text
(let [title (-?> md-text (split #"\n") first (sreplace #"[_\*#]" ""))] (let [title (-?> md-text (split #"\n") first (sreplace #"[_\*#]" ""))]
@ -51,7 +52,7 @@
; Routes ; Routes
; ====== ; ======
; This function answers to a AJAX request: it gets a sesion key and markdown text. ; This function answers to an AJAX request: it gets a sesion key and a markdown text.
; It returns the html code of the provided markdown and a new session key. ; It returns the html code of the provided markdown and a new session key.
(defremote get-preview-md [session-key md] (defremote get-preview-md [session-key md]
(when (flash-get session-key) (when (flash-get session-key)
@ -69,6 +70,7 @@
[:div.dashed-line] [:div.dashed-line]
[:table.central-element.helvetica-neue [:table.central-element.helvetica-neue
[:tr [:tr
; dynamically generates three column, retrieving corresponding messages
(for [e [:column-why :column-how :column-geeks]] (for [e [:column-why :column-how :column-geeks]]
(html (html
[:td.one-third-column [:td.one-third-column
@ -92,7 +94,7 @@
[:div#preview-start-line.dashed-line.hidden] [:div#preview-start-line.dashed-line.hidden]
[:article#preview])) [:article#preview]))
; Display the note ; Displays the note
(defpage "/:year/:month/:day/:title" {:keys [year month day title theme header-font text-font] :as params} (defpage "/:year/:month/:day/:title" {:keys [year month day title theme header-font text-font] :as params}
(wrap (wrap
(select-keys params [:theme :header-font :text-font]) (select-keys params [:theme :header-font :text-font])
@ -105,7 +107,7 @@
; Provides the number of views of the specified note ; Provides the number of views of the specified note
(defpage "/:year/:month/:day/:title/stat" {:keys [year month day title]} (defpage "/:year/:month/:day/:title/stat" {:keys [year month day title]}
(let [views (get-views [year month day] title)] (let [views (get-note-views [year month day] title)]
(if views (if views
(layout (get-message :statistics) (layout (get-message :statistics)
[:table.helvetica-neue.central-element [:table.helvetica-neue.central-element
@ -117,16 +119,21 @@
[:td views]]]) [:td views]]])
(response 404)))) (response 404))))
; New Note Posting ; New Note Posting — the most "complex" function in the entire app ;)
(defpage [:post "/post-note"] {:keys [draft session-key session-value]} (defpage [:post "/post-note"] {:keys [draft session-key session-value]}
(let [valid-session (flash-get session-key) ; it was posted from a newly generated form ; first we collect all info needed to evaluate the validity of the note creation request
valid-draft (not (ccs/blank? draft)) ; the note has a meaningful content (let [valid-session (flash-get session-key) ; was the note posted from a newly generated form?
valid-draft (not (ccs/blank? draft)) ; has the note a meaningful content?
; is the hash code correct?
valid-hash (try valid-hash (try
(= (Short/parseShort session-value) ; the hash code is correct (= (Short/parseShort session-value)
(lib/hash #(.codePointAt % 0) (str draft session-key))) (lib/hash #(.codePointAt % 0) (str draft session-key)))
(catch Exception e nil))] (catch Exception e nil))]
; check whether the new note can be added ; check whether the new note can be added
(if (and valid-session valid-draft valid-hash) (if (and valid-session valid-draft valid-hash)
; if yes, we compute the current date, extract a title string from the text,
; which will be a part of the url and look whether this title is free today;
; if not, append "-n", where "n" is the next free number
(let [[year month day] (map #(+ (second %) (.get (Calendar/getInstance) (first %))) (let [[year month day] (map #(+ (second %) (.get (Calendar/getInstance) (first %)))
{Calendar/YEAR 0, Calendar/MONTH 1, Calendar/DAY_OF_MONTH 0}) {Calendar/YEAR 0, Calendar/MONTH 1, Calendar/DAY_OF_MONTH 0})
; This is the _only_ point where user's content enters the web app, so we escape the content. ; This is the _only_ point where user's content enters the web app, so we escape the content.
@ -136,6 +143,7 @@
trim (fn [s] (apply str (drop-while #(= \- %) s))) trim (fn [s] (apply str (drop-while #(= \- %) s)))
title-uncut (-> untrimmed-line trim reverse trim reverse) title-uncut (-> untrimmed-line trim reverse trim reverse)
max-length (get-setting :max-title-length #(Integer/parseInt %) 80) max-length (get-setting :max-title-length #(Integer/parseInt %) 80)
; TODO: replace to ccs/take when it gets fixed
proposed-title (apply str (take max-length title-uncut)) proposed-title (apply str (take max-length title-uncut))
date [year month day] date [year month day]
title (first (drop-while #(note-exists? date %) title (first (drop-while #(note-exists? date %)
@ -144,5 +152,5 @@
(do (do
(set-note date title draft) (set-note date title draft)
; TODO: the redirect is broken if title contains UTF chars ; TODO: the redirect is broken if title contains UTF chars
(redirect (apply str (interpose "/" ["" year month day title]))))) (redirect (url year month day title))))
(response 400)))) (response 400))))

4
test/NoteHub/test/storage.clj

@ -11,10 +11,10 @@
(set-note date test-title test-note) (set-note date test-title test-note)
(get-note date test-title)) (get-note date test-title))
test-note)) test-note))
(is (= "1" (get-views date test-title))) (is (= "1" (get-note-views date test-title)))
(is (= (do (is (= (do
(get-note date test-title) (get-note date test-title)
(get-views date test-title)) (get-note-views date test-title))
"2"))) "2")))
(testing "of the note access" (testing "of the note access"
(is (not= (get-note date test-title) "any text"))) (is (not= (get-note date test-title) "any text")))

4
test/NoteHub/test/views/pages.clj

@ -1,6 +1,7 @@
(ns NoteHub.test.views.pages (ns NoteHub.test.views.pages
(:use [NoteHub.views.pages] (:use [NoteHub.views.pages]
[noir.util.test] [noir.util.test]
[NoteHub.views.common :only [url]]
[NoteHub.storage] [NoteHub.storage]
[clojure.test])) [clojure.test]))
@ -15,9 +16,6 @@
(use-fixtures :each create-testnote-fixture) (use-fixtures :each create-testnote-fixture)
(defn url [& args]
(apply str (interpose "/" (cons "" args))))
(is (= (url 2010 05 06 "test-title" "export") "/2010/5/6/test-title/export")) (is (= (url 2010 05 06 "test-title" "export") "/2010/5/6/test-title/export"))
(deftest testing-fixture (deftest testing-fixture

Loading…
Cancel
Save