Skip to content

Commit

Permalink
Refactor template function
Browse files Browse the repository at this point in the history
First step toward adding external profiles.
  • Loading branch information
weavejester committed Jan 28, 2019
1 parent f1194d2 commit 91b4af0
Show file tree
Hide file tree
Showing 2 changed files with 145 additions and 103 deletions.
71 changes: 71 additions & 0 deletions lein-template/src/duct/duct_template.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
(ns duct.duct-template
(:require [clojure.java.io :as io]))

(defn resource [name]
(io/resource (str "leiningen/new/duct/" name)))

(def ^:private web-directories
["resources/{{dirs}}/public"
"src/{{dirs}}/handler"
"test/{{dirs}}/handler"])

(defn example-profile [{:keys [profiles]}]
{:vars {:example? true}
:templates
(cond
(profiles :site)
{"src/{{dirs}}/handler/example.clj" (resource "example/handler.clj")
"test/{{dirs}}/handler/example_test.clj" (resource "example/handler_test.clj")
"resources/{{dirs}}/handler/example/example.html" (resource "example/example.html")}

(profiles :api)
{"src/{{dirs}}/handler/example.clj" (resource "example/handler.clj")
"test/{{dirs}}/handler/example_test.clj" (resource "example/handler_test.clj")}

:else
{"src/{{dirs}}/service/example.clj" (resource "example/service.clj")
"test/{{dirs}}/service/example_test.clj" (resource "example/service_test.clj")})})

(defn api-profile [_]
{:deps '[[duct/module.web "0.7.0"]]
:dev-deps '[[kerodon "0.9.0"]]
:vars {:api? true}
:dirs web-directories})

(defn site-profile [_]
{:deps '[[duct/module.web "0.7.0"]]
:dev-deps '[[kerodon "0.9.0"]]
:vars {:site? true}
:dirs web-directories})

(defn cljs-profile [_]
{:deps '[[duct/module.web "0.7.0"]
[duct/module.cljs "0.4.0"]]
:dev-deps '[[kerodon "0.9.0"]]
:vars {:cljs? true}
:dirs web-directories
:templates {"src/{{dirs}}/client.cljs" (resource "cljs/client.cljs")}})

(defn heroku-profile [{:keys [project-name]}]
{:vars {:uberjar-name (str project-name "-standalone.jar")}
:templates {"Procfile" (resource "heroku/Procfile")}})

(defn postgres-profile [_]
{:deps '[[duct/module.sql "0.5.0"]
[org.postgresql/postgresql "42.2.5"]]
:vars {:jdbc? true
:postgres? true
:dev-database "jdbc:postgresql://localhost/postgres"}})

(defn sqlite-profile [_]
{:deps '[[duct/module.sql "0.5.0"]
[org.xerial/sqlite-jdbc "3.25.2"]]
:dirs ["db"]
:vars {:jdbc? true
:sqlite? true
:dev-database "jdbc:sqlite:db/dev.sqlite"}})

(defn ataraxy-profile [_]
{:deps '[[duct/module.ataraxy "0.3.0"]]
:vars {:ataraxy? true, :web? true}
:dirs web-directories})
177 changes: 74 additions & 103 deletions lein-template/src/leiningen/new/duct.clj
Original file line number Diff line number Diff line change
@@ -1,110 +1,84 @@
(ns leiningen.new.duct
(:require [clojure.java.io :as io]
[leiningen.core.main :as main]
[leiningen.new.templates :refer [renderer year project-name
->files sanitize-ns name-to-path]]))

(def render (renderer "duct"))
[leiningen.new.templates :as templates]))

(defn resource [name]
(io/input-stream (io/resource (str "leiningen/new/duct/" name))))

(defmulti profile-data (fn [module name] module))
(defmulti profile-files (fn [module data] module))

(defmethod profile-data :default [_ _] {})
(defmethod profile-files :default [_ _] [])

(defmethod profile-data :base [_ name]
(let [main-ns (sanitize-ns name)]
{:raw-name name
:name (project-name name)
:namespace main-ns
:dirs (name-to-path main-ns)
:year (year)
:web-module :duct.module/web}))

(defmethod profile-files :base [_ data]
[["project.clj" (render "base/project.clj" data)]
["README.md" (render "base/README.md" data)]
[".gitignore" (render "base/gitignore" data)]
["dev/src/user.clj" (render "base/user.clj" data)]
["dev/src/dev.clj" (render "base/dev.clj" data)]
["dev/resources/dev.edn" (render "base/dev.edn" data)]
["resources/{{dirs}}/config.edn" (render "base/config.edn" data)]
["src/{{dirs}}/main.clj" (render "base/main.clj" data)]
["src/duct_hierarchy.edn" (render "base/duct_hierarchy.edn" data)]
"test/{{dirs}}"])

(def ^:private web-directories
["resources/{{dirs}}/public"
"src/{{dirs}}/handler"
"test/{{dirs}}/handler"])

(defmethod profile-data :example [_ _]
{:example? true})

(defmethod profile-files :example [_ data]
(if (:web? data)
(concat
[["src/{{dirs}}/handler/example.clj" (render "example/handler.clj" data)]
["test/{{dirs}}/handler/example_test.clj" (render "example/handler_test.clj" data)]]
(if (:site? data)
[["resources/{{dirs}}/handler/example/example.html"
(render "example/example.html" data)]]))
[["src/{{dirs}}/service/example.clj" (render "example/service.clj" data)]
["test/{{dirs}}/service/example_test.clj" (render "example/service_test.clj" data)]]))

(defmethod profile-data :api [_ _]
{:api? true, :web? true})

(defmethod profile-files :api [_ _]
web-directories)

(defmethod profile-data :site [_ _]
{:site? true, :web? true})

(defmethod profile-files :site [_ data]
web-directories)

(defmethod profile-data :cljs [_ _]
{:cljs? true, :site? true, :web? true})

(defmethod profile-files :cljs [_ data]
(conj web-directories
["src/{{dirs}}/client.cljs" (render "cljs/client.cljs" data)]))

(defmethod profile-data :heroku [_ name]
{:heroku? true
:uberjar-name (str (project-name name) "-standalone.jar")})

(defmethod profile-files :heroku [_ data]
[["Procfile" (render "heroku/Procfile" data)]])

(defmethod profile-data :postgres [_ name]
{:jdbc? true
:postgres? true
:dev-database "jdbc:postgresql://localhost/postgres"})

(defmethod profile-files :postgres [_ name] [])

(defmethod profile-data :sqlite [_ _]
{:jdbc? true
:sqlite? true
:dev-database "jdbc:sqlite:db/dev.sqlite"})

(defmethod profile-files :sqlite [_ _] ["db"])

(defmethod profile-data :ataraxy [_ _]
{:ataraxy? true, :web? true})

(defmethod profile-files :ataraxy [_ _]
web-directories)

(defn profiles [hints]
(io/resource (str "leiningen/new/duct/" name)))

(defn project-data [raw-name profiles]
(let [main-ns (templates/sanitize-ns raw-name)]
{:raw-name raw-name
:project-ns main-ns
:project-name (templates/project-name raw-name)
:project-path (templates/name-to-path main-ns)
:profiles (set profiles)}))

(defn base-profile [{:keys [project-name project-ns project-path raw-name]}]
{:vars
{:raw-name raw-name
:name project-name
:namespace project-ns
:dirs project-path
:year (templates/year)}
:dirs
["test/{{dirs}}"]
:templates
{"project.clj" (resource "base/project.clj")
"README.md" (resource "base/README.md")
".gitignore" (resource "base/gitignore")
"dev/src/user.clj" (resource "base/user.clj")
"dev/src/dev.clj" (resource "base/dev.clj")
"dev/resources/dev.edn" (resource "base/dev.edn")
"resources/{{dirs}}/config.edn" (resource "base/config.edn")
"src/{{dirs}}/main.clj" (resource "base/main.clj")
"src/duct_hierarchy.edn" (resource "base/duct_hierarchy.edn")}})

(defn profile-names [hints]
(for [hint hints :when (re-matches #"\+[A-Za-z0-9-]+" hint)]
(keyword (subs hint 1))))

(defn profile-function-symbol [profile-kw]
(let [ns (or (namespace profile-kw) "duct")]
(symbol (str ns ".duct-template")
(str (name profile-kw) "-profile"))))

(defn profile-function [profile-kw]
(let [sym (profile-function-symbol profile-kw)]
(require (symbol (namespace sym)))
(var-get (resolve sym))))

(defn merge-deps [a b]
(-> {} (into a) (into b) vec))

(defn merge-profiles [a b]
{:vars (merge (:vars a) (:vars b))
:dirs (into (set (:dirs a)) (:dirs b))
:deps (merge-deps (:deps a) (:deps b))
:dev-deps (merge-deps (:dev-deps a) (:dev-deps b))
:templates (into (:templates a) (:templates b))})

(defn project-template [name hints]
(let [profiles (profile-names hints)
data (project-data name profiles)]
(->> profiles
(map profile-function)
(map #(% data))
(reduce merge-profiles (base-profile data)))))

(defn render-resource [data template]
(templates/render-text (slurp template) data))

(defn render-templates [data templates]
(->> templates
(sort-by key)
(map (fn [[path temp]] [path (render-resource data temp)]))))

(defn generate-project [{:keys [vars templates] :as profile}]
(let [data (merge vars (select-keys profile [:deps :dev-deps]))
files (render-templates data templates)]
(apply templates/->files data files)))

(defn duct
"Create a new Duct web application.
Expand All @@ -121,8 +95,5 @@ Accepts the following profile hints:
(when (.startsWith name "+")
(main/abort "Failed to create project: no project name specified."))
(main/info (str "Generating a new Duct project named " name "..."))
(let [mods (cons :base (profiles hints))
data (reduce into {} (map #(profile-data % name) mods))
files (reduce into [] (map #(profile-files % data) mods))]
(apply ->files data files))
(generate-project (project-template name hints))
(main/info "Run 'lein duct setup' in the project directory to create local config files."))

0 comments on commit 91b4af0

Please sign in to comment.