io.project project configuration support

Author: Chris Zheng  (z@caudate.me)
Date: 27 November 2018
Repository: https://github.com/zcaudate/hara
Version: 3.0.2

1    Introduction

hara.io.project reads the project level descriptor.

1.1    Installation

Add to project.clj dependencies:

[hara/io.project "3.0.2"]

All functions are in the hara.io.project namespace.

 (use (quote hara.io.project))

2    API



all-files ^

returns all the clojure files in a directory

v 3.0
(defn all-files
  ([] (all-files ["."]))
  ([paths] (all-files paths {}))
  ([paths opts]
   (all-files paths opts (project)))
  ([paths opts project]
   (let [filt (-> {:include *include*}
                  (merge opts)
                  (update-in [:exclude]
                             conj
                             fs/link?))
         result (->> paths
                     (map #(fs/path (:root project) %))
                     (mapcat #(fs/select % filt))
                     (map str)
                     (map (juxt file-namespace identity))
                     (into {}))]
     (dissoc result nil))))
link
(count (all-files ["test"])) => number? (-> (all-files ["test"]) (get 'hara.io.project-test)) => #(.endsWith ^String % "/test/hara/io/project_test.clj")

exclude ^

helper function for excluding certain namespaces

v 3.0
(defn exclude
  [lookup exclusions]
  (reduce-kv (fn [out ns v]
               (let [nss (str ns)
                     exclude? (->> exclusions
                                   (map (fn [ex]
                                          (.startsWith nss ex)))
                                   (some true?))]
                 (if exclude?
                   out
                   (assoc out ns v))))
             {}

             lookup))
link
(exclude '{lucid.legacy.analyzer :a lucid.legacy :a hara.lib.aether :b} ["lucid.legacy"]) => '{hara.lib.aether :b}

file-lookup ^

creates a lookup of namespaces and files in the project

v 3.0
(defn file-lookup
  ([] (file-lookup (project)))
  ([project]
   (all-files (concat (:source-paths project)
                      (:test-paths project))
              {}
              project)))
link
(-> (file-lookup (project)) (get 'hara.io.project)) => #(.endsWith ^String % "/src/hara/io/project.clj")

file-namespace ^

reads the namespace of the given path

v 3.0
(defn file-namespace
  [path]
  (try
    (->> (fs/read-code path)
         (filter #(-> % first (= 'ns)))
         first
         second)
    (catch Throwable t
      (println path "Cannot be loaded"))))
link
(file-namespace "src/hara/io/project.clj") => 'hara.io.project

file-suffix ^

returns the file suffix for a given type

v 3.0
(defn file-suffix
  ([] (file-suffix common/*type*))
  ([type]
   (-> (common/type-lookup type) :extension)))
link
(file-suffix) => ".clj" (file-suffix :cljs) => ".cljs"

file-type ^

returns the type of file according to the suffix

v 3.0
(defn file-type
  [path]
  (cond (.endsWith (str path)
                   (str (munge (test-suffix))
                        (file-suffix)))
        :test

        :else
        :source))
link
(file-type "project.clj") => :source (file-type "test/hara/code_test.clj") => :test

in-context ^

creates a local context for executing code functions

v 3.0
(defmacro in-context
  [[func & args]]
  (let [project `(project)
        lookup  `(all-files ["src" "test"] {} ~'project)
        current `(.getName *ns*)
        params  `{}]
    (case (count args)
      0  `(let [~'project ~project]
            (~func ~current ~params ~lookup ~'project))
      1   (if (map? (first args))
            `(let [~'project ~project]
               (~func ~current ~(first args) ~lookup ~'project))
            `(let [~'project ~project]
               (~func ~(first args) ~params ~lookup ~'project)))
      2   `(let [~'project ~project]
             (~func ~@args ~lookup ~'project)))))
link
(in-context ((fn [current params _ project] [current (:name project)]))) => '[hara.io.project-test hara]

project ^

returns project options as a map

v 3.0
(defn project
  ([] (project (project-file)))
  ([path]
   (cond (nil? path)
         (throw (ex-info "Cannot find project" {:path nil}))
         
         (fs/input-stream? path)
         (lein/project path)
         
         :else
         (project-fn path (-> (fs/path path)
                              (fs/attributes)
                              (:last-modified-time))))))
link
(project) => (contains {:name symbol? :dependencies vector?})

project-file ^

returns the current project file

v 3.0
(defn project-file
  []
  (->> [lein/*project-file*
        shadow/*shadow-file*]
       (filter fs/exists?)
       (first)))
link
(project-file) => "project.clj"

project-name ^

returns the name, read from the project map

v 3.0
(defn project-name
  ([] (:name (project)))
  ([path]
   (:name (project path))))
link
(project-name) => 'hara

source-ns ^

returns the source namespace

v 3.0
(defn source-ns
  [ns]
  (let [sns (str (sym-name ns))
        suffix (test-suffix)
        sns (if (.endsWith (str sns) suffix)
              (subs sns 0 (- (count sns) (count suffix)))
              sns)]
    (symbol sns)))
link
(source-ns 'a) => 'a (source-ns 'a-test) => 'a

sym-name ^

returns the symbol of the namespace

v 3.0
(defn sym-name
  [x]
  (cond (instance? clojure.lang.Namespace x)
        (.getName x)

        (symbol? x)
        x

        :else
        (throw (ex-info "Only symbols and namespaces are supported" {:type (type x)
                                                                     :value x}))))
link
(sym-name *ns*) => 'hara.io.project-test (sym-name 'a) => 'a

test-ns ^

returns the test namespace

v 3.0
(defn test-ns
  [ns]
  (let [sns (str (sym-name ns))
        suffix (test-suffix)
        sns (if (.endsWith (str sns) suffix)
              sns
              (str sns suffix))]
    (symbol sns)))
link
(test-ns 'a) => 'a-test (test-ns 'a-test) => 'a-test

test-suffix ^

returns the test suffix

v 3.0
(defn test-suffix
  ([] common/*test-suffix*)
  ([s] (alter-var-root #'common/*test-suffix*
                       (constantly s))))
link
(test-suffix) => "-test"