-
Notifications
You must be signed in to change notification settings - Fork 128
The compendium
The compendium stores facts after they've been loaded. It stores two other pieces of information:
- the fact most recently checked.
- the working set, which describes the subset of the compendium that you're working with.
These commands work on the compendium:
- load-facts: Load namespaces and check facts.
- check-facts: Check some or all of the loaded facts again (without reloading them)
- forget-facts: Forget some or all of the loaded facts
- fetch-facts: Return a sequence of some or all of the loaded facts.
- recheck-fact: Check the last fact checked again.
- last-checked-fact: Provide previous fact as a value.
- source-of-last-checked-fact: Show source of previous fact.
See repl tools for brief example of each. Read on for the details.
The working set is intended to track your focus of attention in an intuitive way such that giving commands with no arguments works on the facts you currently care about. In case what's intuitive to me isn't to you, here are the basic rules. Individual commands may vary them slightly.
At the beginning, Midje assumes you're interested in all facts in all namespaces. Therefore, these two ways of loading facts are equivalent:
user=> (load-facts :all) ;; `:all` is shorthand for "all project test and source namespaces"
user=> (load-facts)
The first changes the working set to what it already was. The second uses the original working set.
If you give arguments to load-facts
, you also change the working set. So consider these commands:
user=> (load-facts 'scratch.util.* :core)
user=> (check-facts)
user=> (load-facts)
The first command loads a particular set of facts (the "core" facts in the scratch.util
namespaces). Thereafter, both the argument-free check-facts
and load-facts
will act as if you'd repeated the arguments.
As another example, consider the following:
(load-facts :all) ;; Load everything
(check-facts 'scratch.util.fun) ;; Recheck a subset
(check-facts)
The first check-facts
restricts the working set to one namespace. The following check-facts
will keep working on that namespace.
Midje allows you to change its print level to adjust the verbosity of output. The new print levels are given as keyword arguments to compendium functions. For example, the following loads all namespaces and prints each namespace as it's loaded:
user=> (load-facts :all :print-namespaces)
= Namespace as-documentation.t-assumptions
= Namespace as-documentation.t-defining-checkers
= Namespace as-documentation.t-prerequisites
...
The print level is remembered by the different commands. So, suppose you use check-facts
to narrow the working set:
user=> (check-facts 'midje.util.t-laziness)
= Namespace midje.util.t-laziness
All claims (23) have been confirmed.
true
Note that the namespace was printed even though the specific argument wasn't given.
The compendium is organized by namespace. Except for the commands that work on a specific fact (such as recheck-last-fact
), a command can take one or more namespace arguments. Its attention becomes restricted to those namespaces and, except for forget-facts
, the working set is changed to match.
Namespaces can be either true namespace values (like that pointed to by *ns*
) or symbols naming namespaces. As a special case, load-facts
can take a wildcard namespace like midje.util.*
. That refers to all namespaces whose names begin with "midje.util.".
Within those namespaces, facts can also be filtered by metadata. There are five types of filters:
-
keywords select any fact whose metadata contains a truthy value for that key.
(load-facts 'proj.util.* :integration)
-
strings select any fact whose name contains the string. (It needn't be an exact match.)
(load-facts :all "compendi") ;; Would match facts with either "compendium" or "compendia"
-
regular expressions select any fact whose name contains a substring that matches the regular expression.
(check-facts #"bug-\d+") ;; Check facts whose name contains a bug report number.
-
functions are passed a fact's metadata. If the function returns a truthy value, the fact is included.
(fetch-facts #(> (count (fact-name %)) 33)) ;; facts with really long names (fetch-facts :all (complement :slow)) ;; facts that are not slow
-
multiple filters select facts that match any of the filters.
(load-facts (complement :slow) :core) ;; Load facts that are not slow or are core.
The repl tools provide shorthand for fetching fact metadata:
user=> (fact "hello" (+ 1 1) => 2)
true
user=> (fact-name (last-fact-checked))
"hello"
user=> (fact-line (last-fact-checked))
1
user=> (fact-source (last-fact-checked))
(fact "hello" (+ 1 1) => 2)
The complete list of fact metadata can be found inside the syntax and a little semantics page.
Suppose you have a namespace that contains this fact:
(fact (+ 1 1) => 2)
If you reload that namespace (whether via load-facts
, load-file
, require
, or – for that matter – cutting and pasting the contents into the repl), you don't want to get two copies of it in the compendium. For that reason, a fact is only added to the compendium if it has a different body than a fact that's already there. This check is namespace-specific. If you have the same fact in two different namespaces, you'll get two copies, one under each namespace.
Now let's suppose you have a fact like this:
(fact "the `throws` checker accepts many varieties of arglists"
(throw-exception "msg") => (throws Error)
(throw-exception "msg") => (throws "msg")
(throw-exception "msg") => (throws #(= "msg" (.getMessage %)))
...)
... and you want to add a new check inside the fact. You want the new version to replace the old one. That will happen provided the name (the doc string) remains unchanged.
What if the fact doesn't have a name? (They're not required.) Will loading changed versions lead to many variants cluttering up the compendium? Not if you work in two steps:
- Redefine the fact by giving it a name. That redefinition will replace the old one because the check for redefinition works on the body of the fact, which does not include its metadata.
- Now that the fact has a stable name, change the body.
This section describes how the individual functions vary from the description above. See their doc strings for self-contained descriptions.
load-facts
can take wildcard arguments, unlike all the other functions: prog.util.*
.
When load-facts
is given no arguments, it repeats the working set of the previous load-facts. That is, its working set is not affected by commands like (check-facts)
.
Before loading new namespaces, load-facts
clears them from the compendium. It leaves unmentioned namespaces alone.
When check-facts
is given no arguments, it repeats the working set of the previous load-facts
, check-facts
, or fetch-facts
. (This is the normal behavior.)
A sequence of facts is returned. They may be checked with check-one-fact
. Otherwise, this behaves exactly like check-facts
.
Forget facts does not change the working set. So, you can do the following:
user=> (load-facts 'proj.util.*)
...
# try some on-the-fly facts:
user=> (fact ...)
# get rid of all the facts cluttering up the compendium
user=> (forget-facts :all)
# redo the original `load-facts`
user=> (load-facts)
user=> (fact "fred" 1 => 1)
true
user=> (fact-name (last-fact-checked))
"fred"
The same as (check-one-fact (last-fact-checked))
, but with a little additional verbiage:
user=> (recheck-fact)
All checks (1) succeeded.
true
The same as (:midje/source (meta (last-fact-checked)))
.
user=> (source-of-last-fact-checked)
(fact "fred" 1 => 1)