From 3308c64c5a8f98592a8c3f7001d656eb1de8aaeb Mon Sep 17 00:00:00 2001 From: Christian Mueller Date: Mon, 11 Jun 2012 00:11:59 +0200 Subject: [PATCH] a lot of comments added; minor refactoring --- src/NoteHub/settings.clj | 10 +++++---- src/NoteHub/storage.clj | 2 +- src/NoteHub/views/common.clj | 35 ++++++++++++++++++++++--------- src/NoteHub/views/pages.clj | 30 ++++++++++++++++---------- test/NoteHub/test/storage.clj | 4 ++-- test/NoteHub/test/views/pages.clj | 4 +--- 6 files changed, 54 insertions(+), 31 deletions(-) diff --git a/src/NoteHub/settings.clj b/src/NoteHub/settings.clj index c5dc519..b54dbf6 100644 --- a/src/NoteHub/settings.clj +++ b/src/NoteHub/settings.clj @@ -3,9 +3,8 @@ (:refer-clojure :exclude [replace reverse]) (:use [clojure.string])) -; Loads and parses the settings file; returns a key-value map. -; Assumes, that all string of the setings file are in format: -; key = value +; Loads and parses any file with each line consisting a key and +; a value separated by a "=", and returns a corresponding key-value map. (defn- get-pairs-map [file] (let [file-content (slurp file) pairs (map #(map trim (split % #"=" 2)) @@ -13,9 +12,11 @@ (apply hash-map (mapcat #(list (keyword (first %)) (second %)) pairs)))) +; Loads the setting file to a map (def settings-map (get-pairs-map "settings")) +; Loads the messages file to a map (def messages-map (get-pairs-map "messages")) @@ -27,7 +28,8 @@ "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. 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] (let [converter (first more) default (second more) diff --git a/src/NoteHub/storage.clj b/src/NoteHub/storage.clj index 5573772..b81a416 100644 --- a/src/NoteHub/storage.clj +++ b/src/NoteHub/storage.clj @@ -32,7 +32,7 @@ (redis/hincrby db views key 1) text)))) -(defn get-views +(defn get-note-views "Returns the number of views for the specified date and note title" [date title] (redis/hget db views (build-key date title))) diff --git a/src/NoteHub/views/common.clj b/src/NoteHub/views/common.clj index 0f3e8d0..d89621a 100644 --- a/src/NoteHub/views/common.clj +++ b/src/NoteHub/views/common.clj @@ -5,9 +5,15 @@ [noir.core :only [defpartial]] [noir.options :only [dev-mode?]] [hiccup.util :only [escape-html]] + [hiccup.core] [hiccup.page :only [include-js html5]] [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 (defpartial generate-layout [params title & content] @@ -16,28 +22,37 @@ (html5 [:head [:title (print-str (get-message :name) "—" title)] + ; generating a link to google's webfonts [:link {:href (clojure.string/replace (str "http://fonts.googleapis.com/css?family=" - (apply str - (interpose "|" (concat ["PT+Serif:700" "Noticia+Text:700"] - (vals (select-keys params - [:header-font :text-font]))))) + (apply + str + (interpose "|" + ; 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") " " "+") :rel "stylesheet" :type "text/css"}] + ; generating the global CSS [: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 (params :js) - [:body content - (javascript-tag "var CLOSURE_NO_DEPS = true;") - (include-js "https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js") - (include-js "/cljs/main.js")] - [:body content])))) + [:body content + ; we only need JS during a new note creation, so don't render it otherwise + (when (params :js) + (html + (javascript-tag "var CLOSURE_NO_DEPS = true;") + (include-js "https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js") + (include-js "/cljs/main.js")))]))) (defn layout "Generates the main html layout" [& args] + ; if some parameter weren't added we provide an empty map (if (map? (first args)) (apply generate-layout args) (apply generate-layout {} args))) diff --git a/src/NoteHub/views/pages.clj b/src/NoteHub/views/pages.clj index 6d5f6e1..aaff6cc 100644 --- a/src/NoteHub/views/pages.clj +++ b/src/NoteHub/views/pages.clj @@ -24,13 +24,14 @@ (defn md-to-html [md-text] (.markdownToHtml (PegDownProcessor.) md-text)) -; Creates a random session number +; Creates a random session token (defn- get-flash-key [] (let [k (encrypt (str (rand-int Integer/MAX_VALUE)))] (do (flash-put! k true) (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]] (set-page! code (let [message (get-message (keyword (str "status-" code)))] @@ -41,7 +42,7 @@ (defn- response [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] (if md-text (let [title (-?> md-text (split #"\n") first (sreplace #"[_\*#]" ""))] @@ -51,7 +52,7 @@ ; 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. (defremote get-preview-md [session-key md] (when (flash-get session-key) @@ -69,6 +70,7 @@ [:div.dashed-line] [:table.central-element.helvetica-neue [:tr + ; dynamically generates three column, retrieving corresponding messages (for [e [:column-why :column-how :column-geeks]] (html [:td.one-third-column @@ -92,7 +94,7 @@ [:div#preview-start-line.dashed-line.hidden] [: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} (wrap (select-keys params [:theme :header-font :text-font]) @@ -105,7 +107,7 @@ ; Provides the number of views of the specified note (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 (layout (get-message :statistics) [:table.helvetica-neue.central-element @@ -117,16 +119,21 @@ [:td views]]]) (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]} - (let [valid-session (flash-get session-key) ; it was posted from a newly generated form - valid-draft (not (ccs/blank? draft)) ; the note has a meaningful content + ; first we collect all info needed to evaluate the validity of the note creation request + (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 - (= (Short/parseShort session-value) ; the hash code is correct + (= (Short/parseShort session-value) (lib/hash #(.codePointAt % 0) (str draft session-key))) (catch Exception e nil))] ; check whether the new note can be added (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 %))) {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. @@ -136,6 +143,7 @@ trim (fn [s] (apply str (drop-while #(= \- %) s))) title-uncut (-> untrimmed-line trim reverse trim reverse) 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)) date [year month day] title (first (drop-while #(note-exists? date %) @@ -144,5 +152,5 @@ (do (set-note date title draft) ; 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)))) diff --git a/test/NoteHub/test/storage.clj b/test/NoteHub/test/storage.clj index 9132fb1..ab71dfb 100644 --- a/test/NoteHub/test/storage.clj +++ b/test/NoteHub/test/storage.clj @@ -11,10 +11,10 @@ (set-note date test-title test-note) (get-note date test-title)) test-note)) - (is (= "1" (get-views date test-title))) + (is (= "1" (get-note-views date test-title))) (is (= (do (get-note date test-title) - (get-views date test-title)) + (get-note-views date test-title)) "2"))) (testing "of the note access" (is (not= (get-note date test-title) "any text"))) diff --git a/test/NoteHub/test/views/pages.clj b/test/NoteHub/test/views/pages.clj index ee7c5b0..3cb04bc 100644 --- a/test/NoteHub/test/views/pages.clj +++ b/test/NoteHub/test/views/pages.clj @@ -1,6 +1,7 @@ (ns NoteHub.test.views.pages (:use [NoteHub.views.pages] [noir.util.test] + [NoteHub.views.common :only [url]] [NoteHub.storage] [clojure.test])) @@ -15,9 +16,6 @@ (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")) (deftest testing-fixture