Home | About | Documentation | Discuss

Keypin

The Keypin library is included with the bract.core module. Bract uses Keypin to read/write config and to define keys that can be looked up in the config. Keypin documentation is out of scope here, but let us quickly see how to define keys using Keypin that we can use to look up their values.

Require namespaces:

(ns demo.app
  (:require
    [keypin.core :as keypin]
    [keypin.util :as kputil]))

Key definition:

(keypin/defkey
  addr [:addr string?           "IP address"]
  port [:port #(< 1023 % 65535) "port number" {:parser kputil/any->int}])

Look up value:

(port {:port 1234})  ; returns 1234
(port {:foo "bar"})  ; throws exception because port is missing

Inducer

Bract initialization revolves around the idea of inducers. Let us write a hello-world inducer first:

(defn hello-world
  [context]
  (println "Hello World!")
  context)

Now let us write an inducer that uses the context:

(keypin/defkey
  email-tpl-file [:email-tpl-file string? "Email template filename"]
  email-template [:email-template string? "Email template"])

(defn cache-email-template
  [context]
  (require '[clojure.java.io :as io])
  (let [tpl-file (email-tpl-file context)
        tcontent (slurp)]
    (if-let [tpl-resource (io/resource tpl-file)]
      (assoc context (key email-template) (slurp tpl-resource))
      (throw (ex-info "Missing template file in classpath" {:email-template-file tpl-file})))))

Once you have several inducers and want to execute them in order, below is how you can do so:

(require '[bract.core.inducer :as bc-inducer])

(bc-inducer/induce {}  ; <- initial context
  [app/foo  ; <- this is an inducer
   app/bar  ; <- this is also an inducer
   app/baz  ; <- and this one too
   ])

Demo applications

There are few simple demo applications that may be useful to understand how Bract can be used.

Modules

Documentation of individual Bract modules is as follows:

Howto

Setup automated testing

Automated tests written in Clojure may need access to the config and/or the context for various reasons. You need to rebind known vars to the config/context for use by the tests - this may be enabled with certain inducers. Consider the config snippets below that illustrate such configuration.

;; in file config/config.base.edn
{"bract.core.inducers" [(bract.core.inducer/run-config-inducers
                          "dev-inducers")
                        bract.core.inducer/invoke-launcher      ; launch app
                        ]
 "dev-inducers"        []}

;; in file config/config.dev.edn
{"parent.config.filenames" ["config/config.base.edn"]       ; <- overrides entries in parent file
 "dev-inducers"            [bract.core.dev/record-context!  ; record the app context
                            ]}

In this example, we let the record-context! inducer store the context at bract.core.dev/app-context. The application may have a test namespace for tests to trigger initialization as follows:

(ns myapp.test-init
  (:require
    [bract.core.dev :as dev]))

(dev/ensure-init)  ; <- entry point for Bract initialization (default config: config/config.dev.edn)

All test namespaces should require the namespace myapp.test-init such that Bract initialization is ensured.

Automated Ring webapp testing

For a Ring based webapp you may want to call the Ring handler in your tests in addition to the config/context. In such a scenario the same setup as above would work. You can get the Ring handler using the key :bract.ring/ring-handler from the context.

Fork me on GitHub