CMR-2897 Refactors permission check parameter validations

--- access-control-app/src/cmr/access_control/api/routes.clj
+++ access-control-app/src/cmr/access_control/api/routes.clj
@@ -104,28 +104,44 @@

 ;; Misc route validations

+(defn system_object-concept_id-validation
+  "Validates system_object and concept_id parameter lookup permissions."
+  [{:keys [system_object concept_id]}]
+  (when (or (and (not (str/blank? system_object))
+                 (seq concept_id))
+            (and (str/blank? system_object) (empty? concept_id)))
+    ["One of parameters [concept_id] or [system_object] are required."]))
+
+(defn system_object-validation
+  "Validates that system_object parameter has a valid value, if present."
+  [{:keys [system_object]}]
+  (when system_object
+    (when-not (some #{system_object} acl-schema/system-object-targets)
+      [(str "Parameter [system_object] must be one of: " (pr-str acl-schema/system-object-targets))])))
+
+(defn concept_ids-validation
+  "Validates that all values in the multi-valued concept_id param are valid concept IDs"
+  [{:keys [concept_id]}]
+  (mapcat cc/concept-id-validation concept_id))
+
+(defn user_id-user_type-validation
+  "Validates that only one of user_id or user_type are specified."
+  [{:keys [user_id user_type]}]
+  (if-not (= 1 (count (remove str/blank? [user_id user_type])))
+    ["One of parameters [user_type] or [user_id] are required."]))
+
+(def get-permissions-validations
+  [system_object-concept_id-validation
+   user_id-user_type-validation
+   system_object-validation
+   concept_ids-validation])
+
 (defn- validate-get-permission-params
   "Throws service errors if any invalid params or values are found."
   [params]
   (validate-params params :system_object :concept_id :user_id :user_type)
-  (let [{:keys [system_object concept_id user_id user_type]} params
-        errors []
-        errors (if (or (and (not (str/blank? system_object))
-                            (seq concept_id))
-                       (and (str/blank? system_object) (empty? concept_id)))
-                 (conj errors "One of parameters [concept_id] or [system_object] are required.")
-                 errors)
-        errors (if system_object
-                 (if (some #{system_object} acl-schema/system-object-targets)
-                   errors
-                   (conj errors (str "Parameter [system_object] must be one of: " (pr-str acl-schema/system-object-targets))))
-                 errors)
-        errors (reduce #(concat %1 (cc/concept-id-validation %2)) errors concept_id)
-        errors (if-not (= 1 (count (remove str/blank? [user_id user_type])))
-                 (conj errors "One of parameters [user_type] or [user_id] are required.")
-                 errors)]
-    (when (seq errors)
-      (errors/throw-service-errors :bad-request errors))))
+  (when-let [errors (seq (mapcat #(% params) get-permissions-validations))]
+    (errors/throw-service-errors :bad-request errors)))

 ;;; Group Route Functions


CMR-2050: Add clarification to docstring on why we still index lowercase science keywords.

--- indexer-app/src/cmr/indexer/data/concepts/science_keyword.clj
+++ indexer-app/src/cmr/indexer/data/concepts/science_keyword.clj
@@ -27,7 +27,10 @@
 (defn science-keyword->elastic-doc
   "Converts a science keyword into the portion going in an elastic document. If there is a match
   with the science keywords in KMS we also index the UUID from KMS. We index all of the science
-  keyword fields in all caps since GCMD enforces all caps when adding keywords to KMS."
+  keyword fields in all caps since GCMD enforces all caps when adding keywords to KMS. Note that
+  this means there is no need to also index the keywords in all lowercase; however, we continue to
+  index in lowercase so that science keywords are not treated as a special case in parts of the
+  code that use lowercase mappings."
   [gcmd-keywords-map science-keyword]
   (let [science-keyword-upper-case (util/map-values #(when % (str/upper-case %)) science-keyword)
         {:keys [category topic term variable-level-1 variable-level-2 variable-level-3


Properly name ns to not clash under /test and /src and fix (refresh) for learning

--- test/rill/event_store/atom_store/event_test.clj
+++ test/rill/event_store/atom_store/event_test.clj
@@ -1,4 +1,4 @@
-(ns rill.event-store.atom-store.event
+(ns rill.event-store.atom-store.event-test
   (:require [rill.event-store.atom-store.event :as event]
             [rill.message :as message :refer [defevent]]
             [rill.uuid :refer [new-id]]


Added test cases for milliseconds and timezone.

--- test/cmr/system_int_test/search/granule_equator_crossing_date_search_test.clj
+++ test/cmr/system_int_test/search/granule_equator_crossing_date_search_test.clj
@@ -31,18 +31,23 @@
         g4 (make-gran "2011-02-02T12:00:10Z")
         g5 (make-gran "2011-02-02T12:01:00Z")
         g6 (make-gran "2011-02-03T12:10:00Z")
-        g7 (make-gran "2011-02-07T12:01:00Z")]
+        g7 (make-gran "2011-02-07T12:01:00Z")
+        g8 (make-gran "2011-02-07T12:01:00.100Z")
+        g9 (make-gran "2011-02-08T12:00:00-04:00")]
     (index/flush-elastic-index)
-    (are [date-range items]
-         (d/refs-match? items (search/find-refs :granule {:equator-crossing-date date-range}))
-         "2011-02-01T12:00:00Z,2011-02-02T12:00:01Z" [g1 g2 g3]
-         "2011-02-03T12:00:00Z," [g6 g7]
-         ",2011-02-02T12:00:10Z" [g1 g2 g3 g4])
-    ;; catalog-rest style
-    (are [start-date end-date items]
-         (d/refs-match? items (search/find-refs :granule {:equator-crossing-start-date start-date
-                                                          :equator-crossing-end-date end-date}))
-         "2011-02-01T12:00:00Z" "2011-02-02T12:00:01Z" [g1 g2 g3]
-         "2011-02-03T12:00:00Z" nil [g6 g7]
-         nil "2011-02-02T12:00:10Z" [g1 g2 g3 g4])))
+    (testing "equator-crossing-date"
+      (are [date-range items]
+           (d/refs-match? items (search/find-refs :granule {:equator-crossing-date date-range}))
+           "2011-02-01T12:00:00Z,2011-02-02T12:00:01Z" [g1 g2 g3]
+           "2011-02-03T12:00:00Z," [g6 g7 g8 g9]
+           ",2011-02-02T12:00:10Z" [g1 g2 g3 g4]
+           "2011-02-08T12:00:00-03:00," [g9]
+           "2011-02-07T12:01:00.090Z,2011-02-07T12:01:00.110Z" [g8]))
+    (testing "catalog-rest style"
+      (are [start-date end-date items]
+           (d/refs-match? items (search/find-refs :granule {:equator-crossing-start-date start-date
+                                                            :equator-crossing-end-date end-date}))
+           "2011-02-01T12:00:00Z" "2011-02-02T12:00:01Z" [g1 g2 g3]
+           "2011-02-03T12:00:00Z" nil [g6 g7 g8 g9]
+           nil "2011-02-02T12:00:10Z" [g1 g2 g3 g4]))))


Fix broken servlet tests

Fix broken servlet tests

--- ring-servlet/test/ring/util/test/servlet.clj
+++ ring-servlet/test/ring/util/test/servlet.clj
@@ -42,18 +42,21 @@
       (startAsync [] (async-context (request :completed))))))

 (defn- servlet-response [response]
-  (proxy [javax.servlet.http.HttpServletResponse] []
-    (getOutputStream []
-      (proxy [javax.servlet.ServletOutputStream] []
-        (write [body & _]
-          (swap! response assoc :body body))))
-    (setStatus [status]
-      (swap! response assoc :status status))
-    (setHeader [name value]
-      (swap! response assoc-in [:headers name] value))
-    (setCharacterEncoding [value])
-    (setContentType [value]
-      (swap! response assoc :content-type value))))
+  (let [output-stream (java.io.ByteArrayOutputStream.)]
+    (swap! response assoc :body output-stream)
+    (proxy [javax.servlet.http.HttpServletResponse] []
+      (getOutputStream []
+        (proxy [javax.servlet.ServletOutputStream] []
+          (write
+            ([b] (.write output-stream b))
+            ([b off len] (.write output-stream b off len)))))
+      (setStatus [status]
+        (swap! response assoc :status status))
+      (setHeader [name value]
+        (swap! response assoc-in [:headers name] value))
+      (setCharacterEncoding [value])
+      (setContentType [value]
+        (swap! response assoc :content-type value)))))

 (defn- servlet-config []
   (proxy [javax.servlet.ServletConfig] []
@@ -124,8 +127,7 @@
         (is (= (@response :status) 200))
         (is (= (@response :content-type) "text/plain"))
         (is (= (get-in @response [:headers "X-Server"]) "Bar"))
-        (is (= (take-while (complement zero?) (@response :body))
-               (seq (.getBytes "Hello World"))))))))
+        (is (= (.toString (@response :body)) "Hello World"))))))

 (deftest servlet-cps-test
   (let [handler  (fn [_ respond _]
@@ -147,8 +149,7 @@
     (is (= @(:completed request) true))
     (is (= (@response :status) 200))
     (is (= (@response :content-type) "text/plain"))
-    (is (= (take-while (complement zero?) (@response :body))
-           (seq (.getBytes "Hello World"))))))
+    (is (= (.toString (@response :body)) "Hello World"))))

 (defservice "foo-"
   (fn [_]
@@ -178,5 +179,4 @@
                  (servlet-response response))
     (is (= (@response :status) 200))
     (is (= (get-in @response [:headers "Content-Type" ]) "text/plain"))
-    (is (= (take-while (complement zero?) (@response :body))
-           (seq (.getBytes "Hello World"))))))
+    (is (= (.toString (@response :body)) "Hello World"))))


Revert "Target CharSequence instead of String"

Targeting CharSequence is like targeting Object just becuase you can
call .toString on it get at the string.  This made the code quite a bit
uglier and the using of StringBuffer and StringBuilder is so rare that I
don't see the point in shielding the user from calling .toString herself.

This reverts commit 60b1db28971bc8a3a81fff1ba223692166bb4c17.

Conflicts:
	src/superstring/core.clj

--- src/superstring/core.clj
+++ src/superstring/core.clj
@@ -1,641 +1,600 @@
-(ns superstring.core
-  (:require clojure.string)
-  (:refer-clojure :exclude [reverse replace contains?])
-  (:import java.text.Normalizer))
-
-(declare slice)
-
-;; Taken from [jackknife "0.1.6"]
-(defmacro ^:private defalias
-  "Defines an alias for a var: a new var with the same root binding (if
-  any) and similar metadata. The metadata of the alias is its initial
-  metadata (as provided by def) merged into the metadata of the original."
-  ([name orig]
-   `(do
-      (alter-meta!
-       (if (.hasRoot (var ~orig))
-         (def ~name (.getRawRoot (var ~orig)))
-         (def ~name))
-       ;; When copying metadata, disregard {:macro false}.
-       ;; Workaround for http://www.assembla.com/spaces/clojure/tickets/273
-       #(conj (dissoc % :macro :added)
-              (apply dissoc (meta (var ~orig)) (remove #{:macro} (keys %)))))
-      (var ~name)))
-  ([name orig doc]
-   (list `defalias (with-meta name (assoc (meta name) :doc doc)) orig)))
-
-(defmacro ^:private alias-ns
-  "Create an alias for all public vars in ns in this ns."
-  [namespace]
-  `(do ~@(map
-          (fn [n] `(defalias ~(.sym n) ~(symbol (str (.ns n)) (str (.sym n)))))
-          (vals (ns-publics namespace)))))
-
-(alias-ns clojure.string)
-
-(defn- ^String slice-relative-to-end
-  [^String s index length]
-  (if (neg? (+ (.length s) index)) ; slice outside beg of s
-    nil
-    (slice s (+ (.length s) index) length)))
-
-(defn ^String slice
-  "Return a slice of s beginning at index and of the given length.
-
-  If index is negative the starting index is relative to the end of the string.
-
-  The default length of the slice is 1.
-
-  If the requested slice ends outside the string boundaries, we return
-  the substring of s starting at index.
-
-  Returns nil if index falls outside the string boundaries or if
-  length is negative."
-  ([^CharSequence s ^long index]
-   (slice s index 1))
-  ([^CharSequence s ^long index ^long length]
-   (let [s (.toString s)]
-     (cond
-       (neg? length) nil
-       (neg? (+ (.length s) index)) nil ; slice relative to end falls outside s
-       (neg? index) (slice-relative-to-end s index length)
-       (>= index (.length s)) nil
-       (> (- length index) (.length (.substring s index))) (.substring s index)
-       :else (let [end (+ index length)]
-               (.substring s index end))))))
-
-(defn ^String ends-with?
-  "Return s if s ends with suffix."
-  ([^CharSequence s ^CharSequence suffix]
-   (let [s (.toString s)]
-     (when (.endsWith s suffix)
-       s)))
-  ([^CharSequence s ^CharSequence suffix ignore-case]
-   (if-not ignore-case
-     (ends-with? s suffix)
-     (let [s (.toString s)
-           suffix (.toString suffix)
-           end (.substring s (max 0 (- (.length s) (.length suffix))))]
-       (when (.equalsIgnoreCase end suffix)
-         s)))))
-
-(defn ^String starts-with?
-  "Return s if s starts with with prefix.
-
-  If a third argument is provided the string comparison is insensitive to case."
-  ([^CharSequence s ^CharSequence prefix]
-   (when (.startsWith (.toString s) prefix)
-     s))
-  ([^CharSequence s ^CharSequence prefix ignore-case]
-   (if-not ignore-case
-     (starts-with? s prefix)
-     (let [s (.toString s)
-           prefix (.toString prefix)
-           beg (.substring s 0 (.length prefix))]
-       (when (.equalsIgnoreCase beg prefix)
-         s)))))
-
-(defn ^String chop
-  "Return a new string with the last character removed.
-
-  If the string ends with \\r\\n, both characters are removed.
-
-  Applying chop to an empty string is a no-op."
-  [^CharSequence s]
-  (let [s (.toString s)]
-    (if (.endsWith s "\r\n")
-      (.substring s 0 (- (.length s) 2))
-      (.substring s 0 (max 0 (dec (.length s)))))))
-
-(defn ^String chomp
-  "Return a new string with the given record separator removed from
-  the end (if present).
-
-  If separator is not provided chomp will remove \\n, \\r or \\r\\n from
-  the end of s."
-  ([^CharSequence s]
-   (let [s (.toString s)]
-     (cond
-       (.endsWith s "\r\n") (.substring s 0 (- (.length s) 2))
-       (.endsWith s "\r") (.substring s 0 (dec (.length s)))
-       (.endsWith s "\n") (.substring s 0 (dec (.length s)))
-       :else s)))
-  ([^CharSequence s ^CharSequence separator]
-   (let [s (.toString s)]
-     (if (.endsWith s separator)
-       (.substring s 0 (- (.length s) (.length separator)))
-       s))))
-
-(defn ^String capitalize
-  "Return a new string where the first character is in upper case and
-  all others in lower case."
-  [^CharSequence s]
-  (let [s (.toString s)]
-    (case (.length s)
-      0 ""
-      1 (upper-case s)
-      (str (upper-case (.substring s 0 1)) (lower-case (.substring s 1))))) )
-
-(defn ^String swap-case
-  "Change lower case characters to upper case and vice versa."
-  [^CharSequence s]
-  (let [invert-case (fn [^Character c]
-                      (cond
-                        (Character/isLowerCase c) (Character/toUpperCase c)
-                        (Character/isUpperCase c) (Character/toLowerCase c)
-                        :else c))]
-    (->> s .toString (map invert-case) (apply str))))
-
-(defn- ^String gen-padding
-  "Generate the necessary padding to fill s upto width."
-  [^CharSequence s ^CharSequence padding ^long width]
-  (let [missing (- width (.length s))
-        full-lengths (Math/floor (/ missing (.length padding)))
-        remaining (if (zero? full-lengths) (- width (.length s))
-                      (rem missing (* full-lengths (.length padding))))
-        s (.toString s)
-        padding (.toString padding)]
-    (.concat ^String (apply str (repeat full-lengths padding))
-             (.substring padding 0 remaining))))
-
-
-(defn ^String pad-right
-  "Pad the end of s with padding, or spaces, until the length of s matches
-  width."
-  ([^CharSequence s ^long width]
-   (pad-right s width " "))
-  ([^CharSequence s ^long width ^CharSequence padding]
-   {:pre [(not-empty padding)
-          (not (nil? s))]
-    :post [(= (.length %) width)]}
-   (let [s (.toString s)]
-     (if (<= width (.length s))
-       s
-       (.concat s (gen-padding s padding width))))))
-
-(defn ^String pad-left
-  "Pad the beginning of s with padding, or spaces, until the length of
-  s matches width."
-  ([^CharSequence s ^long width]
-   (pad-left s width " "))
-  ([^CharSequence s ^long width ^CharSequence padding]
-   {:pre [(not-empty padding)
-          (not (nil? s))]
-    :post [(= (.length ^String %) width)]}
-   (let [s (.toString s)]
-     (if (<= width (.length s))
-       s
-       (.concat ^String (gen-padding s padding width) ^String s)))))
-
-(defn ^String center
-  "Pad both ends of s with padding, or spaces, until the length of s
-  matches width."
-  ([^CharSequence s ^long width]
-   (center s width " "))
-  ([^CharSequence s ^long width ^CharSequence padding]
-   {:pre [(not-empty padding)
-          (not (nil? s))]
-    :post [(= (.length %) width)]}
-   (let [s (.toString s)
-         padding (.toString padding)]
-     (if (<= width (.length s))
-       s
-       (let [missing (- width (.length s))
-             full-lengths (Math/ceil (/ missing (.length padding)))
-             ^String p (gen-padding s padding width)
-             lengths-before (Math/floor (/ full-lengths 2))]
-         (str (.substring p 0 (* (.length padding) lengths-before))
-              s
-              (.substring p (* (.length padding) lengths-before))))))))
-
-(defn ^String chop-suffix
-  "If s ends with suffix return a new string without the suffix.
-
-  Otherwise return s."
-  ([^CharSequence s ^CharSequence suffix]
-   (chop-suffix s suffix false))
-  ([^CharSequence s ^CharSequence suffix ignore-case]
-   {:pre [(not (nil? s))
-          (not (nil? suffix))]
-    :post [(not (nil? %))]}
-   (let [s (.toString s)
-         suffix (.toString suffix)]
-     (if (and (>= (.length s) (.length suffix))
-              (ends-with? s suffix ignore-case))
-       (.substring s 0 (- (.length s) (.length suffix)))
-       s))))
-
-(defn ^String chop-prefix
-  "If s starts with with prefix return a new string without the prefix.
-
-  Otherwise return s."
-  ([^CharSequence s ^CharSequence prefix]
-   (chop-prefix s prefix false))
-  ([^CharSequence s ^CharSequence prefix ignore-case]
-   {:pre [(not (nil? s))
-          (not (nil? prefix))]
-    :post [(not (nil? %))]}
-   (let [s (.toString s)
-         prefix (.toString prefix)]
-     (if (and (>= (.length s) (.length prefix))
-              (starts-with? s prefix ignore-case))
-       (.substring s (.length prefix))
-       s))))
-
-(defn- case-sensitive-contains
-  [^String s ^String needle]
-  (if (= needle "")
-    s
-    (when (and (seq s) (seq needle) (.contains s needle))
-      s)))
-
-(defn- case-insensitive-contains
-  [s needle]
-  (if (= needle "")
-    s
-    (when (and (seq s) (seq needle))
-      (let [p (java.util.regex.Pattern/compile
-               (java.util.regex.Pattern/quote needle)
-               (bit-or java.util.regex.Pattern/CASE_INSENSITIVE
-                       java.util.regex.Pattern/UNICODE_CASE))]
-        (when (re-find p s)
-          s)))))
-
-(defn ^CharSequence contains?
-  "Return s if s contains needle."
-  ([^CharSequence s ^CharSequence needle]
-   {:pre [(not (nil? s))
-          (not (nil? needle))]
-    :post [(or (nil? %) (string? %))]}
-   (case-sensitive-contains (.toString s) (.toString needle)))
-  ([^CharSequence s ^CharSequence needle ignore-case]
-   {:pre [(not (nil? s))
-          (not (nil? needle))]
-    :post [(or (nil? %) (string? %))]}
-   (if ignore-case
-     (case-insensitive-contains (.toString s) (.toString needle))
-     (case-sensitive-contains (.toString s) (.toString needle)))))
-
-(defn ^String contains-all?
-  "Return s if s contains all needles."
-  ([^CharSequence s needles]
-   (let [s (.toString s)
-         needles (map #(.toString ^String %) needles)]
-     (when (every? (partial case-sensitive-contains s) needles)
-       s)))
-  ([^CharSequence s needles ignore-case]
-   (let [s (.toString s)
-         needles (map #(.toString ^String %) needles)]
-     (if ignore-case
-       (when (every? (partial case-insensitive-contains s) needles)
-         s)
-       (contains-all? s needles)))))
-
-(defn ^String contains-any?
-  "Return s if s contains any of the needles."
-  ([^CharSequence s needles]
-   (let [s (.toString s)
-         needles (map #(.toString ^String %) needles)]
-     (some (partial case-sensitive-contains s) needles)))
-  ([^CharSequence s needles ignore-case]
-   (let [s (.toString s)
-         needles (map #(.toString ^String %) needles)]
-     (if ignore-case
-       (some (partial case-insensitive-contains s) needles)
-       (contains-any? s needles)))))
-
-(defn ^String truncate
-  "If s is longer than len-3, cut it down to len-3 and append '...'."
-  [^CharSequence s len]
-  {:pre [(not (nil? s)) (>= len 3)]}
-  (let [s (.toString s)]
-    (if (> (.length s) (max 3 (- len 3)))
-      (str (.substring s 0 (- len 3)) "...")
-      s)))
-
-(defn- char-equal-ignore-case
-  [^Character c1 ^Character c2]
-  (when (or (= c1 c2)
-            (= (Character/toUpperCase c1) c2)
-            (= (Character/toLowerCase c1) c2))
-    c1))
-
-(defn ^String common-prefix
-  "Return the longest common prefix of s1 and s2."
-  ([^CharSequence s1 ^CharSequence s2]
-   {:pre [(not (nil? s1)) (not (nil? s2))]
-    :post [(string? %)]}
-   (->> s1
-        .toString
-        (map #(when (= %1 %2) %1) (.toString s2))
-        (take-while (complement nil?))
-        (apply str)))
-  ([^CharSequence s1 ^CharSequence s2 ignore-case]
-   {:pre [(not (nil? s1)) (not (nil? s2))]
-    :post [(string? %)]}
-   (if-not ignore-case
-     (common-prefix s1 s2)
-     (->> s1
-          .toString
-          (map char-equal-ignore-case (.toString s2))
-          (take-while (complement nil?))
-          (apply str)))))
-
-(defn ^String common-suffix
-  "Return the longest common suffix of s1 and s2."
-  ([^CharSequence s1 ^CharSequence s2]
-   {:pre [(not (nil? s1)) (not (nil? s2))]
-    :post [(string? %)]}
-   (->> s1
-        reverse
-        (map #(when (= %1 %2) %1) (reverse s2))
-        (take-while (complement nil?))
-        (apply str)
-        reverse))
-  ([^CharSequence s1 ^CharSequence s2 ignore-case]
-   {:pre [(not (nil? s1)) (not (nil? s2))]
-    :post [(string? %)]}
-   (if-not ignore-case
-     (common-suffix s1 s2)
-     (->> s1
-          reverse
-          (map char-equal-ignore-case (reverse s2))
-          (take-while (complement nil?))
-          (apply str)
-          reverse))))
-
-(defn- upper-exists?
-  "Does c exist in an upper case version?"
-  [^Character c]
-  (or (Character/isUpperCase c) (not= c (Character/toUpperCase c))))
-
-(defn ^String title-case
-  "Capitalize the first character of s and lower case the rest."
-  [^CharSequence s]
-  {:pre [(not (nil? s))]
-   :post [(string? %)]}
-  (let [s (.toString s)
-        first-char (first s)
-        start (if (upper-exists? first-char) (upper-case first-char) first-char)]
-    (str start (lower-case (.substring s 1)))))
-
-(defn ^String upper-case?
-  "Return s if s is all upper case.
-
-  Characters without case, e.g. numbers, are considered to be trivially
-  upper case."
-  [^CharSequence s]
-  {:pre [(not (nil? s))]
-   :post [(or (nil? %) (string? %))]}
-  (when (reduce
-         (fn [acc ^Character c]
-           (and acc (or (Character/isUpperCase c) (= c (Character/toUpperCase c)))))
-         true (.toString s))
-    s))
-
-(defn ^String lower-case?
-  "Return s if s is all lower case.
-
-  Characters without case, e.g. numbers, are considered to be trivially
-  lower case."
-  [^CharSequence s]
-  {:pre [(not (nil? s))]
-   :post [(or (nil? %) (string? %))]}
-  (when (reduce
-         (fn [acc ^Character c]
-           (and acc (or (Character/isLowerCase c) (= c (Character/toLowerCase c)))))
-         true (.toString s))
-    s))
-
-(defn- split-words [^CharSequence s]
-  (remove empty?
-          (-> s
-              (replace #"_|-" " ")
-              (replace #"(\p{javaUpperCase})((\p{javaUpperCase})[(\p{javaLowerCase})0-9])"
-                       "$1 $2")
-              (replace
-               #"(\p{javaLowerCase})(\p{javaUpperCase})" "$1 $2")
-              (split
-               #"[^\w0-9]+"))))
-
-(defn wrap-words
-  "Insert newlines in s so the length of each line doesn't exceed width."
-  [^CharSequence s width]
-  (->> (split s #"\n|\t|\n| ")
-       (remove empty?)
-       (reduce
-        (fn [{:keys [len res]} ^String word]
-          (if (< (+ len (.length word)) width)
-            {:len (+ len (.length word) 1) :res (conj res " " word)}
-            {:len (.length word) :res (conj res "\n" word)}))
-        {:len 0 :res []})
-       :res
-       rest ; drop leading " "
-       (apply str)))
-
-(defn ^String lisp-case
-  "Lower case s and separate words with dashes.
-
-  foo bar => foo-bar
-  camelCase => camel-case
-
-  This is also referred to as kebab-case in some circles."
-  [^CharSequence s]
-  {:pre [(not (nil? s))]
-   :post [(string? %)]}
-  (join "-" (map lower-case (split-words s))))
-
-(defn ^String camel-case
-  "Lower case first char in s and use capitalization to separate words.
-
-  foo bar => fooBar
-  camelCase => camelCase
-  PascalCase => pascalCase"
-  [^CharSequence s]
-  {:pre [(not (nil? s))]
-   :post [(string? %)]}
-  (let [words (split-words s)]
-    (join ""  (conj (map capitalize (rest words)) (lower-case (first words))))))
-
-(defn ^String pascal-case
-  "Lower case first char in s and use capitalization to separate words.
-
-  foo bar => FooBar
-  camelCase => CamelCase
-  PascalCase => PascalCase"
-  [^CharSequence s]
-  {:pre [(not (nil? s))]
-   :post [(string? %)]}
-  (join ""  (map capitalize (split-words s))))
-
-(defn ^String snake-case
-  "Lower case s and use underscores to separate words.
-
-  foo bar => foo_bar
-  camelCase => camel_case
-  PascalCase => pascal_case"
-  [^CharSequence s]
-  {:pre [(not (nil? s))]
-   :post [(string? %)]}
-  (join "_"  (map lower-case (split-words s))))
-
-(defn ^String screaming-snake-case
-  "Upper case s and use underscores to separate words.
-
-  foo bar => FOO_BAR
-  camelCase => CAMEL_CASE
-  PascalCase => PASCAL_CASE"
-  [^CharSequence s]
-  {:pre [(not (nil? s))]
-   :post [(string? %)]}
-  (join "_"  (map upper-case (split-words s))))
-
-(defn ^String strip-accents
-  "Strip all accents (diacritical marks) from s.
-
-  Et ça sera sa moitié => Et ca sera sa moitie"
-  [^CharSequence s]
-  {:pre [(not (nil? s))]
-   :post [(string? %)]}
-  (-> s
-      .toString
-      (Normalizer/normalize java.text.Normalizer$Form/NFD)
-      (.replaceAll  "\\p{InCombiningDiacriticalMarks}+" "")))
-
-(defn ^String ascii?
-  "Return s if s only contains ASCII characters."
-  [^CharSequence s]
-  {:pre [(not (nil? s))]
-   :post [(or (nil? %) (string? %))]}
-  ;; The ASCII character set is encoded as the integers from 0 to 127.
-  (when (reduce (fn [acc c] (and acc (< (int c) 128))) true (.toString s))
-    s))
-
-(defn ^String slug
-  "Transform s so it's suitable for use in URLs.
-
-  The following transformations are applied:
-
-  * Diacritical marks are removed from all characters.
-  * Any character which isn't alphanumeric or in #{_-.~} is removed.
-  * Lower case
-  * Whitespace is collapsed and replaced by a single dash."
-  [^CharSequence s]
-  {:pre [(not (nil? s))]
-   :post [(string? %)]}
-  (-> s
-      .toString
-      (.replaceAll "\\s+" "-")
-      strip-accents
-      (.replaceAll "[^A-Za-z0-9_.~-]" "")
-      (.replaceAll "-+" "-")
-      lower-case))
-
-(defn- upper-if-upper-exists?
-  "Is c uppercase for those characters which have an upper case version?"
-  [^Character c]
-  (or (Character/isUpperCase c) (= c (Character/toUpperCase c))))
-
-(defn- lower-if-lower-exists?
-  "Is c lower for those characters which have an lower case version?"
-  [^Character c]
-  (or (Character/isLowerCase c) (= c (Character/toLowerCase c))))
-
-(defn ^String mixed-case?
-  "Return s if s contains both upper and lower case letters. "
-  [^CharSequence s]
-  {:pre [(not (nil? s))]
-   :post [(or (nil? %) (string? %))]}
-  (let [s (.toString s)]
-    (when (and (seq (drop-while (complement upper-if-upper-exists?) s))
-               (seq (drop-while (complement lower-if-lower-exists?) s)))
-      s)))
-
-(defn ^String collapse-whitespace
-  "Convert all adjacent whitespace characters in s to a single space."
-  [^CharSequence s]
-  {:pre [(not (nil? s))]
-   :post [(string? %)]}
-  (.replaceAll (.toString s) "[ \t\n\r]+" " "))
-
-(defn- levenshtein-distance
-  [^String s1 ^String s2]
-  (let [subsolutions (atom {})
-        subsolution (fn [i j]
-                      (if (or (zero? i) (zero? j))
-                        (max i j)
-                        (get @subsolutions [i j])))]
-    (doseq [i (range 1 (inc (.length s1)))]
-      (doseq [j (range 1 (inc (.length s2)))]
-        (swap! subsolutions assoc [i j]
-               (min (inc (subsolution (dec i) j))
-                    (inc (subsolution i (dec j)))
-                    (+ (subsolution (dec i) (dec j))
-                       (if (= (.charAt s1 (dec i))
-                              (.charAt s2 (dec j)))
-                         0
-                         1))))))
-    (subsolution (.length s1) (.length s2))))
-
-(defn- hamming-distance [^String s1 ^String s2]
-  (+
-   (reduce + (map #(if (= %1 %2) 0 1) s1 s2))
-   (- (max (.length s1) (.length s2))
-      (min (.length s1) (.length s2)))))
-
-(defn ^Long distance
-  "Get the distance between s1 and s2.
-
-  The default distance metric is the Lehvenstein distance.
-
-  The optional algorithm argument can be either :levenshtein to get
-  the default, or :hamming to get the Hamming distance between s1 and
-  s2."
-  ([^CharSequence s1 ^CharSequence s2]
-   {:pre [(not (nil? s1))
-          (not (nil? s2))]
-    :post [(integer? %)]}
-   (levenshtein-distance (.toString s1) (.toString s2)))
-  ([^CharSequence s1 ^CharSequence s2 algorithm]
-   {:pre [(not (nil? s1))
-          (not (nil? s2))]
-    :post [(integer? %)]}
-   (let [s1 (.toString s1)
-         s2 (.toString s2)]
-     (case algorithm
-       :levenshtein (distance s1 s2)
-       :hamming (hamming-distance s1 s2)
-       (throw (IllegalArgumentException. (str "Unknown algorithm: " algorithm)))))))
-
-(defn longest-common-substrings
-  "Returns the set of the longest common substrings in s1 and s2.
-
-  This implementation uses dynamic programming, and not a generalized
-  suffix tree, so the runtime is O(nm)."
-  [^CharSequence s1 ^CharSequence s2]
-  {:pre [(not (nil? s1))
-         (not (nil? s2))]
-   :post [(set? %)]}
-  (let [s1 (.toString s1)
-        s2 (.toString s2)
-        rows (inc (.length s1))
-        cols (inc (.length  s2))
-        ls (make-array Long rows cols)
-        z (atom 0)
-        ret (atom #{})]
-    (doseq [i (range 0 rows)]
-      (doseq [j (range 0 cols)]
-        (aset ls i j 0)))
-    (doseq [i (range 1 rows)]
-      (doseq [j (range 1 cols)]
-        (when(= (.charAt s1 (dec i)) (.charAt s2 (dec j)))
-          (if (or (= i 0) (= j 0))
-            (aset ls i j 1)
-            (aset ls i j (inc (aget ls (dec i) (dec j)))))
-          (if (> (aget ls i j) @z)
-            (do
-              (reset! z (aget ls i j))
-              (reset! ret #{(.substring s1 (- i @z) i)}))
-            (when (= (aget ls i j) @z)
-              (swap! ret conj (.substring s1 (- i @z) i)))))))
-    @ret))
+(ns superstring.core
+  (:require clojure.string)
+  (:refer-clojure :exclude [reverse replace contains?])
+  (:import java.text.Normalizer))
+
+(declare slice)
+
+;; Taken from [jackknife "0.1.6"]
+(defmacro ^:private defalias
+  "Defines an alias for a var: a new var with the same root binding (if
+  any) and similar metadata. The metadata of the alias is its initial
+  metadata (as provided by def) merged into the metadata of the original."
+  ([name orig]
+   `(do
+      (alter-meta!
+       (if (.hasRoot (var ~orig))
+         (def ~name (.getRawRoot (var ~orig)))
+         (def ~name))
+       ;; When copying metadata, disregard {:macro false}.
+       ;; Workaround for http://www.assembla.com/spaces/clojure/tickets/273
+       #(conj (dissoc % :macro :added)
+              (apply dissoc (meta (var ~orig)) (remove #{:macro} (keys %)))))
+      (var ~name)))
+  ([name orig doc]
+   (list `defalias (with-meta name (assoc (meta name) :doc doc)) orig)))
+
+(defmacro ^:private alias-ns
+  "Create an alias for all public vars in ns in this ns."
+  [namespace]
+  `(do ~@(map
+          (fn [n] `(defalias ~(.sym n) ~(symbol (str (.ns n)) (str (.sym n)))))
+          (vals (ns-publics namespace)))))
+
+(alias-ns clojure.string)
+
+(defn- ^String slice-relative-to-end
+  [s index length]
+  (if (neg? (+ (.length s) index)) ; slice outside beg of s
+    nil
+    (slice s (+ (.length s) index) length)))
+
+(defn ^String slice
+  "Return a slice of s beginning at index and of the given length.
+
+  If index is negative the starting index is relative to the end of the string.
+
+  The default length of the slice is 1.
+
+  If the requested slice ends outside the string boundaries, we return
+  the substring of s starting at index.
+
+  Returns nil if index falls outside the string boundaries or if
+  length is negative."
+  ([^String s ^long index]
+   (slice s index 1))
+  ([^String s ^long index ^long length]
+   (cond
+     (neg? length) nil
+     (neg? (+ (.length s) index)) nil ; slice relative to end falls outside s
+     (neg? index) (slice-relative-to-end s index length)
+     (>= index (.length s)) nil
+     (> (- length index) (.length (.substring s index))) (.substring s index)
+     :else (let [end (+ index length)]
+             (.substring s index end)))))
+
+(defn ^String ends-with?
+  "Return s if s ends with suffix."
+  ([^String s ^String suffix]
+   (when (.endsWith s suffix)
+     s))
+  ([^String s ^String suffix ignore-case]
+   (if-not ignore-case
+     (ends-with? s suffix)
+     (let [end (.substring s (max 0 (- (.length s) (.length suffix))))]
+       (when (.equalsIgnoreCase end suffix)
+         s)))))
+
+(defn ^String starts-with?
+  "Return s if s starts with with prefix.
+
+  If a third argument is provided the string comparison is insensitive to case."
+  ([^String s ^String prefix]
+   (when (.startsWith s prefix)
+     s))
+  ([^String s ^String prefix ignore-case]
+   (if-not ignore-case
+     (starts-with? s prefix)
+     (let [beg (.substring s 0 (.length prefix))]
+       (when (.equalsIgnoreCase beg prefix)
+         s)))))
+
+(defn ^String chop
+  "Return a new string with the last character removed.
+
+  If the string ends with \\r\\n, both characters are removed.
+
+  Applying chop to an empty string is a no-op."
+  [^String s]
+  (if (.endsWith s "\r\n")
+    (.substring s 0 (- (.length s) 2))
+    (.substring s 0 (max 0 (dec (.length s))))))
+
+(defn ^String chomp
+  "Return a new string with the given record separator removed from
+  the end (if present).
+
+  If separator is not provided chomp will remove \\n, \\r or \\r\\n from
+  the end of s."
+  ([^String s]
+   (cond
+     (.endsWith s "\r\n") (.substring s 0 (- (.length s) 2))
+     (.endsWith s "\r") (.substring s 0 (dec (.length s)))
+     (.endsWith s "\n") (.substring s 0 (dec (.length s)))
+     :else s))
+  ([^String s ^String separator]
+   (if (.endsWith s separator)
+     (.substring s 0 (- (.length s) (.length separator)))
+     s)))
+
+(defn ^String capitalize
+  "Return a new string where the first character is in upper case and
+  all others in lower case."
+  [^String s]
+  (case (.length s)
+    0 ""
+    1 (upper-case s)
+    (str (upper-case (.substring s 0 1)) (lower-case (.substring s 1)))) )
+
+(defn ^String swap-case
+  "Change lower case characters to upper case and vice versa."
+  [^String s]
+  (let [invert-case (fn [c]
+                      (cond
+                        (Character/isLowerCase c) (Character/toUpperCase c)
+                        (Character/isUpperCase c) (Character/toLowerCase c)
+                        :else c))]
+    (->> s (map invert-case) (apply str))))
+
+(defn- ^String gen-padding
+  "Generate the necessary padding to fill s upto width."
+  [^String s ^String padding width]
+  (let [missing (- width (.length s))
+        full-lengths (Math/floor (/ missing (.length padding)))
+        remaining (if (zero? full-lengths) (- width (.length s))
+                      (rem missing (* full-lengths (.length padding))))]
+    (.concat (apply str (repeat full-lengths padding))
+             (.substring padding 0 remaining))))
+
+
+(defn ^String pad-right
+  "Pad the end of s with padding, or spaces, until the length of s matches
+  width."
+  ([^String s width]
+   (pad-right s width " "))
+  ([^String s width ^String padding]
+   {:pre [(not-empty padding)
+          (not (nil? s))]
+    :post [(= (.length %) width)]}
+   (if (<= width (.length s))
+     s
+     (.concat s (gen-padding s padding width)))))
+
+(defn ^String pad-left
+  "Pad the beginning of s with padding, or spaces, until the length of
+  s matches width."
+  ([^String s width]
+   (pad-left s width " "))
+  ([^String s width ^String padding]
+   {:pre [(not-empty padding)
+          (not (nil? s))]
+    :post [(= (.length %) width)]}
+   (if (<= width (.length s))
+     s
+     (.concat (gen-padding s padding width) s))))
+
+(defn ^String center
+  "Pad both ends of s with padding, or spaces, until the length of s
+  matches width."
+  ([^String s width]
+   (center s width " "))
+  ([^String s width ^String padding]
+   {:pre [(not-empty padding)
+          (not (nil? s))]
+    :post [(= (.length %) width)]}
+   (if (<= width (.length s))
+     s
+     (let [missing (- width (.length s))
+           full-lengths (Math/ceil (/ missing (.length padding)))
+           p (gen-padding s padding width)
+           lengths-before (Math/floor (/ full-lengths 2))]
+       (str (.substring p 0 (* (.length padding) lengths-before))
+            s
+            (.substring p (* (.length padding) lengths-before)))))))
+
+(defn ^String chop-suffix
+  "If s ends with suffix return a new string without the suffix.
+
+  Otherwise return s."
+  ([^String s ^String suffix]
+   (chop-suffix s suffix false))
+  ([^String s ^String suffix ignore-case]
+   {:pre [(not (nil? s))
+          (not (nil? suffix))]
+    :post [(not (nil? %))]}
+   (if (and (>= (.length s) (.length suffix))
+            (ends-with? s suffix ignore-case))
+     (.substring s 0 (- (.length s) (.length suffix)))
+     s)))
+
+(defn ^String chop-prefix
+  "If s starts with with prefix return a new string without the prefix.
+
+  Otherwise return s."
+  ([^String s ^String prefix]
+   (chop-prefix s prefix false))
+  ([^String s ^String prefix ignore-case]
+   {:pre [(not (nil? s))
+          (not (nil? prefix))]
+    :post [(not (nil? %))]}
+   (if (and (>= (.length s) (.length prefix))
+            (starts-with? s prefix ignore-case))
+     (.substring s (.length prefix))
+     s)))
+
+(defn- case-sensitive-contains
+  [s needle]
+  (if (= needle "")
+    s
+    (when (and (seq s) (seq needle) (.contains s needle))
+      s)))
+
+(defn- case-insensitive-contains
+  [s needle]
+  (if (= needle "")
+    s
+    (when (and (seq s) (seq needle))
+      (let [p (java.util.regex.Pattern/compile
+               (java.util.regex.Pattern/quote needle)
+               (bit-or java.util.regex.Pattern/CASE_INSENSITIVE
+                       java.util.regex.Pattern/UNICODE_CASE))]
+        (when (re-find p s)
+          s)))))
+
+(defn ^String contains?
+  "Return s if s contains needle."
+  ([^String s ^String needle]
+   (case-sensitive-contains s needle))
+  ([^String s ^String needle ignore-case]
+   (if ignore-case
+     (case-insensitive-contains s needle)
+     (case-sensitive-contains s needle))))
+
+(defn ^String contains-all?
+  "Return s if s contains all needles."
+  ([^String s needles]
+   (when (every? (partial case-sensitive-contains s) needles)
+     s))
+  ([^String s needles ignore-case]
+   (if ignore-case
+     (when (every? (partial case-insensitive-contains s) needles)
+       s)
+     (contains-all? s needles))))
+
+(defn ^String contains-any?
+  "Return s if s contains any of the needles."
+  ([^String s needles]
+   (some (partial case-sensitive-contains s) needles))
+  ([^String s needles ignore-case]
+   (if ignore-case
+     (some (partial case-insensitive-contains s) needles)
+     (contains-any? s needles))))
+
+(defn ^String truncate
+  "If s is longer than len-3, cut it down to len-3 and append '...'."
+  [^String s len]
+  {:pre [(not (nil? s)) (>= len 3)]}
+  (if (> (.length s) (max 3 (- len 3)))
+    (str (.substring s 0 (- len 3)) "...")
+    s))
+
+(defn- char-equal-ignore-case
+  [^Character c1 ^Character c2]
+  (when (or (= c1 c2)
+            (= (Character/toUpperCase c1) c2)
+            (= (Character/toLowerCase c1) c2))
+    c1))
+
+(defn ^String common-prefix
+  "Return the longest common prefix of s1 and s2."
+  ([^String s1 ^String s2]
+   {:pre [(string? s1) (string? s2)]
+    :post [(string? %)]}
+   (->> s1
+        (map #(when (= %1 %2) %1) s2)
+        (take-while (complement nil?))
+        (apply str)))
+  ([^String s1 ^String s2 ignore-case]
+   {:pre [(string? s1) (string? s2)]
+    :post [(string? %)]}
+   (if-not ignore-case
+     (common-prefix s1 s2)
+     (->> s1
+          (map #(when (or (= %1 %2)
+                          (= (Character/toUpperCase %1) %2)
+                          (= (Character/toLowerCase %1) %2))
+                  %1)
+               s2)
+          (take-while (complement nil?))
+          (apply str)))))
+
+(defn ^String common-suffix
+  "Return the longest common suffix of s1 and s2."
+  ([^String s1 ^String s2]
+   {:pre [(string? s1) (string? s2)]
+    :post [(string? %)]}
+   (->> s1
+        reverse
+        (map #(when (= %1 %2) %1) (reverse s2))
+        (take-while (complement nil?))
+        (apply str)
+        reverse))
+  ([^String s1 ^String s2 ignore-case]
+   {:pre [(string? s1) (string? s2)]
+    :post [(string? %)]}
+   (if-not ignore-case
+     (common-suffix s1 s2)
+     (->> s1
+          reverse
+          (map char-equal-ignore-case (reverse s2))
+          (take-while (complement nil?))
+          (apply str)
+          reverse))))
+
+(defn- upper-exists?
+  "Does c exist in an upper case version?"
+  [^Character c]
+  (or (Character/isUpperCase c) (not= c (Character/toUpperCase c))))
+
+(defn ^String title-case
+  "Capitalize the first character of s and lower case the rest."
+  [^String s]
+  {:pre [(string? s)]
+   :post [(string? %)]}
+  (let [first-char (first s)
+        start (if (upper-exists? first-char) (upper-case first-char) first-char)]
+    (str start (lower-case (.substring s 1)))))
+
+(defn ^String upper-case?
+  "Return s if s is all upper case.
+
+  Characters without case, e.g. numbers, are considered to be trivially
+  upper case."
+  [^String s]
+  {:pre [(string? s)]
+   :post [(or (nil? %) (string? %))]}
+  (when (reduce
+         (fn [acc ^Character c]
+           (and acc (or (Character/isUpperCase c) (= c (Character/toUpperCase c)))))
+         true s)
+    s))
+
+(defn ^String lower-case?
+  "Return s if s is all lower case.
+
+  Characters without case, e.g. numbers, are considered to be trivially
+  lower case."
+  [^String s]
+  {:pre [(string? s)]
+   :post [(or (nil? %) (string? %))]}
+  (when (reduce
+         (fn [acc ^Character c]
+           (and acc (or (Character/isLowerCase c) (= c (Character/toLowerCase c)))))
+         true s)
+    s))
+
+(defn- split-words [^String s]
+  (remove empty?
+          (-> s
+              (replace #"_|-" " ")
+              (replace #"(\p{javaUpperCase})((\p{javaUpperCase})[(\p{javaLowerCase})0-9])"
+                       "$1 $2")
+              (replace
+               #"(\p{javaLowerCase})(\p{javaUpperCase})" "$1 $2")
+              (split
+               #"[^\w0-9]+"))))
+
+(defn wrap-words
+  "Insert newlines in s so the length of each line doesn't exceed width."
+  [^String s width]
+  (->> (split s #"\n|\t|\n| ")
+       (remove empty?)
+       (reduce
+        (fn [{:keys [len res]} ^String word]
+          (if (< (+ len (.length word)) width)
+            {:len (+ len (.length word) 1) :res (conj res " " word)}
+            {:len (.length word) :res (conj res "\n" word)}))
+        {:len 0 :res []})
+       :res
+       rest ; drop leading " "
+       (apply str)))
+
+(defn ^String lisp-case
+  "Lower case s and separate words with dashes.
+
+  foo bar => foo-bar
+  camelCase => camel-case
+
+  This is also referred to as kebab-case in some circles."
+  [^String s]
+  {:pre [(string? s)]
+   :post [(string? %)]}
+  (join "-" (map lower-case (split-words s))))
+
+(defn ^String camel-case
+  "Lower case first char in s and use capitalization to separate words.
+
+  foo bar => fooBar
+  camelCase => camelCase
+  PascalCase => pascalCase"
+  [^String s]
+  {:pre [(string? s)]
+   :post [(string? %)]}
+  (let [words (split-words s)]
+    (join ""  (conj (map capitalize (rest words)) (lower-case (first words))))))
+
+(defn ^String pascal-case
+  "Lower case first char in s and use capitalization to separate words.
+
+  foo bar => FooBar
+  camelCase => CamelCase
+  PascalCase => PascalCase"
+  [^String s]
+  {:pre [(string? s)]
+   :post [(string? %)]}
+  (join ""  (map capitalize (split-words s))))
+
+(defn ^String snake-case
+  "Lower case s and use underscores to separate words.
+
+  foo bar => foo_bar
+  camelCase => camel_case
+  PascalCase => pascal_case"
+  [^String s]
+  {:pre [(string? s)]
+   :post [(string? %)]}
+  (join "_"  (map lower-case (split-words s))))
+
+(defn ^String screaming-snake-case
+  "Upper case s and use underscores to separate words.
+
+  foo bar => FOO_BAR
+  camelCase => CAMEL_CASE
+  PascalCase => PASCAL_CASE"
+  [^String s]
+  {:pre [(string? s)]
+   :post [(string? %)]}
+  (join "_"  (map upper-case (split-words s))))
+
+(defn ^String strip-accents
+  "Strip all accents (diacritical marks) from s.
+
+  Et ça sera sa moitié => Et ca sera sa moitie"
+  [^String s]
+  {:pre [(string? s)]
+   :post [(string? %)]}
+  (-> s
+      (Normalizer/normalize java.text.Normalizer$Form/NFD)
+      (.replaceAll  "\\p{InCombiningDiacriticalMarks}+" "")))
+
+(defn ^String ascii?
+  "Return s if s only contains ASCII characters."
+  [^String s]
+  {:pre [(string? s)]
+   :post [(or (nil? %) (string? %))]}
+  ;; The ASCII character set is encoded as the integers from 0 to 127.
+  (when (reduce (fn [acc c] (and acc (< (int c) 128))) true s)
+    s))
+
+(defn ^String slug
+  "Transform s so it's suitable for use in URLs.
+
+  The following transformations are applied:
+
+  * Diacritical marks are removed from all characters.
+  * Any character which isn't alphanumeric or in #{_-.~} is removed.
+  * Lower case
+  * Whitespace is collapsed and replaced by a single dash."
+  [^String s]
+  {:pre [(string? s)]
+   :post [(string? %)]}
+  (-> s
+      (.replaceAll "\\s+" "-")
+      strip-accents
+      (.replaceAll "[^A-Za-z0-9_.~-]" "")
+      (.replaceAll "-+" "-")
+      lower-case))
+
+(defn- upper-if-upper-exists?
+  "Is c uppercase for those characters which have an upper case version?"
+  [^Character c]
+  (or (Character/isUpperCase c) (= c (Character/toUpperCase c))))
+
+(defn- lower-if-lower-exists?
+  "Is c lower for those characters which have an lower case version?"
+  [^Character c]
+  (or (Character/isLowerCase c) (= c (Character/toLowerCase c))))
+
+(defn ^String mixed-case?
+  "Return s if s contains both upper and lower case letters. "
+  [^String s]
+  {:pre [(string? s)]
+   :post [(or (nil? %) (string? %))]}
+  (when (and (seq (drop-while (complement upper-if-upper-exists?) s))
+             (seq (drop-while (complement lower-if-lower-exists?) s)))
+    s))
+
+(defn ^String collapse-whitespace
+  "Convert all adjacent whitespace characters in s to a single space."
+  [^String s]
+  {:pre [(string? s)]
+   :post [(string? %)]}
+  (.replaceAll s "[ \t\n\r]+" " "))
+
+(defn- ^Long levenshtein-distance
+  [s1 s2]
+  (let [subsolutions (atom {})
+        subsolution (fn [i j]
+                      (if (or (zero? i) (zero? j))
+                        (max i j)
+                        (get @subsolutions [i j])))]
+    (doseq [i (range 1 (inc (.length s1)))]
+      (doseq [j (range 1 (inc (.length s2)))]
+        (swap! subsolutions assoc [i j]
+               (min (inc (subsolution (dec i) j))
+                    (inc (subsolution i (dec j)))
+                    (+ (subsolution (dec i) (dec j))
+                       (if (= (.charAt s1 (dec i))
+                              (.charAt s2 (dec j)))
+                         0
+                         1))))))
+    (subsolution (.length s1) (.length s2))))
+
+(defn- hamming-distance [s1 s2]
+  (+
+   (reduce + (map #(if (= %1 %2) 0 1) s1 s2))
+   (- (max (.length s1) (.length s2))
+      (min (.length s1) (.length s2)))))
+
+(defn ^Long distance
+  "Get the distance between s1 and s2.
+
+  The default distance metric is the Lehvenstein distance.
+
+  The optional algorithm argument can be either :levenshtein to get
+  the default, or :hamming to get the Hamming distance between s1 and
+  s2."
+  ([^String s1 ^String s2]
+   {:pre [(string? s1)
+          (string? s2)]
+    :post [(integer? %)]}
+   (levenshtein-distance s1 s2))
+  ([^String s1 ^String s2 algorithm]
+   {:pre [(string? s1)
+          (string? s2)]
+    :post [(integer? %)]}
+   (case algorithm
+     :levenshtein (distance s1 s2)
+     :hamming (hamming-distance s1 s2)
+     (throw (IllegalArgumentException. (str "Unknown algorithm: " algorithm))))))
+
+(defn longest-common-substrings
+  "Returns the set of the longest common substrings in s1 and s2.
+
+  This implementation uses dynamic programming, and not a generalized
+  suffix tree, so the runtime is O(nm)."
+  [^String s1 ^String s2]
+  {:pre [(string? s1)
+         (string? s2)]
+   :post [(set? %)]}
+  (let [rows (inc (.length s1))
+        cols (inc (.length  s2))
+        ls (make-array Long rows cols)
+        z (atom 0)
+        ret (atom #{})]
+    (doseq [i (range 0 rows)]
+      (doseq [j (range 0 cols)]
+        (aset ls i j 0)))
+    (doseq [i (range 1 rows)]
+      (doseq [j (range 1 cols)]
+        (when(= (.charAt s1 (dec i)) (.charAt s2 (dec j)))
+          (if (or (= i 0) (= j 0))
+            (aset ls i j 1)
+            (aset ls i j (inc (aget ls (dec i) (dec j)))))
+          (if (> (aget ls i j) @z)
+            (do
+              (reset! z (aget ls i j))
+              (reset! ret #{(.substring s1 (- i @z) i)}))
+            (when (= (aget ls i j) @z)
+              (swap! ret conj (.substring s1 (- i @z) i)))))))
+    @ret))


Housekeeping

Housekeeping

--- src/taoensso/faraday.clj
+++ src/taoensso/faraday.clj
@@ -267,14 +267,14 @@
 (defn block-while-table-status
   "BLOCKS to poll for a change to table's status. On status change, polling will
   terminate and the table's new description will be returned."
-  [creds table-name status & [{:keys [poll-ms timeout-ms timeout-val]
-                               :or   {poll-ms 5000}}]]
+  [creds table status & [{:keys [poll-ms timeout-ms timeout-val]
+                          :or   {poll-ms 5000}}]]
   (assert (#{:creating :updating :deleting :active} (utils/un-enum status))
           (str "Invalid table status: " status))
   (let [polling-future
         (future
           (loop []
-            (let [current-descr (describe-table creds table-name)]
+            (let [current-descr (describe-table creds table)]
               (if-not (= (:status current-descr) (utils/un-enum status))
                 current-descr
                 (do (Thread/sleep poll-ms)
@@ -400,8 +400,8 @@
        throughput (.setProvisionedThroughput (provisioned-throughput g))))))

 (defn delete-table "Deletes a table, go figure."
-  [creds table-name]
-  (as-map (.deleteTable (db-client creds) (DeleteTableRequest. (name table-name)))))
+  [creds table]
+  (as-map (.deleteTable (db-client creds) (DeleteTableRequest. (name table)))))

 ;;;; API - items


Change macros to not output hardcoded epoch times

--- src/tenor/constructs.clj
+++ src/tenor/constructs.clj
@@ -162,23 +162,24 @@

 ;; --- Playback --- ;;

-(defmacro play-note [pivot-time position base-time player entity]
+(defmacro play-note [position base-time player entity]
   "Take time, instrument and note entity as arguments,
   and return overtone code that plays the note."
   `(list 'at
-         `(+ pivot-time ~(* ~position ~base-time))
+         `(+ ~(symbol "time") ~(* ~position ~base-time))
          (list ~player ~entity)))

-(defmacro construct-piece [entity-maps base-time player pivot-time]
+(defmacro construct-piece [entity-maps base-time player]
   "Take an entity map and run play-note on each
   note, creating a list of overtone playback code components."
-  `(map #(play-note pivot-time (:pos %) ~base-time ~player (:note %)) ~entity-maps))
+  `(map #(play-note (:pos %) ~base-time ~player (:note %)) ~entity-maps))

 (defn play-piece [entity-maps base-time player
                     & {:keys [pivot-time] :or {pivot-time (now)}}]
   "Play the constructed piece with current time as start time."
-  `(let [pivot-time (now)]
-     ~@(construct-piece entity-maps base-time player pivot-time)))
+  (let [pivot-time (symbol "time")]
+    `(let [~pivot-time (now)]
+       ~@(construct-piece entity-maps base-time player))))

 ;; --- Multiple voices --- ;;


Add some 'bulk' to the content

Add some 'bulk' to the content

--- src-cljs/frontend/components/enterprise.cljs
+++ src-cljs/frontend/components/enterprise.cljs
@@ -250,7 +250,7 @@
              [:article
               (common/feature-icon "github")
               [:h2.text-center "Works with GitHub Enterprise"]
-              [:p.text-center "CircleCI has integrated seamlessly with GitHub from the very beginning, and CircleCI Enterprise is no different."]]]
+              [:p.text-center "CircleCI has integrated seamlessly with GitHub from the very beginning, and CircleCI Enterprise is no different. Sign in with GitHub Enterprise, view build status right from PR pages, and quickly navigate from builds on CircleCI to corresponding commits and PRs on GitHub Enterprise."]]]
             [:div.col-xs-6
              [:article
               (common/feature-icon "key-hole")
@@ -261,12 +261,12 @@
              [:article
               (common/feature-icon "circle")
               [:h2.text-center "We come to you"]
-              [:p "Run your own CircleCI installation together with your existing Azure infrastructure."]]]
+              [:p.text-center "Run your own CircleCI installation together with your existing Azure infrastructure. You've invested time, money, and people in your cloud infrastructure. CircleCI Enterprise on Azure makes those investments pay dividends by integrating seemlessly with your existing setup."]]]
             [:div.col-xs-6
              [:article
               (common/feature-icon "security")
               [:h2.text-center "Security first"]
-              [:p.text-center "CircleCI runs in your own VPC, where you have control over security and network settings"]]]]]]
+              [:p.text-center "CircleCI runs in your own VPC, where you have control over security and network settings.  Now you don't have to choose between using state-of-the-art CI and CD tools and complying with your company security policies."]]]]]]
          [:div.outer-section
           [:section.container
            (common/feature-icon "phone")
@@ -296,24 +296,24 @@
              [:article
               (common/feature-icon "github")
               [:h2.text-center "Integrates with GitHub Enterprise"]
-              [:p.text-center "CircleCI has worked seamlessly with GitHub from the very beginning, and the same is true for CircleCI Enterprise."]]]
+              [:p.text-center "CircleCI has worked seamlessly with GitHub from the very beginning, and the same is true for CircleCI Enterprise. Enjoy the same PR status intgegration and navigation from CircleCI builds to GitHub commits that is available on circleci.com."]]]
             [:div.col-xs-6
              [:article
               (common/feature-icon "aws")
               [:h2.text-center "CircleCI knows AWS"]
               [:p.text-center
-               "The public CircleCI.com offering itself runs on AWS, so you can take advantage of our extensive experience with AWS machines."]]]]
+               "The public CircleCI.com offering itself runs on AWS, so you can take advantage of our extensive experience with AWS machines. We've run literally millions of builds using CircleCI AWS hardware, and we can help you choose the best hardware for any workload."]]]]
            [:section.row.extra-row
             [:div.col-xs-6
              [:article
               (common/feature-icon "circle")
               [:h2.text-center "Don't go anywhere"]
-              [:p.text-center "You can run your own CircleCI installation right on your existing AWS account."]]]
+              [:p.text-center "You can run your own CircleCI installation right on your existing AWS account. Whether you manage your infrastructure with AWS CloudFormation, Chef, Puppet, Terraform, or none of the above, we'll meet you where you are and integrate seemlessly with your setup."]]]
             [:div.col-xs-6
              [:article
               (common/feature-icon "security")
               [:h2.text-center "Built for Security"]
-              [:p.text-center "CircleCI runs in your own Virtual Private Cloud, where everything from firewall settings to IAM privileges are under your control."]]]]]]
+              [:p.text-center "CircleCI runs in your own Virtual Private Cloud, where everything from firewall settings to IAM privileges are under your control. CircleCI only requires a few simple resource types and network settings, so you can get started quickly and securely."]]]]]]
          [:div.outer-section
           [:section.container
            (common/feature-icon "phone")


CMR-760: Re-added 'inherited' attribute to match ECHO

--- src/cmr/search/results_handlers/atom_results_handler.clj
+++ src/cmr/search/results_handlers/atom_results_handler.clj
@@ -242,9 +242,7 @@
   [atom-link]
   (let [{:keys [href link-type title mime-type size inherited]} atom-link
         attribs (-> {}
-                    ;; TODO remove or uncomment the next line depending on whether or not
-                    ;; we want to keep the 'inherited' attribute (See CMR-637).
-                    ;(add-attribs :inherited inherited)
+                    (add-attribs :inherited inherited)
                     (add-attribs :length size)
                     (add-attribs :rel (link-type->link-type-uri (keyword link-type)))
                     (add-attribs :type mime-type)


Cosmetics

Cosmetics

--- src/clj/clojurewerkz/statistiker/metrics.clj
+++ src/clj/clojurewerkz/statistiker/metrics.clj
@@ -1,7 +1,7 @@
 (ns clojurewerkz.statistiker.metrics
-  (:require [clojure.math.combinatorics :refer [cartesian-product]]
+  (:require [clojure.math.combinatorics       :refer [cartesian-product]]
             [clojurewerkz.statistiker.entropy :refer [shannon-entropy]]
-            [clojurewerkz.statistiker.utils :refer [factorial prot-log prot-shannon-entropy]]))
+            [clojurewerkz.statistiker.utils   :refer [factorial safe-log prot-shannon-entropy]]))

 ; The Mutual Information is a measure of the similarity between two labels of the same data.
 ; Where P(i) is the probability of a random sample occurring in cluster U_i and P'(j) is the probability of a random sample


Refactor face style click handler

Refactor face style click handler

--- src/cljs/koeeoadi/components/faceeditor.cljs
+++ src/cljs/koeeoadi/components/faceeditor.cljs
@@ -25,15 +25,18 @@
            :style     #js {:backgroundColor rgb}
            :tabIndex  (when-not disabled? "0")})))

+(defn face-update [name prop e]
+  (let [checked (util/target-checked e)]
+    (om/transact! (code-display-comp)
+      `[(face/update {:face/name ~name
+                      ~prop      ~checked})])))
+
 (defn face-style [comp prop]
   (let [{:keys [face/name]} (om/props comp)]
     (dom/li nil
       (dom/input #js
         {:type    "checkbox"
-         :onClick #(let [checked (util/target-checked %)]
-                     (om/transact! (code-display-comp)
-                       `[(face/update {:face/name  ~name
-                                       ~prop       ~checked})]))})
+         :onClick #(face-update name prop %)})
       (clojure.core/name prop))))

 (defn face-styles [comp]


Fix creation of GC tasks

Fix creation of GC tasks

This was inadvertently using `apply` to call a function on a set of
arguments when a regular function call was appropriate, due to a bad
refactor.

--- src/com/puppetlabs/puppetdb/cli/services.clj
+++ src/com/puppetlabs/puppetdb/cli/services.clj
@@ -421,7 +421,7 @@

       ;; Pretty much this helper just knows our job-pool and gc-interval
       (let [gc-interval-millis (to-millis gc-interval)
-            gc-task (fn [f] (apply interspaced gc-interval-millis f job-pool))
+            gc-task #(interspaced gc-interval-millis % job-pool)
             db-maintenance-tasks [garbage-collect!
                                   (when (pos? (to-secs node-ttl))
                                     (partial auto-deactivate-nodes! node-ttl))
@@ -431,7 +431,7 @@
                                     (partial sweep-reports! report-ttl))]]

         (gc-task #(apply perform-db-maintenance! db (remove nil? db-maintenance-tasks)))
-        (gc-task #(compress-dlo! dlo-compression-threshold discard-dir )))
+        (gc-task #(compress-dlo! dlo-compression-threshold discard-dir)))

       ;; Start debug REPL if necessary
       (let [{:keys [enabled type host port] :or {type "nrepl" host "localhost"}} (:repl config)]


Add HSL color space functions.

Add HSL color space functions.

--- src/org/playasophy/wonderdome/util/color.clj
+++ src/org/playasophy/wonderdome/util/color.clj
@@ -67,6 +67,35 @@

+;;;;; HSL COLOR SPACE ;;;;;
+
+(defn hsl
+  "Creates a color value from a hue, saturation, and lightness. This is similar
+  to HSV, except that the RGB color space is projected into a bicone instead of
+  a cone. This means a lightness of 0.0 is black, as with HSV, but 1.0 gives
+  white instead of a 'fully bright' color."
+  [h s l]
+  (let [l' (* 2 l)
+        s' (* s (if (> l' 1) (- 2 l') l'))]
+    (hsv
+      h
+      (/ (* 2 s') (+ l' s'))
+      (/ (+ l' s') 2))))
+
+
+(defn hsl-components
+  "Takes a color value and returns a vector containing the hue, saturation, and
+  lightness components as floating-point numbers."
+  [color]
+  {:pre [(color? color)]}
+  (let [[h s v] (hsv-components color)
+        l' (* v (- 2 s))
+        s' (/ (* v s) (if (> l' 1) (- 2 l') l'))
+        l' (/ l' 2)]
+    [h s' l']))
+
+
+
 ;;;;; COLOR GRADIENTS ;;;;;

 (defn blend-rgb


Bypass Optimus when we're not serving frozen assets

We can't use both Optimus and Figwheel at the same time, because
Figwheel can't tell the browser which file to reload if Optimus
concatenates files and keeps them somewhere like
`/bundles/abc123/dev.js`.

As Figwheel provides us more immediate value I'm opting for dropping
Optimus in development, and will instead rely solely on the test suite
to verify Optimus is working as expected.

--- resources/leiningen/new/clover/src/clj/clover/server.clj
+++ resources/leiningen/new/clover/src/clj/clover/server.clj
@@ -50,6 +50,7 @@
 (defn make-handler [{:keys [debug-exceptions? freeze-assets?]}]
   (let [{:keys [js strategy]} (asset-config freeze-assets?)]
     (-> (make-routes js)
-        (optimus/wrap get-assets asset-optimizations strategy)
+        (cond-> freeze-assets?
+          (optimus/wrap get-assets asset-optimizations strategy))
         (cond-> debug-exceptions? prone/wrap-exceptions)
         (wrap-defaults site-defaults))))


Add onChange handler to operations list items.

--- src/cljs/hatnik/web/client/form/github_pull_request.cljs
+++ src/cljs/hatnik/web/client/form/github_pull_request.cljs
@@ -36,6 +36,15 @@
            error-handler))
         1000)))))

+
+(defn update-operation-item [data key new-val]
+  ((:handler data)
+   (:id data)
+   (assoc {:file (:file data)
+           :regex (:regex data)
+           :replace (:replace data)}
+     key new-val)))
+
 (defn pull-request-operation [data owner]
   (reify
     om/IRender
@@ -60,16 +69,19 @@
                                    (dom/label nil "file")
                                    (dom/input #js {:type "text"
                                                    :value (:file data)
+                                                   :onChange #(update-operation-item data :file (.. % -target -value))
                                                    :className "form-control"}))
                           (dom/div #js {:className "form-group"}
                                    (dom/label nil "regex")
                                    (dom/input #js {:type "text"
                                                    :value (:regex data)
+                                                   :onChange #(update-operation-item data :regex (.. % -target -value))
                                                    :className "form-control"}))
                           (dom/div #js {:className "form-group"}
                                    (dom/label nil "replacement")
                                    (dom/input #js {:type "text"
                                                    :value (:replace data)
+                                                   :onChange #(update-operation-item data :replace (.. % -target -value))
                                                    :className "form-control"})))))))))

 (defn add-new-operation [data]
@@ -91,7 +103,13 @@
                                                       "Add operation")))

                         (apply dom/div nil
-                               (map-indexed #(om/build pull-request-operation (merge {:id %1} %2)) (:value data))))))))
+                               (map-indexed #(om/build pull-request-operation
+                                                       (merge {:id %1
+                                                               :handler (fn [id val]
+                                                                          ((:handler data)
+                                                                           (assoc (:value data) id val)))}
+                                                              %2))
+                                            (:value data))))))))

 (defn github-pull-request-component [data owner]
   (reify


Add delete button for pull request operation component.

--- src/cljs/hatnik/web/client/form/github_pull_request.cljs
+++ src/cljs/hatnik/web/client/form/github_pull_request.cljs
@@ -67,12 +67,17 @@
             :id (str "gh-pull-req-op-" (:id data))}
        (dom/div #js {:className "panel panel-default"}
                 (dom/div
-                 #js {:className "panel-heading"}
-                 (dom/h4 #js {:className "panel-title"}
+                 #js {:className "panel-heading clearfix"}
+                 (dom/h4 #js {:className "panel-title pull-left"}
                          (dom/a #js {:data-toggle "collapse"
                                      :data-parent (str "#gh-pull-req-op-" (:id data))
                                      :href (str "#gh-pull-req-op-col-" (:id data))}
-                                (str "Operation No. " (+ 1 (:id data))))))
+                                (str "Operation No. " (+ 1 (:id data)))))
+
+                 (dom/div #js {:className "dropdown pull-right"}
+                          (dom/div #js {:className "btn btn-danger"
+                                        :onClick #((:delete-handler data) (:id data))}
+                                   "Delete")))

                 (dom/div
                  #js {:className "panel-collapse collapse in"
@@ -134,7 +139,12 @@
                                                        (merge {:id %1
                                                                :handler (fn [id val]
                                                                           ((:handler data)
-                                                                           (assoc (:value data) id val)))}
+                                                                           (assoc (:value data) id val)))
+                                                               :delete-handler (fn [id]
+                                                                                ((:handler data)
+                                                                                (mapv second
+                                                                                (filter (fn [pr] (not= (first pr) id))
+                                                                                (map-indexed (fn [i e] (list i e)) (:value data))))))}
                                                               %2))
                                             (:value data))))))))


Remove old code.

Remove old code.

--- src/cljs/hatnik/web/client/form/github_pull_request.cljs
+++ src/cljs/hatnik/web/client/form/github_pull_request.cljs
@@ -5,37 +5,6 @@
             [hatnik.schema :as schm]
             [schema.core :as s])
   (:use [clojure.string :only [split replace]]))
-
-(defn github-issue-on-change [gh-repo timer form-handler form-status-handler error-handler]
-  (js/clearTimeout timer)
-
-  (let [pr (split gh-repo "/")
-        user (first pr)
-        repo (second pr)]
-    (form-handler
-     (if (or (nil? repo)
-             (nil? user)
-             (= "" user)
-             (= "" repo))
-       (do
-         (form-status-handler "has-warning")
-         nil)
-
-       (js/setTimeout
-        (fn []
-          (action/get-github-repos
-           user
-           (fn [reply]
-             (let [rest (js->clj reply)
-                   result (->> rest
-                               (map #(get % "name"))
-                               (filter #(= % repo)))]
-               (if (first result)
-                 (form-status-handler "has-success")
-                 (form-status-handler "has-error"))))
-           error-handler))
-        1000)))))
-

 (defn update-operation-item [data key new-val]
   ((:handler data)


Stub start-less and start-sass in prod env

--- src/leiningen/new/chestnut/env/prod/clj/chestnut/dev.clj
+++ src/leiningen/new/chestnut/env/prod/clj/chestnut/dev.clj
@@ -12,3 +12,11 @@
   (throw (Exception. "Browser connected REPL is not available in prod mode")))
 (defn start-figwheel []
   (throw (Exception. "Figwheel is not available in prod mode")))
+{{#less?}}
+(defn start-less []
+  (throw (Exception. "less is not available in prod mode")))
+{{/less?}}
+{{#sass?}}
+(defn start-sass []
+  (throw (Exception. "sass is not available in prod mode")))
+{{/sass?}}


Can add stats

Can add stats

--- test/little_monster_simulation/core_test.clj
+++ test/little_monster_simulation/core_test.clj
@@ -53,4 +53,10 @@
                    :behaviours {:one-shot (fn [m _]
                                             (dissoc-in m [:behaviours :one-shot]))}}
           monster' (c/tick monster 1)]
-      (is (empty? (:behaviours monster'))))))
+      (is (empty? (:behaviours monster')))))
+  (testing "behaviours can add stats"
+    (let [monster {:stats {:life 100}
+                   :behaviours {:walkies (fn [m _]
+                                           (assoc-in m [:stats :walk] 100))}}
+          monster' (c/tick monster 1)]
+      (is (= 100 (c/value-of monster' :walk))))))


Remove allow shared key in top level project

--- src/cider_ci/builder/jobs/validator/project_configuration.clj
+++ src/cider_ci/builder/jobs/validator/project_configuration.clj
@@ -87,7 +87,6 @@
           :required true }
    :name {:validator validate-string!}
    :description {:validator validate-string!}
-   :shared nil ; can be freely chosen
    })

 (defn validate! [project-config]


remove all registered metrics when stopping test server

--- ssclj/jar/test/com/sixsq/slipstream/ssclj/app/test_server.clj
+++ ssclj/jar/test/com/sixsq/slipstream/ssclj/app/test_server.clj
@@ -4,6 +4,7 @@
     [com.sixsq.slipstream.db.es.utils :as esu]
     [com.sixsq.slipstream.ssclj.util.zookeeper :as zku]
     [com.sixsq.slipstream.ssclj.app.server :as server]
+    [metrics.core :refer [remove-all-metrics]]
     [zookeeper :as zk])
   (:import (org.apache.curator.test TestingServer)))

@@ -79,7 +80,8 @@
     (try
       (stop-fn)
       (catch Exception _))
-    (stop-clients)))
+    (stop-clients)
+    (remove-all-metrics)))

 (defn refresh-es-indices


better organizatioons of project.clj

better organizatioons of project.clj

--- webapp/project.clj
+++ webapp/project.clj
@@ -7,19 +7,20 @@
   :dependencies [[org.clojure/clojure "1.7.0"]
                  [org.clojure/core.async "0.2.374"]
                  [org.clojure/clojurescript "1.7.228"]
+                 [http-kit "2.1.18"]
                  [ring/ring-core "1.4.0"]
-                 [ring/ring-jetty-adapter "1.4.0"]
-                 [javax.servlet/servlet-api "2.5"]
                  [compojure "1.4.0"]
-                 [mount "0.1.9"]]
+                 [com.stuartsierra/component "0.3.1"]]

-  :plugins [[cider/cider-nrepl "0.11.0-SNAPSHOT"]
-            [refactor-nrepl "2.0.0-SNAPSHOT"]
+  :plugins [[lein-pprint "1.1.1"]
             [lein-ring "0.9.7"]
             [lein-cljsbuild "1.1.2"]
             [lein-figwheel "0.5.0-6"]]

   :source-paths ["src", "src-cljs"]
+
+  ;; Entry point
+  :main ^:skip-aot cljat.server.main

   :cljsbuild {:builds [{:id "dev"
                         :source-paths ["src-cljs"]
@@ -31,28 +32,37 @@
              :port 3449
              :css-dirs ["resources/public/css"]}

-  :ring {:handler webapp.core/app-routes
-         :uberwar-name "cljat.war"}
-
-  :profiles {;; Development Profile
+  :profiles {
+             ;; Activated automatically in repl task
+             :repl {:plugins [[cider/cider-nrepl "0.11.0-SNAPSHOT"]
+                              [refactor-nrepl "2.0.0-SNAPSHOT"]]
+                    :repl-options {:host "0.0.0.0"
+                                   :port 7888}}
+
+             ;; Activated by uberjar task
+             :uberjar {:aot :all}
+
+             ;; Activated by default
              :dev {:source-paths ["dev"]
-                   :dependencies [[org.clojure/tools.nrepl "0.2.11"]
+                   :dependencies [[org.clojure/tools.nrepl "0.2.12"]
                                   [org.clojure/tools.namespace "0.2.11"]
                                   [org.clojure/java.classpath "0.2.3"]
+                                  [ring/ring-devel "1.4.0"]
+                                  [javax.servlet/servlet-api "2.5"]
                                   [com.cemerick/piggieback "0.2.1"]
                                   [figwheel-sidecar "0.5.0-4"]]
-
-                   :repl-options {:host "0.0.0.0"
-                                  :nrepl-middleware [cemerick.piggieback/wrap-cljs-repl]}
+
+                   :repl-options {:nrepl-middleware [cemerick.piggieback/wrap-cljs-repl]}

                    :ring {:nrepl {:start? true
                                   :host "0.0.0.0"
                                   :port 7888}}}
+
              ;; Test Profile
              :test {}

              ;; Production Profile
-             :production {:open-browser? false
+             :prod {:open-browser? false
                           :stacktracers? false
-                          :auto-reload? false}}
+                    :auto-reload? false}}
   )


Updated add word to glossary dialog: use-it checkbox, correct forms and incorrect forms input

--- src/zg/html_renderer.clj
+++ src/zg/html_renderer.clj
@@ -301,12 +301,15 @@
                     [:tr [:td [:div {:class "_label _label-primary"} "New word:"]]
                          [:td " "]
                          [:td (form/text-field {:size "30"} "new-word")]]
-                    [:tr [:td [:div {:class "_label _label-default"} "Description (optional):"]]
+                    [:tr [:td [:div {:class "_label _label-default"} "Description:"]]
                          [:td " "]
                          [:td (form/text-field {:size "50"} "description")]]
                     [:tr [:td [:div {:class "_label _label-default"} "Class:"]]
                          [:td " "]
                          [:td (form/drop-down "class" ["N/A" "Noun" "Verb" "Adjective" "Adverb" "Pronoun" "Preposition" "Conjunction" "Determiner" "Exclamation"])]]
+                    [:tr [:td [:div {:class "_label _label-default"} "Use it"]]
+                         [:td " "]
+                         [:td (form/check-box "use-it")]]
                     [:tr [:td [:div {:class "_label _label-default"} "Internal:"]]
                          [:td " "]
                          [:td (form/check-box "internal")]]
@@ -316,6 +319,12 @@
                     [:tr [:td [:div {:class "_label _label-default"} "Source:"]]
                          [:td " "]
                          [:td (form/drop-down "source" (map (fn [i] [(val i)(key i)]) sources) )]]
+                    [:tr [:td [:div {:class "_label _label-default"} "Correct forms"]]
+                         [:td " "]
+                         [:td (form/text-field {:size "30"} "correct-forms")]]
+                    [:tr [:td [:div {:class "_label _label-default"} "Incorrect forms"]]
+                         [:td " "]
+                         [:td (form/text-field {:size "30"} "incorrect-forms")]]
                     [:tr [:td " "]
                          [:td " "]
                          [:td (form/submit-button {:class "btn btn-primary"} "Add new word")]]]


Pruning dead code

Pruning dead code

--- src-cljs/tropology/core.cljs
+++ src-cljs/tropology/core.cljs
@@ -177,37 +177,9 @@
 ; Components
 ;

-(defn row [label & body]
-  [:div.row
-   [:div.col-md-2 [:span label]]
-   [:div.col-md-3 body]])
-
-(defn text-input [id label]
-  (row label [:input.form-control {:field :text
-                                   :id    id}]))
-
-
 (defn button-item [label class dispatch-vals is-disabled? extra-items]
-  [:button {:type "button" :class (str "btn " class) :disabled is-disabled? :on-click #(re-frame/dispatch dispatch-vals)} extra-items label])
-
-(defn nav-item [selected item-key label]
-  [:li {:class (when (= selected item-key) "active")}
-   [:a {:on-click #(re-frame/dispatch [:navbar-click item-key])} label]])
-
-
-(def trope-code-form
-  [:div
-   (text-input :trope-code "Article code:")])
-
-
-
-(defn graph-display [form-data class]
-  [:div {:class class}
-   [bind-fields trope-code-form form-data]
-   [:div
-    [button-item "Graph!" "btn-primary" [:draw-graph (:trope-code @form-data)] false]
-    [:div {:id "graph-container"}]]]
-  )
+  [:button {:type :button :class (str "btn " class) :disabled is-disabled? :on-click #(re-frame/dispatch dispatch-vals)} extra-items label])
+

 (defn header-display []
   (let [current-article (re-frame/subscribe [:article-data :current-article])]
@@ -260,7 +232,7 @@
        ])))

 (defn error-list-display []
-  (let [errors          (re-frame/subscribe [:ui-state :errors])]
+  (let [errors (re-frame/subscribe [:ui-state :errors])]
     (fn []
       (if @errors
         [:section {:class "section latest has-error form-group" :on-click #(re-frame/dispatch [:clear-errors])}
@@ -271,51 +243,7 @@
              ^{:key (rand-int 999999)} [:div [:label {:class "control-label"} error]])
            ]
           ]]
-
-        )
-      )
-    )
-  )
-
-(defn article-display [form-data class]
-  (let [current-article (re-frame/subscribe [:article-data :current-article])
-        current-ref     (re-frame/subscribe [:article-data :current-reference])
-        like-list       (re-frame/subscribe [:article-data :like-list])
-        references      (re-frame/subscribe [:article-data :tropes])
-        remaining       (reaction (count @references))
-        errors          (re-frame/subscribe [:ui-state :errors])]
-
-    [:div {:class class}
-     [bind-fields trope-code-form form-data]
-     (if errors
-       [:div {:class "form-group has-error" :on-click #(re-frame/dispatch [:clear-errors])}
-        (for [error @errors]
-          ^{:key (rand-int 999999)} [:div [:label {:class "control-label"} error]])])
-     [:div
-      [button-item "Retrieve references" "btn-primary" [:load-article (:trope-code @form-data) false]]
-      [:div {:id "current-trope"}
-       [:h2 {:class "trope-title"} (:title @current-article)]
-       [:p (:description @current-article)]]
-      (if (some? @current-article)
-        [:div {:id "current-piece"}
-         [:h3 "Random reference"]
-         [:p (process-trope @current-ref [true])]
-         [:div [:span (str "(" @remaining " remaining)")]]
-         [:div
-          [button-item "Interesting" "btn-success" [:vote :like] (empty? @current-ref)]
-          [button-item "Skip" "btn-info" [:vote :skip] (>= 0 @remaining)]]])
-      (if (some? @like-list)
-        [:div {:id "trope-list-container"}
-         [:hr]
-         [:h2 "Selected items"]
-         [:ul
-          (for [trope @like-list]
-            ^{:key (hash trope)} [:li (process-trope (:ref trope) [false])
-                                  " (" [:a {:on-click #(re-frame/dispatch [:load-article (:code trope) false])} (:display trope)] ")"])]
-         ])
-      ]])
-  )
-
+        ))))

 (defn init! []


sorted clojure syntax

sorted clojure syntax

--- compute-clojure/src/compute_clojure/compute_examples.clj
+++ compute-clojure/src/compute_clojure/compute_examples.clj
@@ -16,7 +16,8 @@
 (defn get-credentials []
   (-> (LoginCredentials/builder)
     (.user (System/getProperty "user.name"))
-    (.privateKey (slurp (str (System/getProperty "user.home") "/.ssh/id_rsa") :encoding "utf-8"))))
+    (.privateKey (slurp (str (System/getProperty "user.home") "/.ssh/id_rsa") :encoding "utf-8"))
+    (.build)))

 (defn options [login]
   (-> (TemplateOptions$Builder/overrideLoginCredentials login)


Remove println statements from record_spec.

--- src/active/clojure/record_spec.cljc
+++ src/active/clojure/record_spec.cljc
@@ -52,7 +52,6 @@
               (throw (IllegalArgumentException. (str "incomplete field spec for " spec " in " *ns* " " (meta &form)))))
             (when-not (symbol? (fnext specs))
               (throw (IllegalArgumentException. (str "invalid accessor " (fnext specs) " for " spec " in " *ns* " " (meta &form)))))
-            (println "spec:" (:spec (meta (fnext specs))))
             (recur (nnext specs)
                    (conj triples [spec (fnext specs) {:spec (or (:spec (meta spec)) any?)}])))
           :else
@@ -81,7 +80,6 @@
   => (clojure.spec.alpha/def :calling.name.space/foo bar?)
   ```"
   [the-name the-predicate]
-  (println "define-spec-form" the-name the-predicate)
   (let [ns-key (ns-keyword the-name)]
     `(s/def ~ns-key ~the-predicate)))

@@ -160,9 +158,7 @@
   (when-not (even? (count (remove seq? ?field-specs)))
     (throw (IllegalArgumentException. (str "odd number of elements in field specs in " *ns* " " (meta &form)))))
   (when-not (every? true? (map #(= 3 (count %)) (filter seq? ?field-specs)))
-    (do
-      (println (every? true? (map #(= 3 (count %)) (filter seq? ?field-specs))))
-      (throw (IllegalArgumentException. (str "wrong number of elements in field specs with lens in " *ns* " " (meta &form))))))
+    (throw (IllegalArgumentException. (str "wrong number of elements in field specs with lens in " *ns* " " (meta &form)))))

   (let [?field-triples (field-triples ?field-specs &form)
         ?constructor (first ?constructor-call)


Fix regression introduced in commit dc73bd26c7531ab768e92b7a4202ce2e6c7dea38

--- src/adzerk/boot_cljs/middleware.clj
+++ src/adzerk/boot_cljs/middleware.clj
@@ -75,7 +75,7 @@
         (update-in [:opts :asset-path] #(if % % asset-path))
         (set-option :output-dir out-path)
         (set-option :output-to js-path)
-        (set-option :main main))))
+        (set-option :main cljs-ns))))

 (defn modules
   "If .cljs.edn file contains modules declaration, use it to create options


fix datomic connection

fix datomic connection

--- src/clj/lesezeichen/core.clj
+++ src/clj/lesezeichen/core.clj
@@ -121,7 +121,7 @@

 (defn init-db [state]
-  (let [conn (scratch-conn)]
+  (let [conn (db-conn)]
     (swap! state #(assoc %1 :conn %2) conn))
   (init-schema (:conn @state) (:schema @state))
   state)


learned about difference between special-symbols and special-forms… changed clj-info to do the right thing with those special-things, and also changed the special-forms related list/map in utils to reflect this new "understanding"

--- src/clj_ns_browser/utils.clj
+++ src/clj_ns_browser/utils.clj
@@ -20,7 +20,137 @@
         [clj-info.doc2html :only [doc2html]]
         [alex-and-georges.debug-repl]))

-(declare fqname)
+
+;; clojure.core/special-symbol? tests for inclusion in:
+;; clj-ns-browser.utils=> (sort (keys (. clojure.lang.Compiler specials)))
+;; (& . case* catch def deftype* do finally fn* if let* letfn* loop* monitor-enter
+;; monitor-exit new quote recur reify* set! throw try var clojure.core/import*)
+;;
+;; following list of special forms is from clojure website specs:
+;; [. def do fn if let loop monitor-enter monitor-exit new quote recur set! throw try var]
+;; some special forms seem to be implemented as macros with help of special-symbols (?)
+
+
+(def special-forms
+  "List of name-strings for those special-form that are not vars also."
+  (sort (map name (keys (. clojure.lang.Compiler specials)))))
+
+
+;; resolve-fqname and fqname are the two basic resolution functions
+;; to find objects for names and names for objects, respectively.
+
+(defn resolve-fqname
+  "Returns the resolved var/class/namespace for the (fully qualified) name a-fqn (string, symbol, var, class or namespace). Name can be a local name mapped in namespace a-ns.
+  Returns nil if name cannot be resolved or if it's a non-var special-form.
+  Name is resolved within namespace a-ns (string, symbol or ns), which defaults to *ns*.
+  "
+  ([a-fqn] (resolve-fqname *ns* a-fqn))
+  ([a-ns a-fqn]
+    (when-let [ns-0 (or (and (= (type a-ns) clojure.lang.Namespace) a-ns)
+                        (find-ns (symbol a-ns)))]
+      (let [t (type a-fqn)]
+        (if (or (var? a-fqn)(= (type a-fqn) clojure.lang.Namespace)(instance? java.lang.Class a-fqn))
+          a-fqn
+          (when-let [fqn-str (or (and (string? a-fqn) (not= a-fqn "") a-fqn)
+                                 (and (symbol? a-fqn) (str a-fqn)))]
+            (let [fqn (if (= fqn-str "clojure.core//") "/" fqn-str)] ;; special corner case :-(
+              (if-let [ns (find-ns (symbol fqn))]
+                ns
+                (when-let [var-or-class (try (resolve ns-0 (symbol fqn))(catch Exception e))]
+                  var-or-class)))))))))
+
+
+(defn fqname
+  "Returns the fully qualified name-string of a var, class or ns
+  for n within an (optional) namespace, which may or
+  may not be used for the resolution, depending whether n is already FQ'ed.
+  Input n: string, (quoted-)symbol, var, or actual class or ns.
+        ns: (optional) namespace as ns, symbol or string - default to *ns*.
+  Output: FQName as string or nil."
+  ([n] (cond
+          (keyword? n) (if-let [nsp (namespace n)]
+                         (str nsp "/" (name n))
+                         (name n))
+          :else (fqname *ns* n)))
+  ([ns n]
+    (let [n-str (str n)
+          n-sym (symbol n-str)]
+      (if (= n-str "clojure.core//")
+        "clojure.core//"  ;; special corner case :-(
+        (if (some #(= % n-str) special-forms) n-str
+          (if-let [n-ns (find-ns n-sym)]
+            (str n-ns)
+            (if-let [n-class (= (type n) java.lang.Class)]
+              (.getName n)
+              (when-let [ns-ns (if (= (type ns) clojure.lang.Namespace)
+                                 ns
+                                 (find-ns (symbol (str ns))))]
+                (if-let [var-n (when (var? n) (str (.ns n) "/" (.sym n)))]
+                  var-n
+                  (let [n-sym (symbol (str n))]
+                    (when-let [v-n (try (ns-resolve ns-ns n-sym)(catch Exception e))]
+                      (if (var? v-n)
+                        (str (.ns v-n) "/" (.sym v-n))
+                        (.getName v-n)))))))))))))
+
+
+(defn ns-name-class-str
+  "Given a FQN, return the namespace and name in a list as separate strings.
+  clojure.core/map => [\"clojure.core\" \"map\"]
+  clojure.core => [\"clojure.core\" nil]
+  clojure.lang.Var => [nil \"clojure.lang.Var\"]
+  any error returns nil."
+  [fqn]
+  (when-let [fqn-sym (and fqn (= (type fqn) java.lang.String) (symbol fqn))]
+    (let [n-str (name fqn-sym)
+          ns-str (namespace fqn-sym)]
+      (if ns-str
+        [ns-str n-str]
+        (if (find-ns (symbol n-str))
+          [n-str nil]
+          [nil n-str])))))
+
+
+;; "clojure.core//" appears to be a special case not handled well by
+;; the function symbol.
+(defn better-symbol [fqn]
+  (if (= fqn "clojure.core//")
+    (symbol "clojure.core" "/")
+    (symbol fqn)))
+
+
+;; basic type-predicates
+;; note that we already have char? class? coll? decimal? empty? fn? ifn? future? keyword? list?
+;; map? nil? number? seq? sequential? set? special-symbol? string? symbol? var? vector?
+
+(defn special-form?
+  "Predicate that returns true if given name n (string or symbol) is a special-form and false otherwise.
+  Note that some special forms are vars/macros, and some are special-symbols.
+  All the none-vars are tested by clojure.core/special-symbol?"
+  [n]
+  (if (and (var? n) (:special-form (meta n)))
+    true
+    (when-let [n-str (and n (str n))]
+      (or (some #(= % n-str) special-forms)
+        (when-let [v (resolve-fqname n-str)]
+          (and (var? v) (:special-form (meta v))))))))
+
+
+(defn macro?
+  "Predicate that returns true when var v is a macro, and false otherwise.
+  Note that input is a var - if you want to input a name-string or -symbol, use:
+  (macro? (resolve-fqname n))"
+  [v]
+  (and (var? v) (:macro (meta v))))
+
+
+(defn namespace?
+  "Predicate that returns true when n refers to a namespace, and false otherwise.
+  Note that input is a name referring to a namespace - if you want to input a name-string or -symbol, use:
+  (namespace? (resolve-fqname n))"
+  [maybe-ns]
+  (isa? clojure.lang.Namespace (type maybe-ns)))
+

 ;; Two convenience function for clojure.tools.trace
 ;; that should ideally be part of that library
@@ -85,15 +215,16 @@
 	  input-string)))))

-;;
-
-(def special-forms
-  (sort (map name '[def if do let quote var fn loop recur throw try monitor-enter monitor-exit . new set!])))
-
-(defn special-form? [n-str] (some #(= % n-str) special-forms))
-
-(defn ns-special-forms [& no-op]
-  '{"def" def "if" if "do" do "let" let "quote" quote "var" var "fn" fn "loop" loop "recur" recur "throw" throw "try" try "monitor-enter" monitor-enter "monitor-exit" monitor-exit "." . "new" new "set!" set!})
+;; functions to collect different (filtered/sub-) lists of vars, classes, special-forms, etc.
+
+(defn ns-special-forms
+  "Collects the special form string-symbol map from both the special-symbol list and the special-form vars in clojure.core.
+  Returns a map that has same format as ns-map, ns-publics and friends."
+  [& no-op]
+  (let [str-list (concat special-forms (map name (filter special-form? (keys (ns-map *ns*)))))
+        sym-list (map symbol str-list)]
+    (zipmap str-list sym-list)))
+

 (defn symbols-of-ns-coll [ns-action f ns-coll display-fqn? search-doc-strings?]
   (when-not (or (nil? ns-coll) (empty? ns-coll) (nil? (first ns-coll)))
@@ -141,6 +272,7 @@
              (mapcat (fn [a-ns] (map g (f a-ns)))
                      ns-coll))))))

+
 (defn filter-key [keyfn pred amap]
   (loop [ret {} es (seq amap)]
     (if es
@@ -148,6 +280,7 @@
         (recur (assoc ret (key (first es)) (val (first es))) (next es))
         (recur ret (next es)))
       ret)))
+

 (defn ns-privates
   "Returns a map of the private (i.e. not public) intern mappings for
@@ -160,6 +293,7 @@
                            (not (.isPublic v))))
                 (ns-map ns))))

+
 (defn all-ns-classpath
   "Returns a sorted set of the name-strings of all namespaces found on class-path."
   []
@@ -179,6 +313,7 @@
   (apply sorted-set
     (clojure.set/difference (all-ns-classpath) (all-ns-loaded))))

+
 (defn all-ns-loaded-unloaded
   "Returns a sorted set of the name-strings of all unloaded namespaces."
   []
@@ -190,68 +325,6 @@
   "Returns whether a ns is on the classpath but not loaded/required (yet)."
   [ns]
   (let [nss (str ns)] (get (all-ns-unloaded) nss)))
-
-
-(defn fqname
-  "Returns the fully qualified name-string of a var, class or ns
-  for a name n within an (optional) namespace, which may or
-  may not be used for the resolution, depending whether n is already FQ'ed.
-  Input n: string, (quoted-)symbol, var, or actual class or ns.
-        ns: (optional) namespace as ns, symbol or string - default *ns*.
-  Output: FQName as string or nil."
-  ([n] (cond
-          (keyword? n) (if-let [nsp (namespace n)]
-                         (str nsp "/" (name n))
-                         (name n))
-          :else (fqname *ns* n)))
-  ([ns n]
-    (let [n-str (str n)
-          n-sym (symbol n-str)]
-      (if (= n-str "clojure.core//")
-        "clojure.core//"  ;; special corner case :-(
-        (if (some #(= % n-str) special-forms) n-str
-          (if-let [n-ns (find-ns n-sym)]
-            (str n-ns)
-            (if-let [n-class (= (type n) java.lang.Class)]
-              (.getName n)
-              (when-let [ns-ns (if (= (type ns) clojure.lang.Namespace)
-                                 ns
-                                 (find-ns (symbol (str ns))))]
-                (if-let [var-n (when (var? n) (str (.ns n) "/" (.sym n)))]
-                  var-n
-                  (let [n-sym (symbol (str n))]
-                    (when-let [v-n (try (ns-resolve ns-ns n-sym)(catch Exception e))]
-                      (if (var? v-n)
-                        (str (.ns v-n) "/" (.sym v-n))
-                        (.getName v-n)))))))))))))
-
-
-(defn ns-name-class-str
-  "Given a FQN, return the namespace and name in a list as separate strings.
-  clojure.core/map => [\"clojure.core\" \"map\"]
-  clojure.core => [\"clojure.core\" nil]
-  clojure.lang.Var => [nil \"clojure.lang.Var\"]
-  any error returns nil."
-  [fqn]
-  (when-let [fqn-sym (and fqn (= (type fqn) java.lang.String) (symbol fqn))]
-    (let [n-str (name fqn-sym)
-          ns-str (namespace fqn-sym)]
-      (if ns-str
-        [ns-str n-str]
-        (if (find-ns (symbol n-str))
-          [n-str nil]
-          [nil n-str])))))
-
-
-(defn resolve-fqname
-  "Returns the resolved fully qualified name fqn (string) or nil."
-  [a-fqn]
-  (when (and (string? a-fqn) (not= a-fqn ""))
-    (let [fqn (if (= a-fqn "clojure.core//") "/" a-fqn)] ;; special corner case :-(
-      (if-let [ns (find-ns (symbol fqn))]
-        ns
-        (when-let [var-or-class (try (resolve *ns* (symbol fqn))(catch Exception e))]
-          var-or-class)))))

 (defn pprint-str
@@ -278,9 +351,6 @@
 ;; (val-kv-filter {} {:a true :b false :c true})
 ;; (val-kv-filter {:a "fn-a" :b "fn-b" :c "fn-c" :d "fn-d"} {})
 ;; (val-kv-filter {} {})
-
-
-(defn no-nils [coll] (filter #(not (nil? %)) coll))

 ;; The clean-* functions are probably best removed after changing
@@ -349,7 +419,7 @@
         (str "Sorry, not a fully qualified clojure name: " fqn)))))

-(def ^:dynamic *max-value-display-size* 500)
+(def ^:dynamic *max-value-display-size* 2500)

 ;; TBD: Problems with these, probably because of problems with
 ;; cd-client.core behavior:
@@ -377,14 +447,6 @@
 ;; var?

 ;; (in-ns 'clj-ns-browser.utils)
-
-
-;; "clojure.core//" appears to be a special case not handled well by
-;; the function symbol.
-(defn better-symbol [fqn]
-  (if (= fqn "clojure.core//")
-    (symbol "clojure.core" "/")
-    (symbol fqn)))

 (defn eval-sym [sym]


Small type hint

Small type hint

Signed-off-by: Daniel Borchmann <25857343a15bf1edafebc2912e82ecf775c585c1@mailbox.tu-dresden.de>

--- src/conexp/tests/io/util.clj
+++ src/conexp/tests/io/util.clj
@@ -22,7 +22,7 @@
         reader (resolve (symbol "conexp.io" (str "read-" type)))]
     (when (or (nil? writer) (nil? reader))
       (illegal-argument "out-in called with invalid type " type "."))
-    (let [tmp (. (tmpfile) getAbsolutePath)]
+    (let [tmp (.getAbsolutePath ^java.io.File (tmpfile))]
       (@writer format object tmp)
       (@reader tmp))))


Give middleware anonymous handler functions better names, so they are more meaningful in stack traces

--- src/re_frame/middleware.cljs
+++ src/re_frame/middleware.cljs
@@ -82,7 +82,7 @@
         ....)
   "
   [handler]
-  (fn new-handler
+  (fn trim-v-handler
     [db v]
     (handler db (vec (rest v)))))

@@ -140,16 +140,16 @@
   complete reassesment of duplication errors and warnings. Eg: that edit
   update might have introduced a new duplicate or removed one. Same with a
   todo removal.
-  And to perform this enrichment, a function has inspect all the todos,
+  And to perform this enrichment, a function has to inspect all the todos,
   possibly set flags on each, and set some overall list of duplicates.
-  And this duplicates checking might be just one check amoung a number.
+  And this duplication check might just be one check amoung many.
   \"f\" would need to be both adding and removing the duplicate warnings.
   By applying \"f\" in middleware, we keep the handlers simple and yet we
-  ensure this important step is not missed. "
+  ensure this important step is not missed."
   [f]
   (fn middleware
     [handler]
-    (fn validate-handler
+    (fn enrich-handler
       [db v]
       (f (handler db v) v))))


Gracefully stop server

Gracefully stop server

--- src/servisne_info/webapp.clj
+++ src/servisne_info/webapp.clj
@@ -2,8 +2,19 @@
   (:use org.httpkit.server)
   (:require [servisne-info.handler :as handler]))

+(defonce server (atom nil))
+
+(defn stop-server []
+  (when-not (nil? @server)
+    ;; graceful shutdown: wait 100ms for existing requests to be finished
+    ;; :timeout is optional, when no timeout, stop immediately
+    (@server :timeout 100)
+    (handler/destroy)
+    (reset! server nil)))
+
 (defn -main [& args]
   (let [port (Integer/parseInt (get (System/getenv) "OPENSHIFT_CLOJURE_HTTP_PORT" "8080"))
         ip (get (System/getenv) "OPENSHIFT_CLOJURE_HTTP_IP" "0.0.0.0")]
-    (run-server handler/app {:ip ip :port port})
+    (reset! server (run-server handler/app {:ip ip :port port}))
+    (.addShutdownHook (Runtime/getRuntime) (Thread. stop-server))
     (handler/init)))


bootstrap ocamlbuild again (needed for the cross-compilation patches)

git-svn-id: e7e61699ce1738538ffebb75374fbea0924bc775@14174 f963ae5c-01c2-4b8c-9fe0-0dff7051ff02