;; You can write a 'new' task yourself without any extra plugins like ;; lein-newnew. What makes lein-new so useful is the `templates` task for ;; listing templates and this file. The primary problem with writing your ;; own project scaffolding tools that are domain-specific is you ;; generally have to reimplement the same things every single time. With ;; lein-new, you have this little library that your templates can use. ;; It has all the things a template is likely to need: ;; * an easy way to generate files and namespaces ;; * a way to render files written with a flexible template language ;; * a way to get those files off of the classpath transparently (ns leiningen.new.templates (:require [clojure.java.io :as io] [clojure.string :as string] [leiningen.core.eval :as eval] [leiningen.core.user :as user] [leiningen.core.utils :as utils] [leiningen.core.main :as main] [stencil.core :as stencil]) (:import (java.util Calendar))) (defn project-name "Returns project name from (possibly group-qualified) name: mygroup/myproj => myproj myproj => myproj" [s] (last (string/split s #"/"))) (defn fix-line-separators "Replace all \\n with system specific line separators." [s] (let [line-sep (if (user/getenv "LEIN_NEW_UNIX_NEWLINES") "\n" (user/getprop "line.separator"))] (string/replace s "\n" line-sep))) (defn slurp-to-lf "Returns the entire contents of the given reader as a single string. Converts all line endings to \\n." [r] (let [sb (StringBuilder.)] (loop [s (.readLine r)] (if (nil? s) (str sb) (do (.append sb s) (.append sb "\n") (recur (.readLine r))))))) (defn slurp-resource "Reads the contents of a resource. Temporarily converts line endings in the resource to \\n before converting them into system specific line separators using fix-line-separators." [resource] (if (string? resource) ; for 2.0.0 compatibility, can break in 3.0.0 (-> resource io/resource io/reader slurp-to-lf fix-line-separators) (-> resource io/reader slurp-to-lf fix-line-separators))) (defn sanitize "Replace hyphens with underscores." [s] (string/replace s "-" "_")) (defn multi-segment "Make a namespace multi-segmented by adding another segment if necessary. The additional segment defaults to \"core\"." ([s] (multi-segment s "core")) ([s final-segment] (if (.contains s ".") s (format "%s.%s" s final-segment)))) (defn name-to-path "Constructs directory structure from fully qualified artifact name: \"foo-bar.baz\" becomes \"foo_bar/baz\" and so on. Uses platform-specific file separators." [s] (-> s sanitize (string/replace "." java.io.File/separator))) (defn sanitize-ns "Returns project namespace name from (possibly group-qualified) project name: mygroup/myproj => mygroup.myproj myproj => myproj mygroup/my_proj => mygroup.my-proj" [s] (-> s (string/replace "/" ".") (string/replace "_" "-"))) (defn group-name "Returns group name from (a possibly unqualified) name: my.long.group/myproj => my.long.group mygroup/myproj => mygroup myproj => nil" [s] (let [grpseq (butlast (string/split (sanitize-ns s) #"\."))] (if (seq grpseq) (->> grpseq (interpose ".") (apply str))))) (defn year "Get the current year. Useful for setting copyright years and such." [] (.get (Calendar/getInstance) Calendar/YEAR)) (defn date "Get the current date as a string in ISO8601 format." [] (let [df (java.text.SimpleDateFormat. "yyyy-MM-dd")] (.format df (java.util.Date.)))) ;; It'd be silly to expect people to pull in stencil just to render a mustache ;; string. We can just provide this function instead. In doing so, it is much ;; less likely that template authors will have to pull in any external ;; libraries. Though they are welcome to if they need. (def render-text stencil/render-string) ;; Templates are expected to store their mustache template files in ;; `leiningen/new/