about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMorgan Astra <givengravity@gmail.com>2018-11-15 00:04:50 -0800
committerGitHub <noreply@github.com>2018-11-15 00:04:50 -0800
commitf81ef6b889807dd0edbd3249e1069224ab87782a (patch)
tree99c63b25061e7c6b134fda7ba577a993d183a1a1
parenta1f10d3b04b65030f660d41f92be2bf34c230222 (diff)
parent4e6941768364d3337bff6ac2e2be5c8db5df197b (diff)
Merge pull request #91 from witch-house/develop
Merge develop into master... for the last time!
-rw-r--r--.circleci/config.yml42
-rw-r--r--COPYING2
-rw-r--r--README.md83
-rw-r--r--project.clj18
-rw-r--r--resources/pronouns.tab42
-rw-r--r--src/pronouns/config.clj4
-rw-r--r--src/pronouns/pages.clj46
-rw-r--r--src/pronouns/util.clj18
-rw-r--r--src/pronouns/web.clj30
-rw-r--r--test/pronouns/pages_test.clj16
-rw-r--r--test/pronouns/resource_test.clj12
-rw-r--r--test/pronouns/util_test.clj32
12 files changed, 271 insertions, 74 deletions
diff --git a/.circleci/config.yml b/.circleci/config.yml
new file mode 100644
index 0000000..f3e8a34
--- /dev/null
+++ b/.circleci/config.yml
@@ -0,0 +1,42 @@
+# Clojure CircleCI 2.0 configuration file
+#
+# Check https://circleci.com/docs/2.0/language-clojure/ for more details
+#
+version: 2
+jobs:
+  build:
+    docker:
+      # specify the version you desire here
+      - image: circleci/clojure:lein-2.7.1
+      
+      # Specify service dependencies here if necessary
+      # CircleCI maintains a library of pre-built images
+      # documented at https://circleci.com/docs/2.0/circleci-images/
+      # - image: circleci/postgres:9.4
+
+    working_directory: ~/repo
+
+    environment:
+      LEIN_ROOT: "true"
+      # Customize the JVM maximum heap limit
+      JVM_OPTS: -Xmx3200m
+    
+    steps:
+      - checkout
+
+      # Download and cache dependencies
+      - restore_cache:
+          keys:
+          - v1-dependencies-{{ checksum "project.clj" }}
+          # fallback to using the latest cache if no exact match is found
+          - v1-dependencies-
+
+      - run: lein deps
+
+      - save_cache:
+          paths:
+            - ~/.m2
+          key: v1-dependencies-{{ checksum "project.clj" }}
+        
+      # run tests!
+      - run: lein test
diff --git a/COPYING b/COPYING
index 29c3097..84592c6 100644
--- a/COPYING
+++ b/COPYING
@@ -2,7 +2,7 @@
 
                           Version 3, 19 November 2007
 
-Copyright © 2007 Free Software Foundation, Inc. <http://fsf.org/> Everyone is
+Copyright © 2007 Free Software Foundation, Inc. <https://fsf.org/> Everyone is
 permitted to copy and distribute verbatim copies of this license document, but
 changing it is not allowed. Preamble
 
diff --git a/README.md b/README.md
index f4bdae5..d306cc1 100644
--- a/README.md
+++ b/README.md
@@ -1,15 +1,15 @@
-**[pronoun.is](http://pronoun.is) is a website for personal pronoun usage examples**
+**[pronoun.is](https://pronoun.is) is a website for personal pronoun usage examples**
 
 ## For users
 
 You can use any pronouns you like simply by filling them into the
-url path. For example, http://pronoun.is/ze/zir/zir/zirs/zirself
+url path. For example, https://pronoun.is/ze/zir/zir/zirs/zirself
 
 That's pretty unwieldy! Fortunately you can also give it only the
-first pronoun or two: http://pronoun.is/she/her or http://pronoun.is/they
+first pronoun or two: https://pronoun.is/she/her or https://pronoun.is/they
 
 Automatically filling in the rest from only one or two forms only
-works for pronouns in the [database](resources/pronouns.tab). If the
+works for pronouns in the [database][pronoun-database]. If the
 pronouns you or a friend uses aren't supported, please let us know and
 we'll add them. Alternatively you could add them yourself and submit a
 pull request (see the next section for details)
@@ -18,38 +18,89 @@ pull request (see the next section for details)
 
 ### The database
 
-The pronouns "database" is a tab-delimited file with fields and
-example values as follows:
+The pronouns "database" is a tab-delimited file located in [resources/pronouns.tab][pronoun-database] with fields and example values as follows:
 
 subject|object|possessive-determiner|possessive-pronoun|reflexive
 -------|------|---------------------|------------------|---------
 they   | them | their               | theirs           | themselves
 
-If you edit it with a text editor, make sure your editor inputs real
-tab characters in that file (a thing your editor might normally be
-configured not to do!) In Emacs, you can input real tabs by doing
-Ctrl+q <tab>
+The top 6 pronouns are displayed on the front page. Please don't edit these
+without talking to me, they've been hand-curated based on usage frequency.
+Below the top 6, the remaining pronouns are sorted in alphabetical order by
+subject and then in roughly frequency order for sets that have the same subject
+pronoun. If you're adding a set that shares the same object pronoun as other
+set(s) already in the database, please insert it immediately below those ones.
+
+If you edit the database with a text editor, make sure your editor inputs real
+tab characters in that file (a thing your editor might normally be configured 
+not to do!) In Emacs, you can input real tabs by doing Ctrl+q <tab>. 
+In Vi you can use Ctrl+v <tab>.
+
+[pronoun-database]: resources/pronouns.tab
+
+### The code
+
+The top-level logic for running the server lives in [`pronouns.web`](src/pronouns/web.clj)
+
+Page rendering markup is in [`pronouns.pages`](src/pronouns/pages.clj), it uses
+[hiccup](https://github.com/weavejester/hiccup) for rendering HTML from Clojure
+datastructures.
+
+[`pronouns.config`](src/pronouns/config.clj) is currently used only for loading
+the [pronouns database][pronoun-database]
+
+The unfortunately-named [`pronouns.util`](src/pronouns/util.clj) includes both
+actual utility functions used elsewhere in the code, but also what you might
+think of as "controllers" if you're used to the MVC model of web design - code
+that does the computations necessary for the `pages` (analogous to "views")
+to render themselves. We should probably break up `util` into (at least) two
+namespaces and be a little more deliberate about where everything currently
+in that namespace should live!
+
+### Tests
+
+Run the suite with `lein test`
+
+Test coverage is not great but getting better. Please run the tests and
+confirm that everything passes before merging changes, and please include
+tests with any new logic you introduce in a PR!
+
+Goals for the future include setting up automated CI to run the tests for
+us on every PR branch
 
 ### Running the app in a dev environment
 
-You can launch the app on your own computer by running the following
-command:
+First, install [leiningen](https://leiningen.org/). Then you can launch the app
+on your own computer by running the following command:
 
 ```
 $ lein ring server
 ```
 
-This will launch a server running the app and open your default web browser to the index page. The server will automatically reload files as you edit them.
+This will launch a server running the app and open your default web browser to 
+the index page. The server will automatically reload files as you edit them -
+with the unfortunate exception of `pronouns.tab`, which is loaded as a resource
+and requires an app restart to reload.
+
+### The git repo
+
+For most of this project's history we had separate `master` and `develop`
+branches but that's proven to be more trouble than it's worth. Going
+forward we'll be doing all development in feature branches off of `master`,
+and PRs should be issued against `master`.
+
+Please follow [this guide](https://chris.beams.io/posts/git-commit/) 
+for writing good commit messages :)
 
 ## Philosophy on pronoun inclusion
 
 Pronoun.is aims foremost and exclusively to be a useful resource for people to communicate the personal pronoun they use for themselves.
 
-It is possible to use these example sentences to demonstrate the usage of words that are not personal pronouns, or even cleverly insert an [entire story](http://pronoun.is/she/or%20they,%20those%20ships%20who%20were%20docked%20and%20still%20equipped%20with%20ancillaries,%20arranged%20to%20share%20the%20duty%20of%20monitoring%20our%20guest%20as%20it%20fit%20into%20their%20routines;%20that%20was%20the%20agreement,%20despite%20it%20being%20less%20convenient%20for%20me%20to%20participate%20at%20all,%20on%20the%20grounds%20that%20certain%20visitors%20might%20prefer%20a%20constant%20individual%20companion%20to%20what%20might%20seem,%20depending%20on%20their%20past%20experiences,%20to%20be%20undue%20attention%20from%20every%20soldier%20they%20passed.%20As%20usual,%20then,%20I%20took%20the%20first%20shift/the%20one%20possession%20of%20hers%20that%20Station%20Security%20hadn't%20confiscated,%20a/knowingly%20left%20with%20her.%20What%20a%20Presger%20frisbee%20might%20do%20or%20even%20look%20like%20I%20couldn't%20say.%20She%20hadn't%20seemed%20the%20sort%20to%20have%20alien%20technology,%20but,%20then%20again,%20neither%20had%20I/another%20unremarkable%20stranger,%20quite%20a%20ways%20down%20the%20concourse,%20who%20caught%20it%20with%20a%20degree%20of%20coordination%20that%20most%20would%20have%20overlooked.%20It%20did%20not%20escape%20my%20notice,%20however.%20"Cousin,"%20I%20said,%20enough%20to%20convey%20-%20unless%20our%20visitor%20were%20quite%20ignorant,%20but,%20of%20course,%20at%20this%20point%20I%20was%20certain%20that%20she%20couldn't%20be%20-%20both%20that%20I%20knew%20what%20she%20was%20not%20and%20that%20I%20was%20giving%20her%20the%20benefit%20of%20the%20doubt%20as%20to%20what,%20or%20who,%20she%20was)! However, as a policy we will not include such entries in the database.
+It is possible to use these example sentences to demonstrate the usage of words that are not personal pronouns, or even cleverly insert an [entire story](https://pronoun.is/she/or%20they,%20those%20ships%20who%20were%20docked%20and%20still%20equipped%20with%20ancillaries,%20arranged%20to%20share%20the%20duty%20of%20monitoring%20our%20guest%20as%20it%20fit%20into%20their%20routines;%20that%20was%20the%20agreement,%20despite%20it%20being%20less%20convenient%20for%20me%20to%20participate%20at%20all,%20on%20the%20grounds%20that%20certain%20visitors%20might%20prefer%20a%20constant%20individual%20companion%20to%20what%20might%20seem,%20depending%20on%20their%20past%20experiences,%20to%20be%20undue%20attention%20from%20every%20soldier%20they%20passed.%20As%20usual,%20then,%20I%20took%20the%20first%20shift/the%20one%20possession%20of%20hers%20that%20Station%20Security%20hadn't%20confiscated,%20a/knowingly%20left%20with%20her.%20What%20a%20Presger%20frisbee%20might%20do%20or%20even%20look%20like%20I%20couldn't%20say.%20She%20hadn't%20seemed%20the%20sort%20to%20have%20alien%20technology,%20but,%20then%20again,%20neither%20had%20I/another%20unremarkable%20stranger,%20quite%20a%20ways%20down%20the%20concourse,%20who%20caught%20it%20with%20a%20degree%20of%20coordination%20that%20most%20would%20have%20overlooked.%20It%20did%20not%20escape%20my%20notice,%20however.%20"Cousin,"%20I%20said,%20enough%20to%20convey%20-%20unless%20our%20visitor%20were%20quite%20ignorant,%20but,%20of%20course,%20at%20this%20point%20I%20was%20certain%20that%20she%20couldn't%20be%20-%20both%20that%20I%20knew%20what%20she%20was%20not%20and%20that%20I%20was%20giving%20her%20the%20benefit%20of%20the%20doubt%20as%20to%20what,%20or%20who,%20she%20was)! However, as a policy we will not include such entries in the database.
 
 
 ## License
-Copyright © 2014-2017 Morgan Astra <m@morganastra.me>
+Copyright © 2014-2018 Morgan Astra <m@morganastra.me>
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU Affero General Public License as
@@ -62,5 +113,5 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU Affero General Public License for more details.
 
 You should have received a copy of the GNU Affero General Public License
-along with this program.  If not, see <http://www.gnu.org/licenses/>
+along with this program.  If not, see <https://www.gnu.org/licenses/>
 
diff --git a/project.clj b/project.clj
index b14ac4e..a0f77fd 100644
--- a/project.clj
+++ b/project.clj
@@ -1,19 +1,19 @@
-(defproject witch-house/pronouns "1.11.0-SNAPSHOT"
+(defproject witch-house/pronouns "1.12.0-SNAPSHOT"
   :description "Pronoun.is is a website for personal pronoun usage examples"
-  :url "http://pronoun.is"
+  :url "https://pronoun.is"
   :license "GNU Affero General Public License 3.0"
-  :dependencies [[org.clojure/clojure "1.6.0"]
-                 [compojure "1.1.8"]
-                 [ring/ring-jetty-adapter "1.2.2"]
-                 [ring.middleware.logger "0.5.0"]
-                 [ring/ring-devel "1.2.2"]
-                 [environ "0.5.0"]
+  :dependencies [[org.clojure/clojure "1.9.0"]
+                 [compojure "1.6.1"]
+                 [ring/ring-jetty-adapter "1.7.1"]
+                 [environ "1.1.0"]
                  [hiccup "1.0.5"]]
   :min-lein-version "2.0.0"
   :plugins [[environ/environ.lein "0.2.1"]
             [lein-ring "0.9.7"]]
   :hooks [environ.leiningen.hooks]
   :uberjar-name "pronouns-standalone.jar"
+  ;; FIXME morgan.astra <2018-11-14 Wed>
+  ;; Is this production profile used for anything?
   :profiles {:production {:env {:production true}}
-             :dev {:dependencies [[midje "1.6.3"]]}}
+             :test {:dependencies [[ring/ring-devel "1.7.1"]]}}
   :ring {:handler pronouns.web/app})
diff --git a/resources/pronouns.tab b/resources/pronouns.tab
index 69120c3..7f788b2 100644
--- a/resources/pronouns.tab
+++ b/resources/pronouns.tab
@@ -1,32 +1,40 @@
-ze	hir	hir	hirs	hirself
-ze	zir	zir	zirs	zirself
 she	her	her	hers	herself
 he	him	his	his	himself
 they	them	their	theirs	themselves
-they	them	their	theirs	themself
+ze	hir	hir	hirs	hirself
+ze	zir	zir	zirs	zirself
 xey	xem	xyr	xyrs	xemself
-sie	hir	hir	hirs	hirself
-it	it	its	its	itself
-ey	em	eir	eirs	eirself
+ae	aer	aer	aers	aerself
 e	em	eir	eirs	emself
+ey	em	eir	eirs	eirself
+fae	faer	faer	faers	faerself
+fey	fem	feir	feirs	feirself
 hu	hum	hus	hus	humself
+it	it	its	its	itself
+jee	jem	jeir	jeirs	jemself
+kit	kit	kits	kits	kitself
+ne	nem	nir	nirs	nemself
 peh	pehm	peh's	peh's	pehself
 per	per	per	pers	perself
+sie	hir	hir	hirs	hirself
+se	sim	ser	sers	serself
+shi	hir	hir	hirs	hirself
+si	hyr	hyr	hyrs	hyrself
+they	them	their	theirs	themself
 thon	thon	thons	thons	thonself
-jee	jem	jeir	jeirs	jemself
 ve	ver	vis	vis	verself
+ve	vem	vir	virs	vemself
+vi	ver	ver	vers	verself
+vi	vim	vir	virs	vimself
+vi	vim	vim	vims	vimself
+xie	xer	xer	xers	xerself
 xe	xem	xyr	xyrs	xemself
-zie	zir	zir	zirs	zirself
+xey	xem	xeir	xeirs	xemself
+yo	yo	yos	yos	yosself
 ze	zem	zes	zes	zirself
-zie	zem	zes	zes	zirself
 ze	mer	zer	zers	zemself
-se	sim	ser	sers	serself
-zme	zmyr	zmyr	zmyrs	zmyrself
-ve	vem	vir	virs	vemself
 zee	zed	zeta	zetas	zedself
-fae	faer	faer	faers	faerself
+zie	zir	zir	zirs	zirself
+zie	zem	zes	zes	zirself
 zie	hir	hir	hirs	hirself
-si	hyr	hyr	hyrs	hyrself
-kit	kit	kits	kits	kitself
-ne	nem	nir	nirs	nemself
-fey	fem	feir	feirs	femself
+zme	zmyr	zmyr	zmyrs	zmyrself
diff --git a/src/pronouns/config.clj b/src/pronouns/config.clj
index 19e20f1..3a07c36 100644
--- a/src/pronouns/config.clj
+++ b/src/pronouns/config.clj
@@ -1,5 +1,5 @@
 ;; pronoun.is - a website for pronoun usage examples
-;; Copyright (C) 2014 - 2017 Morgan Astra
+;; Copyright (C) 2014 - 2018 Morgan Astra
 
 ;; This program is free software: you can redistribute it and/or modify
 ;; it under the terms of the GNU Affero General Public License as
@@ -12,7 +12,7 @@
 ;; GNU Affero General Public License for more details.
 
 ;; You should have received a copy of the GNU Affero General Public License
-;; along with this program.  If not, see <http://www.gnu.org/licenses/>
+;; along with this program.  If not, see <https://www.gnu.org/licenses/>
 
 (ns pronouns.config
   (:require [pronouns.util :as u]))
diff --git a/src/pronouns/pages.clj b/src/pronouns/pages.clj
index 62007bc..1226ea9 100644
--- a/src/pronouns/pages.clj
+++ b/src/pronouns/pages.clj
@@ -1,5 +1,5 @@
 ;; pronoun.is - a website for pronoun usage examples
-;; Copyright (C) 2014 - 2017 Morgan Astra
+;; Copyright (C) 2014 - 2018 Morgan Astra
 
 ;; This program is free software: you can redistribute it and/or modify
 ;; it under the terms of the GNU Affero General Public License as
@@ -12,13 +12,14 @@
 ;; GNU Affero General Public License for more details.
 
 ;; You should have received a copy of the GNU Affero General Public License
-;; along with this program.  If not, see <http://www.gnu.org/licenses/>
+;; along with this program.  If not, see <https://www.gnu.org/licenses/>
 
 (ns pronouns.pages
   (:require [clojure.string :as s]
             [pronouns.config :refer [*pronouns-table*]]
             [pronouns.util :as u]
             [hiccup.core :refer :all]
+            [hiccup.element :as e]
             [hiccup.util :refer [escape-html]]))
 
 (defn prose-comma-list
@@ -33,6 +34,8 @@
   [url text]
   [:a {:href url} text])
 
+;; FIXME morgan.astra <2018-11-14 Wed>
+;; use a div for this instead of a plain bold tag
 (defn wrap-pronoun
   [pronoun]
   [:b pronoun])
@@ -89,7 +92,9 @@
 (defn usage-block []
   [:div {:class "section usage"}
    [:p "Full usage: "
-       [:tt "http://pronoun.is/subject-pronoun/object-pronoun/possessive-determiner/possessive-pronoun/reflexive"]
+    ;; FIXME morgan.astra <2018-11-14 Wed>
+    ;; This looks really ugly in the browser
+       [:tt "https://pronoun.is/subject-pronoun/object-pronoun/possessive-determiner/possessive-pronoun/reflexive"]
        " displays examples of your pronouns."]
    [:p "This is a bit unwieldy. If we have a good guess we'll let you use"
        " just the first one or two."]])
@@ -101,7 +106,7 @@
      [:p "Written by "
          (twitter-name "morganastra")
          ", whose "
-         (href "http://pronoun.is/ze/zir?or=she" "pronoun.is/ze/zir?or=she")]
+         (href "https://pronoun.is/she" "pronoun.is/she")]
      [:p "pronoun.is is free software under the "
          (href "https://www.gnu.org/licenses/agpl.html" "AGPLv3")
          "! visit the project on "
@@ -114,16 +119,21 @@
 (defn format-pronoun-examples
   [pronoun-declensions]
   (let [sub-objs (map #(s/join "/" (take 2 %)) pronoun-declensions)
-        title (str "Pronoun Island: " (prose-comma-list sub-objs) " examples")]
+        title (str "Pronoun Island: " (prose-comma-list sub-objs) " examples")
+        examples (map #(apply examples-block %) pronoun-declensions)]
     (html
      [:html
       [:head
        [:title title]
        [:meta {:name "viewport" :content "width=device-width"}]
+       [:meta {:name "description" :content (u/strip-markup examples)}]
+       [:meta {:name "twitter:card" :content "summary"}]
+       [:meta {:name "twitter:title" :content title}]
+       [:meta {:name "twitter:description" :content (u/strip-markup examples)}]
        [:link {:rel "stylesheet" :href "/pronouns.css"}]]
       [:body
        (header-block title)
-       (map #(apply examples-block %) pronoun-declensions)
+       examples
        (footer-block)]])))
 
 (defn lookup-pronouns [pronouns-string]
@@ -139,6 +149,25 @@
     [:li (href link label)]))
 
 (defn front []
+  (let [abbreviations (take 6 (u/abbreviate *pronouns-table*))
+        links (map make-link abbreviations)
+        title "Pronoun Island"]
+    (html
+     [:html
+      [:head
+       [:title title]
+       [:meta {:name "viewport" :content "width=device-width"}]
+       [:link {:rel "stylesheet" :href "/pronouns.css"}]]
+      [:body
+       (header-block title)
+       [:div {:class "section table"}
+        [:p "pronoun.is is a website for personal pronoun usage examples"]
+        [:p "here are some pronouns the site knows about:"]
+        [:ul links]
+        [:p [:small (href "all-pronouns" "see all pronouns in the database")]]]]
+      (footer-block)])))
+
+(defn all-pronouns []
   (let [abbreviations (u/abbreviate *pronouns-table*)
         links (map make-link abbreviations)
         title "Pronoun Island"]
@@ -151,9 +180,8 @@
       [:body
        (header-block title)
        [:div {:class "section table"}
-       [:p "pronoun.is is a website for personal pronoun usage examples"]
-       [:p "here are some pronouns the site knows about:"]
-       [:ul links]]]
+        [:p "All pronouns the site knows about:"]
+        [:ul links]]]
       (footer-block)])))
 
 (defn not-found []
diff --git a/src/pronouns/util.clj b/src/pronouns/util.clj
index a000201..7469dcf 100644
--- a/src/pronouns/util.clj
+++ b/src/pronouns/util.clj
@@ -1,5 +1,5 @@
 ;; pronoun.is - a website for pronoun usage examples
-;; Copyright (C) 2014 - 2017 Morgan Astra
+;; Copyright (C) 2014 - 2018 Morgan Astra
 
 ;; This program is free software: you can redistribute it and/or modify
 ;; it under the terms of the GNU Affero General Public License as
@@ -12,15 +12,14 @@
 ;; GNU Affero General Public License for more details.
 
 ;; You should have received a copy of the GNU Affero General Public License
-;; along with this program.  If not, see <http://www.gnu.org/licenses/>
+;; along with this program.  If not, see <https://www.gnu.org/licenses/>
 
 (ns pronouns.util
   (:require [clojure.string :as s]))
 
-(defn print-and-return "for debugging" [x] (println x) x)
-
-(defn slurp-tabfile [path]
-  "read a tabfile from a filesystem <path> as a table"
+(defn slurp-tabfile
+  "Read a tabfile from a filesystem <path> as a table"
+  [path]
   (let [lines (s/split (slurp path) #"\n")]
     (map #(s/split % #"\t") lines)))
 
@@ -98,8 +97,13 @@
   [table]
   (map (partial shortest-unambiguous-path table) table))
 
-(defn vec-coerce [x]
+(defn vec-coerce
   "wrap a value <x> in a vector if it is not already in one. note that if
   <x> is already in a sequence for which vector? is false, this will add
   another layer of nesting."
+  [x]
   (if (vector? x) x [x]))
+
+(defn strip-markup [form]
+  (s/join " " (filter string? (flatten form))))
+
diff --git a/src/pronouns/web.clj b/src/pronouns/web.clj
index bbbce82..ab887d0 100644
--- a/src/pronouns/web.clj
+++ b/src/pronouns/web.clj
@@ -1,5 +1,5 @@
 ;; pronoun.is - a website for pronoun usage examples
-;; Copyright (C) 2014 - 2017 Morgan Astra
+;; Copyright (C) 2014 - 2018 Morgan Astra
 
 ;; This program is free software: you can redistribute it and/or modify
 ;; it under the terms of the GNU Affero General Public License as
@@ -12,18 +12,23 @@
 ;; GNU Affero General Public License for more details.
 
 ;; You should have received a copy of the GNU Affero General Public License
-;; along with this program.  If not, see <http://www.gnu.org/licenses/>
+;; along with this program.  If not, see <https://www.gnu.org/licenses/>
 
 (ns pronouns.web
   (:require [compojure.core :refer [defroutes GET PUT POST DELETE ANY]]
             [compojure.handler :refer [site]]
             [compojure.route :as route]
-            [clojure.java.io :as io]
             [clojure.string :as s]
-            [ring.middleware.logger :as logger]
+            [clojure.java.io :as io]
+            [ring.adapter.jetty :as jetty]
+            ;; FIXME morgan.astra <2018-11-14 Wed>
+            ;; make this logger work or use another one
+            ;; [ring.middleware.logger :as logger]
             [ring.middleware.stacktrace :as trace]
             [ring.middleware.params :as params]
-            [ring.adapter.jetty :as jetty]
+            [ring.middleware.resource :refer [wrap-resource]]
+            [ring.middleware.content-type :refer [wrap-content-type]]
+            [ring.middleware.not-modified :refer [wrap-not-modified]]
             [environ.core :refer [env]]
             [pronouns.util :as u]
             [pronouns.pages :as pages]))
@@ -34,6 +39,12 @@
         :headers {"Content-Type" "text/html"}
         :body (pages/front)})
 
+  (GET "/all-pronouns" []
+       {:status 200
+        :headers {"Content-Type" "text/html"}
+        :body (pages/all-pronouns)})
+
+
   (GET "/pronouns.css" []
      {:status 200
      :headers {"Content-Type" "text/css"}
@@ -63,7 +74,14 @@
 
 (def app
   (-> app-routes
-      logger/wrap-with-logger
+      ;; FIXME morgan.astra <2018-11-14 Wed>
+      ;; use this resource or delete it
+      ;; (wrap-resource "images")
+      wrap-content-type
+      wrap-not-modified
+      ;; FIXME morgan.astra <2018-11-14 Wed>
+      ;; make this logger work or use another one
+      ;logger/wrap-with-logger
       wrap-error-page
       wrap-gnu-natalie-nguyen
       trace/wrap-stacktrace
diff --git a/test/pronouns/pages_test.clj b/test/pronouns/pages_test.clj
index 04c14ce..e32a2ee 100644
--- a/test/pronouns/pages_test.clj
+++ b/test/pronouns/pages_test.clj
@@ -1,10 +1,12 @@
 (ns pronouns.pages-test
   (:require [pronouns.pages :as pages]
-            [midje.sweet :refer :all]))
+            [clojure.test :refer [deftest testing is are]]))
 
-(fact "prose-comma-list turns a list of strings into a prose list with commas"
-      (pages/prose-comma-list ["foo"]) => "foo"
-      (pages/prose-comma-list ["foo" "bar"]) => "foo and bar"
-      (pages/prose-comma-list ["foo" "bar" "baz"]) => "foo, bar, and baz"
-      (pages/prose-comma-list ["foo" "bar" "baz" "bobble"]) => "foo, bar, baz, and bobble"
-      (pages/prose-comma-list []) => "")
+(deftest prose-comma-list
+  (testing "prose-comma-list turns a list of strings into a prose list"
+    (are [call result] (= call result)
+      (pages/prose-comma-list ["foo"]) "foo"
+      (pages/prose-comma-list ["foo" "bar"]) "foo and bar"
+      (pages/prose-comma-list ["foo" "bar" "baz"]) "foo, bar, and baz"
+      (pages/prose-comma-list ["foo" "bar" "baz" "bobble"]) "foo, bar, baz, and bobble"
+      (pages/prose-comma-list []) "")))
diff --git a/test/pronouns/resource_test.clj b/test/pronouns/resource_test.clj
new file mode 100644
index 0000000..38ee4e3
--- /dev/null
+++ b/test/pronouns/resource_test.clj
@@ -0,0 +1,12 @@
+(ns pronouns.resource-test
+  (:require [pronouns.util :as util]
+            [clojure.test :refer [deftest testing is]]))
+
+(deftest valid-pronouns-table
+  (let [table (util/slurp-tabfile "resources/pronouns.tab")]
+    (is table "pronouns.tab exists and is non-empty")
+    (doseq [row table]
+      (is (= (count row) 5)
+          "row has five elements")
+      (is (re-matches #".*sel(f|ves)$" (last row))
+          "final element is reflexive"))))
diff --git a/test/pronouns/util_test.clj b/test/pronouns/util_test.clj
new file mode 100644
index 0000000..3aecc2a
--- /dev/null
+++ b/test/pronouns/util_test.clj
@@ -0,0 +1,32 @@
+(ns pronouns.util-test
+  (:require [pronouns.util :as util]
+            [clojure.test :refer [deftest testing is are]]))
+
+(def test-table [["ze" "hir" "hir" "hirs" "hirself"]
+                 ["ze" "zir" "zir" "zirs" "zirself"]
+                 ["she" "her" "her" "hers" "herself"]
+                 ["he" "him" "his" "his" "himself"]
+                 ["they" "them" "their" "theirs" "themselves"]
+                 ["they" "them" "their" "theirs" "themself"]])
+
+(deftest table-filters
+  (testing "table-front-filter"
+    (are [arg return] (= (util/table-front-filter arg test-table) return)
+      ["she"] [["she" "her" "her" "hers" "herself"]]
+      ["ze"] [["ze" "hir" "hir" "hirs" "hirself"]
+              ["ze" "zir" "zir" "zirs" "zirself"]]
+      ["ze" "zir"] [["ze" "zir" "zir" "zirs" "zirself"]]))
+
+  (testing "table-end-filter"
+    (are [arg return] (= (util/table-end-filter arg test-table) return)
+      ["themself"] [["they" "them" "their" "theirs" "themself"]]
+      ["themselves"] [["they" "them" "their" "theirs" "themselves"]])))
+
+(deftest table-lookup
+  (are [arg return] (= (util/table-lookup arg test-table) return)
+    ["she"] ["she" "her" "her" "hers" "herself"]
+    ["ze"] ["ze" "hir" "hir" "hirs" "hirself"]
+    ["ze" "zir"] ["ze" "zir" "zir" "zirs" "zirself"]
+    ["they"] ["they" "them" "their" "theirs" "themselves"]
+    ["they" "..." "themself"] ["they" "them" "their" "theirs" "themself"]))
+