1    Introduction

1.1    Overview

std.lib.env provides environment and system utilities including:

  • Namespace introspection
  • Resource management
  • Development helpers
  • Pretty printing utilities
  • Debug facilities

2    Walkthrough

2.1    Namespace and resource introspection

std.lib.env provides helpers for inspecting the current namespace and locating classpath resources.

inspect the current namespace and resources

^{:refer std.lib.env/ns-sym :added "3.0"}
(ns-sym)
=> 'documentation.std-lib-env

^{:refer std.lib.env/sys:resource :added "3.0"}
(sys:resource "std/lib.clj")
=> java.net.URL

^{:refer std.lib.env/sys:ns-url :added "4.0"}
(sys:ns-url 'std.lib.env)
=> java.net.URL

2.2    Local function overrides

local:set and local allow you to override functions like println within a scope. p is a shortcut for the local println.

override and call a local function

^{:refer std.lib.env/local:set :added "3.0"}
(local:set :test (fn [] :hello))
=> map?

^{:refer std.lib.env/local :added "3.0"}
(local :test)
=> :hello

^{:refer std.lib.env/p :added "3.0"}
(with-out-str (p "hello"))
=> "hello\n"

2.3    Pretty printing

pp-str returns a pretty-printed string, and pp writes it to the local pprint output.

pretty print values

^{:refer std.lib.env/pp-str :added "4.0"}
(pp-str {:a 1})
=> "{:a 1}"

^{:refer std.lib.env/pp :added "3.0"}
(with-out-str (pp {:a 1}))
=> string?

3    Namespace Utilities



dev? ^

[]
Added 3.0

checks if current environment is dev

v 3.0
(defn dev?
  ([]
   (-> (try
         (the-ns 'nrepl.core)
         (catch Exception e))
       boolean)))
link
(dev?) => boolean?

ns-get ^

[sym] [ns k]
Added 3.0

gets a symbol in the current namespace

v 3.0
(defn ns-get
  ([sym]
   (ns-get clojure.core/*ns* sym))
  ([ns k]
   (let [elem (resolve (symbol (str ns) (str k)))]
     (if elem
       @elem
       (ex-info (format "Cannot find %s in namespace" k)
                {:ns ns})))))
link
(ns-get 'std.lib.env "ns-get") => fn?

ns-sym ^

[]
Added 3.0

returns the namespace symbol

v 3.0
(defn ns-sym
  ([]
   (if (instance? clojure.lang.Namespace *ns*)
     (.getName *ns*)
     (throw (ex-info "Not a namespace" {:input *ns*})))))
link
(ns-sym) => 'std.lib.env-test

require ^

[sym]
Added 3.0

a concurrency safe require

v 3.0
(defn require
  ([sym]
   (#'clojure.core/serialized-require sym)))
link
(require 'std.lib.env) => nil

4    Resource Management



sys:ns-dir ^

[] [ns]
Added 4.0

gets the dir for ns

v 4.0
(defn sys:ns-dir
  ([]
   (sys:ns-dir (.getName *ns*)))
  ([ns]
   (-> (.getFile (sys:ns-url ns))
       (java.io.File.)
       (.getParent))))
link
(sys:ns-dir 'std.lib.env) => string?

sys:ns-file ^

[] [ns]
Added 4.0

gets the file for ns

v 4.0
(defn sys:ns-file
  ([]
   (sys:ns-file (.getName *ns*)))
  ([ns]
   (-> (.getFile (sys:ns-url ns))
       (java.io.File.)
       (str))))
link
(sys:ns-file 'std.lib.env) => string?

sys:ns-url ^

[] [ns]
Added 4.0

gets the url for ns

v 4.0
(defn ^java.net.URL sys:ns-url
  ([]
   (sys:ns-url (.getName *ns*)))
  ([ns]
   (-> (name ns)
       (.replace - _)
       (.replace . /)
       (str ".clj")
       (sys:resource))))
link
(sys:ns-url 'std.lib.env) => java.net.URL

sys:resource ^

[n] [n loader]
Added 3.0

finds a resource on class path

v 3.0
(defn ^java.net.URL sys:resource
  ([n] (sys:resource n (.getContextClassLoader (Thread/currentThread))))
  ([n ^ClassLoader loader] (.getResource loader n)))
link
(sys:resource "std/lib.clj") => java.net.URL

sys:resource-cached ^

[atom path f]
Added 4.0

caches the operation on a resource call

v 4.0
(defn sys:resource-cached
  [atom path f]
  (let [url   (sys:resource path)
        fpath (.getFile url)
        t     (if fpath (.lastModified (java.io.File. fpath)))]
    (cond (not t)
          (f url)

          :else
          (at/swap-return! atom
            (fn [m]
              (let [{:keys [time content]} (get m path)]
                (if (= time t)
                  [content m]
                  (let [content (f url)]
                    [content (assoc m path {:time t :content content})]))))))))
link
(sys:resource-cached (atom {}) "scratch.clj" slurp) => string?

sys:resource-content ^

[path]
Added 3.0

reads the content

v 3.0
(defn sys:resource-content
  ([path]
   (sys:resource-cached +sys:resource-content+
                        path
                        slurp)))
link
(sys:resource-content "scratch.clj") => string?

5    Pretty Printing



^

[& args]
Added 3.0

shortcut to `(local :println)`

v 3.0
(defn p
  ([& args]
   (apply local :println args)))
link
(with-out-str (p "hello")) => "hellon"

pl ^

[body] [body range]
Added 3.0

print with lines

v 3.0
(defmacro pl
  ([body]
   (with-meta `(pl ~body nil) (meta &form)))
  ([body range]
   (let [{:keys [line column]} (meta &form)]
     `(do (local :println (format "%s (%d:%d)"
                                  ~(str (ns-sym))
                                  ~line
                                  ~column))
          (local :println
                 (pl-add-lines (with-out-str (print ~body))
                               ~range)
                 "n")))))
link
(with-out-str (pl "hello")) => string?

pp-fn ^

[& body]
Added 4.0

the pp print function

v 4.0
(defn pp-fn
  ([& body]
   (doseq [v body]
     (p (pp-str v) "n"))))
link
(with-out-str (pp-fn {:a 1})) => (any string? nil?)

pp-str ^

[& args]
Added 4.0

pretty prints a string

v 4.0
(defn pp-str
  [& args]
  (clojure.string/join "n" (map #(local :pprint-str %) args)))
link
(pp-str {:a 1}) => "{:a 1}"

pr ^

[& args]
Added 4.0

shortcut to (local :print ...)

v 4.0
(defn pr
  ([& args]
   (apply local :print args)))
link
(with-out-str (pr "hello")) => "hello"

prf ^

[v & [no-pad]]
Added 4.0

pretty prints with format

v 4.0
(defn prf
  [v & [no-pad]]
  (when (not no-pad) (local :println ""))
  (if (string? v)
    (local :println (pl-add-lines v []))
    (do (when (not no-pad)
          (local :println ""))
        (local :pprint v))))
link
(with-out-str (prf "hello")) => string?

6    Debug Utilities



dbg-global ^

[] [flag]
Added 4.0

access or set the global *debug* flag

v 4.0
(defn dbg-global
  ([] *debug*)
  ([flag]
   (alter-var-root #'*debug* (fn [_] flag))))
link
(dbg-global) => boolean?

dbg-print ^

[ns-str {:keys [line column]} & args]
Added 4.0

prints debug info with namespace and location

v 4.0
(defn dbg-print
  [ns-str {:keys [line column]} & args]
  (when (or *debug*
            (some (fn [filt]
                    (match-filter filt ns-str))
                  (seq @+debug+)))
    (local :println (format "%s (%d:%d)"
                            ns-str
                            line
                            column))
    (doseq [arg args]
      (local :pprint arg))))
link
(with-out-str (dbg-print "hello" {:line 1 :column 1} "world")) => string?

dbg:add-filters ^

[& filters]
Added 4.0

adds filters to the debug allowlist

v 4.0
(defn dbg:add-filters
  ([& filters]
   (swap! +debug+
          (fn [coll] (apply conj coll filters)))))
link
(dbg:add-filters :test) => set?

dbg:remove-filters ^

[& filters]
Added 4.0

removes filters from the debug allowlist

v 4.0
(defn dbg:remove-filters
  ([& filters]
   (swap! +debug+
          (fn [coll] (apply disj coll filters)))))
link
(dbg:remove-filters :test) => (complement #(contains? % :test))

wrap-print ^

[f & [format-fn]]
Added 4.0

wraps a function to print its result

v 4.0
(defn wrap-print
  [f & [format-fn]]
  (fn [& args]
    (let [res (apply f args)]
      (p ((or format-fn
              identity) res))
      res)))
link
((wrap-print +) 1 2) => 3

7    Local State



local ^

[k & args]
Added 3.0

applies the local function

v 3.0
(defn local
  ([k & args]
   (apply (get @+local+ k) args)))
link
(local:set :test (fn [] :hello)) (local :test) => :hello

local:clear ^

[k & more]
Added 3.0

clears the local functions

v 3.0
(defn local:clear
  ([k & more]
   (apply swap! +local+ dissoc k more)))
link
(local:clear :test) => map?

local:set ^

[k v & more]
Added 3.0

sets the local functions

v 3.0
(defn local:set
  ([k v & more]
   (apply swap! +local+ assoc k v more)))
link
(local:set :test (fn [] :hello)) => map?

8    Error Handling



close ^

[obj]
Added 3.0

closes any object implementing `java.io.Closable`

v 3.0
(defn close
  ([^java.io.Closeable obj]
   (.close obj)))
link
(close (java.io.StringReader. "hello")) => nil

throwable-string ^

[t]
Added 3.0

creates a string from a throwable

v 3.0
(defn throwable-string
  ([^Throwable t]
   (let [errors (StringWriter.)
         _ (.printStackTrace t (PrintWriter. errors))]
     (str errors))))
link
(throwable-string (ex-info "ERROR" {})) => string?

9    std.lib.env: A Comprehensive Summary

The std.lib.env namespace provides a collection of utility functions and macros for interacting with the Clojure runtime environment, managing resources, and enhancing debugging and logging capabilities. It offers tools for namespace introspection, class path resource loading, dynamic function binding, and advanced printing/metering.

Key Features and Concepts:

  1. Namespace and Symbol Utilities:n ns-sym []: Returns the symbol representing the current namespace.n ns-get [ns k]: Resolves and returns the value of a symbol k within a given namespace ns.n require [sym]: A concurrency-safe wrapper around clojure.core/require.nn2. Environment Detection:n dev? []: Checks if the current environment is a development environment (by looking for nrepl.core).nn3. Class Path Resource Management:n sys:resource [n & [loader]]: Finds a resource n on the class path, returning its java.net.URL.n sys:resource-cached [atom path f]: Caches the result of applying a function f to a class path resource path, invalidating the cache if the resource's modification time changes.n sys:resource-content [path]: Reads and caches the content of a class path resource as a string.n sys:ns-url [& [ns]]: Gets the java.net.URL for a given namespace (or the current one).n sys:ns-file [& [ns]]: Gets the file path for a given namespace.n sys:ns-dir [& [ns]]: Gets the directory path for a given namespace.nn4. Resource Management:n close [obj]: Closes any object implementing java.io.Closeable.nn5. Local Function Overrides:n *local*: A dynamic var controlling whether local function overrides are active.n +local+: An atom holding a map of locally overridden functions (e.g., println, prn, pprint).n local:set [k v & more]: Sets or updates local function overrides in +local+.n local:clear [k & more]: Clears local function overrides from +local+.n local [k & args]: Applies the locally overridden function associated with key k.nn6. Enhanced Printing and Debugging:n p [& args]: Shortcut for (local :println).n pp-str [& args]: Pretty-prints arguments to a string.n pp-fn [& body]: A function that pretty-prints its arguments.n pp [& body]: A macro that pretty-prints its arguments, including namespace, line, and column information.n do:pp [v]: A doto-like macro for pp.n pl-add-lines [body & [range pad]]: Helper for pl, adds line numbers to a string.n pl [body & [range]]: A macro that prints body with line numbers, including namespace, line, and column information.n do:pl [v]: A doto-like macro for pl.n pl:fn []: Creates a function that prints with line numbers.n prn [& body]: A macro that prints its arguments (like clojure.core/prn), but also includes namespace, line, and column information.n do:prn [& args]: A doto-like macro for prn.n prn:fn []: Creates a function that prints with namespace and file info.n prf [v & [no-pad]]: Pretty-prints with optional padding.n meter [label & body]: A macro that measures and prints the time taken to execute a code block, including namespace, line, and column information.n meter-out [& body]: Measures and returns the time taken and the result of a code block.n throwable-string [t]: Converts a Throwable into a stack trace string.n explode [& body]: A macro that executes a body, catches any Throwable, and prints its stack trace with context information.nn7. Conditional Debugging (dbg):n *debug*: A dynamic var to globally enable/disable dbg output.n +debug+: An atom holding a set of filters (regex, string, symbol, function) to selectively enable dbg output for specific namespaces or symbols.n match-filter [filt id]: A helper function to check if an id matches a given filter.n dbg-print [ns-str meta & args]: The underlying function for dbg, which prints debug information if *debug* is true or if the namespace matches a filter.n dbg [& body]: A macro for conditional debugging output. It prints arguments along with source location if debugging is enabled globally or for the current namespace.n with:dbg [flag & body]: A macro to temporarily bind *debug* for a block of code.n dbg-global [& [flag]]: Getter/setter for the global *debug* flag.n dbg:add-filters [& filters]: Adds filters to +debug+ to enable selective debugging.n dbg:remove-filters [& filters]: Removes filters from +debug+.

Overall Importance:

The std.lib.env module is a crucial utility for developers working within the foundation-base project. It provides:

  • Enhanced Development Workflow: Tools like pp, pl, prn, meter, and dbg significantly improve the debugging and introspection experience, offering more context-rich output than standard Clojure functions.n Resource Management: Functions for loading and caching class path resources are essential for managing application assets and configurations.n Dynamic Environment Control: The ability to override local functions and control debugging output dynamically allows for flexible adaptation to different development and production needs.n* Robust Error Reporting: explode and throwable-string provide better visibility into exceptions, aiding in faster bug resolution.

By offering these comprehensive environment and debugging utilities, std.lib.env contributes significantly to the productivity and maintainability of the foundation-base codebase.