├── .gitignore ├── .notes.zk.org ├── .projectile ├── LICENSE ├── Procfile ├── Procfile.dev ├── Procfile.prod-local ├── README.md ├── bin ├── .devenv ├── build ├── clean ├── db-reset ├── dev ├── prod ├── prod-local └── ship ├── clojuredocs.sketch ├── data └── mongodb │ ├── example-histories.bson │ ├── example-histories.metadata.json │ ├── examples.bson │ ├── examples.metadata.json │ ├── legacy-var-redirects.bson │ ├── legacy-var-redirects.metadata.json │ ├── notes.bson │ ├── notes.metadata.json │ ├── see-alsos.bson │ ├── see-alsos.metadata.json │ ├── system.indexes.bson │ ├── users.bson │ └── users.metadata.json ├── dev-db └── .gitkeep ├── externs ├── fastclick.js ├── marked.js ├── morpheus.js └── react.js ├── project.clj ├── resources ├── nginx.conf └── public │ ├── css │ ├── app.css │ ├── bootstrap.min.css │ └── font-awesome.min.css │ ├── favicon.ico │ ├── fonts │ ├── FontAwesome.otf │ ├── fontawesome-webfont.eot │ ├── fontawesome-webfont.svg │ ├── fontawesome-webfont.ttf │ └── fontawesome-webfont.woff │ ├── github-btn.html │ ├── img │ ├── adzerk-logo.png │ ├── amazon-logo.png │ ├── amperity-logo.jpg │ ├── amperity-logo.png │ ├── brightcove-logo.png │ ├── circleci-logo.png │ ├── clearwater-logo.png │ ├── climate-corporation-logo.png │ ├── cognician-logo.png │ ├── cognitect-logo.png │ ├── diligenceengine-logo.png │ ├── exoscale-logo.png │ ├── factual-logo.png │ ├── farmlogs-logo.png │ ├── fluent-logo.png │ ├── funding-circle-box-logo.png │ ├── funding-circle-logo.png │ ├── fy-logo.jpg │ ├── griffin-logo.png │ ├── heroku-logo.png │ ├── intentmedia-logo.png │ ├── kira-logo.png │ ├── lisp_cycles.png │ ├── living-social-logo.png │ ├── loading-button.gif │ ├── loading.gif │ ├── netflix-logo.png │ ├── nubank-logo.png │ ├── oscaro-logo.png │ ├── outpost-logo.png │ ├── pisano-logo.jpg │ ├── pitch-logo.png │ ├── prismatic-logo.png │ ├── puppet-logo.jpg │ ├── qubit-logo.png │ ├── reify-health-logo.png │ ├── rjmetrics-logo.png │ ├── room-key-logo.jpg │ ├── soundcloud-logo.png │ ├── spacious-logo.jpg │ ├── twitter-logo.png │ ├── walmart-labs.png │ ├── whimsical-logo.png │ └── witai-logo.png │ ├── js │ ├── fastclick.min.js │ ├── marked.min.js │ ├── morpheus.min.js │ └── react.js │ └── opensearch.xml ├── reup.clj ├── src ├── clj │ ├── clojuredocs │ │ ├── api │ │ │ ├── common.clj │ │ │ ├── examples.clj │ │ │ ├── notes.clj │ │ │ ├── see_alsos.clj │ │ │ └── server.clj │ │ ├── config.clj │ │ ├── css.clj │ │ ├── data.clj │ │ ├── data │ │ │ └── import.clj │ │ ├── entry.clj │ │ ├── env.clj │ │ ├── export.clj │ │ ├── github.clj │ │ ├── mail.clj │ │ ├── main.clj │ │ ├── pages.clj │ │ ├── pages │ │ │ ├── common.clj │ │ │ ├── dev.clj │ │ │ ├── gh_auth.clj │ │ │ ├── intro.clj │ │ │ ├── jobs.clj │ │ │ ├── nss.clj │ │ │ ├── quickref.clj │ │ │ ├── quickref │ │ │ │ └── static.clj │ │ │ ├── user.clj │ │ │ └── vars.clj │ │ ├── search.clj │ │ └── search │ │ │ └── static.clj │ └── nsfw │ │ └── reup.clj ├── cljc │ ├── clojuredocs │ │ ├── md5.cljc │ │ ├── syntax.cljc │ │ └── util.cljc │ └── nsfw │ │ ├── css.cljc │ │ └── util.cljc ├── cljs │ ├── clojuredocs │ │ ├── ajax.cljs │ │ ├── anim.cljs │ │ ├── examples.cljs │ │ ├── main.cljs │ │ ├── metrics.cljs │ │ ├── mods │ │ │ ├── search.cljs │ │ │ ├── styleguide.cljs │ │ │ └── var_page.cljs │ │ ├── notes.cljs │ │ ├── see_alsos.cljs │ │ └── sticky.cljs │ └── nsfw │ │ ├── ops.cljs │ │ └── page.cljs ├── examples │ └── clj │ │ └── first.clj ├── less │ ├── app.less │ ├── bootstrap.min.less │ └── font-awesome.min.less └── md │ ├── api │ └── overview.md │ ├── concepts │ ├── destructuring.md │ └── functional-programming.md │ ├── core-library.md │ ├── examples-styleguide.md │ └── namespaces │ ├── clojure.core.async.md │ ├── clojure.core.logic.md │ ├── clojure.core.md │ ├── clojure.core.server.md │ ├── clojure.edn.md │ ├── clojure.pprint.md │ ├── clojure.set.md │ ├── clojure.string.md │ ├── clojure.test.md │ └── clojure.zip.md ├── system.properties ├── test └── clojuredocs │ └── core_test.clj └── tools ├── dev_export.clj ├── old_import.clj ├── sanity_check.clj └── top_contribs.clj /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /classes 3 | /checkouts 4 | /.idea 5 | *.iml 6 | pom.xml 7 | pom.xml.asc 8 | *.jar 9 | *.class 10 | /.lein-* 11 | /.nrepl-port 12 | 13 | .DS_Store 14 | .sass-cache 15 | .lein-errors 16 | 17 | /resources/public/cljs 18 | /resources/public/cljs-advanced 19 | 20 | resources/public/css/app.css 21 | resources/public/css/highlighting.css 22 | resources/public/clojuredocs-export.json 23 | 24 | TAGS 25 | 26 | /dev-db 27 | /data 28 | -------------------------------------------------------------------------------- /.projectile: -------------------------------------------------------------------------------- 1 | -/resources/public/css 2 | -/resources/public/js 3 | -/resources/public/cljs 4 | -/resources/public/fonts -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: lein run -m clojuredocs.main -------------------------------------------------------------------------------- /Procfile.dev: -------------------------------------------------------------------------------- 1 | web: lein repl :headless 2 | cljs: lein figwheel 3 | mongo: mongod --dbpath ./dev-db 4 | -------------------------------------------------------------------------------- /Procfile.prod-local: -------------------------------------------------------------------------------- 1 | web: env PORT=4000 lein run -m clojuredocs.main 2 | mongo: mongod --dbpath ./data/proddb 3 | 4 | cljs: lein cljsbuild auto dev-advanced 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # clojuredocs 2 | 3 | The clojuredocs.org webapp 4 | 5 | ![](http://cl.ly/image/1C2o2d181716/Screen%20Shot%202014-07-12%20at%202.03.25%20AM.png) 6 | 7 | 8 | ## Contributing 9 | 10 | A few ways to contribute: 11 | 12 | * Find & report bugs: https://github.com/zk/clojuredocs/issues 13 | * Suggestions on how to make development on the site more friendly 14 | (docs, codebase organization). 15 | 16 | Let's use GH issues for discussion for now 17 | 18 | If you're looking for a project: 19 | 20 | * Content for namespaces (see `src/md/namespaces`) needs to be added / 21 | edited. Example of 22 | [clojure.core.async](http://next.clojuredocs.org/clojure.core.async) 23 | * Listing of clojure training / classes / events on home page 24 | * Stand-alone example page, maybe have the var info (signature, doc 25 | string, etc) at the top. 26 | * Source-linking on libs not included in the standard library 27 | e.g. core.async. 28 | 29 | ## Deploy 30 | 31 | Production is deployed on an AWS t2.micro instance. There's an nginx 32 | process running on the box, balancing to two JVMs managed by Upstart 33 | to support zero-downtime deploys. 34 | 35 | To regenerate the upstart scripts: 36 | 37 | ``` 38 | cd $REPO 39 | sudo foreman export -a clojuredocs -e ./.env -u ubuntu -c "web=2" upstart /etc/init/ 40 | ``` 41 | 42 | To start the app processes: 43 | 44 | ``` 45 | sudo service clojuredocs-web-1 start 46 | sudo service clojuredocs-web-2 start 47 | ``` 48 | 49 | To redeploy: 50 | 51 | ``` 52 | # in $REPO 53 | sudo service clojuredocs-web-1 stop 54 | git pull origin master 55 | # This will compile assets & run tests 56 | bin/build 57 | sudo service clojuredocs-web-1 start 58 | # Wait for proc 1 to start serving requests 59 | sudo service clojuredocs-web-2 restart 60 | ``` 61 | 62 | 63 | ## Reqs 64 | 65 | * [> JDK 7](http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html) 66 | * [JCE Unlimited Strength Jurisdiction Policy Files](http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html) 67 | * [lein](http://leiningen.org) 68 | * [foreman](https://github.com/ddollar/foreman) (see `Procfile`, `bin/dev`) 69 | * [less](http://lesscss.org/#using-less-installation) 70 | * [entr](http://entrproject.org/) (available in homebrew) 71 | * MongoDB 72 | 73 | 74 | 75 | ## Dev 76 | 77 | Run `bin/dev`, which will start all the things (repl, web process, 78 | scss compiler, etc). See `Procfile` for more info. 79 | 80 | Connect to the repl and / or visit http://localhost:4000 81 | 82 | You'll notice that var information is already populated. In an effort to not make the same mistakes again, all core-related var info is loaded from the runtime version of Clojure on start up. 83 | 84 | OTOH, examples, see-alsos, and notes (and any other user-generated content) are stored in the database. 85 | 86 | 87 | ### Local Data 88 | 89 | The app uses a MongoDB database named `clojuredocs` to store data. Run `bin/db-reset` to seed the database with a recent production export (you must be running `mongod` for this to work). 90 | 91 | ### Prod Local 92 | 93 | Occasionally you'll need to compile and run things as they would be in production (checking advanced cljs compilation, for example): `bin/prod-local`. 94 | 95 | 96 | ### CLJS Source Maps 97 | 98 | The ClojureDocs project is set-up to emit source-maps for compiled javascript. To enable in Chrome, check the 'Enable JS source maps' option in the Developer Tools settings pane. 99 | 100 | 101 | ### Clojure Version 102 | 103 | Clojure vars are pulled directly from the runtime, and are not stored in the database. When new versions of Clojure are released: 104 | 105 | * Change the Clojure dep in `project.clj` 106 | * Update the version string, source base url, and gh tag url in 107 | `clojuredocs.search/clojure-lib` 108 | * Update the version for the mobile nav in `clojuredocs.pages.common` 109 | * Update the github URL in `clojuredocs.pages.vars/source-url`. 110 | 111 | 112 | ### App Structure 113 | 114 | Interesting files: 115 | 116 | * `src/clj/clojuredocs/main.clj` -- Main entry-point into the app, starts the jetty server 117 | * `src/clj/clojuredocs/entry.clj` -- Root routes and middleware 118 | * `src/clj/clojuredocs/pages.clj` -- User-facing HTML pages 119 | * `src/clj/clojuredocs/api.clj` -- API endpoints for ajax calls from the frontend. 120 | * `src/cljs/clojuredocs/main.cljs` -- Main js entry-point into the app 121 | 122 | 123 | ### Conventions 124 | 125 | * Functions that return hiccup structures should be prefixed with a `$`, like `$layout`. 126 | * Mutable state should be prefixed with a `!`, ex: `!my-atom`. 127 | 128 | 129 | ### Adding Functions, Macros, Special Forms, Namespaces & Other Vars 130 | 131 | Most vars are picked up from Clojure at runtime, using core namespace 132 | and var introspection utilities. Special forms and namespaces to 133 | include on the site are specified explicitly in the 134 | `clojuredocs.search.static` namespace. 135 | 136 | Vars are picked up automatically based on the namespaces specified in 137 | `clojuredocs.search.static/clojure-namespaces` vector. Any namespace 138 | in this vector will be queried for public vars to be made searchable 139 | and displayable on the site. 140 | 141 | Special forms are specified in the 142 | `clojuredocs.search.static/special-forms` list, and require a server 143 | restart to be picked up in a dev environment. 144 | 145 | 146 | ### Adding Core Libraries 147 | 148 | Sometimes we'd like to add core libraries that are not part of the 149 | standard Clojure distribution (like core.async) to the site. Here's 150 | how to do that: 151 | 152 | 1. Add dependency to `project.clj` 153 | 1. Add ns sym to `clojure-namespaces` in `clojuredocs.search.static` 154 | 1. Add a short description + links to community articles / videos / 155 | other resources to `src/md/namespaces/` 156 | 157 | 158 | ## Dev-Prod differences 159 | 160 | * Dev starts the environment using `lein repl :headless`, prod uses `lein run -m clojuredocs.main`. See `:repl-options` in `project.clj` for initialization options. 161 | 162 | ## Data Exports 163 | 164 | * [See also relations](https://clojuredocs.org/api/exports/see-alsos-relations) (ns/name -> [ns/name]), results cached for 1 minute. 165 | 166 | 167 | ## Contributors 168 | 169 | [Zachary Kim](https://github.com/zk), [Lee Hinman](https://github.com/dakrone), and [more](https://github.com/zk/clojuredocs/graphs/contributors). 170 | 171 | 172 | ## License 173 | 174 | Copyright © 2010-present Zachary Kim 175 | 176 | Distributed under the Eclipse Public License either version 1.0 or (at 177 | your option) any later version. 178 | -------------------------------------------------------------------------------- /bin/.devenv: -------------------------------------------------------------------------------- 1 | # Dummy credentials for use in dev 2 | 3 | export SESSION_KEY=Uy9V4nWSL2g92OGe 4 | 5 | export BASE_URL=http://localhost:5000 6 | 7 | export CLJS_DEV=true 8 | 9 | export MONGO_URL="mongodb://localhost:27017/clojuredocs" 10 | 11 | export GH_CLIENT_ID=00c7338906c4ac29315b 12 | export GH_CLIENT_SECRET=1af3fca29ce3fb36075ca28bb7513e6070847d71 13 | 14 | export ALLOW_ROBOTS=true 15 | 16 | export LOG_EXCEPTIONS=false 17 | export DEBUG_EXCEPTIONS=true 18 | 19 | export STAGING_BANNER=false 20 | 21 | export MAILGUN_API_KEY=key-2006f8148abdc36fcf192c60d9e59329 22 | export MAILGUN_API_ENDPOINT="https://api.mailgun.net/v2/sandbox424576b6653e4c64a6882868b08c3858.mailgun.org/messages" 23 | -------------------------------------------------------------------------------- /bin/build: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | env JVM_OPTS="-Xmx256m -Xms256m -server" lein cljsbuild once prod 6 | 7 | lein test 8 | 9 | lein with-profile production compile :all 10 | -------------------------------------------------------------------------------- /bin/clean: -------------------------------------------------------------------------------- 1 | #/bin/bash 2 | 3 | rm -rf ./resources/public/cljs 4 | rm -rf ./resources/public/cljs-advanced 5 | lein clean 6 | -------------------------------------------------------------------------------- /bin/db-reset: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 4 | 5 | mongorestore -d clojuredocs --drop $SCRIPT_DIR/../data/mongodb 6 | -------------------------------------------------------------------------------- /bin/dev: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 4 | 5 | source $SCRIPT_DIR/.devenv 6 | 7 | # Test account used to verify data's making it to New Relic 8 | 9 | # export NEW_RELIC_APP_NAME=cd-dev 10 | # export NEW_RELIC_LICENSE_KEY=d239f729cebbb2f0742fadcf9a6616dfb9bbcf73 11 | # export NEW_RELIC_BROWSER_ID=4996968 12 | # export NEW_RELIC_BROWSER_KEY=da19d87a54 13 | 14 | export PORT=4000 15 | 16 | cd $SCRIPT_DIR && lein clean 17 | cd $SCRIPT_DIR/.. && foreman start -f Procfile.dev 18 | -------------------------------------------------------------------------------- /bin/prod: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Script Dir 4 | pushd `dirname $0` > /dev/null 5 | SP=`pwd` 6 | popd > /dev/null 7 | 8 | 9 | pushd $SP/.. > /dev/null 10 | 11 | env IN_PROD=true lein run -m clojuredocs.main 12 | 13 | popd > /dev/null 14 | -------------------------------------------------------------------------------- /bin/prod-local: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 4 | 5 | export SESSION_KEY="b46ca2a152173e68" 6 | 7 | export BASE_URL=http://localhost:4000 8 | 9 | export DEBUG_EXCEPTIONS=true 10 | export REPL_PORT=7888 11 | 12 | export CLJS_DEV=false 13 | 14 | export APP_ID="dev-$USER-`uname -n`" 15 | 16 | # export EXCEPTIONAL_KEY=4de15b3207d63564e95f712ed69a80e053504f6f 17 | 18 | export MONGO_URL="mongodb://localhost:27017/clojuredocs" 19 | 20 | export GH_CLIENT_ID=00c7338906c4ac29315b 21 | export GH_CLIENT_SECRET=1af3fca29ce3fb36075ca28bb7513e6070847d71 22 | 23 | export STAGING_BANNER=false 24 | export ALLOW_ROBOTS=true 25 | export MAILGUN_API_KEY=nokey 26 | export MAILGUN_API_ENDPOINT=noendpoint 27 | 28 | set -e 29 | 30 | lein clean 31 | 32 | bin/build 33 | 34 | foreman start -f Procfile.prod-local 35 | -------------------------------------------------------------------------------- /bin/ship: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | if [[ $# -eq 0 ]] ; then 6 | echo "Usage: ship ENVIRONMENT" 7 | exit 0 8 | fi 9 | 10 | SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 11 | 12 | # $SCRIPT_DIR/test 13 | 14 | cd $SCRIPT_DIR/.. && git push git@heroku.com:clojuredocs-$1.git clj-rewrite:master 15 | -------------------------------------------------------------------------------- /clojuredocs.sketch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zk/clojuredocs/31d4bd2dc7b1d5d1950e2b5e5f204735331d885e/clojuredocs.sketch -------------------------------------------------------------------------------- /data/mongodb/example-histories.bson: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zk/clojuredocs/31d4bd2dc7b1d5d1950e2b5e5f204735331d885e/data/mongodb/example-histories.bson -------------------------------------------------------------------------------- /data/mongodb/example-histories.metadata.json: -------------------------------------------------------------------------------- 1 | { "indexes" : [ { "v" : 1, "key" : { "_id" : 1 }, "ns" : "clojuredocs.example-histories", "name" : "_id_" } ] } -------------------------------------------------------------------------------- /data/mongodb/examples.bson: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zk/clojuredocs/31d4bd2dc7b1d5d1950e2b5e5f204735331d885e/data/mongodb/examples.bson -------------------------------------------------------------------------------- /data/mongodb/examples.metadata.json: -------------------------------------------------------------------------------- 1 | { "indexes" : [ { "v" : 1, "key" : { "_id" : 1 }, "ns" : "clojuredocs.examples", "name" : "_id_" }, { "v" : 1, "key" : { "var" : 1 }, "ns" : "clojuredocs.examples", "name" : "var_1", "sparse" : false, "background" : false }, { "v" : 1, "key" : { "deleted-at" : 1 }, "ns" : "clojuredocs.examples", "name" : "deleted-at_1", "sparse" : false, "background" : false }, { "v" : 1, "key" : { "author.login" : 1 }, "ns" : "clojuredocs.examples", "name" : "author.login_1", "sparse" : false, "background" : false }, { "v" : 1, "key" : { "author.account-source" : 1 }, "ns" : "clojuredocs.examples", "name" : "author.account-source_1", "sparse" : false, "background" : false }, { "v" : 1, "key" : { "editors.login" : 1 }, "ns" : "clojuredocs.examples", "name" : "editors.login_1", "sparse" : false, "background" : false }, { "v" : 1, "key" : { "editors.account-source" : 1 }, "ns" : "clojuredocs.examples", "name" : "editors.account-source_1", "sparse" : false, "background" : false }, { "v" : 1, "key" : { "name" : 1 }, "ns" : "clojuredocs.examples", "name" : "name_1", "sparse" : false, "background" : false }, { "v" : 1, "key" : { "from-var.name" : 1 }, "ns" : "clojuredocs.examples", "name" : "from-var.name_1", "sparse" : false, "background" : false }, { "v" : 1, "key" : { "from-var.ns" : 1 }, "ns" : "clojuredocs.examples", "name" : "from-var.ns_1", "sparse" : false, "background" : false }, { "v" : 1, "key" : { "from-var.library-url" : 1 }, "ns" : "clojuredocs.examples", "name" : "from-var.library-url_1", "sparse" : false, "background" : false }, { "v" : 1, "key" : { "to-var.ns" : 1 }, "ns" : "clojuredocs.examples", "name" : "to-var.ns_1", "sparse" : false, "background" : false }, { "v" : 1, "key" : { "to-var.name" : 1 }, "ns" : "clojuredocs.examples", "name" : "to-var.name_1", "sparse" : false, "background" : false }, { "v" : 1, "key" : { "to-var.library-url" : 1 }, "ns" : "clojuredocs.examples", "name" : "to-var.library-url_1", "sparse" : false, "background" : false }, { "v" : 1, "key" : { "account.login" : 1 }, "ns" : "clojuredocs.examples", "name" : "account.login_1", "sparse" : false, "background" : false }, { "v" : 1, "key" : { "account.account-source" : 1 }, "ns" : "clojuredocs.examples", "name" : "account.account-source_1", "sparse" : false, "background" : false }, { "v" : 1, "key" : { "namespaces" : 1 }, "ns" : "clojuredocs.examples", "name" : "namespaces_1", "sparse" : false, "background" : false }, { "v" : 1, "key" : { "var.ns" : 1 }, "ns" : "clojuredocs.examples", "name" : "var.ns_1", "sparse" : false, "background" : false }, { "v" : 1, "key" : { "var.name" : 1 }, "ns" : "clojuredocs.examples", "name" : "var.name_1", "sparse" : false, "background" : false }, { "v" : 1, "key" : { "var.library-url" : 1 }, "ns" : "clojuredocs.examples", "name" : "var.library-url_1", "sparse" : false, "background" : false }, { "v" : 1, "key" : { "function-id" : 1 }, "ns" : "clojuredocs.examples", "name" : "function-id_1", "sparse" : false, "background" : false }, { "v" : 1, "key" : { "editor.login" : 1 }, "ns" : "clojuredocs.examples", "name" : "editor.login_1", "sparse" : false, "background" : false }, { "v" : 1, "key" : { "editor.account-source" : 1 }, "ns" : "clojuredocs.examples", "name" : "editor.account-source_1", "sparse" : false, "background" : false }, { "v" : 1, "key" : { "login" : 1 }, "ns" : "clojuredocs.examples", "name" : "login_1", "sparse" : false, "background" : false }, { "v" : 1, "key" : { "account-source" : 1 }, "ns" : "clojuredocs.examples", "name" : "account-source_1", "sparse" : false, "background" : false }, { "v" : 1, "key" : { "email" : 1 }, "ns" : "clojuredocs.examples", "name" : "email_1", "sparse" : false, "background" : false }, { "v" : 1, "key" : { "migraion-key" : 1 }, "ns" : "clojuredocs.examples", "name" : "migraion-key_1", "sparse" : false, "background" : false } ] } -------------------------------------------------------------------------------- /data/mongodb/legacy-var-redirects.bson: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zk/clojuredocs/31d4bd2dc7b1d5d1950e2b5e5f204735331d885e/data/mongodb/legacy-var-redirects.bson -------------------------------------------------------------------------------- /data/mongodb/legacy-var-redirects.metadata.json: -------------------------------------------------------------------------------- 1 | { "indexes" : [ { "v" : 1, "key" : { "_id" : 1 }, "ns" : "clojuredocs.legacy-var-redirects", "name" : "_id_" } ] } -------------------------------------------------------------------------------- /data/mongodb/notes.bson: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zk/clojuredocs/31d4bd2dc7b1d5d1950e2b5e5f204735331d885e/data/mongodb/notes.bson -------------------------------------------------------------------------------- /data/mongodb/notes.metadata.json: -------------------------------------------------------------------------------- 1 | { "indexes" : [ { "v" : 1, "key" : { "_id" : 1 }, "ns" : "clojuredocs.notes", "name" : "_id_" } ] } -------------------------------------------------------------------------------- /data/mongodb/see-alsos.bson: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zk/clojuredocs/31d4bd2dc7b1d5d1950e2b5e5f204735331d885e/data/mongodb/see-alsos.bson -------------------------------------------------------------------------------- /data/mongodb/see-alsos.metadata.json: -------------------------------------------------------------------------------- 1 | { "indexes" : [ { "v" : 1, "key" : { "_id" : 1 }, "ns" : "clojuredocs.see-alsos", "name" : "_id_" } ] } -------------------------------------------------------------------------------- /data/mongodb/system.indexes.bson: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zk/clojuredocs/31d4bd2dc7b1d5d1950e2b5e5f204735331d885e/data/mongodb/system.indexes.bson -------------------------------------------------------------------------------- /data/mongodb/users.bson: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zk/clojuredocs/31d4bd2dc7b1d5d1950e2b5e5f204735331d885e/data/mongodb/users.bson -------------------------------------------------------------------------------- /data/mongodb/users.metadata.json: -------------------------------------------------------------------------------- 1 | { "indexes" : [ { "v" : 1, "key" : { "_id" : 1 }, "ns" : "clojuredocs.users", "name" : "_id_" } ] } -------------------------------------------------------------------------------- /dev-db/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zk/clojuredocs/31d4bd2dc7b1d5d1950e2b5e5f204735331d885e/dev-db/.gitkeep -------------------------------------------------------------------------------- /externs/fastclick.js: -------------------------------------------------------------------------------- 1 | var FastClick = {}; 2 | FastClick.attach = function(target){}; 3 | -------------------------------------------------------------------------------- /externs/marked.js: -------------------------------------------------------------------------------- 1 | var marked = function(){}; 2 | marked.setOptions = function(options){}; 3 | -------------------------------------------------------------------------------- /externs/morpheus.js: -------------------------------------------------------------------------------- 1 | var morpheus = function(elements, options) {} 2 | 3 | morpheus.tween = function(duration, callback, complete, ease) {} 4 | -------------------------------------------------------------------------------- /project.clj: -------------------------------------------------------------------------------- 1 | (defproject clojuredocs "0.1.0-SNAPSHOT" 2 | :description "FIXME: write description" 3 | :url "http://example.com/FIXME" 4 | :license {:name "Eclipse Public License" 5 | :url "http://www.eclipse.org/legal/epl-v10.html"} 6 | :min-lein-version "2.0.0" 7 | :source-paths ["src/clj" "src/cljc"] 8 | :test-paths ["test/clj"] 9 | :dependencies [[org.clojure/clojure "1.11.1"] 10 | [org.clojure/clojurescript "1.9.946"] 11 | [ring "1.5.1"] 12 | [compojure "1.1.6"] 13 | [aleph "0.4.2-alpha12"] 14 | [clucy "0.4.0"] 15 | [watchtower "0.1.1"] 16 | [org.clojure/java.jdbc "0.3.0-beta2"] 17 | [mysql/mysql-connector-java "5.1.25"] 18 | [unk "0.9.1"] 19 | [org.clojure/core.async "1.6.681"] 20 | [org.clojure/core.logic "0.8.11"] 21 | [com.vladsch.flexmark/flexmark-all "0.64.8"] 22 | [clj-fuzzy "0.1.8"] 23 | [prone "0.6.0"] 24 | [nrepl "0.6.0"] 25 | [javax.xml.bind/jaxb-api "2.4.0-b180830.0359"] 26 | [org.clojure/data.csv "1.0.1"] 27 | [clojure-interop/java.security "1.0.5"] 28 | [garden "1.2.5" :exclusions [org.clojure/clojure]] 29 | [com.cognitect/transit-clj "1.0.333"] 30 | [com.cognitect/transit-cljs "0.8.280"] 31 | [bidi "1.23.1" :exclusions [org.clojure/clojure]] 32 | [slingshot "0.12.2"] 33 | [cheshire "5.5.0"] 34 | [clj-http "3.6.0"] 35 | [camel-snake-kebab "0.3.2"] 36 | [prismatic/dommy "1.1.0"] 37 | [reagent "0.6.0"] 38 | [congomongo "2.6.0"]] 39 | :repl-options {:init (load-file "reup.clj")} 40 | :plugins [[lein-cljsbuild "1.1.5"] 41 | [lein-figwheel "0.5.18"] 42 | [cider/cider-nrepl "0.22.3"]] 43 | :cljsbuild {:builds 44 | {:dev {:source-paths ["src/cljs" "src/cljc"] 45 | :compiler {:output-to "resources/public/cljs/clojuredocs.js" 46 | :output-dir "resources/public/cljs" 47 | :main "clojuredocs.main" 48 | :optimizations :none 49 | :source-map true 50 | :asset-path "/cljs" 51 | :externs ["externs/morpheus.js"]} 52 | :figwheel {:on-jsload "clojuredocs.main/reload-hook"}} 53 | 54 | ;; for debugging advanced compilation problems 55 | :dev-advanced {:source-paths ["src/cljs" "src/cljc"] 56 | :compiler {:output-to "resources/public/cljs/clojuredocs.js" 57 | :output-dir "resources/public/cljs-advanced" 58 | :source-map "resources/public/cljs/clojuredocs.js.map" 59 | :optimizations :advanced 60 | :preamble ["public/js/morpheus.min.js" 61 | "public/js/marked.min.js" 62 | "public/js/fastclick.min.js"] 63 | :externs ["externs/morpheus.js" 64 | "externs/marked.js" 65 | "externs/fastclick.js"]}} 66 | 67 | :prod {:source-paths ["src/cljs" "src/cljc"] 68 | :compiler {:output-to "resources/public/cljs/clojuredocs.js" 69 | :optimizations :advanced 70 | :main "clojuredocs.main" 71 | :pretty-print false 72 | :preamble ["public/js/morpheus.min.js" 73 | "public/js/marked.min.js" 74 | "public/js/fastclick.min.js"] 75 | :externs ["externs/morpheus.js" 76 | "externs/marked.js" 77 | "externs/fastclick.js"]} 78 | :jar true}}} 79 | :figwheel {:http-server-root "resources/public" 80 | :css-dirs ["resources/public/css"] 81 | :repl false}) 82 | -------------------------------------------------------------------------------- /resources/nginx.conf: -------------------------------------------------------------------------------- 1 | user www-data; 2 | worker_processes 4; 3 | pid /run/nginx.pid; 4 | 5 | events { 6 | worker_connections 768; 7 | # multi_accept on; 8 | } 9 | 10 | http { 11 | 12 | upstream backend { 13 | server 127.0.0.1:5000; 14 | server 127.0.0.1:5001; 15 | } 16 | 17 | server { 18 | listen 80; 19 | 20 | location / { 21 | proxy_pass http://backend; 22 | } 23 | } 24 | 25 | ## 26 | # Basic Settings 27 | ## 28 | 29 | sendfile on; 30 | tcp_nopush on; 31 | tcp_nodelay on; 32 | keepalive_timeout 65; 33 | types_hash_max_size 2048; 34 | # server_tokens off; 35 | 36 | # server_names_hash_bucket_size 64; 37 | # server_name_in_redirect off; 38 | 39 | include /etc/nginx/mime.types; 40 | default_type application/octet-stream; 41 | 42 | ## 43 | # Logging Settings 44 | ## 45 | 46 | access_log /var/log/nginx/access.log; 47 | error_log /var/log/nginx/error.log; 48 | 49 | ## 50 | # Gzip Settings 51 | ## 52 | 53 | gzip on; 54 | gzip_disable "msie6"; 55 | 56 | # gzip_vary on; 57 | # gzip_proxied any; 58 | # gzip_comp_level 6; 59 | # gzip_buffers 16 8k; 60 | # gzip_http_version 1.1; 61 | # gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript; 62 | 63 | ## 64 | # nginx-naxsi config 65 | ## 66 | # Uncomment it if you installed nginx-naxsi 67 | ## 68 | 69 | #include /etc/nginx/naxsi_core.rules; 70 | 71 | ## 72 | # Virtual Host Configs 73 | ## 74 | 75 | #include /etc/nginx/conf.d/*.conf; 76 | #include /etc/nginx/sites-enabled/*; 77 | } 78 | -------------------------------------------------------------------------------- /resources/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zk/clojuredocs/31d4bd2dc7b1d5d1950e2b5e5f204735331d885e/resources/public/favicon.ico -------------------------------------------------------------------------------- /resources/public/fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zk/clojuredocs/31d4bd2dc7b1d5d1950e2b5e5f204735331d885e/resources/public/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /resources/public/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zk/clojuredocs/31d4bd2dc7b1d5d1950e2b5e5f204735331d885e/resources/public/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /resources/public/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zk/clojuredocs/31d4bd2dc7b1d5d1950e2b5e5f204735331d885e/resources/public/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /resources/public/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zk/clojuredocs/31d4bd2dc7b1d5d1950e2b5e5f204735331d885e/resources/public/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /resources/public/github-btn.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/public/img/adzerk-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zk/clojuredocs/31d4bd2dc7b1d5d1950e2b5e5f204735331d885e/resources/public/img/adzerk-logo.png -------------------------------------------------------------------------------- /resources/public/img/amazon-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zk/clojuredocs/31d4bd2dc7b1d5d1950e2b5e5f204735331d885e/resources/public/img/amazon-logo.png -------------------------------------------------------------------------------- /resources/public/img/amperity-logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zk/clojuredocs/31d4bd2dc7b1d5d1950e2b5e5f204735331d885e/resources/public/img/amperity-logo.jpg -------------------------------------------------------------------------------- /resources/public/img/amperity-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zk/clojuredocs/31d4bd2dc7b1d5d1950e2b5e5f204735331d885e/resources/public/img/amperity-logo.png -------------------------------------------------------------------------------- /resources/public/img/brightcove-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zk/clojuredocs/31d4bd2dc7b1d5d1950e2b5e5f204735331d885e/resources/public/img/brightcove-logo.png -------------------------------------------------------------------------------- /resources/public/img/circleci-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zk/clojuredocs/31d4bd2dc7b1d5d1950e2b5e5f204735331d885e/resources/public/img/circleci-logo.png -------------------------------------------------------------------------------- /resources/public/img/clearwater-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zk/clojuredocs/31d4bd2dc7b1d5d1950e2b5e5f204735331d885e/resources/public/img/clearwater-logo.png -------------------------------------------------------------------------------- /resources/public/img/climate-corporation-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zk/clojuredocs/31d4bd2dc7b1d5d1950e2b5e5f204735331d885e/resources/public/img/climate-corporation-logo.png -------------------------------------------------------------------------------- /resources/public/img/cognician-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zk/clojuredocs/31d4bd2dc7b1d5d1950e2b5e5f204735331d885e/resources/public/img/cognician-logo.png -------------------------------------------------------------------------------- /resources/public/img/cognitect-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zk/clojuredocs/31d4bd2dc7b1d5d1950e2b5e5f204735331d885e/resources/public/img/cognitect-logo.png -------------------------------------------------------------------------------- /resources/public/img/diligenceengine-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zk/clojuredocs/31d4bd2dc7b1d5d1950e2b5e5f204735331d885e/resources/public/img/diligenceengine-logo.png -------------------------------------------------------------------------------- /resources/public/img/exoscale-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zk/clojuredocs/31d4bd2dc7b1d5d1950e2b5e5f204735331d885e/resources/public/img/exoscale-logo.png -------------------------------------------------------------------------------- /resources/public/img/factual-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zk/clojuredocs/31d4bd2dc7b1d5d1950e2b5e5f204735331d885e/resources/public/img/factual-logo.png -------------------------------------------------------------------------------- /resources/public/img/farmlogs-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zk/clojuredocs/31d4bd2dc7b1d5d1950e2b5e5f204735331d885e/resources/public/img/farmlogs-logo.png -------------------------------------------------------------------------------- /resources/public/img/fluent-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zk/clojuredocs/31d4bd2dc7b1d5d1950e2b5e5f204735331d885e/resources/public/img/fluent-logo.png -------------------------------------------------------------------------------- /resources/public/img/funding-circle-box-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zk/clojuredocs/31d4bd2dc7b1d5d1950e2b5e5f204735331d885e/resources/public/img/funding-circle-box-logo.png -------------------------------------------------------------------------------- /resources/public/img/funding-circle-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zk/clojuredocs/31d4bd2dc7b1d5d1950e2b5e5f204735331d885e/resources/public/img/funding-circle-logo.png -------------------------------------------------------------------------------- /resources/public/img/fy-logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zk/clojuredocs/31d4bd2dc7b1d5d1950e2b5e5f204735331d885e/resources/public/img/fy-logo.jpg -------------------------------------------------------------------------------- /resources/public/img/griffin-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zk/clojuredocs/31d4bd2dc7b1d5d1950e2b5e5f204735331d885e/resources/public/img/griffin-logo.png -------------------------------------------------------------------------------- /resources/public/img/heroku-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zk/clojuredocs/31d4bd2dc7b1d5d1950e2b5e5f204735331d885e/resources/public/img/heroku-logo.png -------------------------------------------------------------------------------- /resources/public/img/intentmedia-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zk/clojuredocs/31d4bd2dc7b1d5d1950e2b5e5f204735331d885e/resources/public/img/intentmedia-logo.png -------------------------------------------------------------------------------- /resources/public/img/kira-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zk/clojuredocs/31d4bd2dc7b1d5d1950e2b5e5f204735331d885e/resources/public/img/kira-logo.png -------------------------------------------------------------------------------- /resources/public/img/lisp_cycles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zk/clojuredocs/31d4bd2dc7b1d5d1950e2b5e5f204735331d885e/resources/public/img/lisp_cycles.png -------------------------------------------------------------------------------- /resources/public/img/living-social-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zk/clojuredocs/31d4bd2dc7b1d5d1950e2b5e5f204735331d885e/resources/public/img/living-social-logo.png -------------------------------------------------------------------------------- /resources/public/img/loading-button.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zk/clojuredocs/31d4bd2dc7b1d5d1950e2b5e5f204735331d885e/resources/public/img/loading-button.gif -------------------------------------------------------------------------------- /resources/public/img/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zk/clojuredocs/31d4bd2dc7b1d5d1950e2b5e5f204735331d885e/resources/public/img/loading.gif -------------------------------------------------------------------------------- /resources/public/img/netflix-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zk/clojuredocs/31d4bd2dc7b1d5d1950e2b5e5f204735331d885e/resources/public/img/netflix-logo.png -------------------------------------------------------------------------------- /resources/public/img/nubank-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zk/clojuredocs/31d4bd2dc7b1d5d1950e2b5e5f204735331d885e/resources/public/img/nubank-logo.png -------------------------------------------------------------------------------- /resources/public/img/oscaro-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zk/clojuredocs/31d4bd2dc7b1d5d1950e2b5e5f204735331d885e/resources/public/img/oscaro-logo.png -------------------------------------------------------------------------------- /resources/public/img/outpost-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zk/clojuredocs/31d4bd2dc7b1d5d1950e2b5e5f204735331d885e/resources/public/img/outpost-logo.png -------------------------------------------------------------------------------- /resources/public/img/pisano-logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zk/clojuredocs/31d4bd2dc7b1d5d1950e2b5e5f204735331d885e/resources/public/img/pisano-logo.jpg -------------------------------------------------------------------------------- /resources/public/img/pitch-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zk/clojuredocs/31d4bd2dc7b1d5d1950e2b5e5f204735331d885e/resources/public/img/pitch-logo.png -------------------------------------------------------------------------------- /resources/public/img/prismatic-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zk/clojuredocs/31d4bd2dc7b1d5d1950e2b5e5f204735331d885e/resources/public/img/prismatic-logo.png -------------------------------------------------------------------------------- /resources/public/img/puppet-logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zk/clojuredocs/31d4bd2dc7b1d5d1950e2b5e5f204735331d885e/resources/public/img/puppet-logo.jpg -------------------------------------------------------------------------------- /resources/public/img/qubit-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zk/clojuredocs/31d4bd2dc7b1d5d1950e2b5e5f204735331d885e/resources/public/img/qubit-logo.png -------------------------------------------------------------------------------- /resources/public/img/reify-health-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zk/clojuredocs/31d4bd2dc7b1d5d1950e2b5e5f204735331d885e/resources/public/img/reify-health-logo.png -------------------------------------------------------------------------------- /resources/public/img/rjmetrics-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zk/clojuredocs/31d4bd2dc7b1d5d1950e2b5e5f204735331d885e/resources/public/img/rjmetrics-logo.png -------------------------------------------------------------------------------- /resources/public/img/room-key-logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zk/clojuredocs/31d4bd2dc7b1d5d1950e2b5e5f204735331d885e/resources/public/img/room-key-logo.jpg -------------------------------------------------------------------------------- /resources/public/img/soundcloud-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zk/clojuredocs/31d4bd2dc7b1d5d1950e2b5e5f204735331d885e/resources/public/img/soundcloud-logo.png -------------------------------------------------------------------------------- /resources/public/img/spacious-logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zk/clojuredocs/31d4bd2dc7b1d5d1950e2b5e5f204735331d885e/resources/public/img/spacious-logo.jpg -------------------------------------------------------------------------------- /resources/public/img/twitter-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zk/clojuredocs/31d4bd2dc7b1d5d1950e2b5e5f204735331d885e/resources/public/img/twitter-logo.png -------------------------------------------------------------------------------- /resources/public/img/walmart-labs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zk/clojuredocs/31d4bd2dc7b1d5d1950e2b5e5f204735331d885e/resources/public/img/walmart-labs.png -------------------------------------------------------------------------------- /resources/public/img/whimsical-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zk/clojuredocs/31d4bd2dc7b1d5d1950e2b5e5f204735331d885e/resources/public/img/whimsical-logo.png -------------------------------------------------------------------------------- /resources/public/img/witai-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zk/clojuredocs/31d4bd2dc7b1d5d1950e2b5e5f204735331d885e/resources/public/img/witai-logo.png -------------------------------------------------------------------------------- /resources/public/js/fastclick.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | FastClick: polyfill to remove click delays on browsers with touch UIs. 3 | 4 | @version 1.0.2 5 | @codingstandard ftlabs-jsv2 6 | @copyright The Financial Times Limited [All Rights Reserved] 7 | @license MIT License (see LICENSE.txt) 8 | */ 9 | function FastClick(a,b){function c(a,b){return function(){return a.apply(b,arguments)}}var d;b=b||{};this.trackingClick=!1;this.trackingClickStart=0;this.targetElement=null;this.lastTouchIdentifier=this.touchStartY=this.touchStartX=0;this.touchBoundary=b.touchBoundary||10;this.layer=a;this.tapDelay=b.tapDelay||200;if(!FastClick.notNeeded(a)){for(var g="onMouse onClick onTouchStart onTouchMove onTouchEnd onTouchCancel".split(" "),f=0,h=g.length;fc.offsetHeight){b=c;a.fastClickScrollParent=c;break}c=c.parentElement}while(c)}b&&(b.fastClickLastScrollTop=b.scrollTop)}; 17 | FastClick.prototype.getTargetElementFromEventTarget=function(a){return a.nodeType===Node.TEXT_NODE?a.parentNode:a}; 18 | FastClick.prototype.onTouchStart=function(a){var b,c,d;if(1b||Math.abs(a.pageY-this.touchStartY)>b?!0:!1};FastClick.prototype.onTouchMove=function(a){if(!this.trackingClick)return!0;if(this.targetElement!==this.getTargetElementFromEventTarget(a.target)||this.touchHasMoved(a))this.trackingClick=!1,this.targetElement=null;return!0}; 20 | FastClick.prototype.findControl=function(a){return void 0!==a.control?a.control:a.htmlFor?document.getElementById(a.htmlFor):a.querySelector("button, input:not([type=hidden]), keygen, meter, output, progress, select, textarea")}; 21 | FastClick.prototype.onTouchEnd=function(a){var b,c,d=this.targetElement;if(!this.trackingClick)return!0;if(a.timeStamp-this.lastClickTime=0&&(t=w.slice(n+1),w.length=n,w=w.concat(t))}function N(e,t){var n={},r;if(r=e.match(c))n.rotate=B(r[1],t?t.rotate:null);if(r=e.match(h))n.scale=B(r[1],t?t.scale:null);if(r=e.match(p))n.skewx=B(r[1],t?t.skewx:null),n.skewy=B(r[3],t?t.skewy:null);if(r=e.match(d))n.translatex=B(r[1],t?t.translatex:null),n.translatey=B(r[3],t?t.translatey:null);return n}function C(e){var t="";return"rotate"in e&&(t+="rotate("+e.rotate+"deg) "),"scale"in e&&(t+="scale("+e.scale+") "),"translatex"in e&&(t+="translate("+e.translatex+"px,"+e.translatey+"px) "),"skewx"in e&&(t+="skew("+e.skewx+"deg,"+e.skewy+"deg)"),t}function k(e,t,n){return"#"+(1<<24|e<<16|t<<8|n).toString(16).slice(1)}function L(e){var t=e.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/);return(t?k(t[1],t[2],t[3]):e).replace(/#(\w)(\w)(\w)$/,"#$1$1$2$2$3$3")}function A(e){return e.replace(/-(.)/g,function(e,t){return t.toUpperCase()})}function O(e){return typeof e=="function"}function M(e){return Math.sin(e*Math.PI/2)}function _(e,t,n,r,s,o){function d(e){var i=e-c;if(i>a||h)return o=isFinite(o)?o:1,h?p&&t(o):t(o),T(d),n&&n.apply(f);isFinite(o)?t(l*r(i/a)+s):t(r(i/a))}r=O(r)?r:j.easings[r]||M;var a=e||u,f=this,l=o-s,c=i(),h=0,p=0;return x(d),{stop:function(e){h=1,p=e,e||(n=null)}}}function D(e,t){var n=e.length,r=[],i,s;for(i=0;i15?15:s<0?0:s,r[i]=s.toString(16);return"#"+r.join("")}function H(e,t,n,r,i,s,o){if(i=="transform"){o={};for(var a in n[s][i])o[a]=a in r[s][i]?Math.round(((r[s][i][a]-n[s][i][a])*e+n[s][i][a])*u)/u:n[s][i][a];return o}return typeof n[s][i]=="string"?P(e,n[s][i],r[s][i]):(o=Math.round(((r[s][i]-n[s][i])*e+n[s][i])*u)/u,i in v||(o+=t[s][i]||"px"),o)}function B(e,t,n,r,i){return(n=f.exec(e))?(i=parseFloat(n[2]))&&t+(n[1]=="+"?1:-1)*i:parseFloat(e)}function j(e,t){var n=e?n=isFinite(e.length)?e:[e]:[],r,i=t.complete,s=t.duration,o=t.easing,u=t.bezier,f=[],c=[],h=[],p=[],d,v;u&&(d=t.left,v=t.top,delete t.right,delete t.bottom,delete t.left,delete t.top);for(r=n.length;r--;){f[r]={},c[r]={},h[r]={};if(u){var b=y(n[r],"left"),w=y(n[r],"top"),E=[B(O(d)?d(n[r]):d||0,parseFloat(b)),B(O(v)?v(n[r]):v||0,parseFloat(w))];p[r]=O(u)?u(n[r],E):u,p[r].push(E),p[r].unshift([parseInt(b,10),parseInt(w,10)])}for(var S in t){switch(S){case"complete":case"duration":case"easing":case"bezier":continue}var x=y(n[r],S),T,k=O(t[S])?t[S](n[r]):t[S];if(typeof k=="string"&&a.test(k)&&!a.test(x)){delete t[S];continue}f[r][S]=S=="transform"?N(x):typeof k=="string"&&a.test(k)?L(x).slice(1):parseFloat(x),c[r][S]=S=="transform"?N(k,f[r][S]):typeof k=="string"&&k.charAt(0)=="#"?L(k).slice(1):B(k,parseFloat(x)),typeof k=="string"&&(T=k.match(l))&&(h[r][S]=T[1])}}return _.apply(n,[s,function(e,i,s){for(r=n.length;r--;){u&&(s=D(p[r],e),n[r].style.left=s[0]+"px",n[r].style.top=s[1]+"px");for(var o in t)i=H(e,h,f,c,o,r),o=="transform"?n[r].style[m]=C(i):o=="opacity"&&!g?n[r].style.filter="alpha(opacity="+i*100+")":n[r].style[A(o)]=i}},i,o])}var e=document,t=window,n=t.performance,r=n&&(n.now||n.webkitNow||n.msNow||n.mozNow),i=r?function(){return r.call(n)}:function(){return+(new Date)},s=!1,o=e.documentElement,u=1e3,a=/^rgb\(|#/,f=/^([+\-])=([\d\.]+)/,l=/^(?:[\+\-]=?)?\d+(?:\.\d+)?(%|in|cm|mm|em|ex|pt|pc|px)$/,c=/rotate\(((?:[+\-]=)?([\-\d\.]+))deg\)/,h=/scale\(((?:[+\-]=)?([\d\.]+))\)/,p=/skew\(((?:[+\-]=)?([\-\d\.]+))deg, ?((?:[+\-]=)?([\-\d\.]+))deg\)/,d=/translate\(((?:[+\-]=)?([\-\d\.]+))px, ?((?:[+\-]=)?([\-\d\.]+))px\)/,v={lineHeight:1,zoom:1,zIndex:1,opacity:1,transform:1},m=function(){var t=e.createElement("a").style,n=["webkitTransform","MozTransform","OTransform","msTransform","Transform"],r;for(r=0;r1e12!=i()>1e12});var w=[];return j.tween=_,j.getStyle=y,j.bezier=D,j.transform=m,j.parseTransform=N,j.formatTransform=C,j.easings={},j}) -------------------------------------------------------------------------------- /resources/public/opensearch.xml: -------------------------------------------------------------------------------- 1 | 2 | ClojureDocs 3 | 4 | ClojureDocs is a community-powered documentation and examples repository for the Clojure programming language. 5 | 6 | UTF-8 7 | http://clojuredocs.org/favicon.ico 8 | 9 | 10 | -------------------------------------------------------------------------------- /reup.clj: -------------------------------------------------------------------------------- 1 | (in-ns 'user) 2 | 3 | (require '[nsfw.reup]) 4 | 5 | (def reup 6 | (nsfw.reup/setup 7 | {:start-app-sym 'clojuredocs.main/start-app 8 | :stop-app-sym 'clojuredocs.main/stop-app 9 | :tests-regex #"clojuredocs.*-test"})) 10 | 11 | (reup) 12 | -------------------------------------------------------------------------------- /src/clj/clojuredocs/api/common.clj: -------------------------------------------------------------------------------- 1 | (ns clojuredocs.api.common 2 | (:require [slingshot.slingshot :refer [throw+]] 3 | [schema.core :as s] 4 | [clojuredocs.util :as util])) 5 | 6 | ;; Schemas 7 | 8 | (def User 9 | {:login s/Str 10 | :account-source s/Str 11 | :avatar-url s/Str}) 12 | 13 | (def Var 14 | {:ns s/Str 15 | :name s/Str 16 | :library-url s/Str}) 17 | 18 | 19 | 20 | (defn require-login! [user] 21 | (when-not user 22 | (throw+ 23 | {:body {:error "You must be logged in to edit an example."} 24 | :status 401}))) 25 | 26 | (defn validate-schema! [payload schema] 27 | (let [res (s/check schema payload)] 28 | (when res 29 | (throw+ 30 | {:status 421 31 | :body {:failures res 32 | :schema schema}}))) 33 | payload) 34 | 35 | 36 | ;; Validations 37 | 38 | (defn run-validations [payload validations] 39 | (->> validations 40 | (map #(% payload)) 41 | (reduce merge))) 42 | 43 | (defn validate! [payload validations] 44 | (let [res (run-validations payload validations)] 45 | (when-not (empty? res) 46 | (throw+ 47 | {:status 422 48 | :body res})))) 49 | 50 | (defn parse-mongo-id! [id] 51 | (try 52 | (org.bson.types.ObjectId. id) 53 | (catch java.lang.IllegalArgumentException e 54 | (throw+ 55 | {:status 400 56 | :body {:message "Couldn't parse mongo id"}})))) 57 | 58 | (defn update-timestamps [{:keys [created-at updated-at] :as m}] 59 | (let [now (util/now) 60 | m (if created-at m (assoc m :created-at now))] 61 | (assoc m :updated-at now))) 62 | -------------------------------------------------------------------------------- /src/clj/clojuredocs/api/examples.clj: -------------------------------------------------------------------------------- 1 | (ns clojuredocs.api.examples 2 | (:require [slingshot.slingshot :refer [throw+]] 3 | [somnium.congomongo :as mon] 4 | [schema.core :as s] 5 | [clojuredocs.util :as util] 6 | [clojuredocs.api.common :as c])) 7 | ;; Utils 8 | 9 | (defn edn-response [payload] 10 | {:body payload 11 | :status 200}) 12 | 13 | (defn insert! [payload] 14 | (mon/insert! :examples payload) 15 | payload) 16 | 17 | (defn create-example-history [example user new-body] 18 | {:_id (org.bson.types.ObjectId.) 19 | :editor user 20 | :body new-body 21 | :created-at (util/now) 22 | :example-id (:_id example)}) 23 | 24 | (defn add-editor [user] 25 | (fn [editors] 26 | (let [eds (map #(select-keys % [:login :account-source]) editors)] 27 | (if (get (set eds) (select-keys user [:login :account-source])) 28 | editors 29 | (concat editors [user]))))) 30 | 31 | 32 | (defn body-not-empty [{:keys [body]}] 33 | (when (empty? body) 34 | {:message "Whoops, looks like your example is blank."})) 35 | 36 | (defn var-required [{:keys [var]}] 37 | (when (empty? var) 38 | {:message "Please provide the var that your example is about"})) 39 | 40 | 41 | ;; Handlers 42 | 43 | (def InsertExample 44 | {:author c/User 45 | :body s/Str 46 | :created-at s/Int 47 | :updated-at s/Int 48 | :var c/Var 49 | :_id org.bson.types.ObjectId}) 50 | 51 | (defn post-example-handler [{:keys [edn-body user]}] 52 | (c/require-login! user) 53 | (c/validate! edn-body [body-not-empty var-required]) 54 | (-> edn-body 55 | (assoc :author user) 56 | c/update-timestamps 57 | (assoc :_id (org.bson.types.ObjectId.)) 58 | (c/validate-schema! InsertExample) 59 | insert! 60 | edn-response)) 61 | 62 | (def UpdateExample 63 | (merge 64 | InsertExample 65 | {:editors [c/User]})) 66 | 67 | (def InsertExampleHistory 68 | {:_id org.bson.types.ObjectId 69 | :example-id org.bson.types.ObjectId 70 | :created-at s/Int 71 | :body s/Str 72 | :editor c/User}) 73 | 74 | (defn patch-example-handler [id] 75 | (fn [{:keys [edn-body user]}] 76 | (c/require-login! user) 77 | (let [_id (c/parse-mongo-id! id) 78 | example-update (assoc edn-body :_id _id) 79 | example (mon/fetch-one :examples :where {:_id (:_id example-update)}) 80 | new-example (-> example 81 | (assoc :body (:body example-update)) 82 | (update-in [:editors] (add-editor user)) 83 | c/update-timestamps) 84 | example-history (create-example-history 85 | example 86 | user 87 | (:body example-update))] 88 | (when-not example 89 | (throw+ 90 | (-> (edn-response {:error "Couldn't find the example you're trying to update."}) 91 | (assoc :status 422)))) 92 | (c/validate! new-example [body-not-empty]) 93 | (c/validate-schema! new-example UpdateExample) 94 | (c/validate-schema! example-history InsertExampleHistory) 95 | (mon/update! :examples 96 | {:_id (:_id example)} 97 | new-example) 98 | (mon/insert! :example-histories example-history) 99 | {:status 200 100 | :headers {"Content-Type" "application/edn;charset=utf-8"} 101 | :body new-example}))) 102 | 103 | (defn delete-example-handler [id] 104 | (fn [{:keys [user]}] 105 | (c/require-login! user) 106 | (let [_id (c/parse-mongo-id! id) 107 | example (mon/fetch-one :examples :where {:_id _id})] 108 | (when-not example 109 | (throw+ {:status 404 110 | :body {:message "Not Found"}})) 111 | (when-not (util/is-author? user example) 112 | (throw+ {:status 401 113 | :body {:message "Not authorized to delete that example"}})) 114 | (mon/update! :examples 115 | {:_id _id} 116 | (assoc example :deleted-at (util/now))) 117 | {:status 200 :body example}))) 118 | -------------------------------------------------------------------------------- /src/clj/clojuredocs/api/notes.clj: -------------------------------------------------------------------------------- 1 | (ns clojuredocs.api.notes 2 | (:require [clojuredocs.api.common :as c] 3 | [schema.core :as s] 4 | [somnium.congomongo :as mon] 5 | [slingshot.slingshot :refer [throw+]])) 6 | 7 | (defn body-not-empty [m] 8 | (when (empty? (:body m)) 9 | {:message "Whoops, looks like your note is empty."})) 10 | 11 | (def Note 12 | {:body s/Str 13 | :var c/Var 14 | :author c/User 15 | :created-at s/Int 16 | :updated-at s/Int 17 | :_id org.bson.types.ObjectId}) 18 | 19 | (defn post-note-handler [{:keys [edn-body user]}] 20 | (c/require-login! user) 21 | (let [new-note (-> edn-body 22 | (assoc :_id (org.bson.types.ObjectId.)) 23 | c/update-timestamps 24 | (assoc :author user))] 25 | (c/validate! new-note [body-not-empty]) 26 | (c/validate-schema! new-note Note) 27 | (mon/insert! :notes new-note) 28 | {:status 200 29 | :body (assoc new-note :can-edit? true :can-delete? true)})) 30 | 31 | (defn patch-note-handler [id] 32 | (fn [{:keys [edn-body user]}] 33 | (c/require-login! user) 34 | (let [_id (c/parse-mongo-id! id) 35 | note (mon/fetch-one :notes :where {:_id _id}) 36 | new-note (-> note 37 | (assoc :body (:body edn-body)) 38 | c/update-timestamps)] 39 | (c/validate! new-note [body-not-empty]) 40 | (c/validate-schema! new-note Note) 41 | (mon/update! :notes {:_id (:_id note)} new-note) 42 | {:status 200 43 | :body (assoc new-note :can-edit? true :can-delete? true)}))) 44 | 45 | (defn is-author [user] 46 | (fn [m] 47 | (when-not (= (select-keys user [:login :account-source]) 48 | (select-keys (:author m) [:login :account-source])) 49 | {:message "You must be the author of a note to delete it."}))) 50 | 51 | (defn delete-note-handler [id] 52 | (fn [{:keys [user]}] 53 | (c/require-login! user) 54 | (let [_id (c/parse-mongo-id! id) 55 | note (mon/fetch-one :notes :where {:_id _id})] 56 | (when-not note 57 | (throw+ 58 | {:status 404 59 | :body {:message (str "Note with id " id " not found.")}})) 60 | (c/validate! note [(is-author user)]) 61 | (mon/destroy! :notes {:_id _id}) 62 | {:status 200 63 | :body {:message "Note deleted"}}))) 64 | -------------------------------------------------------------------------------- /src/clj/clojuredocs/api/see_alsos.clj: -------------------------------------------------------------------------------- 1 | (ns clojuredocs.api.see-alsos 2 | (:require [slingshot.slingshot :refer [throw+]] 3 | [somnium.congomongo :as mon] 4 | [schema.core :as s] 5 | [clojuredocs.util :as util] 6 | [clojuredocs.api.common :as c] 7 | [clojuredocs.search :as search]) 8 | (:import [org.bson.types ObjectId])) 9 | 10 | ;; Schemas 11 | 12 | (def User 13 | {:login s/Str 14 | :account-source s/Str 15 | :avatar-url s/Str}) 16 | 17 | (def Var 18 | {:ns s/Str 19 | :name s/Str 20 | :library-url s/Str}) 21 | 22 | (def SeeAlso 23 | {:from-var Var 24 | :to-var Var 25 | :author User 26 | :created-at s/Int 27 | :_id ObjectId}) 28 | 29 | (defn prevent-duplicates [{:keys [from-var to-var]}] 30 | (when-let [existing (mon/fetch-one :see-alsos :where {:from-var from-var :to-var to-var})] 31 | {:message "Sorry, that see also already exists."})) 32 | 33 | (defn prevent-same [{:keys [from-var to-var]}] 34 | (when (= from-var to-var) 35 | {:message "Sorry, a var can't see-also itself."})) 36 | 37 | (defn post-see-also-handler [{:keys [edn-body user]}] 38 | (c/require-login! user) 39 | (let [ns-name (:fq-to-var-name edn-body) 40 | from-var (:from-var edn-body) 41 | to-var (search/lookup ns-name) 42 | new-see-also {:from-var (select-keys from-var 43 | [:ns :name :library-url]) 44 | :to-var (select-keys to-var 45 | [:ns :name :library-url]) 46 | :author user 47 | :created-at (util/now) 48 | :_id (ObjectId.)}] 49 | (when-not to-var 50 | (throw+ 51 | {:status 404 52 | :body {:message (str "Couldn't find var \"" ns-name "\". Don't forget to fully qualify the var name.")} })) 53 | (c/validate! new-see-also [prevent-duplicates prevent-same]) 54 | (c/validate-schema! new-see-also SeeAlso) 55 | (mon/insert! :see-alsos new-see-also) 56 | {:status 200 :body (assoc new-see-also :doc (:doc to-var) :can-delete? true)})) 57 | 58 | (defn is-author? [user] 59 | (fn [m] 60 | (when-not (= user (:author m)) 61 | {:message "Sorry, you can't delete that see also."}))) 62 | 63 | (defn delete-see-also-handler [id] 64 | (fn [{:keys [edn-body user]}] 65 | (c/require-login! user) 66 | (let [_id (ObjectId. (str id)) 67 | sa (mon/fetch-one :see-alsos :where {:_id _id})] 68 | (c/validate! sa [(is-author? user)]) 69 | (mon/destroy! :see-alsos {:_id _id}) 70 | {:status 200 71 | :body sa}))) 72 | -------------------------------------------------------------------------------- /src/clj/clojuredocs/api/server.clj: -------------------------------------------------------------------------------- 1 | (ns clojuredocs.api.server 2 | (:require [clojuredocs.api.examples :as examples] 3 | [clojuredocs.api.notes :as notes] 4 | [clojuredocs.api.see-alsos :as see-alsos] 5 | [compojure.core :refer [DELETE GET PATCH POST defroutes]] 6 | [compojure.route :refer [not-found]] 7 | [fogus.unk :as unk] 8 | [nsfw.util :as nu] 9 | [slingshot.slingshot :refer [throw+ try+]] 10 | [somnium.congomongo :as mon]) 11 | (:import [org.bson.types ObjectId])) 12 | 13 | (defn all-see-alsos-relations-map [] 14 | (->> (mon/fetch 15 | :see-alsos) 16 | (group-by #(select-keys (:from-var %) [:ns :name])) 17 | (map (fn [[{:keys [ns name]} to-vars]] 18 | [(str ns "/" name) 19 | (->> to-vars 20 | (map (fn [{:keys [to-var]}] 21 | (str (:ns to-var) "/" (:name to-var)))) 22 | vec)])) 23 | (into {}) 24 | nu/pp-str)) 25 | 26 | (def memo-all-see-alsos-relations-map 27 | (unk/memo-ttl all-see-alsos-relations-map 28 | (* 1000 60 60 1))) 29 | 30 | (defn see-alsos-relations-handler [r] 31 | {:body (memo-all-see-alsos-relations-map) 32 | :headers {"Content-Type" "application/edn"}}) 33 | 34 | (defroutes _routes 35 | (POST "/examples" [] examples/post-example-handler) 36 | (DELETE "/examples/:id" [id] (examples/delete-example-handler id)) 37 | (PATCH "/examples/:id" [id] (examples/patch-example-handler id)) 38 | 39 | (POST "/see-alsos" [] see-alsos/post-see-also-handler) 40 | (DELETE "/see-alsos/:id" [id] (see-alsos/delete-see-also-handler id)) 41 | 42 | (POST "/notes" [] notes/post-note-handler) 43 | (PATCH "/notes/:id" [id] (notes/patch-note-handler id)) 44 | (DELETE "/notes/:id" [id] (notes/delete-note-handler id)) 45 | 46 | 47 | (GET "/exports/see-alsos-relations" [] see-alsos-relations-handler) 48 | 49 | (not-found 50 | {:status 404 51 | :body {:message "Route not found"}})) 52 | 53 | (defn string-body? [r] 54 | (string? (:body r))) 55 | 56 | (defn edn-response? [{:keys [headers]}] 57 | (re-find #"application/edn" 58 | (or (get headers "Content-Type") 59 | (get headers "content-type")))) 60 | 61 | (defn wrap-format-edn-body [h] 62 | (fn [r] 63 | (let [res (h r)] 64 | (if (and (not (string-body? res)) 65 | (edn-response? res)) 66 | (update-in res [:body] pr-str) 67 | res)))) 68 | 69 | (defn wrap-mongo-id->str [h] 70 | (fn [r] 71 | (let [{:keys [body] :as res} (h r) 72 | _id (:_id body)] 73 | (if (and _id (instance? ObjectId _id)) 74 | (update-in res [:body :_id] str) 75 | res)))) 76 | 77 | (defn wrap-str->mongo-id [h] 78 | (fn [r] 79 | (let [{:keys [_id] :as res} (h r)] 80 | (if (and _id (string? _id)) 81 | (try 82 | (assoc res :_id (ObjectId. (str _id))) 83 | (catch IllegalArgumentException e 84 | (throw+ 85 | {:status 400 86 | :body {:message (str "Error parsing Mongo ID: " _id)}}))) 87 | res)))) 88 | 89 | (defn wrap-render-errors [h] 90 | (fn [r] 91 | (try+ 92 | (h r) 93 | (catch map? m m) 94 | (catch Exception e 95 | (.printStackTrace e) 96 | {:body {:message "Unknown server error"} 97 | :status 500})))) 98 | 99 | (defn wrap-force-edn [h] 100 | (fn [r] 101 | (-> (h r) 102 | (assoc-in [:headers "Content-Type"] "application/edn;charset=utf-8")))) 103 | 104 | (def routes 105 | (-> _routes 106 | wrap-mongo-id->str 107 | wrap-str->mongo-id 108 | wrap-render-errors 109 | wrap-force-edn 110 | wrap-format-edn-body)) 111 | -------------------------------------------------------------------------------- /src/clj/clojuredocs/config.clj: -------------------------------------------------------------------------------- 1 | (ns clojuredocs.config 2 | (:require [clojuredocs.env :as env])) 3 | 4 | (def env-vars 5 | [{:key :gh-client-id 6 | :type :string 7 | :doc "GitHub application client id" 8 | :required? true} 9 | 10 | {:key :gh-client-secret 11 | :type :string 12 | :doc "GitHub application secret key" 13 | :required? true} 14 | 15 | {:key :base-url 16 | :type :string 17 | :doc "Base URL of app, used to construct fully-qualified urls to the app (emails, etc)." 18 | :required? true} 19 | 20 | {:key :debug-exceptions 21 | :type :bool 22 | :doc "Render debug info to browser?" 23 | :required? true} 24 | 25 | {:key :session-key 26 | :type :string 27 | :doc "String key used for encrypting the session (stored in cookie)" 28 | :required? true} 29 | 30 | {:key :staging-banner 31 | :type :bool 32 | :doc "Show staging banner at top of pages?" 33 | :required? true} 34 | 35 | {:key :cljs-dev 36 | :type :bool 37 | :doc "Include dev cljs deps?" 38 | :required? false 39 | :default false} 40 | 41 | {:key :mongo-url 42 | :type :string 43 | :doc "'mongodb://'-style url" 44 | :required? true} 45 | 46 | {:key :allow-robots 47 | :type :bool 48 | :doc "Allow crawl of the site?" 49 | :required? true} 50 | 51 | {:key :log-exceptions 52 | :type :bool 53 | :doc "Log exceptions to console?" 54 | :required? false 55 | :default false} 56 | 57 | {:key :mailgun-api-key 58 | :type :string 59 | :doc "Mailgun API key" 60 | :required? true} 61 | 62 | {:key :mailgun-api-endpoint 63 | :type :string 64 | :doc "Mailgun message endpoint (including domain to send from)." 65 | :required? true} 66 | 67 | {:key :ga-tracking-id 68 | :type :string 69 | :doc "GA id (UA-...)" 70 | :required? false 71 | :default "UA-17348828-3"} 72 | 73 | {:key :cache-markdown 74 | :type :bool 75 | :doc "Cache markdown from disk for duration of app process?" 76 | :required? false 77 | :default false} 78 | 79 | {:key :from-email 80 | :type :string 81 | :doc "Email address to put in 'from' field " 82 | :default "ClojureDocs Development "} 83 | 84 | {:key :new-relic-app-name 85 | :type :string 86 | :doc "App name for display in new relic, ex `cd-dev`, `cd-staging`" 87 | :required? false} 88 | 89 | {:key :new-relic-license-key 90 | :type :string 91 | :doc "New Relic license key" 92 | :required? false} 93 | 94 | {:key :new-relic-browser-id 95 | :type :string 96 | :doc "New Relic Browser perf tracking id" 97 | :required? false} 98 | 99 | {:key :new-relic-browser-key 100 | :type :string 101 | :doc "New Relic Browser perf tracking key" 102 | :required? false}]) 103 | 104 | (defn get-env [lookup key] 105 | (let [{:keys [key type doc required? default] :as env-var-schema} 106 | (->> lookup 107 | (filter #(= key (:key %))) 108 | first) 109 | f (condp = type 110 | :int env/int 111 | :bool env/bool 112 | env/str)] 113 | (when-not env-var-schema 114 | (throw (Exception. (str "Can't find description of env var " key)))) 115 | (let [res (f key)] 116 | (if (not (nil? res)) 117 | res 118 | default)))) 119 | 120 | (defn resolve-env [env-vars] 121 | (->> env-vars 122 | (map #(assoc % :value (get-env env-vars (:key %)))))) 123 | 124 | (def missing-env-vars 125 | (->> env-vars 126 | resolve-env 127 | (filter #(and (:required? %) (nil? (:value %)))))) 128 | 129 | (def gh-creds {:client-id (get-env env-vars :gh-client-id) 130 | :client-secret (get-env env-vars :gh-client-secret)}) 131 | 132 | (def base-url (get-env env-vars :base-url)) 133 | 134 | (def staging-banner? (get-env env-vars :staging-banner)) 135 | 136 | (def ga-tracking-id (get-env env-vars :ga-tracking-id)) 137 | 138 | (def cljs-dev? (get-env env-vars :cljs-dev)) 139 | 140 | (def allow-robots? (get-env env-vars :allow-robots)) 141 | 142 | (defn url [& s] (apply str base-url s)) 143 | 144 | (def cache-markdown? (get-env env-vars :cache-markdown)) 145 | 146 | (def debug-exceptions? (get-env env-vars :debug-exceptions)) 147 | 148 | (def log-exceptions? (get-env env-vars :log-exceptions)) 149 | 150 | (def mailgun-api-key (get-env env-vars :mailgun-api-key)) 151 | (def mailgun-api-endpoint (get-env env-vars :mailgun-api-endpoint)) 152 | 153 | (def from-email (get-env env-vars :from-email)) 154 | 155 | (def mailgun-config 156 | {:endpoint mailgun-api-endpoint 157 | :api-key mailgun-api-key 158 | :from from-email}) 159 | 160 | (def new-relic-browser-key (get-env env-vars :new-relic-browser-key)) 161 | 162 | (def new-relic-browser-id (get-env env-vars :new-relic-browser-id)) 163 | 164 | (def session-key (env/str :session-key)) 165 | -------------------------------------------------------------------------------- /src/clj/clojuredocs/data.clj: -------------------------------------------------------------------------------- 1 | (ns clojuredocs.data 2 | (:require [somnium.congomongo :as mon])) 3 | 4 | ;; Examples 5 | 6 | (defn find-examples-for [{:keys [ns name library-url]}] 7 | (mon/fetch :examples 8 | :where {:var.name name 9 | :var.ns ns 10 | :var.library-url library-url 11 | :deleted-at nil} 12 | :sort {:created-at 1})) 13 | 14 | ;; Notes 15 | 16 | (defn find-notes-for [{:keys [ns name library-url]}] 17 | (mon/fetch :notes 18 | :where {:var.ns ns 19 | :var.name name 20 | :var.library-url library-url} 21 | :sort {:created-at 1})) 22 | 23 | 24 | ;; See Alsos 25 | 26 | (defn find-see-alsos-for [{:keys [ns name library-url]}] 27 | (mon/fetch :see-alsos 28 | :where {:from-var.name name 29 | :from-var.ns ns 30 | :from-var.library-url library-url})) 31 | -------------------------------------------------------------------------------- /src/clj/clojuredocs/data/import.clj: -------------------------------------------------------------------------------- 1 | (ns clojuredocs.data.import 2 | (:require [clojure.pprint :refer [pprint]] 3 | [somnium.congomongo :as mon] 4 | [clojure.edn :as edn])) 5 | (comment 6 | (def var-keys 7 | [:ns 8 | :name 9 | :file 10 | :column 11 | :line 12 | :added 13 | :arglists 14 | :doc 15 | :static 16 | :tag ; convert to string 17 | :macro 18 | :dynamic 19 | :special-form 20 | :forms ; -> list of strings 21 | :deprecated 22 | :url 23 | :no-doc]) 24 | 25 | (defn cond-update-in [m keys & rest] 26 | (if (get-in m keys) 27 | (apply update-in m keys rest) 28 | m)) 29 | 30 | (defn transform-var-meta [m] 31 | (-> m 32 | (select-keys var-keys) 33 | (cond-update-in [:tag] #(if (class? %) 34 | (.getName %) 35 | (str %))) 36 | (cond-update-in [:forms] #(map str %)) 37 | (update-in [:ns] str) 38 | (update-in [:arglists] #(map 39 | (fn [arg-list-coll] 40 | (->> arg-list-coll 41 | (map str) 42 | (interpose " ") 43 | (apply str))) 44 | %)) 45 | (update-in [:name] str))) 46 | 47 | (defn gather-var [ns-obj] 48 | (->> ns-obj 49 | ns-publics 50 | (map second) 51 | (map meta) 52 | (map transform-var-meta))) 53 | 54 | (defn gather-vars [{:keys [namespaces library-url] :as lib}] 55 | (assoc lib :vars (->> namespaces 56 | (map :name) 57 | (map symbol) 58 | (map find-ns) 59 | (mapcat gather-var) 60 | (map #(assoc % :library-url library-url))))) 61 | 62 | (defn gather-namespace [ns-name] 63 | (require (symbol ns-name)) 64 | (let [sym (symbol ns-name) 65 | namespace (find-ns sym) 66 | meta (meta namespace)] 67 | (merge 68 | (select-keys meta [:doc :no-doc :added]) 69 | {:name ns-name}))) 70 | 71 | (defn gather-namespaces [{:keys [namespaces] :as lib}] 72 | (assoc lib 73 | :namespaces 74 | (->> namespaces 75 | (map gather-namespace) 76 | (remove :no-doc)))) 77 | 78 | ["clojure.core" 79 | "clojure.core.server" 80 | "clojure.data" 81 | "clojure.edn" 82 | "clojure.inspector" 83 | "clojure.instant" 84 | "clojure.java.browse" 85 | "clojure.java.io" 86 | "clojure.java.javadoc" 87 | "clojure.java.shell" 88 | "clojure.main" 89 | "clojure.pprint" 90 | "clojure.reflect" 91 | "clojure.repl" 92 | "clojure.set" 93 | "clojure.stacktrace" 94 | "clojure.string" 95 | "clojure.template" 96 | "clojure.test" 97 | "clojure.walk" 98 | "clojure.xml" 99 | "clojure.zip"] 100 | 101 | (defn import-clojure [] 102 | (->> {:library-url "https://github.com/clojure/clojure" 103 | :version "1.6.0" 104 | :source-base-url "https://github.com/clojure/clojure/1.6.0/blob" 105 | :namespaces ["clojure.data" 106 | "clojure.edn" 107 | "clojure.zip"]} 108 | gather-namespaces 109 | gather-vars)) 110 | 111 | (pprint (import-clojure)) 112 | 113 | #_(-> {:library-url "https://github.com/clojure/clojure" 114 | :version "1.6.0" 115 | :source-base-url "https://github.com/clojure/clojure/1.6.0/blob" 116 | :namespaces ["clojure.core" 117 | "clojure.core.server" 118 | "clojure.data" 119 | "clojure.edn" 120 | "clojure.inspector" 121 | "clojure.instant" 122 | "clojure.java.browse" 123 | "clojure.java.io" 124 | "clojure.java.javadoc" 125 | "clojure.java.shell" 126 | "clojure.main" 127 | "clojure.pprint" 128 | "clojure.reflect" 129 | "clojure.repl" 130 | "clojure.set" 131 | "clojure.stacktrace" 132 | "clojure.string" 133 | "clojure.template" 134 | "clojure.test" 135 | "clojure.walk" 136 | "clojure.xml" 137 | "clojure.zip"]} 138 | (update-in [:namespaces] gather-namespaces) 139 | (update-in [:vars] gather-vars) 140 | pprint) 141 | 142 | (declare special-forms) 143 | 144 | (mon/fetch-one :vars :where {:name "if-not"}) 145 | 146 | (->> (ns-publics 'clojure.core) 147 | (map second) 148 | (map str) 149 | (filter #(re-find #"if" %))) 150 | 151 | (meta #'if-not) 152 | 153 | (pprint (meta #'map)) 154 | 155 | (meta #'*print-level*) 156 | (meta #'all-ns) 157 | 158 | (first searchable-vars) 159 | 160 | (def clojure-namespaces 161 | '[clojure.core 162 | clojure.core.server 163 | clojure.data 164 | clojure.edn 165 | clojure.inspector 166 | clojure.instant 167 | clojure.java.browse 168 | clojure.java.io 169 | clojure.java.javadoc 170 | clojure.java.shell 171 | clojure.main 172 | clojure.pprint 173 | clojure.reflect 174 | clojure.repl 175 | clojure.set 176 | clojure.stacktrace 177 | clojure.string 178 | clojure.template 179 | clojure.test 180 | clojure.walk 181 | clojure.xml 182 | clojure.zip]) 183 | 184 | (defn get-vars [] 185 | (doseq [ns-sym clojure-namespaces] 186 | (require ns-sym)) 187 | (->> clojure-namespaces 188 | (mapcat ns-publics) 189 | (map second) 190 | (map meta))) 191 | 192 | (defn get-nss [] 193 | (doseq [ns-sym clojure-namespaces] 194 | (require ns-sym)) 195 | (->> clojure-namespaces 196 | (map find-ns) 197 | (map meta))) 198 | 199 | (->> (get-vars) 200 | (mapcat keys) 201 | distinct 202 | pprint) 203 | 204 | (->> (get-vars) 205 | (filter :see-also) 206 | first 207 | pprint) 208 | 209 | (def searchable-vars 210 | (do 211 | (doseq [ns-sym clojure-namespaces] 212 | (require ns-sym)) 213 | (->> clojure-namespaces 214 | (mapcat ns-publics) 215 | (map second) 216 | (map meta) 217 | (map #(update-in % [:ns] str)) 218 | (map #(update-in % [:name] str)) 219 | (map #(select-keys % [:ns :arglists :file :name 220 | :column :added :static :doc :line 221 | :added :static :tag :forms :deprecated])) 222 | (concat special-forms) 223 | (map #(-> % 224 | (update-in [:ns] str) 225 | (update-in [:name] str) 226 | (assoc :type (cond 227 | (:type %) (:type %) 228 | (:macro %) "macro" 229 | (> (count (:arglists %)) 0) "function" 230 | (:special-form %) "special-form" 231 | :else "var"))))))) 232 | 233 | 234 | (def special-forms 235 | (->> [{:name 'def 236 | :ns "clojure.core" 237 | :doc "Creates and interns or locates a global var with the name of symbol and a namespace of the value of the current namespace (*ns*). See http://clojure.org/special_forms for more information."} 238 | {:name 'if 239 | :ns "clojure.core" 240 | :doc "Evaluates test."} 241 | {:name 'do 242 | :ns "clojure.core" 243 | :doc "Evaluates the expressions in order and returns the value of the last. If no expressions are supplied, returns nil. See http://clojure.org/special_forms for more information."} 244 | {:name 'quote 245 | :ns "clojure.core" 246 | :doc "Yields the unevaluated form. See http://clojure.org/special_forms for more information."} 247 | {:name 'var 248 | :ns "clojure.core" 249 | :doc "The symbol must resolve to a var, and the Var object itself (not its value) is returned. The reader macro #'x expands to (var x). See http://clojure.org/special_forms for more information."} 250 | {:name 'recur 251 | :ns "clojure.core" 252 | :doc "Evaluates the exprs in order, then, in parallel, rebinds the bindings of the recursion point to the values of the exprs. See http://clojure.org/special_forms for more information."} 253 | {:name 'throw 254 | :ns "clojure.core" 255 | :doc "The expr is evaluated and thrown, therefore it should yield an instance of 256 | some derivee of Throwable. Please see http://clojure.org/special_forms#throw"} 257 | {:name 258 | 'try 259 | :ns "clojure.core" 260 | :doc "The exprs are evaluated and, if no exceptions occur, the value of the last is returned. If an exception occurs and catch clauses are provided, each is examined in turn and the first for which the thrown exception is an instance of the named class is considered a matching catch clause. If there is a matching catch clause, its exprs are evaluated in a context in which name is bound to the thrown exception, and the value of the last is the return value of the function. If there is no matching catch clause, the exception propagates out of the function. Before returning, normally or abnormally, any finally exprs will be evaluated for their side effects. See http://clojure.org/special_forms for more information."} 261 | {:name '. 262 | :ns "clojure.core" 263 | :doc "The '.' special form is the basis for access to Java. It can be considered a member-access operator, and/or read as 'in the scope of'. See http://clojure.org/special_forms for more information."} 264 | {:name 'set! 265 | :ns "clojure.core" 266 | :doc "Assignment special form. When the first operand is a field member access form, the assignment is to the corresponding field. If it is an instance field, the instance expr will be evaluated, then the expr. In all cases the value of expr is returned. Note - you cannot assign to function params or local bindings. Only Java fields, Vars, Refs and Agents are mutable in Clojure. See http://clojure.org/special_forms for more information."}] 267 | (map #(assoc % :type "special-form")))) 268 | 269 | ) 270 | -------------------------------------------------------------------------------- /src/clj/clojuredocs/entry.clj: -------------------------------------------------------------------------------- 1 | (ns clojuredocs.entry 2 | (:use [ring.middleware 3 | file 4 | file-info 5 | session 6 | params 7 | nested-params 8 | multipart-params 9 | keyword-params] 10 | [ring.middleware.session.cookie :only (cookie-store)] 11 | [ring.util.response :only (response content-type)]) 12 | (:require [clojuredocs.config :as config] 13 | [compojure.core :refer (defroutes GET POST PUT DELETE context)] 14 | [compojure.response :refer (Renderable render)] 15 | [compojure.route :refer (not-found)] 16 | [ring.util.response :refer (redirect)] 17 | [clojure.string :as str] 18 | [hiccup.page :refer (html5)] 19 | [clojuredocs.env :as env] 20 | [clojuredocs.util :as util] 21 | [clojuredocs.pages.common :as common] 22 | [clojuredocs.pages :as pages] 23 | [clojure.pprint :refer (pprint)] 24 | [clojuredocs.api.server :as api.server] 25 | [somnium.congomongo :as mon] 26 | [clojure.edn :as edn] 27 | [clojuredocs.search :as search] 28 | [prone.middleware :as prone])) 29 | 30 | (defn decode-body [content-length body] 31 | (when (and content-length 32 | (> content-length 0)) 33 | (let [buf (byte-array content-length)] 34 | (.read body buf 0 content-length) 35 | (.close body) 36 | (String. buf)))) 37 | 38 | (defn response-body 39 | "Turn a InputStream into a string." 40 | [{:keys [content-length body]}] 41 | (if (string? body) 42 | body 43 | (decode-body content-length body))) 44 | 45 | (defn hiccup->html-string [body] 46 | (if-not (vector? body) 47 | body 48 | (let [bodys (if (= :html5 (first body)) 49 | (rest body) 50 | [body])] 51 | (html5 bodys)))) 52 | 53 | ;; Extend hiccup to support rendering of hiccup vectors 54 | (extend-protocol Renderable 55 | clojure.lang.PersistentVector 56 | (render [v request] 57 | (render (hiccup->html-string v) request)) 58 | 59 | clojure.lang.APersistentMap 60 | (render [resp-map _] 61 | (if (-> resp-map :body vector?) 62 | (-> resp-map 63 | (update-in [:headers "Content-Type"] #(or % "text/html;charset=utf-8")) 64 | (assoc :body (-> resp-map :body hiccup->html-string))) 65 | (merge (with-meta (response "") (meta resp-map)) 66 | resp-map)))) 67 | 68 | (defn redirect-to-var [ns name] 69 | (fn [r] 70 | (let [name (->> name 71 | util/cd-decode 72 | util/cd-encode)] 73 | {:status 301 74 | :headers {"Location" (str "/" ns "/" name)}}))) 75 | 76 | (defn old-v-page-redirect [id] 77 | (fn [r] 78 | (try 79 | (let [{:keys [ns name] :as legacy-var} 80 | (mon/fetch-one :legacy-var-redirects :where {:function-id (Integer/parseInt id)})] 81 | (when legacy-var 82 | {:status 307 83 | :headers {"Location" (str "/" ns "/" (util/cd-encode name))}})) 84 | (catch Exception e nil)))) 85 | 86 | (defroutes old-url-redirects 87 | (GET "/clojure_core/:ns/:name" [ns name] (redirect-to-var ns name)) 88 | (GET "/clojure_core/:version/:ns/:name" [ns name] (redirect-to-var ns name)) 89 | (GET "/quickref/*" [] {:status 301 :headers {"Location" "/quickref"}}) 90 | (GET "/clojure_core" [] {:status 301 :headers {"Location" "/core-library"}}) 91 | (GET "/clojure_core/:ns" [ns] {:status 301 :headers {"Location" (str "/" ns)}}) 92 | (GET "/v/:id" [id] (old-v-page-redirect id)) 93 | (GET "/examples_style_guide" [] {:status 301 :headers {"Location" "/examples-styleguide"}}) 94 | (GET "/Clojure%20Core/:ns" [ns] {:status 301 :headers {"Location" (str "/" ns)}}) 95 | (GET "/Clojure%20Core/:ns/" [ns] {:status 301 :headers {"Location" (str "/" ns)}}) 96 | (GET "/Clojure%20Core" [] {:status 301 :headers {"Location" "/core-library"}}) 97 | (GET "/clojure_core/:ns/" [ns] {:status 301 :headers {"Location" (str "/" ns)}}) 98 | (GET "/clojure_core" [] {:status 301 :headers {"Location" "/core-library"}})) 99 | 100 | (defroutes _routes 101 | (context "/api" [] api.server/routes) 102 | (var pages/routes) 103 | ;; Redirect old urls 104 | (var old-url-redirects) 105 | (not-found (fn [r] (common/four-oh-four r)))) 106 | 107 | (def session-store 108 | (cookie-store 109 | {:key config/session-key 110 | :domain ".clojuredocs.org"})) 111 | 112 | (defn promote-session-user [h] 113 | (fn [{:keys [session] :as r}] 114 | (h (assoc r :user (:user session))))) 115 | 116 | (defn wrap-long-caching [h] 117 | (fn [r] 118 | (let [res (h r) 119 | res-ct (get-in res [:headers "Content-Type"]) 120 | content-types #{"text/css" 121 | "text/javascript" 122 | "application/font-woff" 123 | "font/opentype" 124 | "application/vnd.ms-fontobject" 125 | "image/svg+xml" 126 | "application/x-font-ttf"}] 127 | (if (get content-types res-ct) 128 | (update-in res [:headers] merge {"Cache-Control" "public, max-age=31536000"}) 129 | res)))) 130 | 131 | (defn edn-content-type? [{:keys [headers]}] 132 | (when-let [ct (or (get headers "Content-Type") 133 | (get headers "content-type"))] 134 | (re-find #"application/edn" ct))) 135 | 136 | (defn decode-edn-body [h] 137 | (fn [r] 138 | (if (edn-content-type? r) 139 | (try 140 | (h (assoc r :edn-body (-> r response-body edn/read-string))) 141 | (catch Exception e 142 | (if (re-find #"EOF while reading" (str e)) 143 | {:status 400 144 | :body "Malformed EDN"} 145 | (throw e)))) 146 | (h r)))) 147 | 148 | (defn stacktrace-el->clj [s] 149 | {:class-name (.getClassName s) 150 | :line-number (.getLineNumber s) 151 | :method-name (.getMethodName s)}) 152 | 153 | (defn exception->log-entry [ e] 154 | {:message (.getMessage e) 155 | :stacktrace (->> (.getStackTrace e) 156 | (map stacktrace-el->clj))}) 157 | 158 | (defn wrap-exception-logging [h] 159 | (fn [r] 160 | (try 161 | (h r) 162 | (catch Exception e 163 | (println (exception->log-entry e)) 164 | (throw e))))) 165 | 166 | (defn enable-mw [h mw enabled?] 167 | (fn [r] 168 | (if enabled? 169 | ((mw h) r) 170 | (h r)))) 171 | 172 | (defn wrap-500-page [h] 173 | (fn [r] 174 | (try 175 | (h r) 176 | (catch Exception e 177 | (.printStackTrace e) 178 | {:status 500 179 | :headers {"Content-Type" "text/html"} 180 | :body (hiccup->html-string 181 | (common/five-hundred (:user r)))})))) 182 | 183 | (def routes 184 | (-> _routes 185 | promote-session-user 186 | decode-edn-body 187 | wrap-keyword-params 188 | wrap-nested-params 189 | wrap-params 190 | (wrap-session {:store session-store}) 191 | (wrap-file "resources/public" {:allow-symlinks? true}) 192 | (wrap-file-info {"woff" "application/font-woff" 193 | "otf" "font/opentype" 194 | "eot" "application/vnd.ms-fontobject" 195 | "svg" "image/svg+xml" 196 | "ttf" "application/x-font-ttf"}) 197 | wrap-long-caching 198 | (enable-mw 199 | wrap-exception-logging config/log-exceptions?) 200 | (enable-mw 201 | prone/wrap-exceptions config/debug-exceptions?) 202 | wrap-500-page)) 203 | -------------------------------------------------------------------------------- /src/clj/clojuredocs/env.clj: -------------------------------------------------------------------------------- 1 | (ns clojuredocs.env 2 | "Shell environment helpers." 3 | (:require [clojure.string :as str]) 4 | (:refer-clojure :exclude (int str))) 5 | 6 | (defn clj->env [sym-or-str] 7 | (-> sym-or-str 8 | name 9 | (str/replace #"-" "_") 10 | (str/upper-case))) 11 | 12 | (defn env 13 | "Retrieve environment variables by clojure keyword style. 14 | ex. (env :user) ;=> \"zk\"" 15 | [sym & [default]] 16 | (or (System/getenv (clj->env sym)) 17 | default)) 18 | 19 | (defn int 20 | "Retrieve and parse int env var." 21 | [sym & [default]] 22 | (if-let [env-var (env sym)] 23 | (Integer/parseInt env-var) 24 | default)) 25 | 26 | (defn str 27 | "Retrieve and parse string env var." 28 | [sym & [default]] 29 | (env sym default)) 30 | 31 | (defn bool 32 | [sym & [default]] 33 | (if-let [env-var (env sym)] 34 | (Boolean/parseBoolean env-var) 35 | default)) 36 | -------------------------------------------------------------------------------- /src/clj/clojuredocs/export.clj: -------------------------------------------------------------------------------- 1 | (ns clojuredocs.export 2 | (:require [nsfw.util :as nu] 3 | [clojuredocs.search :as search] 4 | [clojuredocs.data :as data] 5 | [clojuredocs.env :as env] 6 | [somnium.congomongo :as mon])) 7 | 8 | (defn ensure-empty->nil [c] 9 | (if (and c (empty? c)) 10 | nil 11 | c)) 12 | 13 | (defn collect-var [var] 14 | (merge 15 | var 16 | {:examples (->> var 17 | data/find-examples-for 18 | (map #(dissoc % :var)) 19 | (map #(update % :_id str)) 20 | vec 21 | ensure-empty->nil) 22 | :see-alsos (->> var 23 | data/find-see-alsos-for 24 | (map #(dissoc % :from-var)) 25 | (map #(update % :_id str)) 26 | vec 27 | ensure-empty->nil) 28 | :notes (->> var 29 | data/find-notes-for 30 | (map #(dissoc % :var)) 31 | (map #(update % :_id str)) 32 | vec 33 | ensure-empty->nil)})) 34 | 35 | (defn run-export [output-path] 36 | (spit 37 | output-path 38 | (nu/to-json 39 | {:created-at (nu/now) 40 | :description "ClojureDocs Data Export" 41 | :vars (->> search/clojure-lib 42 | :vars 43 | (map collect-var) 44 | vec)}))) 45 | 46 | 47 | (defn -main [] 48 | (mon/set-connection! 49 | (mon/make-connection 50 | (env/str :mongo-url))) 51 | (run-export "resources/public/clojuredocs-export.json")) 52 | -------------------------------------------------------------------------------- /src/clj/clojuredocs/github.clj: -------------------------------------------------------------------------------- 1 | (ns clojuredocs.github 2 | "Provides auth^2 from the v3 github api. 3 | 4 | This library assumes you're using a ring-compatible response format. 5 | 6 | The GitHub auth flow is relatively simple, and consists of three 7 | steps: 8 | 9 | 1. Redirect users to github to request access permissions. 10 | 2. Handle the redirect back from github and exchange the recieved 11 | temporary code for a long-lived access token by posting to the 12 | github api. 13 | 3. Use the long-lived access token to query the user's protected 14 | information from the github api. 15 | 16 | See http://developer.github.com/v3/oauth#web-application-flow for 17 | more information." 18 | 19 | (:require [clj-http.client :as hc] 20 | [clojuredocs.util :as u])) 21 | 22 | (def login-base-url "https://github.com/login/oauth") 23 | 24 | (defn auth-redirect-url 25 | "Generates a github authorization URL." 26 | [{:keys [client-id redirect-uri scopes]}] 27 | (str login-base-url 28 | "/authorize?" 29 | "client_id=" (u/url-encode client-id) "&" 30 | "redirect_uri=" (u/url-encode redirect-uri) "&" 31 | "scope=" (->> scopes 32 | (map name) 33 | (interpose ",") 34 | (apply str) 35 | u/url-encode))) 36 | 37 | (defn exchange-code [{:keys [client-id client-secret]} code] 38 | (when code 39 | (let [qp {:client_id client-id 40 | :client_secret client-secret 41 | :code code} 42 | res (hc/post (str login-base-url "/access_token") 43 | {:query-params qp 44 | :headers {"Accept" "application/json"}})] 45 | (if (= 200 (:status res)) 46 | (u/from-json (:body res)) 47 | {:error "unknown"})))) 48 | 49 | (defn user [token] 50 | (when token 51 | (let [res (hc/get "https://api.github.com/user" 52 | {:basic-auth ["token" token]})] 53 | (-> res :body u/from-json)))) 54 | -------------------------------------------------------------------------------- /src/clj/clojuredocs/mail.clj: -------------------------------------------------------------------------------- 1 | (ns clojuredocs.mail 2 | (:require [clj-http.client :as client] 3 | [clojuredocs.util :as util] 4 | [clojuredocs.config :as config] 5 | [somnium.congomongo :as mon])) 6 | 7 | (defn migrate-account-content [migration-key] 8 | (format 9 | "Hey There, 10 | 11 | You're receiving this message because somebody (probably you) requested that we migrate your ClojureDocs account. You can do this by visiting the following link: 12 | 13 | %s 14 | 15 | If you didn't request this email, you can safely ignore it. 16 | 17 | Thanks!" 18 | (config/url "/migrate-account/migrate/" migration-key))) 19 | 20 | (defn migration-request [to-email migration-key] 21 | (let [{:keys [endpoint api-key from]} config/mailgun-config] 22 | {:method :post 23 | :url endpoint 24 | :basic-auth ["api" api-key] 25 | :form-params {:from from 26 | :to to-email 27 | :subject "Migrate Your ClojureDocs Account" 28 | :text (migrate-account-content migration-key)}})) 29 | 30 | (defn send-email [payload] 31 | (let [res (client/request payload)] 32 | (mon/insert! :events 33 | {:tag "email-sent" 34 | :payload (assoc payload :basic-auth "REDACTED") 35 | :response res}))) 36 | -------------------------------------------------------------------------------- /src/clj/clojuredocs/main.clj: -------------------------------------------------------------------------------- 1 | (ns clojuredocs.main 2 | (:require [ring.adapter.jetty :as jetty] 3 | [somnium.congomongo :as mon] 4 | [clojuredocs.env :as env] 5 | [clojuredocs.entry :as entry] 6 | [clojuredocs.config :as config] 7 | [clojuredocs.css :as css] 8 | [garden.core :as garden])) 9 | 10 | (defn compile-css [] 11 | (garden/css 12 | {:output-to "resources/public/css/app.css" 13 | :pretty-print? false 14 | :vendors ["webkit" "moz" "ms"] 15 | :auto-prefix #{:justify-content 16 | :align-items 17 | :flex-direction 18 | :flex-wrap 19 | :align-self 20 | :transition 21 | :transform 22 | :box-shadow}} 23 | css/app)) 24 | 25 | (defn start-http-server [entry-point opts] 26 | (jetty/run-jetty 27 | (fn [r] 28 | (let [resp (entry-point r)] 29 | (if (:status resp) 30 | resp 31 | (assoc resp :status 200)))) 32 | opts)) 33 | 34 | (defn create-app [] 35 | {:port (env/int :port 8080) 36 | :entry #'entry/routes 37 | :mongo-url (env/str :mongo-url)}) 38 | 39 | (defn report-and-exit-on-missing-env-vars! [] 40 | (when-not (empty? config/missing-env-vars) 41 | (println) 42 | (println "!!! Missing Env Vars:") 43 | (doseq [{:keys [key doc type]} config/missing-env-vars] 44 | (println "!!! " key (str "[" type "]:") doc)) 45 | (println "!!! Exiting...") 46 | (println) 47 | (System/exit -1))) 48 | 49 | (defn add-indexes-to-coll! [coll ks] 50 | (doseq [k ks] 51 | (mon/add-index! :examples [k]))) 52 | 53 | (defn add-all-indexes! [] 54 | (add-indexes-to-coll! 55 | :examples [:var :deleted-at 56 | :author.login :author.account-source 57 | :editors.login :editors.account-source]) 58 | 59 | (add-indexes-to-coll! :namespaces [:name]) 60 | 61 | (add-indexes-to-coll! 62 | :see-alsos [:from-var.name :from-var.ns :from-var.library-url 63 | :to-var.ns :to-var.name :to-var.library-url 64 | :account.login :account.account-source]) 65 | 66 | (add-indexes-to-coll! :libraries [:namespaces]) 67 | 68 | (add-indexes-to-coll! 69 | :notes [:var.ns :var.name :var.library-url 70 | :account.login :account.account-source]) 71 | 72 | (add-indexes-to-coll! 73 | :legacy-var-redirects [:function-id 74 | :editor.login :editor.account-source]) 75 | 76 | (add-indexes-to-coll! :users [:login :account-source]) 77 | 78 | (add-indexes-to-coll! :migrate-users [:email :migraion-key])) 79 | 80 | (defn start-app [] 81 | (compile-css) 82 | (let [{:keys [mongo-url port entry] :as app} (create-app) 83 | mongo-conn (mon/make-connection mongo-url)] 84 | (report-and-exit-on-missing-env-vars!) 85 | (mon/set-connection! mongo-conn) 86 | (add-all-indexes!) 87 | (let [stop-server (start-http-server entry 88 | {:port port :join? false})] 89 | (println (format "Server running on port %d" port)) 90 | (fn [] 91 | (mon/close-connection mongo-conn) 92 | (.stop stop-server))))) 93 | 94 | (defn stop-app [f] 95 | (when f (f))) 96 | 97 | (defn -main [] 98 | (start-app)) 99 | -------------------------------------------------------------------------------- /src/clj/clojuredocs/pages/gh_auth.clj: -------------------------------------------------------------------------------- 1 | (ns clojuredocs.pages.gh-auth 2 | (:require [clojuredocs.config :as config] 3 | [clojuredocs.env :as env] 4 | [clojuredocs.github :as gh] 5 | [ring.util.response :refer (redirect)] 6 | [compojure.core :refer (defroutes GET)] 7 | [somnium.congomongo :as mon])) 8 | 9 | (defn gh-user->user [{:keys [avatar_url id login]}] 10 | {:avatar-url avatar_url 11 | :account-source "github" 12 | :login login}) 13 | 14 | (defn callback-handler [path] 15 | (fn [{:keys [params]}] 16 | (try 17 | (let [token (:access_token (gh/exchange-code config/gh-creds (:code params))) 18 | gh-user (gh/user token) 19 | user (gh-user->user gh-user)] 20 | (mon/update! :users 21 | {:login (:login user) 22 | :account-source (:account-source user)} 23 | user) 24 | (-> (redirect (if (empty? path) "/" path)) 25 | (assoc :session {:user user}))) 26 | (catch Exception e 27 | (prn e) 28 | (-> (redirect "/gh-auth") 29 | (assoc :session nil)))))) 30 | -------------------------------------------------------------------------------- /src/clj/clojuredocs/pages/nss.clj: -------------------------------------------------------------------------------- 1 | (ns clojuredocs.pages.nss 2 | (:require [clojuredocs.config :as config] 3 | [somnium.congomongo :as mon] 4 | [clojuredocs.util :as util] 5 | [clojuredocs.pages.common :as common] 6 | [clojuredocs.search :as search] 7 | [clojure.string :as str])) 8 | 9 | (defn library-for [ns] 10 | search/clojure-lib) 11 | 12 | (defn namespace-for [ns] 13 | (->> search/clojure-lib 14 | :namespaces 15 | (filter #(= ns (:name %))) 16 | first)) 17 | 18 | (defn vars-for [ns] 19 | (->> search/clojure-lib 20 | :vars 21 | (filter #(= ns (:ns %))))) 22 | 23 | (defn group-vars [vars] 24 | (->> vars 25 | (group-by 26 | (fn [v] 27 | (let [char (-> v :name first str/lower-case)] 28 | (if (< (int (first char)) 97) 29 | "*^%" 30 | char)))) 31 | (sort-by #(-> % first)) 32 | (map (fn [[c vs]] 33 | {:heading c 34 | :vars vs})))) 35 | 36 | (defn $var-group [{:keys [heading vars]}] 37 | [:div.var-group 38 | [:h4.heading heading] 39 | (vec 40 | (concat 41 | [:dl.dl-horizontal] 42 | (->> vars 43 | (map (fn [{:keys [ns name doc]}] 44 | (let [html-enc-name 45 | (-> name 46 | (str/replace #"<" "<") 47 | (str/replace #">" ">"))] 48 | [:div.dl-row 49 | [:dt.name 50 | (util/$var-link ns name html-enc-name)] 51 | (if doc 52 | [:dd.doc doc] 53 | [:dd.no-doc "no doc"])]))))))]) 54 | 55 | (defn page-handler [ns-str] 56 | (fn [{:keys [user uri] :as r}] 57 | (let [lib (library-for ns-str) 58 | ns (namespace-for ns-str) 59 | vars (sort-by #(-> % :name str/lower-case) (vars-for ns-str))] 60 | (when ns 61 | (common/$main 62 | {:body-class "ns-page" 63 | :user user 64 | :title (str (:name ns) " namespace | ClojureDocs - Community-Powered Clojure Documentation and Examples") 65 | :page-uri uri 66 | :mobile-nav [{:title "Namespaces" 67 | :links (->> lib 68 | :namespaces 69 | (map (fn [{:keys [name]}] 70 | [:a {:href (str "/" name)} name])))}] 71 | :content [:div 72 | [:div.row 73 | [:div.col-sm-2.sidenav 74 | [:div 75 | {:data-sticky-offset "20"} 76 | (common/$library-nav lib)]] 77 | [:div.col-sm-10 78 | [:h1 ns-str] 79 | [:section.markdown 80 | (when (:doc ns) 81 | [:pre.doc (:doc ns)]) 82 | (common/memo-markdown-file (str "src/md/namespaces/" ns-str ".md"))] 83 | [:section 84 | [:h5 "Vars in " ns-str] 85 | (->> vars 86 | group-vars 87 | (map $var-group)) 88 | #_[:table {:class "ns-table"} 89 | ]]]]]}))))) 90 | -------------------------------------------------------------------------------- /src/clj/clojuredocs/pages/quickref.clj: -------------------------------------------------------------------------------- 1 | (ns clojuredocs.pages.quickref 2 | (:require [clojure.string :as str] 3 | [clojuredocs.util :as util] 4 | [clojuredocs.search :as search] 5 | [clojuredocs.pages.common :as common] 6 | [clojuredocs.pages.quickref.static :as static])) 7 | 8 | (defn title->id [k] 9 | (-> k 10 | name 11 | str/lower-case 12 | (str/replace #"[^a-z0-9]" "-") 13 | (str/replace #"-+" "-"))) 14 | 15 | (defn $group [{:keys [title syms]} parent-title] 16 | [:div.group 17 | [:div.quickref-header.clearfix 18 | [:h4 title] 19 | [:h4.header-reference 20 | (when parent-title 21 | parent-title) 22 | (when parent-title 23 | " > ") 24 | title]] 25 | [:dl.dl-horizontal 26 | (mapcat #(vector 27 | [:div.dl-row 28 | [:dt (util/$var-link "clojure.core" (str %) (str %))] 29 | [:dd (->> (str "clojure.core/" %) 30 | search/lookup-vars 31 | :doc 32 | (take 110) 33 | (apply str)) 34 | #_[:span.examples-count.pull-right 35 | "1 ex."]]]) 36 | syms)]]) 37 | 38 | (defn $category [{:keys [title groups]} parent-title] 39 | [:div.category 40 | [:div.category-header.clearfix 41 | [:h3 {:id (title->id title)} title] 42 | [:h3.header-reference parent-title]] 43 | (map #($group % title) groups)]) 44 | 45 | (defn $sphere [{:keys [title categories]}] 46 | [:div.sphere 47 | [:div.sphere-header 48 | [:h2 {:id (title->id title)} title]] 49 | (map #($category % title) categories)]) 50 | 51 | (defn $toc-category [{:keys [title]}] 52 | [:li [:a {:href (str "#" (title->id title)) 53 | :data-animate-scroll "true" 54 | :data-animate-buffer "10"} 55 | title]]) 56 | 57 | (defn $toc-sphere [{:keys [title categories]}] 58 | [:div.toc-sphere 59 | [:h5 [:a {:href (str "#" (title->id title)) 60 | :data-animate-scroll "true" 61 | :data-animate-buffer "10"} title]] 62 | [:ul 63 | (map $toc-category categories)]]) 64 | 65 | (defn $toc [quickref-data] 66 | (let [toc-groups (partition-all 2 quickref-data)] 67 | [:div.toc.clearfix 68 | [:h5 "Table of Contents"] 69 | [:h6 [:a {:href "#" 70 | :data-animate-scroll "true" 71 | :data-animate-buffer "10"} "Top"]] 72 | (for [tg toc-groups] 73 | [:div 74 | (map $toc-sphere tg)])])) 75 | 76 | (defn mobile-nav [quickref-data] 77 | {:title "Table of Contents" 78 | :links 79 | (->> quickref-data 80 | (map (fn [{:keys [title categories]}] 81 | [:a 82 | {:href (str "#" (title->id title)) 83 | :data-animate-scroll "true" 84 | :data-animate-buffer "60"} 85 | [:div.quickref-mobile-toc 86 | [:h5 title] 87 | [:span.categories 88 | (->> categories 89 | (map :title) 90 | (interpose ", ") 91 | (apply str))]]])))}) 92 | 93 | (defn page-handler [{:keys [user uri]}] 94 | (common/$main 95 | {:body-class "quickref-page" 96 | :title "Clojure Quick Reference | ClojureDocs - Community-Powered Clojure Documentation and Examples" 97 | :user user 98 | :page-uri uri 99 | :mobile-nav [(mobile-nav static/quickref-data)] 100 | :content 101 | [:div 102 | [:div.row 103 | [:div.col-sm-3.sidenav.toc-sidenav 104 | [:div.page-toc 105 | {:data-sticky-offset "10"} 106 | ($toc static/quickref-data)]] 107 | [:div.col-sm-9 108 | [:h1 "Quickref for Clojure Core"] 109 | [:p 110 | "Adapted from Johannes Friestad's excellent quick ref."] 111 | (map $sphere static/quickref-data)]]]})) 112 | -------------------------------------------------------------------------------- /src/clj/clojuredocs/pages/vars.clj: -------------------------------------------------------------------------------- 1 | (ns clojuredocs.pages.vars 2 | (:require [clojuredocs.util :as util] 3 | [clojure.string :as str] 4 | [somnium.congomongo :as mon] 5 | [clojuredocs.search :as search] 6 | [clojuredocs.pages.common :as common] 7 | [clojuredocs.data :as data] 8 | [ring.util.codec :as codec] 9 | [hiccup.core :as hc])) 10 | 11 | (defn ellipsis [s n] 12 | (cond 13 | (<= (count s) 3) s 14 | (> n (count s)) s 15 | :else (str (->> s 16 | (take n) 17 | (apply str)) 18 | "..."))) 19 | 20 | (defn library-for [{:keys [ns]}] 21 | search/clojure-lib) 22 | 23 | (defn $arglist [name a] 24 | [:li.arglist 25 | (str "(" 26 | (util/html-encode name) 27 | (when-not (empty? a) " ") 28 | (util/html-encode a) 29 | ")")]) 30 | 31 | (defn $argform [s] 32 | [:li.arglist (util/html-encode s)]) 33 | 34 | (defn see-alsos-for [{:keys [ns name library-url]}] 35 | (->> (mon/fetch :see-alsos 36 | :where {:from-var.ns ns 37 | :from-var.name name 38 | :from-var.library-url library-url}) 39 | (map (fn [{:keys [to-var] :as sa}] 40 | (let [ns-name (str (:ns to-var) "/" (:name to-var)) 41 | looked-up-var (search/lookup ns-name)] 42 | (if (nil? looked-up-var) nil 43 | (assoc sa :doc (:doc looked-up-var)))))) 44 | (remove nil?))) 45 | 46 | (defn source-url [{:keys [file line ns] :as var}] 47 | (when (and (= "clojure.core" ns) file) 48 | (str "https://github.com/clojure/clojure/blob/clojure-1.11.1/src/clj/" file "#L" line))) 49 | 50 | (defn lookup-var [ns name] 51 | (search/lookup (str ns "/" name))) 52 | 53 | (defn $note [{:keys [body user created-at]}] 54 | [:div.note 55 | [:div.note-meta 56 | "By " 57 | (util/$avatar user) 58 | " " 59 | (:login user) 60 | ", " 61 | (util/timeago created-at) 62 | " ago."] 63 | [:div.note-body 64 | (-> (util/markdown body) 65 | (str/replace #"
" "
")
 66 |         (str/replace #"
" "
"))]]) 67 | 68 | (defn $notes [notes name] 69 | [:div.var-notes 70 | [:h5 (util/pluralize (count notes) "Note" "Notes")] 71 | [:div 72 | (if (empty? notes) 73 | [:div.null-state "No notes for " [:code name]] 74 | [:ul 75 | (for [n notes] 76 | ($note n))])] 77 | [:div.add-note-widget]]) 78 | 79 | 80 | (defn clean-id [{:keys [_id] :as m}] 81 | (assoc m :_id (str _id))) 82 | 83 | (defn clean-example [{:keys [_id user history] :as m}] 84 | (-> m 85 | (update-in [:user] dissoc :email) 86 | (update-in [:_id] str))) 87 | 88 | (defn clean-see-also [m] 89 | (-> m 90 | (update-in [:user] dissoc :email) 91 | (update-in [:_id] str))) 92 | 93 | (defn $number-badge [num] 94 | [:span.badge num]) 95 | 96 | (defn $var-header [{:keys [ns name added arglists forms] :as v}] 97 | [:div.row.var-header 98 | [:div.col-sm-8 99 | [:h1.var-name (util/html-encode name)]] 100 | [:div.col-sm-4 101 | [:div.var-meta 102 | [:h4 [:a {:href (str "/" ns)} ns]] 103 | (when added 104 | [:span "Available since " added]) 105 | (when-let [su (source-url v)] 106 | [:span.source-link 107 | " (" 108 | [:a {:href su} "source"] 109 | ") "])]] 110 | [:div.col-sm-12 111 | [:section 112 | [:ul.arglists 113 | (if forms 114 | (map #($argform %) forms) 115 | (map #($arglist name %) arglists))]]]]) 116 | 117 | (defn var-page-handler [ns name] 118 | (let [name (util/cd-decode (codec/url-decode name)) 119 | {:keys [arglists name ns doc runtimes added file] :as v} (lookup-var ns name)] 120 | (fn [{:keys [user session uri]}] 121 | (when v 122 | (let [examples (data/find-examples-for v) 123 | see-alsos (->> v 124 | see-alsos-for 125 | (map #(assoc % :can-delete? (util/is-author? user %)))) 126 | library (library-for v) 127 | recent (:recent session) 128 | notes (->> v 129 | data/find-notes-for 130 | (map #(let [author? (util/is-author? user %)] 131 | (-> % 132 | (assoc :can-delete? author?) 133 | (assoc :can-edit? author?)))))] 134 | {:session (update-in session [:recent] 135 | #(->> % 136 | (concat [{:text name 137 | :href (str "/" ns "/" (util/cd-encode name))}]) 138 | distinct 139 | (filter :text) 140 | (take 4))) 141 | :body 142 | (common/$main 143 | {:body-class "var-page" 144 | :title (util/html-encode (str name " - " ns " | ClojureDocs - Community-Powered Clojure Documentation and Examples")) 145 | :page-data {:examples (mapv clean-example examples) 146 | :var v 147 | :notes (vec (map clean-id notes)) 148 | :see-alsos (vec (map clean-see-also see-alsos)) 149 | :user (when user (select-keys user [:login :avatar-url :account-source]))} 150 | :page-uri uri 151 | :user user 152 | :mobile-nav [{:title "Nav" 153 | :links [[:a {:href "#" 154 | :data-animate-scroll "true" 155 | :data-animate-buffer "70"} 156 | "Top"] 157 | [:a {:href "#examples" 158 | :data-animate-scroll "true" 159 | :data-animate-buffer "70"} 160 | "Examples " 161 | [:span.examples-count 162 | ($number-badge (count examples))]] 163 | [:a {:href "#see-also" 164 | :data-animate-scroll "true" 165 | :data-animate-buffer "70"} 166 | "See Also " ($number-badge (count see-alsos))] 167 | (when (> (count notes) 0) 168 | [:a {:href "#notes" 169 | :data-animate-scroll "true" 170 | :data-animate-buffer "70"} 171 | "Notes " ($number-badge (count notes))])]} 172 | {:title "Namespaces" 173 | :links (->> library 174 | :namespaces 175 | (map (fn [{:keys [name]}] 176 | [:a {:href (str "/" name)} name])))}] 177 | :content [:div 178 | [:div.row 179 | [:div.col-sm-2.sidenav 180 | [:div.desktop-side-nav {:data-sticky-offset "10"} 181 | [:div.var-page-nav] 182 | (common/$library-nav library ns)]] 183 | [:div.col-sm-10 184 | ($var-header v) 185 | [:section 186 | [:div.docstring 187 | (if doc 188 | [:pre (-> doc 189 | (str/replace #"\n\s\s" "\n") 190 | util/html-encode)] 191 | [:div.null-state "No Doc"]) 192 | (when doc 193 | [:div.copyright 194 | "© Rich Hickey. All rights reserved." 195 | " " 196 | [:a {:href "http://www.eclipse.org/legal/epl-v10.html"} 197 | "Eclipse Public License 1.0"]])]] 198 | [:section 199 | [:div.examples-widget {:id "examples"}]] 200 | [:section 201 | [:div.see-alsos-widget {:id "see-also"}]] 202 | [:section 203 | [:div.notes-widget {:id "notes"}]]]]]})}))))) 204 | 205 | (defn $example-body [{:keys [body]}] 206 | [:div.example-body 207 | [:pre.raw-example {:class "brush: clojure"} body]]) 208 | 209 | (defn $example-history-point [{:keys [user body created-at updated-at] :as ex}] 210 | [:div.var-example 211 | [:div 212 | (let [num-to-show 7] 213 | [:div.example-meta 214 | [:div.contributors 215 | "Created by  " 216 | (util/$avatar user) 217 | "" 218 | (util/timeago created-at) " ago."] 219 | [:div.links 220 | ]])] 221 | [:div ($example-body ex)]]) 222 | 223 | (defn example-handler [id] 224 | (fn [{:keys [user session uri]}] 225 | (let [{:keys [history name ns] :as ex} 226 | (mon/fetch-one :examples :where {:_id (util/bson-id id)})] 227 | (common/$main 228 | {:body-class "example-page" 229 | :user user 230 | :page-uri uri 231 | :content [:div.row 232 | [:div.col-md-12 233 | [:p 234 | "Example history for " 235 | (util/$var-link ns name (str ns "/" name)) 236 | ", in order from newest to oldest. " 237 | "The currrent version is outlined in yellow."] 238 | #_[:div.current-example 239 | ($example ex)] 240 | (->> history 241 | reverse 242 | (map $example-history-point))]]})))) 243 | -------------------------------------------------------------------------------- /src/clj/clojuredocs/search.clj: -------------------------------------------------------------------------------- 1 | (ns clojuredocs.search 2 | (:require [clucy.core :as clucy] 3 | [clojure.string :as str] 4 | [clojuredocs.util :as util] 5 | [clojure.pprint :refer [pprint]] 6 | [clojuredocs.search.static :as static])) 7 | 8 | (def search-index (clucy/memory-index)) 9 | 10 | (defn tokenize-name [s] 11 | (when s 12 | (str 13 | s 14 | " " 15 | (->> (str/split s #"-") 16 | (interpose " ") 17 | (apply str))))) 18 | 19 | (def var-keys 20 | [:ns 21 | :name 22 | :file 23 | :column 24 | :line 25 | :added 26 | :arglists 27 | :doc 28 | :static 29 | :tag ; convert to string 30 | :macro 31 | :dynamic 32 | :special-form 33 | :forms ; -> list of strings 34 | :deprecated 35 | :url 36 | :no-doc]) 37 | 38 | (defn cond-update-in [m keys & rest] 39 | (if (get-in m keys) 40 | (apply update-in m keys rest) 41 | m)) 42 | 43 | (defn type-of [{:keys [type macro arglists special-form]}] 44 | (cond 45 | type type 46 | macro "macro" 47 | (> (count arglists) 0) "function" 48 | special-form "special-form" 49 | :else "var")) 50 | 51 | (defn transform-var-meta [m] 52 | (-> m 53 | (select-keys var-keys) 54 | (cond-update-in [:tag] #(if (class? %) 55 | (.getName %) 56 | (str %))) 57 | (cond-update-in [:forms] #(map str %)) 58 | (update-in [:ns] str) 59 | (update-in [:arglists] #(map 60 | (fn [arg-list-coll] 61 | (->> arg-list-coll 62 | (map str) 63 | (interpose " ") 64 | (apply str))) 65 | %)) 66 | (update-in [:name] str))) 67 | 68 | (defn gather-var [ns-obj] 69 | (->> ns-obj 70 | ns-publics 71 | (map second) 72 | (map meta))) 73 | 74 | (defn gather-vars [{:keys [namespaces library-url] :as lib}] 75 | (assoc lib :vars (->> namespaces 76 | (map :name) 77 | (map symbol) 78 | (map find-ns) 79 | (mapcat gather-var) 80 | (concat static/special-forms) 81 | (map transform-var-meta) 82 | (map #(assoc % :library-url library-url)) 83 | (map #(assoc % :type (type-of %))) 84 | (map #(assoc % :href (str "/" (:ns %) "/" (util/cd-encode (:name %)))))))) 85 | 86 | (defn gather-namespace [ns-name] 87 | (require (symbol ns-name)) 88 | (let [sym (symbol ns-name) 89 | namespace (find-ns sym) 90 | meta (meta namespace)] 91 | (merge 92 | (select-keys meta [:doc :no-doc :added]) 93 | {:name (str ns-name)}))) 94 | 95 | (defn gather-namespaces [{:keys [namespaces] :as lib}] 96 | (assoc lib 97 | :namespaces 98 | (->> namespaces 99 | (map gather-namespace) 100 | (remove :no-doc)))) 101 | 102 | (def clojure-lib 103 | (-> {:library-url "https://github.com/clojure/clojure" 104 | :version "1.11.1" 105 | :source-base-url "https://github.com/clojure/clojure/1.11.1/blob" 106 | :gh-tag-url "https://github.com/clojure/clojure/tree/clojure-1.11.1" 107 | :namespaces static/clojure-namespaces} 108 | gather-namespaces 109 | gather-vars)) 110 | 111 | (def searchable-vars 112 | (->> clojure-lib 113 | :vars 114 | (map #(assoc % :keywords (tokenize-name (:name %)))))) 115 | 116 | (def searchable-nss 117 | (->> static/clojure-namespaces 118 | (map (fn [sym] 119 | {:name (str sym) 120 | :keywords (str 121 | (str sym) 122 | " " 123 | (->> (str/split (str sym) #"\.") 124 | (interpose " ") 125 | (apply str))) 126 | :type "namespace"})) 127 | (map (fn [{:keys [name] :as ns}] 128 | (assoc ns :href (str "/" name)))))) 129 | 130 | (binding [clucy/*analyzer* (org.apache.lucene.analysis.core.WhitespaceAnalyzer. clucy/*version*)] 131 | (doseq [nm searchable-vars] 132 | (clucy/add search-index nm)) 133 | (doseq [ns searchable-nss] 134 | (clucy/add search-index ns)) 135 | (doseq [page static/searchable-pages] 136 | (clucy/add search-index page))) 137 | 138 | (def lookup-vars 139 | (->> searchable-vars 140 | (reduce #(assoc %1 (str (:ns %2) "/" (:name %2)) %2) {}))) 141 | 142 | (defn lookup [ns-name] 143 | (get lookup-vars ns-name)) 144 | 145 | (defn lucene-escape [s] 146 | (str/replace s #"[\+\-\!\(\)\{\}\[\]\^\"\~\*\?\:\\\/]" "\\\\$0")) 147 | 148 | ;; https://gist.github.com/ck/960716 149 | (defn- compute-next-row 150 | "computes the next row using the prev-row current-element and the other seq" 151 | [prev-row current-element other-seq pred] 152 | (reduce 153 | (fn [row [diagonal above other-element]] 154 | (let [update-val 155 | (if (pred other-element current-element) 156 | diagonal 157 | (inc (min diagonal above (peek row))) 158 | )] 159 | (conj row update-val))) 160 | [(inc (first prev-row))] 161 | (map vector prev-row (next prev-row) other-seq))) 162 | 163 | (defn levenshtein-distance 164 | "Levenshtein Distance - http://en.wikipedia.org/wiki/Levenshtein_distance 165 | In information theory and computer science, the Levenshtein distance is a metric for measuring the amount of difference between two sequences. This is a functional implementation of the levenshtein edit 166 | distance with as little mutability as possible. 167 | 168 | Still maintains the O(n*m) guarantee. 169 | " 170 | [a b & {p :predicate :or {p =}}] 171 | (peek 172 | (reduce 173 | (fn [prev-row current-element] 174 | (compute-next-row prev-row current-element b p)) 175 | (map #(identity %2) (cons nil b) (range)) 176 | a))) 177 | 178 | (defn drop-leading-stars [q] 179 | (when q 180 | (let [stripped (if (.startsWith q "*") 181 | (->> (str/replace q #"\**" "") 182 | (apply str)) 183 | q)] 184 | (when-not (empty? stripped) 185 | stripped)))) 186 | 187 | (defn escape-query [q] 188 | (when-not (empty? q) 189 | (org.apache.lucene.queryparser.classic.QueryParser/escape q))) 190 | 191 | (defn format-query [q] 192 | (some-> q 193 | str/trim 194 | drop-leading-stars 195 | lucene-escape 196 | (str "*"))) 197 | 198 | (defn query [q] 199 | (cond 200 | (= "*" q) [(lookup "clojure.core/*")] 201 | 202 | :else 203 | (when-let [q (format-query q)] 204 | (->> (clucy/search search-index q 1000 :default-field "keywords") 205 | (map #(assoc % :edit-distance (levenshtein-distance (str (:name %)) q))) 206 | (sort-by :edit-distance))))) 207 | -------------------------------------------------------------------------------- /src/clj/clojuredocs/search/static.clj: -------------------------------------------------------------------------------- 1 | (ns clojuredocs.search.static) 2 | 3 | (def clojure-namespaces 4 | '[clojure.core 5 | clojure.core.async 6 | clojure.core.logic 7 | clojure.core.logic.fd 8 | clojure.core.logic.pldb 9 | clojure.core.protocols 10 | clojure.core.reducers 11 | clojure.core.server 12 | clojure.data 13 | clojure.data.csv 14 | clojure.datafy 15 | clojure.edn 16 | clojure.inspector 17 | clojure.instant 18 | clojure.java.browse 19 | clojure.java.io 20 | clojure.java.javadoc 21 | clojure.java.shell 22 | clojure.main 23 | clojure.math 24 | clojure.pprint 25 | clojure.reflect 26 | clojure.repl 27 | clojure.set 28 | clojure.spec.alpha 29 | clojure.stacktrace 30 | clojure.string 31 | clojure.template 32 | clojure.test 33 | clojure.test.junit 34 | clojure.test.tap 35 | clojure.walk 36 | clojure.xml 37 | clojure.zip]) 38 | 39 | (def special-forms 40 | (->> [{:name 'def 41 | :ns "clojure.core" 42 | :doc "Creates and interns or locates a global var with the name of symbol and a 43 | namespace of the value of the current namespace (*ns*). See 44 | http://clojure.org/special_forms for more information."} 45 | {:name 'if 46 | :ns "clojure.core" 47 | :doc "Evaluates test."} 48 | {:name 'do 49 | :ns "clojure.core" 50 | :doc "Evaluates the expressions in order and returns the value of the last. If no 51 | expressions are supplied, returns nil. See http://clojure.org/special_forms 52 | for more information."} 53 | {:name 'quote 54 | :ns "clojure.core" 55 | :doc "Yields the unevaluated form. See http://clojure.org/special_forms for more 56 | information."} 57 | {:name 'var 58 | :ns "clojure.core" 59 | :doc "The symbol must resolve to a var, and the Var object itself (not its value) 60 | is returned. The reader macro #'x expands to (var x). See 61 | http://clojure.org/special_forms for more information."} 62 | {:name 'recur 63 | :ns "clojure.core" 64 | :doc "Evaluates the exprs in order, then, in parallel, rebinds the bindings of 65 | the recursion point to the values of the exprs. See 66 | http://clojure.org/special_forms for more information."} 67 | {:name 'throw 68 | :ns "clojure.core" 69 | :doc "The expr is evaluated and thrown, therefore it should yield an instance of 70 | some derivee of Throwable. Please see http://clojure.org/special_forms#throw"} 71 | {:name 'try 72 | :ns "clojure.core" 73 | :doc "The exprs are evaluated and, if no exceptions occur, the value of the last 74 | is returned. If an exception occurs and catch clauses are provided, each is 75 | examined in turn and the first for which the thrown exception is an instance 76 | of the named class is considered a matching catch clause. If there is a 77 | matching catch clause, its exprs are evaluated in a context in which name is 78 | bound to the thrown exception, and the value of the last is the return value 79 | of the function. If there is no matching catch clause, the exception 80 | propagates out of the function. Before returning, normally or abnormally, 81 | any finally exprs will be evaluated for their side effects. See 82 | http://clojure.org/special_forms for more information."} 83 | {:name 'catch 84 | :ns "clojure.core" 85 | :doc "The exprs are evaluated and, if no exceptions occur, the value of the last 86 | is returned. If an exception occurs and catch clauses are provided, each is 87 | examined in turn and the first for which the thrown exception is an instance 88 | of the named class is considered a matching catch clause. If there is a 89 | matching catch clause, its exprs are evaluated in a context in which name is 90 | bound to the thrown exception, and the value of the last is the return value 91 | of the function. If there is no matching catch clause, the exception 92 | propagates out of the function. Before returning, normally or abnormally, 93 | any finally exprs will be evaluated for their side effects. See 94 | http://clojure.org/special_forms for more information."} 95 | {:name 'finally 96 | :ns "clojure.core" 97 | :doc "The exprs are evaluated and, if no exceptions occur, the value of the last 98 | is returned. If an exception occurs and catch clauses are provided, each is 99 | examined in turn and the first for which the thrown exception is an instance 100 | of the named class is considered a matching catch clause. If there is a 101 | matching catch clause, its exprs are evaluated in a context in which name is 102 | bound to the thrown exception, and the value of the last is the return value 103 | of the function. If there is no matching catch clause, the exception 104 | propagates out of the function. Before returning, normally or abnormally, 105 | any finally exprs will be evaluated for their side effects. See 106 | http://clojure.org/special_forms for more information."} 107 | {:name '. 108 | :ns "clojure.core" 109 | :doc "The '.' special form is the basis for access to Java. It can be considered 110 | a member-access operator, and/or read as 'in the scope of'. See 111 | http://clojure.org/special_forms for more information."} 112 | {:name 'set! 113 | :ns "clojure.core" 114 | :doc "Assignment special form. When the first operand is a field member access 115 | form, the assignment is to the corresponding field. If it is an instance 116 | field, the instance expr will be evaluated, then the expr. In all cases 117 | the value of expr is returned. Note - you cannot assign to function params 118 | or local bindings. Only Java fields, Vars, Refs and Agents are mutable in 119 | Clojure. See http://clojure.org/special_forms for more information."} 120 | {:name 'monitor-enter 121 | :ns "clojure.core" 122 | :doc "A synchronization primitive that should be avoided in user code. Use the 123 | locking macro. See http://clojure.org/special_forms for more information."} 124 | {:name 'monitor-exit 125 | :ns "clojure.core" 126 | :doc "A synchronization primitive that should be avoided in user code. Use the 127 | locking macro. See http://clojure.org/special_forms for more information."} 128 | {:name 'new 129 | :ns "clojure.core" 130 | :doc "Instantiate a class. See http://clojure.org/java_interop#new for 131 | more information."}] 132 | (map #(assoc % :type "special-form")))) 133 | 134 | (def concept-pages 135 | [{:name "Destructuring" 136 | :keywords "destructuring destructure destruct" 137 | :href "/concepts/destructuring" 138 | :desc "Destructuring allows you to assign names to values based on the structure of a parameter."} 139 | {:name "Functional Programming" 140 | :keywords "functional programming" 141 | :href "/concepts/functional-programming" 142 | :desc "Rooted in lambda calculus, functional programming is a the style of building programs in a declarative way favoring composition of first-class, pure, and higher-order functions, immutable data structures, laziness, and the elimination of side effects. "}]) 143 | 144 | (def searchable-pages 145 | (->> [{:name "Quick Reference" 146 | :keywords "help, getting started, quickref, quick reference" 147 | :href "/quickref" 148 | :desc "Clojure functions broken down by conceptual area (string manipulation, collections, etc)."} 149 | {:name "Laziness in Clojure" 150 | :keywords "lazy laziness lazyness sequences seq lazy evaluation" 151 | :href "/concepts/lazyness" 152 | :desc "Laziness is the deferred or delayed execution of some bit of code, opposite of eager or immediate evaluation. Laziness is used Clojure to enable execution composition and solutions to problems that involve infinite sequences. FIX THIS"} 153 | {:name "Read-Eval-Print Loop (REPL)" 154 | :keywords "repl read eval print loop" 155 | :href "/concepts/repl" 156 | :desc "A read–eval–print loop (REPL), also known as an interactive toplevel or language shell, is a simple, interactive computer programming environment that takes single user inputs (i.e. single expressions), evaluates them, and returns the result to the user; a program written in a REPL environment is executed piecewise. The term is most usually used to refer to programming interfaces similar to the classic Lisp interactive environment. Common examples include command line shells and similar environments for programming languages."} 157 | {:name "Thrush Operators (->, ->>)" 158 | :keywords "thrush operators -> ->> as->" 159 | :href "/concepts/thrush" 160 | :desc "http://thecomputersarewinning.com/post/Clojure-Thrush-Operator/"} 161 | {:name "Recursion" 162 | :keywords "recursion loop recur trampoline" 163 | :href "https://www.google.com/search?q=recursion" 164 | :desc "Recursion is the process of repeating items in a self-similar way. For instance, when the surfaces of two mirrors are exactly parallel with each other the nested images that occur are a form of infinite recursion."}] 165 | (concat concept-pages) 166 | (map #(assoc % :type "page")))) 167 | -------------------------------------------------------------------------------- /src/clj/nsfw/reup.clj: -------------------------------------------------------------------------------- 1 | (ns nsfw.reup 2 | "Utilities for supporting a clojure.tools.namespace reloading dev 3 | lifecycle. 4 | 5 | Add the following to your project.clj 6 | 7 | `:repl-options {:init (load-file \"reup.clj\")}`" 8 | (:require [clojure.tools.namespace.repl :as repl] 9 | [clojure.tools.namespace.find :as ns-find] 10 | [clojure.java.classpath :as cp] 11 | [clojure.string :as str])) 12 | 13 | (defn exception? [e] 14 | (isa? (type e) Exception)) 15 | 16 | (defn ns-for-sym [sym] 17 | (when (.contains (str sym) "/") 18 | (-> sym 19 | str 20 | (str/split #"/") 21 | first 22 | symbol))) 23 | 24 | (defn setup 25 | "Helper for initializing a clojure.tools.namespace dev 26 | lifecycle. See 27 | http://thinkrelevance.com/blog/2013/06/04/clojure-workflow-reloaded 28 | for more info. 29 | 30 | This will return a function that, when called, will stop the 31 | current environment, reload all namespaces, and start a new 32 | environment. 33 | 34 | Params: 35 | * `start-app-sym` -- FQ symbol of a no-arg function which 36 | starts the environment 37 | * `stop-app-sym` -- FQ symbol of a 1-arg 38 | function which stops the environment. The result of calling the 39 | start app function is passed in as it's first parameter 40 | * `tests-regex` -- Run tests after reload for all namespaces matching" 41 | 42 | [{:keys [start-app-sym stop-app-sym tests-regex]}] 43 | (when start-app-sym 44 | (when-not (resolve 'user/reup-app) 45 | (intern 'user 'reup-app nil)) 46 | (when-not (resolve 'user/after-reup) 47 | (intern 'user 'after-reup 48 | (fn [] 49 | (when start-app-sym 50 | (binding [*ns* (find-ns 'user)] 51 | (alter-var-root (resolve 'user/reup-app) 52 | (constantly (when-let [a (resolve start-app-sym)] 53 | (let [f (deref a)] 54 | (f)))))))))) 55 | 56 | (require (ns-for-sym start-app-sym) :reload) 57 | (require (ns-for-sym stop-app-sym) :reload) 58 | 59 | (when-not (resolve start-app-sym) 60 | (throw (Exception. (str "Can't resolve start-app-sym: " start-app-sym)))) 61 | 62 | (when-not (resolve stop-app-sym) 63 | (throw (Exception. (str "Can't resolve stop-app-sym: " stop-app-sym))))) 64 | 65 | (fn [] 66 | (time 67 | (do 68 | (when start-app-sym 69 | (binding [*ns* (find-ns 'user)] 70 | (do 71 | (try 72 | (@(resolve stop-app-sym) @(resolve 'user/reup-app)) 73 | (catch Exception e 74 | (println "Exception stopping app:" e))))) 75 | (alter-var-root (resolve 'user/reup-app) (constantly nil))) 76 | (let [res (if start-app-sym 77 | (repl/refresh :after 'user/after-reup) 78 | (repl/refresh))] 79 | (when (exception? res) 80 | (throw res))) 81 | 82 | (when tests-regex 83 | (doseq [ns-sym (->> (cp/classpath-directories) 84 | ns-find/find-namespaces 85 | (filter #(re-find tests-regex (str %))))] 86 | (require ns-sym)) 87 | (clojure.test/run-all-tests tests-regex)))))) 88 | -------------------------------------------------------------------------------- /src/cljc/clojuredocs/md5.cljc: -------------------------------------------------------------------------------- 1 | (ns clojuredocs.md5 2 | #?(:clj 3 | (:require [clojure.string :as str]) 4 | :cljs 5 | (:require [clojure.string :as str] 6 | [goog.crypt :as gcrypt] 7 | [goog.crypt.Md5 :as Md5] 8 | [goog.crypt.Sha1 :as Sha1] 9 | [goog.string :as gstring] 10 | [goog.string.format]))) 11 | 12 | 13 | 14 | #?(:cljs 15 | (do 16 | (defn string->bytes [s] 17 | (gcrypt/stringToUtf8ByteArray s)) ;; must be utf8 byte array 18 | 19 | (defn bytes->hex 20 | "convert bytes to hex" 21 | [bytes-in] 22 | (gcrypt/byteArrayToHex bytes-in)) 23 | 24 | (defn hash-bytes [digester bytes-in] 25 | (do 26 | (.update digester bytes-in) 27 | (.digest digester))) 28 | 29 | (defn md5- 30 | "convert bytes to md5 bytes" 31 | [bytes-in] 32 | (hash-bytes (goog.crypt.Md5.) bytes-in)) 33 | 34 | (defn md5-bytes 35 | "convert utf8 string to md5 byte array" 36 | [string] 37 | (md5- (string->bytes string))) 38 | 39 | (defn md5-hex 40 | "convert utf8 string to md5 hex string" 41 | [string] 42 | (when string 43 | (bytes->hex (md5-bytes string)))))) 44 | 45 | 46 | 47 | #? (:clj 48 | (defn md5-hex 49 | "Compute the hex MD5 sum of a string." 50 | [#^String str] 51 | (when str 52 | (let [alg (doto (java.security.MessageDigest/getInstance "MD5") 53 | (.reset) 54 | (.update (.getBytes str)))] 55 | (try 56 | (.toString (new BigInteger 1 (.digest alg)) 16) 57 | (catch java.security.NoSuchAlgorithmException e 58 | (throw (new RuntimeException e)))))))) 59 | -------------------------------------------------------------------------------- /src/cljc/clojuredocs/syntax.cljc: -------------------------------------------------------------------------------- 1 | ;; From https://github.com/holmsand/reagent 2 | 3 | (ns clojuredocs.syntax 4 | (:require [clojure.string :as string])) 5 | 6 | (def builtins #{"def" "defn" "ns" "atom" "let" "if" "when" 7 | "cond" "merge" "assoc" "swap!" "reset!" "for" 8 | "range" "nil?" "int" "or" "->" "->>" "%" "fn" "if-not" 9 | "empty?" "case" "str" "pos?" "zero?" "map" "remove" 10 | "empty" "into" "assoc-in" "dissoc" "get-in" "when-not" 11 | "filter" "vals" "count" "complement" "identity" "dotimes" 12 | "update-in" "sorted-map" "inc" "dec" "false" "true" "not" 13 | "=" "partial" "first" "second" "rest" "list" "conj" 14 | "drop" "when-let" "if-let" "add-watch" "mod" "quot" 15 | "bit-test" "vector"}) 16 | 17 | (def styles {:comment {:style {:color "#008200"}} 18 | :str-litt {:style {:color "#2D2DFE"}} 19 | :keyw {:style {:color "#77f"}} 20 | :builtin {:style {:font-weight "normal" 21 | :color "#687868"}} 22 | :def {:style {:color "#55c" 23 | :font-weight "normal"}}}) 24 | 25 | (def paren-styles [{:style {:color "#272"}} 26 | {:style {:color "#940"}} 27 | {:style {:color "#44a"}}]) 28 | 29 | (defn tokenize [src] 30 | (let [ws " \\t\\n" 31 | open "\\[\\(\\{" 32 | close "\\)\\]\\}" 33 | sep (str ws open close) 34 | comment-p ";.*" 35 | str-p "\"[^\"]*\"" 36 | open-p (str "[" open "]") 37 | close-p (str "[" close "]") 38 | iden-p (str "[^" sep "]+") 39 | meta-p (str "\\^" iden-p) 40 | any-p (str "[" ws "]+" "|\\^[^" sep "]+|.") 41 | patt (re-pattern (str "(" 42 | (string/join ")|(" [ 43 | comment-p 44 | str-p open-p 45 | close-p meta-p iden-p any-p 46 | ]) 47 | ")")) 48 | keyw-re #"^:"] 49 | (for [[s comment str-litt open close met iden any] (re-seq patt src)] 50 | (cond 51 | comment [:comment s] 52 | str-litt [:str-litt s] 53 | open [:open s] 54 | close [:close s] 55 | met [:other s] 56 | iden (cond 57 | (re-find keyw-re s) [:keyw s] 58 | (builtins s) [:builtin s] 59 | :else [:iden s]) 60 | any [:other s])))) 61 | 62 | (defn format-style-clj [s] 63 | (->> s 64 | (map (fn [[k v]] 65 | (str (name k) ":" v))) 66 | (interpose ";") 67 | (apply str))) 68 | 69 | (defn format-style [{:keys [style] :as opts}] 70 | (assoc opts :style (format-style-clj style))) 71 | 72 | (defn syntaxify [src & opts] 73 | (let [{:keys [stringify-style?]} (apply hash-map opts) 74 | def-re #"^def|^ns\b" 75 | ncol (count paren-styles) 76 | paren-style (fn [level] 77 | (nth paren-styles (mod level ncol)))] 78 | (loop [tokens (tokenize src) 79 | prev nil 80 | level 0 81 | res []] 82 | (let [[kind val] (first tokens) 83 | level' (case kind 84 | :open (inc level) 85 | :close (dec level) 86 | level) 87 | style (case kind 88 | :iden (when (and prev (re-find def-re prev)) 89 | (:def styles)) 90 | :open (paren-style level) 91 | :close (paren-style level') 92 | (styles kind)) 93 | remain (rest tokens)] 94 | (if-not (empty? remain) 95 | (recur remain 96 | (if (= kind :other) prev val) 97 | level' 98 | (conj res [:span 99 | (if stringify-style? 100 | (format-style style) 101 | style) 102 | val])) 103 | (apply vector :pre.syntaxify (conj res [:span style val]))))))) 104 | -------------------------------------------------------------------------------- /src/cljc/clojuredocs/util.cljc: -------------------------------------------------------------------------------- 1 | (ns clojuredocs.util 2 | (:require [clojure.string :as str] 3 | [clojuredocs.md5 :as md5] 4 | #? (:clj [cheshire.core :as json]) 5 | #? (:clj [clojure.pprint :refer [pprint]]) 6 | #? (:cljs [goog.string :as gstring]) 7 | #? (:cljs [cljs.reader :as reader])) 8 | 9 | 10 | #? (:clj 11 | (:import [java.net URLEncoder] 12 | [org.bson.types ObjectId] 13 | [com.vladsch.flexmark.html HtmlRenderer] 14 | [com.vladsch.flexmark.parser Parser] 15 | [com.vladsch.flexmark.profile.pegdown Extensions] 16 | [com.vladsch.flexmark.profile.pegdown PegdownOptionsAdapter] 17 | [com.vladsch.flexmark.util.data DataHolder]))) 18 | 19 | #? (:clj 20 | (do 21 | (defn url-encode [s] 22 | (when s 23 | (URLEncoder/encode s))) 24 | 25 | 26 | (defn url-decode [s] 27 | (when s 28 | (java.net.URLDecoder/decode s))))) 29 | 30 | #? (:cljs 31 | (do 32 | (defn url-encode 33 | [string] 34 | (some-> string 35 | str 36 | (js/encodeURIComponent) 37 | (.replace "+" "%20"))) 38 | 39 | (defn url-decode [s] 40 | (some-> s 41 | str 42 | js/decodeURIComponent)))) 43 | 44 | #? (:clj 45 | (do 46 | (defn html-encode [s] 47 | (when s 48 | (-> s 49 | (str/replace #"&" "&") 50 | (str/replace #"<" "<") 51 | (str/replace #">" ">")))) 52 | 53 | (defn cd-decode [s] 54 | (when s 55 | (cond 56 | (= "_dot" s) "." 57 | (= "_." s) "." 58 | (= "_.." s) ".." 59 | :else (-> s 60 | (str/replace #"_fs" "/") 61 | (str/replace #"_bs" "\\\\") 62 | (str/replace #"_q" "?") 63 | 64 | ;; legacy 65 | (str/replace #"_dot" "."))))))) 66 | 67 | (defn cd-encode [s] 68 | (when s 69 | (cond 70 | (= "." s) "_." 71 | (= ".." s) "_.." 72 | :else (-> s 73 | (str/replace #"/" "_fs") 74 | (str/replace #"\\" "_bs") 75 | (str/replace #"\?" "_q"))))) 76 | 77 | (defn var-path [ns name] 78 | (str "/" ns "/" (cd-encode name))) 79 | 80 | (defn $var-link [ns name & contents] 81 | (vec 82 | (concat 83 | [:a {:href (var-path ns name)}] 84 | contents))) 85 | 86 | #? (:cljs 87 | (defn navigate-to [url] 88 | (aset (.-location js/window) "href" url))) 89 | 90 | (def md5 md5/md5-hex) 91 | 92 | #? (:cljs 93 | (defn markdown [s] 94 | (when s 95 | (js/marked s)))) 96 | 97 | 98 | #? (:clj 99 | (defn markdown [s] 100 | (when s 101 | (let [OPTIONS (PegdownOptionsAdapter/flexmarkOptions Extensions/ALL 102 | (make-array com.vladsch.flexmark.util.misc.Extension 0)) 103 | PARSER (-> (Parser/builder OPTIONS) .build) 104 | RENDERER (-> (HtmlRenderer/builder OPTIONS) .build)] 105 | (->> s (str) (.parse PARSER) (.render RENDERER)))))) 106 | 107 | (defn pluralize [n single plural] 108 | (str n " " (if (= 1 n) single plural))) 109 | 110 | (defn now [] 111 | #? (:clj 112 | (System/currentTimeMillis) 113 | :cljs 114 | (.now js/Date))) 115 | 116 | (defn profile-url [{:keys [login account-source]}] 117 | (str (if (= "github" account-source) 118 | "/u/" 119 | "/uc/") 120 | login)) 121 | 122 | (defn $avatar [{:keys [email login avatar-url account-source] :as user} & [{:keys [size]}]] 123 | (let [size (str (or size 32))] 124 | ^{:key (or avatar-url email)} 125 | [:a.avatar-link 126 | {:href (profile-url user)} 127 | [:img.avatar 128 | {:src (or (str avatar-url "&s=" size) 129 | (str "https://www.gravatar.com/avatar/" 130 | (md5 email) 131 | "?r=PG&s=" size "&default=identicon")) }]])) 132 | 133 | (defn sformat [& args] 134 | #?(:cljs 135 | (apply gstring/format args) 136 | :clj 137 | (apply format args))) 138 | 139 | (defn timeago [millis] 140 | (when millis 141 | (let [ms (- (now) millis) 142 | s (/ ms 1000) 143 | m (/ s 60) 144 | h (/ m 60) 145 | d (/ h 24) 146 | y (/ d 365.0)] 147 | (cond 148 | (< s 60) "less than a minute" 149 | (< m 2) "1 minute" 150 | (< h 1) (str (int m) " minutes") 151 | (< h 2) "1 hour" 152 | (< d 1) (str (int h) " hours") 153 | (< d 2) "1 day" 154 | (< y 1) (str (int d) " days") 155 | :else (str (sformat "%.1f" y) " years"))))) 156 | 157 | (defn to-json [o] 158 | #? (:clj 159 | (json/generate-string o) 160 | :cljs 161 | (.stringify js/JSON o))) 162 | 163 | (defn from-json [s] 164 | #? (:clj 165 | (json/parse-string s true) 166 | :cljs 167 | (.parse js/JSON s))) 168 | 169 | 170 | #? (:clj 171 | (defn bson-id 172 | ([] 173 | (ObjectId.)) 174 | ([id-or-str] 175 | (ObjectId. (str id-or-str))))) 176 | 177 | #? (:clj 178 | (defn uuid [] 179 | (-> (java.util.UUID/randomUUID) 180 | str 181 | (str/replace #"-" "")))) 182 | 183 | #? (:clj 184 | (defn pp-str [o] 185 | (let [w (java.io.StringWriter.)] 186 | (pprint o w) 187 | (str/trim (.toString w))))) 188 | 189 | #? (:cljs 190 | (defn page-data! [] 191 | (reader/read-string (aget js/window "PAGE_DATA")))) 192 | 193 | 194 | (defn is-author? [user o] 195 | (= (select-keys user [:login :account-source]) 196 | (select-keys (:author o) [:login :account-source]))) 197 | 198 | 199 | #? (:cljs 200 | (defn location-hash [] 201 | (let [hash-str (.. js/window -location -hash)] 202 | (->> hash-str 203 | (drop 1) 204 | (apply str) 205 | url-decode)))) 206 | 207 | (defn ellipsis [n s] 208 | (when s 209 | (let [len (count s)] 210 | (if (> len n) 211 | (str (->> s 212 | (take n) 213 | (apply str) 214 | str/trim) 215 | "...") 216 | s)))) 217 | -------------------------------------------------------------------------------- /src/cljc/nsfw/util.cljc: -------------------------------------------------------------------------------- 1 | (ns nsfw.util 2 | #?(:clj 3 | (:require [clojure.string :as str] 4 | [cheshire.custom :as json] 5 | [clojure.pprint :as pprint] 6 | [camel-snake-kebab.core :as csk] 7 | [cognitect.transit :as transit] 8 | [camel-snake-kebab.core :as csk]) 9 | :cljs 10 | (:require [clojure.string :as str] 11 | [cognitect.transit :as transit] 12 | [cljs.pprint :as pprint] 13 | [camel-snake-kebab.core :as csk])) 14 | 15 | #?(:clj 16 | (:import 17 | [java.io ByteArrayInputStream ByteArrayOutputStream StringWriter] 18 | [java.security SecureRandom] 19 | [java.util Date UUID] 20 | [java.text SimpleDateFormat] 21 | [java.net URLDecoder URLEncoder] 22 | [org.bson.types ObjectId] 23 | [org.joda.time DateTime] 24 | [org.joda.time.format ISODateTimeFormat])) 25 | #?(:cljs 26 | (:import [goog.string StringBuffer])) 27 | 28 | (:refer-clojure :exclude [uuid])) 29 | 30 | 31 | (defn now [] 32 | #?(:clj 33 | (System/currentTimeMillis) 34 | :cljs 35 | (.now js/Date))) 36 | 37 | (defn pp-str [o] 38 | #?(:clj 39 | (let [w (StringWriter.)] 40 | (pprint/pprint o w) 41 | (.toString w)) 42 | :cljs 43 | (let [sb (StringBuffer.) 44 | sbw (StringBufferWriter. sb)] 45 | (pprint/pprint o sbw) 46 | (str sb)))) 47 | 48 | #?(:clj (json/add-encoder ObjectId json/encode-str)) 49 | 50 | (defn to-json [o & [opts-or-replacer space]] 51 | #?(:clj 52 | (json/generate-string o opts-or-replacer) 53 | :cljs 54 | (.stringify js/JSON (clj->js o) opts-or-replacer space))) 55 | 56 | 57 | #?(:clj 58 | (defn to-transit [o & [opts]] 59 | (let [bs (ByteArrayOutputStream.)] 60 | (transit/write 61 | (transit/writer bs :json opts) 62 | o) 63 | (.toString bs))) 64 | :cljs 65 | (defn to-transit [o & [opts]] 66 | (transit/write 67 | (transit/writer :json opts) 68 | o))) 69 | 70 | #?(:clj 71 | (defn from-transit [s & [opts]] 72 | (when s 73 | (transit/read 74 | (transit/reader 75 | (if (string? s) 76 | (ByteArrayInputStream. (.getBytes s "UTF-8")) 77 | s) 78 | :json 79 | opts)))) 80 | :cljs 81 | (defn from-transit [s & [opts]] 82 | (when s 83 | (transit/read 84 | (transit/reader :json opts) 85 | s)))) 86 | 87 | 88 | #?(:clj 89 | (defn uuid [] 90 | (-> (UUID/randomUUID) 91 | str 92 | (str/replace #"-" "")))) 93 | 94 | #?(:cljs 95 | (defn uuid [] 96 | (let [d (now) 97 | uuid-str "xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx"] 98 | (str/replace uuid-str 99 | #"[xy]" 100 | (fn [c] 101 | (let [r (bit-or (mod (+ d (* (.random js/Math) 16)) 16) 0) 102 | d (.floor js/Math (/ d 16.0))] 103 | (.toString 104 | (if (= "x" c) 105 | r 106 | (bit-or 107 | (bit-and 0x3 r) 108 | 0x8)) 109 | 16) 110 | )))))) 111 | 112 | (defn env-val [o] 113 | (csk/->SCREAMING_SNAKE_CASE o)) 114 | 115 | (defn env-case [o] 116 | (env-val o)) 117 | 118 | (defn lookup-map [key coll] 119 | (->> coll 120 | (map (fn [o] 121 | [(get o key) 122 | o])) 123 | (into {}))) 124 | 125 | (defn throw-str [& args] 126 | #?(:clj 127 | (throw (Exception. (str (apply str args)))) 128 | :cljs 129 | (throw (js/Error. (apply str args))))) 130 | -------------------------------------------------------------------------------- /src/cljs/clojuredocs/ajax.cljs: -------------------------------------------------------------------------------- 1 | (ns clojuredocs.ajax 2 | (:require [goog.net.XhrIo] 3 | [goog.net.EventType :as EventType] 4 | [clojure.string :as str] 5 | [cljs.reader :as reader])) 6 | 7 | (defn validate-ajax-args [{:keys [method]}] 8 | (let [valid-http-methods #{:get 9 | :post 10 | :put 11 | :patch 12 | :delete 13 | :options 14 | :head 15 | :trace 16 | :connect}] 17 | (when-not (get valid-http-methods method) 18 | (throw (str "nsfw.dom/ajax: " 19 | method 20 | " is not a valid ajax method (" 21 | (->> valid-http-methods 22 | (map pr-str) 23 | (interpose ", ") 24 | (apply str)) 25 | ")"))))) 26 | 27 | (defn safe-name [o] 28 | (when o 29 | (name o))) 30 | 31 | (defn safe-upper-case [s] 32 | (when s 33 | (str/upper-case s))) 34 | 35 | (def ajax-defaults 36 | {:path "/" 37 | :method "GET" 38 | :data {} 39 | :success (fn [] 40 | (throw "nsfw.dom/ajax: Unhandled :success callback from AJAX call.")) 41 | :error (fn [] 42 | (throw "nsfw.dom/ajax: Unhandled :error callback from AJAX call."))}) 43 | 44 | (defn parse-headers [s] 45 | (when s 46 | (->> (str/split s #"\n") 47 | (mapcat (fn [header] 48 | (->> (str/split header #":" 2) 49 | (map str/trim)))) 50 | (apply hash-map)))) 51 | 52 | (defn req->resp [req] 53 | {:headers (parse-headers (.getAllResponseHeaders req)) 54 | :status (.getStatus req) 55 | :body (.getResponseText req) 56 | :success (.isSuccess req)}) 57 | 58 | (defn json-parse [s] 59 | (.parse js/JSON s)) 60 | 61 | (defn json-stringify [s] 62 | (.stringify js/JSON s)) 63 | 64 | (defn format-body [{:keys [headers body] :as r}] 65 | (let [content-type (or (-> headers 66 | (get "content-type")) 67 | (-> headers 68 | (get "Content-Type")) 69 | "") 70 | body (condp #(re-find %1 %2) content-type 71 | #"application/json" (-> body 72 | json-parse 73 | (js->clj :keywordize-keys true)) 74 | #"application/edn" (reader/read-string body) 75 | body)] 76 | (assoc r :body body))) 77 | 78 | ;; To get around https://code.google.com/p/closure-library/issues/detail?id=642 79 | (defn xhrio-send [url callback method content headers & [timeout-interval with-creds]] 80 | (let [x (goog.net.XhrIo.)] 81 | (when callback 82 | (.listen x EventType/COMPLETE callback)) 83 | (.listenOnce x EventType/READY (fn [] (.-dispose x))) 84 | (when timeout-interval 85 | (.setTimeoutInterval x timeout-interval)) 86 | (when with-creds 87 | (.setWithCredentials x with-creds)) 88 | (.send x url method content headers))) 89 | 90 | (defn ajax [opts] 91 | (let [opts (merge ajax-defaults opts) 92 | opts (if-not (:headers opts) 93 | (assoc opts 94 | :headers (condp = (:data-type opts) 95 | :json {"Content-Type" "application/json;charset=utf-8"} 96 | :edn {"Content-Type" "application/edn;charset=utf-8"} 97 | {"Content-Type" "application/edn;charset=utf-8"})) 98 | opts) 99 | opts (cond 100 | (= :json (:data-type opts)) 101 | (assoc opts :data (-> (:data opts) 102 | clj->js 103 | json-stringify)) 104 | 105 | (= :edn (:data-type opts)) 106 | (assoc opts :data (pr-str (:data opts))) 107 | 108 | :else opts) 109 | {:keys [path method data headers success error data-type]} opts] 110 | (validate-ajax-args opts) 111 | (xhrio-send 112 | path 113 | (fn [e] 114 | (try 115 | (let [req (.-target e) 116 | resp (-> req 117 | req->resp 118 | format-body)] 119 | (if (:success resp) 120 | (success resp) 121 | (error resp))) 122 | (catch js/Object e 123 | (.error js/console (.-stack e)) 124 | (throw e)))) 125 | (-> method 126 | name 127 | safe-upper-case) 128 | data 129 | (clj->js headers)))) 130 | -------------------------------------------------------------------------------- /src/cljs/clojuredocs/anim.cljs: -------------------------------------------------------------------------------- 1 | (ns clojuredocs.anim 2 | (:require [dommy.core :as dom :refer-macros [sel1]])) 3 | 4 | (defn offset-parents 5 | "a lazy seq of offset parents of `node`" 6 | [elem] 7 | (->> elem 8 | (iterate #(.-offsetParent %)) 9 | (take-while identity))) 10 | 11 | (defn offset-top [el] 12 | (->> el 13 | offset-parents 14 | (map #(.-offsetTop %)) 15 | (reduce +))) 16 | 17 | (defn tween [el opts] 18 | (js/morpheus el (clj->js opts))) 19 | 20 | (defn scroll-to 21 | [elem & [{:keys [pad]}]] 22 | (let [body (sel1 :body) 23 | html (sel1 :html) 24 | start (max (.-scrollTop body) (.-scrollTop html)) 25 | end (- (offset-top elem) pad)] 26 | (.tween js/morpheus 27 | 250 28 | (fn [pos] 29 | (aset body "scrollTop" pos) 30 | (aset html "scrollTop" pos)) 31 | nil 32 | nil 33 | start 34 | end))) 35 | 36 | (defn scroll-to-top [] 37 | (scroll-to (sel1 :body))) 38 | 39 | ;; From dommy.attrs 40 | (defn bounding-client-rect 41 | "Returns a map of the bounding client rect of `elem` 42 | as a map with [:top :left :right :bottom :width :height]" 43 | [elem] 44 | (let [r (.getBoundingClientRect elem)] 45 | {:top (.-top r) 46 | :bottom (.-bottom r) 47 | :left (.-left r) 48 | :right (.-right r) 49 | :width (.-width r) 50 | :height (.-height r)})) 51 | 52 | (defn scroll-into-view 53 | [elem & [opts]] 54 | (let [{:keys [top bottom] :as res} (bounding-client-rect elem)] 55 | (when (or (< js/window.innerHeight 56 | (+ top (.-offsetHeight elem))) 57 | (< top 0)) 58 | (scroll-to elem opts)))) 59 | -------------------------------------------------------------------------------- /src/cljs/clojuredocs/examples.cljs: -------------------------------------------------------------------------------- 1 | (ns clojuredocs.examples 2 | (:require [dommy.core :as dommy :refer-macros [sel1]] 3 | [reagent.core :as rea] 4 | [nsfw.ops :as ops] 5 | [cljs.core.async :as async 6 | :refer [! chan close! sliding-buffer put! 7 | alts! timeout pipe mult tap]] 8 | [clojuredocs.util :as util] 9 | [nsfw.util :as nu] 10 | [clojuredocs.ajax :refer [ajax]] 11 | [clojuredocs.anim :as anim] 12 | [clojuredocs.syntax :as syntax] 13 | [clojure.string :as str] 14 | [cljs.reader :as reader] 15 | [clojure.data :refer [diff]]) 16 | (:require-macros [cljs.core.async.macros :refer [go go-loop]])) 17 | 18 | (defn $expando-ta 19 | "A textarea the expands downward with the content (no scroll)" 20 | [text opts] 21 | (let [rows (Math/max 22 | (+ (->> text 23 | (filter #(= "\n" %)) 24 | count) 25 | 3) 26 | 10)] 27 | [:textarea 28 | (merge 29 | {:class "form-control" 30 | :autoFocus "autofocus" 31 | :cols 80 32 | :rows rows 33 | :value text} 34 | opts)])) 35 | 36 | (defn eighty-columns [] 37 | (let [text " 80 columns " 38 | char "-" 39 | pre-text ";;" 40 | post-text ">" 41 | n (- 80 (count pre-text) (count text) (count post-text)) 42 | pre-n (Math/ceil (/ n 2)) 43 | post-n (Math/floor (/ n 2))] 44 | (str 45 | pre-text 46 | (apply str (repeat pre-n char)) 47 | text 48 | (apply str (repeat post-n char)) 49 | post-text))) 50 | 51 | (defn $tabbed-clojure-editor 52 | [!editor bus] 53 | (let [{:keys [text body error create-success? loading? active] 54 | :or {active :editor}} @!editor 55 | text (or text body)] 56 | [:div.tabbed-editor 57 | [:ul.nav.nav-tabs 58 | [:li 59 | {:class (when (= :editor active) "active")} 60 | [:a {:href "#" 61 | :on-click (fn [e] 62 | (.preventDefault e) 63 | (swap! !editor assoc :active :editor) 64 | nil)} 65 | [:i.fa.fa-code] " Editor"]] 66 | [:li 67 | {:class (when (= :preview active) "active")} 68 | [:a {:href "#" 69 | :on-click (fn [e] 70 | (.preventDefault e) 71 | (swap! !editor assoc :active :preview) 72 | nil)} 73 | [:i.fa.fa-eye] " Preview"]]] 74 | [:div {:class (when (= :preview active) "hidden")} 75 | [:div.example-editor 76 | {:class (when loading? "disabled")} 77 | ($expando-ta 78 | text 79 | {:on-change (fn [e] 80 | (let [v (.. e -target -value)] 81 | (.preventDefault e) 82 | (swap! !editor assoc :text v) 83 | false)) 84 | :value text 85 | :disabled (when loading? "disabled") 86 | :placeholder "Code Here"}) 87 | [:pre.columns-guide (eighty-columns)]]] 88 | [:div.live-preview {:class (when (= :editor active) "hidden")} 89 | (if-not (empty? text) 90 | (syntax/syntaxify text) 91 | [:div.null-state "Live Preview"])]])) 92 | 93 | (defn user-can-delete? [user {:keys [author]}] 94 | (= (select-keys user [:login :account-source]) 95 | (select-keys author [:login :account-source]))) 96 | 97 | (defn $example-meta [{:keys [can-delete? can-edit?]} 98 | !state 99 | bus] 100 | (let [{:keys [preview-text delete-state editing? _id author editors]} @!state 101 | authors (distinct 102 | (concat 103 | [author] 104 | editors)) 105 | num-to-show 7] 106 | [:div.example-meta 107 | [:div.contributors 108 | (->> authors 109 | (take num-to-show) 110 | (map util/$avatar)) 111 | (when (> (count authors) num-to-show) 112 | [:div.contributors 113 | "+ " 114 | (- (count authors) num-to-show) 115 | " more"])] 116 | [:div.links 117 | [:a {:href (str "#example-" _id)} 118 | "link"] 119 | #_" / " 120 | #_[:a {:href (str "/ex/" _id)} 121 | "history"] 122 | (when can-edit? 123 | [:span 124 | " / " 125 | (if editing? 126 | [:a {:href "#" 127 | :on-click (fn [e] 128 | (.preventDefault e) 129 | (swap! !state assoc :editing? false) 130 | nil)} 131 | "cancel edit"] 132 | [:a {:href "#" 133 | :on-click (fn [e] 134 | (.preventDefault e) 135 | (swap! !state assoc :editing? true :text nil) 136 | nil)} 137 | "edit"])]) 138 | (when (and can-delete? (not editing?)) 139 | [:span 140 | " / " 141 | (if (get #{:confirm :loading} delete-state) 142 | (if (= :loading delete-state) 143 | [:img.loading {:src "/img/loading.gif"}] 144 | [:span 145 | [:a {:href "#" 146 | :on-click (fn [e] 147 | (.preventDefault e) 148 | (swap! !state assoc :delete-state :none) 149 | nil)} 150 | "cancel"] 151 | " | " 152 | [:a {:href "#" 153 | :on-click (fn [e] 154 | (.preventDefault e) 155 | (ops/send bus ::delete _id) 156 | nil)} 157 | "confirm delete?"]]) 158 | [:span 159 | {:class (when (= :error delete-state) "error-deleting bg-danger")} 160 | [:a {:href "#" 161 | :on-click (fn [e] 162 | (.preventDefault e) 163 | (swap! !state assoc :delete-state :confirm) 164 | nil)} 165 | "delete"]])]) 166 | [:span.edit-example-widget]]])) 167 | 168 | (def $example-instructions 169 | [:p.example-instructions 170 | "See our " 171 | [:a {:href "/examples-styleguide"} "examples style guide"] 172 | " for content and formatting guidelines. " 173 | "Examples submitted to ClojureDocs are licensed under the " 174 | [:a {:href "https://creativecommons.org/publicdomain/zero/1.0/"} 175 | "Creative Commons CC0 license"] 176 | "."]) 177 | 178 | (defn $example-editor [{:keys [submit-button-text]} 179 | !state 180 | bus] 181 | (let [{:keys [editing? loading? error var body text _id] :as ex} @!state 182 | text (or text body)] 183 | [:div 184 | [$tabbed-clojure-editor !state bus] 185 | $example-instructions 186 | (when error 187 | [:div.form-group 188 | [:div.error-message.text-danger 189 | [:i.fa.fa-exclamation-circle] 190 | error]]) 191 | [:div.add-example-controls.form-group.clearfix 192 | [:button.btn.btn-default 193 | {:disabled (when loading? "disabled") 194 | :on-click (fn [e] 195 | (.preventDefault e) 196 | (swap! !state assoc :editing? false :text nil) 197 | nil)} 198 | "Cancel"] 199 | [:button.btn.btn-success.pull-right 200 | {:disabled (when loading? "disabled") 201 | :on-click (fn [e] 202 | (.preventDefault e) 203 | (ops/send bus ::save {:text text 204 | :_id _id}) 205 | nil)} 206 | (or submit-button-text "Submit")] 207 | [:img.loading.pull-right 208 | {:class (when-not loading? " hidden") 209 | :src "/img/loading.gif"}]]])) 210 | 211 | 212 | ;; Example API 213 | ;; + [$example opts bus] 214 | 215 | (defn $example [{:keys [can-delete? can-edit?] :as ex} 216 | !state 217 | bus] 218 | (let [{:keys [body editing? _id] :as ex} @!state] 219 | [:div.var-example 220 | {:class (if (= (str "example-" _id) (util/location-hash)) 221 | "highlighted")} 222 | [:a {:id (str "example-" _id)}] 223 | [:div 224 | [$example-meta 225 | {:editing? editing? 226 | :can-delete? can-delete? 227 | :can-edit? can-edit?} 228 | !state 229 | bus]] 230 | (if editing? 231 | [:div 232 | [:h5 "Edit Example"] 233 | [$example-editor 234 | {:submit-button-text "Update Example"} 235 | !state 236 | bus]] 237 | (when body 238 | [:div.example-body 239 | (syntax/syntaxify body)]))])) 240 | 241 | (defn $create-example [!state bus] 242 | (let [{:keys [editing? should-focus? var] :as app} @!state] 243 | [:div.add-example {:ref "wrapper"} 244 | [:div.toggle-controls 245 | [:a.toggle-link 246 | {:href "#" 247 | :on-click (fn [e] 248 | (.preventDefault e) 249 | (swap! !state update-in [:editing?] not) 250 | nil)} 251 | (if-not editing? "Add an Example" "Collapse")]] 252 | [:div.add-example-content {:class (when-not editing? " hidden")} 253 | [$example-editor 254 | {:submit-button-text "Add Example"} 255 | !state 256 | bus]]])) 257 | 258 | (defn $examples [!state bus] 259 | (let [{:keys [user examples var] :as app} @!state] 260 | [:div.var-examples 261 | [:h5 (util/pluralize (count examples) "Example" "Examples")] 262 | (if (empty? examples) 263 | [:div.null-state 264 | "No examples for " (:ns var) "/" (:name var) "."] 265 | (->> examples 266 | (map-indexed 267 | (fn [i example] 268 | (with-meta 269 | [$example 270 | {:can-delete? (user-can-delete? user example) 271 | :can-edit? (not (nil? user))} 272 | (rea/cursor !state [:examples i]) bus] 273 | {:key (:_id example)}))))) 274 | (if user 275 | [$create-example 276 | (rea/cursor !state [:add-example]) 277 | bus] 278 | [:div.login-required-message 279 | "Log in to add an example"])])) 280 | 281 | (defn $examples-widget [!state bus] 282 | [$examples !state bus]) 283 | -------------------------------------------------------------------------------- /src/cljs/clojuredocs/metrics.cljs: -------------------------------------------------------------------------------- 1 | (ns clojuredocs.metrics) 2 | 3 | (defn ga-event [category action label value] 4 | (js/ga "send" "event" category action label value)) 5 | 6 | (defn track-search [query] 7 | (ga-event "search" query "count" 1)) 8 | 9 | (defn track-search-choose [query choice] 10 | (ga-event "search-choose" query choice 1)) 11 | -------------------------------------------------------------------------------- /src/cljs/clojuredocs/see_alsos.cljs: -------------------------------------------------------------------------------- 1 | (ns clojuredocs.see-alsos 2 | (:require [dommy.core :as dommy :refer-macros [sel1]] 3 | [nsfw.ops :as ops] 4 | [nsfw.page :as page] 5 | [reagent.core :as rea] 6 | [cljs.core.async :as async 7 | :refer [! chan close! sliding-buffer put! alts! timeout pipe mult tap]] 8 | [clojuredocs.ajax :refer [ajax]] 9 | [clojuredocs.anim :as anim] 10 | [clojure.string :as str] 11 | [cljs.reader :as reader] 12 | [clojuredocs.util :as util]) 13 | (:require-macros [cljs.core.async.macros :refer [go]])) 14 | 15 | (defn $see-also [opts !sa bus] 16 | (let [{:keys [from-var to-var 17 | created-at 18 | doc 19 | author 20 | can-delete? 21 | delete-state 22 | _id] :as sa} @!sa] 23 | [:div.col-sm-6.see-also 24 | [:div.var-title 25 | (util/$var-link (:ns to-var) (:name to-var) 26 | [:span.ns (:ns to-var) "/"] 27 | [:span.name (:name to-var)])] 28 | [:p 29 | (->> doc 30 | (take 100) 31 | (apply str)) 32 | (when (> (count doc) 100) 33 | "...")] 34 | [:div.meta 35 | "Added by " 36 | [:a {:href (util/profile-url author)} (:login author)] 37 | (when can-delete? 38 | [:span.delete-controls 39 | " / " 40 | (condp = delete-state 41 | :loading [:img.loading {:src "/img/loading.gif"}] 42 | :confirm [:span.delete-confirmation 43 | [:a {:href "#" 44 | :on-click (fn [e] 45 | (.preventDefault e) 46 | (swap! !sa assoc :delete-state :none) 47 | nil)} 48 | "cancel"] 49 | " | " 50 | [:a {:href "#" 51 | :on-click (fn [e] 52 | (.preventDefault e) 53 | (ops/send bus ::delete sa) 54 | nil)} 55 | "confirm delete"]] 56 | 57 | [:span 58 | {:class (when (= :error delete-state) "error-deleting bg-danger")} 59 | [:a {:href "#" 60 | :on-click (fn [e] 61 | (.preventDefault e) 62 | (swap! !sa assoc :delete-state :confirm) 63 | nil)} 64 | "delete"]])])]])) 65 | 66 | (defn $add-sa [{:keys [throttle debounce]} !app bus] 67 | (let [handle-ac-text (page/throttle-debounce 68 | (fn [text] 69 | (ops/send bus ::ac-text (or text ""))) 70 | {:throttle throttle 71 | :debounce debounce})] 72 | (fn [] 73 | (let [{:keys [expanded? loading? completing? 74 | error ac-results ac-text] :as app} @!app] 75 | [:div.add-see-also 76 | [:div.toggle-controls 77 | [:a.toggle-link {:href "#" 78 | :on-click (fn [e] 79 | (.preventDefault e) 80 | (swap! !app update-in [:expanded?] not) 81 | nil)} 82 | (if-not expanded? 83 | "Add See Also" 84 | "Collapse")]] 85 | [:div.add-see-also-content {:class (when-not expanded? "hidden")} 86 | [:form {:autoComplete "off"} 87 | [:input.form-control 88 | {:class (when (or loading? completing?) "loading") 89 | :name "see-also-name" 90 | :ref "input" 91 | :placeholder "Var Name" 92 | :disabled (when loading? "disabled") 93 | :value ac-text 94 | :on-change (fn [e] 95 | (let [text (.. e -target -value)] 96 | (handle-ac-text text) 97 | (swap! !app assoc :ac-text text)))}] 98 | (when error 99 | [:div.error-message.text-danger 100 | [:i.fa.fa-exclamation-circle] 101 | error])] 102 | [:div.ac-results 103 | [:ul 104 | (for [{:keys [ns name disabled? disabled-text] :as res} ac-results] 105 | ^{:key (str ns name)} 106 | [:li.flex-apart 107 | [:div ns "/" name] 108 | (if disabled? 109 | [:button.btn.btn-default.btn-xs 110 | {:disabled "disabled"} 111 | (or disabled-text "Can't Add")] 112 | [:button.btn.btn-success.btn-xs 113 | {:disabled (when loading? "disabled") 114 | :on-click (fn [e] 115 | (.preventDefault e) 116 | (ops/send bus ::create res) 117 | nil)} 118 | "Add"])])]]]])))) 119 | 120 | (defn $see-alsos [!app bus] 121 | (let [{:keys [see-alsos var user add-see-also] :as app} @!app] 122 | [:div 123 | [:h5 "See Also"] 124 | (if (empty? see-alsos) 125 | [:div.null-state 126 | "No see-alsos for " [:code (:ns var) "/" (:name var)]] 127 | (->> see-alsos 128 | (map-indexed 129 | (fn [i sa] 130 | [$see-also {:key i} (rea/cursor !app [:see-alsos i]) bus])) 131 | (partition-all 2) 132 | (map-indexed (fn [i cs] [:div.row 133 | {:key i} 134 | cs])))) 135 | (if user 136 | [$add-sa 137 | {:debounce 100 138 | :throttle 200} 139 | (rea/cursor !app [:add-see-also]) 140 | bus] 141 | [:div.login-required-message 142 | "Log in to add a see-also"])])) 143 | -------------------------------------------------------------------------------- /src/cljs/clojuredocs/sticky.cljs: -------------------------------------------------------------------------------- 1 | (ns clojuredocs.sticky 2 | (:require [dommy.utils :as utils] 3 | [dommy.core :as dom :refer-macros [sel sel1]] 4 | [clojure.string :as str])) 5 | 6 | (defn clog [& args] 7 | (.log js/console (pr-str args))) 8 | 9 | (defn parse-int [s & [default]] 10 | (try 11 | (js/parseInt s) 12 | (catch js/Error e 13 | (if default 14 | default 15 | (throw e))))) 16 | 17 | (defn offset-top [$el] 18 | (loop [y 0 19 | $el $el] 20 | (let [parent (.-offsetParent $el)] 21 | (if-not parent 22 | y 23 | (recur 24 | (+ y (.-offsetTop $el)) 25 | parent))))) 26 | 27 | (defn computed-style [$el style-attr] 28 | (let [attr (name style-attr) 29 | v (.getPropertyValue 30 | (.getComputedStyle js/window $el nil) 31 | attr)] 32 | (when (and v (string? v)) 33 | (js/parseInt (str/replace v #"px" ""))))) 34 | 35 | (defn init [$el] 36 | (let [px-offset (-> $el 37 | (dom/attr :data-sticky-offset) 38 | (parse-int 100)) 39 | $parent (-> $el dom/ancestor-nodes second) 40 | starting-offset (atom nil) 41 | f (fn [_] 42 | (if (> (.-pageYOffset js/window) (max @starting-offset (- (offset-top $el) px-offset))) 43 | (let [{:keys [width]} (-> $el 44 | dom/ancestor-nodes 45 | second 46 | dom/bounding-client-rect) 47 | left-padding (computed-style $parent :padding-left) 48 | right-padding (computed-style $parent :padding-right) 49 | left-margin (computed-style $parent :margin-left) 50 | right-margin (computed-style $parent :margin-right) 51 | width (- width left-padding right-padding left-margin right-margin)] 52 | (swap! starting-offset (fn [v] 53 | (if v 54 | v 55 | (- (offset-top $el) px-offset)))) 56 | (dom/add-class! $el :sticky) 57 | (dom/set-style! $el 58 | :width (str width "px") 59 | :maxHeight (str js/window.innerHeight "px") 60 | :top (str px-offset "px"))) 61 | (do 62 | (reset! starting-offset nil) 63 | (dom/remove-class! $el :sticky))))] 64 | (dom/listen! js/window :scroll f) 65 | (dom/listen! js/window :resize f) 66 | (f nil) 67 | (fn [] 68 | (dom/unlisten! js/window :scroll f) 69 | (dom/unlisten! js/window :resize f)))) 70 | -------------------------------------------------------------------------------- /src/cljs/nsfw/ops.cljs: -------------------------------------------------------------------------------- 1 | (ns nsfw.ops 2 | "Provides message-based dispatching and context sharing. This helps 3 | with decoupling disparate parts of an app while sharing a common 4 | context (e.g. app state, windows, connections) between those parts." 5 | (:require [nsfw.util :as util] 6 | [cljs.core.async :as async 7 | :refer [! chan close! sliding-buffer put! take! 8 | alts! timeout pipe mult tap]] 9 | [cljs.core.async.impl.protocols]) 10 | (:require-macros [cljs.core.async.macros :refer [go go-loop]])) 11 | 12 | (defprotocol Dispatcher 13 | (send [this op] [this op data]) 14 | (bind! [this kw->f]) 15 | (unbind! [this kws]) 16 | (set-ctx! [this ctx]) 17 | (set-debug! [this id f]) 18 | (clear-debug! [this id])) 19 | 20 | (defn bus [context handlers & [{:keys [debug?]}]] 21 | (let [!handlers (atom handlers) 22 | !ctx (atom context) 23 | !debug-fns (atom {}) 24 | bus (reify 25 | Dispatcher 26 | (send [this op] 27 | (send this op nil)) 28 | (send [this op data] 29 | (when-let [msg {::op op ::data data}] 30 | (let [op (or (::op msg) (:op msg))] 31 | (when debug? 32 | (println "[nsfw.ops] Dispatching " op)) 33 | (if-let [f (get @!handlers op)] 34 | (do 35 | (f 36 | (merge {:bus this} 37 | @!ctx) 38 | (::data msg))) 39 | (println "[nsfw.ops] No handler for op" msg)) 40 | (when-not (empty? @!debug-fns) 41 | (doseq [f (vals @!debug-fns)] 42 | (f op)))))) 43 | (bind! [_ kw->f] 44 | (swap! !handlers merge kw->f)) 45 | (unbind! [_ kws] 46 | (swap! !handlers 47 | #(apply dissoc % kws))) 48 | (set-ctx! [_ ctx] 49 | (reset! !ctx ctx)) 50 | (set-debug! [_ id f] 51 | (swap! !debug-fns assoc id f)) 52 | (clear-debug! [_ id] 53 | (swap! !debug-fns dissoc id)))] 54 | bus)) 55 | 56 | (defn data [op] 57 | (::data op)) 58 | 59 | (defn op [{:keys [op op-id data on-ack on-error auth]}] 60 | {::op op 61 | ::data data 62 | ::op-id (or op-id (util/uuid)) 63 | ::on-ack on-ack 64 | ::auth auth 65 | ::on-error on-error}) 66 | 67 | (defn wrap-with-state 68 | ([with-state] 69 | (fn [h] 70 | (wrap-with-state h with-state))) 71 | ([h with-state & args] 72 | (fn [state params ctx] 73 | (let [res (h state params ctx)] 74 | (cond 75 | (map? res) (do (with-state res) res) 76 | (vector? res) (let [[state ch] res] 77 | (with-state state) 78 | [state 79 | (async/map 80 | (fn [state] 81 | (with-state state) 82 | state) 83 | [ch])])))))) 84 | 85 | 86 | (defn apply-state [!state state params] 87 | (let [state' (cond 88 | (fn? state) (state @!state) 89 | :else state)] 90 | (reset! !state state'))) 91 | 92 | (defn chan? [c] 93 | (satisfies? 94 | cljs.core.async.impl.protocols/Channel 95 | c)) 96 | 97 | (defn handle-step [res params !state ctx] 98 | (cond 99 | (chan? res) (go-loop [] 100 | (when-let [res (> handlers 138 | (map (fn [[k v]] 139 | [k (gen-lock-step v)])) 140 | (into {}))) 141 | 142 | (defn kit [!state ctx handlers & [opts]] 143 | (bus 144 | (assoc ctx :!state !state) 145 | (wrap-kit-handlers handlers) 146 | opts)) 147 | -------------------------------------------------------------------------------- /src/examples/clj/first.clj: -------------------------------------------------------------------------------- 1 | ;; Let's define some data using list / map 2 | ;; literals: 3 | 4 | (def scenes [{:subject "Frankie" 5 | :action "say" 6 | :object "relax"} 7 | 8 | {:subject "Lucy" 9 | :action "❤s" 10 | :object "Clojure"} 11 | 12 | {:subject "Rich" 13 | :action "tries" 14 | :object "a new conditioner"}]) 15 | 16 | ;; Define a function 17 | (defn people-in-scenes [scenes] 18 | (->> scenes 19 | (map :subject) 20 | (interpose ", ") 21 | (reduce str))) 22 | 23 | 24 | ;; Who's in our scenes? 25 | 26 | (println "People:" (people-in-scenes scenes)) 27 | 28 | ;;=> People: Frankie, Lucy, Rich 29 | -------------------------------------------------------------------------------- /src/md/api/overview.md: -------------------------------------------------------------------------------- 1 | The ClojureDocs API is used (mainly) to support dynamic content on the site. 2 | 3 | ## Requests 4 | 5 | ### Authentication 6 | 7 | Most POSTs, PUTs, and DELETEs require authentication, where most GETs 8 | don't. See specific endpoint documentation for requirements. 9 | 10 | ### Parameters 11 | 12 | Parameters for GET and DELETE requests are sent as either part of the 13 | request path (`GET /api/examples/:id`), or as query parameters (`GET 14 | /api/examples?sort=desc`). 15 | 16 | ### Request Bodies 17 | 18 | POST and PATCH requests bodies are [EDN](https://github.com/edn-format/edn) encoded. 19 | 20 | 21 | ## Responses 22 | 23 | ClojureDocs responds with EDN encoded bodies. 24 | 25 | 26 | ### Errors 27 | 28 | Here are some common errors you'll encounter using the ClojureDocs API: 29 | 30 | Failure to send an EDN encoded body: 31 | 32 | ``` 33 | ;; HTTP/1.1 415 Unsupported Media Type 34 | 35 | {:message "Request must be Content-Type application/edn"} 36 | ``` 37 | 38 | 39 | Sending invalid EDN 40 | 41 | ``` 42 | HTTP/1.1 400 Bad Request 43 | 44 | {:message "Error parsing request body as EDN"} 45 | ``` 46 | 47 | Trying to POST / DELETE while unauthenticated 48 | 49 | ``` 50 | HTTP/1.1 401 Unauthorized 51 | 52 | {:message "Unauthorized"} 53 | ``` 54 | 55 | Payload validation errors 56 | 57 | ``` 58 | HTTP/1.1 422 Unprocessable Entity 59 | 60 | {:errors [{:field "body" :message "Body can't be empty"} 61 | {:field "var" :message "That var doesn't exist"}]} 62 | ``` 63 | 64 | Occasionally, you might run into a server error 65 | 66 | ``` 67 | HTTP/1.1 500 Internal Server Error 68 | 69 | {:message "There was a problem processing your request"} 70 | ``` 71 | -------------------------------------------------------------------------------- /src/md/concepts/destructuring.md: -------------------------------------------------------------------------------- 1 | # Destructuring in Clojure 2 | 3 | In Clojure, destructuring is a shorthand for assigning names to parts 4 | of data structures based on their forms. Don't worry if that's 5 | confusing at first, it becomes very clear with a few examples. 6 | 7 | Suppose we have a function that prints a greeting based on a user's 8 | name, title, and location. 9 | 10 | Here we'll manually pull out the name, title, and location from the 11 | `user` parameter (a Map), and create bindings named `name`, `title`, 12 | and `location` via [`let`](/clojure.core/let). 13 | 14 | ``` 15 | (defn greet [user] 16 | (let [name (:name user) 17 | location (:location user)] 18 | (println "Hey there" name ", how's the weather in" location "?"))) 19 | 20 | (greet {:name "Josie" :location "San Francisco"}) 21 | ;; Hey there Josie, how's the weather in San Francisco? 22 | ;;=> nil 23 | 24 | (greet {:name "Ivan" :location "Moscow"}) 25 | ;; Hey there Ivan, how's the weather in Moscow? 26 | ;;=> nil 27 | 28 | ``` 29 | 30 | Destructuring lets us specify naming of the parameters directly from the 31 | structure of the passed map: 32 | 33 | ``` 34 | (defn greet2 [{:keys [name location]}] 35 | (println "Hey there" name ", how's the weather in" location "?")) 36 | 37 | (greet2 {:name "Josie" :location "San Francisco"}) 38 | ;; Hey there Josie, how's the weather in San Francisco? 39 | ;;=> nil 40 | 41 | 42 | ``` 43 | 44 | * For more detailed take on destructuring see Daniel Gregoire's [blog post](https://danielgregoire.dev/posts/2021-06-13-code-observation-clojure-destructuring/). 45 | * Bruno Bonacci's [guide on destructuring](https://blog.brunobonacci.com/2014/11/16/clojure-complete-guide-to-destructuring/) is worth a look. 46 | * Jay Field's [introductory blog post](http://blog.jayfields.com/2010/07/clojure-destructuring.html) provides a good starting point. 47 | * See John Del Rosario's [destructuring cheat sheet](https://gist.github.com/john2x/e1dca953548bfdfb9844) for a more comprehensive overview. 48 | * Also there is [the official Clojure guide on destructuring](https://clojure.org/guides/destructuring). 49 | -------------------------------------------------------------------------------- /src/md/concepts/functional-programming.md: -------------------------------------------------------------------------------- 1 | ##### Community Links 2 | * [A beginners guide to functional programming](http://mooc.cs.helsinki.fi/clojure) 3 | -------------------------------------------------------------------------------- /src/md/core-library.md: -------------------------------------------------------------------------------- 1 | # Clojure's Core Library 2 | 3 | Clojure's standard library, i.e. the `clojure.*` namespaces, provide a 4 | ton of general-purpose functionality for writing robust, maintainable 5 | applications. 6 | 7 | Getting a handle on all the functionality you'll want to use can be a 8 | little daunting at first, especially if you're coming from 9 | object-oriented languages like Java, Ruby, or Python, where behavior 10 | is grouped using classes. In Clojure, namespaces are used to group 11 | similar behavior and state, and we've outlined a few of the core 12 | namespaces to help you find what you're looking for. 13 | 14 | We have a 15 | [page listing all functions, macros, and other vars in Clojure's core library](/core-library/vars) 16 | as well. 17 | -------------------------------------------------------------------------------- /src/md/examples-styleguide.md: -------------------------------------------------------------------------------- 1 | # ClojureDocs Example Submission Guidelines 2 | 3 | The example sections on each var page are there to provide simple, isolated examples of var usage. In a nutshell, the examples you add to Clojuredocs should be easy to understand, and to help you with that we've outlined a few guidelines below. 4 | 5 | ## General Guidelines 6 | 7 | Examples should be short, unique, self-contained snippets of code that illustrate var usage in the simplest possible way. 8 | 9 | * Try to imagine clear conceptual boundaries of your example before submitting it. 10 | * Assume the reader has a background in software development, with little Clojure experience. 11 | * Short, sweet, and complete is the name of the game. 12 | 13 | If the target var is not part of the core ns (or otherwise not used by default) please include the use / require statement. 14 | 15 | Bad: 16 | 17 |
 18 | 
 19 | (sh "ls" "-aul")
 20 | 
 21 | ;; {:exit 0,
 22 | ;;  :out "total 64
 23 | ;; drwxr-xr-x  11 zkim  staff    374 Jul  5 13:21 ."
 24 | ;; ...
 25 | 
26 | 27 | Good: 28 | 29 |
 30 | (use '[clojure.java.shell :only [sh]])
 31 | 
 32 | (sh "ls" "-aul")
 33 | 
 34 | ;; {:exit 0,
 35 | ;;  :out "total 64
 36 | ;; drwxr-xr-x  11 zkim  staff    374 Jul  5 13:21 ."
 37 | ...
 38 | 
39 | 40 | Each example should be either broad, or deep, not both. For example, the following example for not= shows the broad range of inputs allowed. 41 | 42 |
 43 | (not= 1 1) ;;=> false
 44 | 
 45 | (not= 1 2) ;;=> true
 46 | 
 47 | (not= true true) ;;=> false
 48 | 
 49 | (not= true false) ;;=> true
 50 | 
 51 | (not= true true true true) ;;=> false
 52 | 
 53 | (not= true true false true) ;;=> true
 54 | 
55 | 56 | Where this example for future has depth. 57 | 58 |
 59 | ;; A future is calculated in another thread
 60 | (def f (future (Thread/sleep 10000) 100))
 61 | ;;=> #'user/f
 62 | 
 63 | ;; When you dereference it you will block until the result is available.
 64 | @f
 65 | ;;=> 100
 66 | 
 67 | ;; Dereferencing again will return the already calculated value immediately.
 68 | @f
 69 | ;;=> 100
 70 | 
71 | 72 | Also, please mention any gotchas you feel are associated with your example (specifically) or the var (in general). 73 | 74 | ## Comments 75 | 76 | Comments should be used to describe the following code block or blocks and/or point out bits of information that may not be obvious to new Clojure devs. 77 | 78 | Bad: 79 | 80 |
 81 | (with-precision 10 (/ 1M 3))
 82 | ;;=> 0.3333333333M
 83 | 
 84 | user=> (.floatValue 0.3333333333M)
 85 | ;;=> 0.33333334
 86 | 
87 | 88 | Good: 89 | 90 |
 91 | ;; The "M" suffix denotes a BigDecimal instance
 92 | ;; http://download.oracle.com/javase/6/docs/api/java/math/BigDecimal.html
 93 | 
 94 | (with-precision 10 (/ 1M 3))
 95 | ;;=> 0.3333333333M
 96 | 
 97 | (.floatValue 0.3333333333M)
 98 | ;;=> 0.33333334
 99 | 
100 | 101 | ;; should be for a general comment about a block of code, ; should be used to add a comment to the end of a line of code. 102 | 103 | Bad: 104 | 105 |
106 | ; This function will print 'hello world' to the console
107 | (defn hello-world []
108 |   (println "hello world"))  ;; Does the actual printing
109 | 
110 | 111 | Good: 112 | 113 |
114 | ;; This function will print 'hello world' to the console
115 | (defn hello-world []
116 |   (println "hello world"))  ; Does the actual printing
117 | 
118 | 119 | Differentiating code from context in your example will help 120 | 121 | Using comments to differentiate code from context and output will help new Clojure devs match up the executable parts of your example in their REPLs. 122 | 123 | Good: 124 | 125 |
126 | ;; You can use destructuring to have keyword arguments. This would be a pretty
127 | ;; verbose version of map (in an example a bit more verbose than the first above):
128 | 
129 | (defn keyworded-map [& {function :function sequence :sequence}]
130 |   (map function seq))
131 | 
132 | 
133 | ;; You call it like this:
134 | 
135 | (keyworded-map :sequence [1 2 3] :function #(+ % 2))
136 | ;;=> (3 4 5)
137 | 
138 | 
139 | ;; The declaration can be shortened with ":keys" if your local variables should be
140 | ;; named in the same way as your keys in the map:
141 | 
142 | (defn keyworded-map [& {:keys [function sequence]}]
143 |   (map function sequence))
144 | 
145 | 146 | Feel free to omit comments for very simple examples. 147 | 148 | ## Indentation / Formatting 149 | 150 | Please follow the conventions outlined in this [Scheme style guide](http://mumble.net/~campbell/scheme/style.txt), which follows Emacs' (among others) indentation and formatting conventions. We realize that code style can often be largely dictated by personal preference, however, uniformity across examples on ClojureDocs is important. 151 | 152 | Lines should have a maximum width of 80 characters to prevent wrapping when displayed on ClojureDocs pages, and please indent with spaces, not tabs. 153 | 154 | Consider leaving one line of whitespace after output from the repl to make your examples easier to visually scan. 155 | 156 | Bad: 157 | 158 |
159 | (println "foo")
160 | ;; foo
161 | ;;=> nil
162 | (println "bar")
163 | ;; bar
164 | ;;=> nil
165 | (println "baz")
166 | ;; baz
167 | ;;=> nil
168 | 
169 | 170 | 171 | Good: 172 | 173 |
174 | (println "foo")
175 | ;; foo
176 | ;;=> nil
177 | 
178 | (println "bar")
179 | ;; bar
180 | ;;=> nil
181 | 
182 | (println "baz")
183 | ;; baz
184 | ;;=> nil
185 | 
186 | 187 | 188 | ## Linking 189 | 190 | Urls in examples source are automatically converted to links. Feel free to use them where appropriate to link to external resources. 191 | -------------------------------------------------------------------------------- /src/md/namespaces/clojure.core.async.md: -------------------------------------------------------------------------------- 1 | A Clojure library providing facilities for async programming and communication. 2 | 3 | **Core.async is not provided as part of Clojure's standard distribution, and must be included as a [dependency](https://github.com/clojure/core.async#releases-and-dependency-information)**. 4 | 5 | 6 | ##### Community Links 7 | 8 | * [Blog post announcing core.async](https://clojure.org/news/2013/06/28/clojure-clore-async-channels) 9 | * [Source repo](https://github.com/clojure/core.async) 10 | * [Rich Hickey on core.async](http://www.infoq.com/presentations/clojure-core-async) (video) 11 | * [Communicating Sequential Processes](http://swannodette.github.io/2013/07/12/communicating-sequential-processes) by David Nolen 12 | * [Brave Clojure: Mastering Concurrent Processes with core.async](http://www.braveclojure.com/core-async/) 13 | * [Go Block Best Practices](https://github.com/clojure/core.async/wiki/Go-Block-Best-Practices) 14 | -------------------------------------------------------------------------------- /src/md/namespaces/clojure.core.logic.md: -------------------------------------------------------------------------------- 1 | **Core.logic is not provided as part of Clojure's standard distribution, and must be included as a [dependency](https://github.com/clojure/core.logic#releases-and-dependency-information)**. 2 | -------------------------------------------------------------------------------- /src/md/namespaces/clojure.core.md: -------------------------------------------------------------------------------- 1 | The largest of the core namespaces, `clojure.core` provides the bulk of the functionality 2 | you'll be using to build Clojure programs. 3 | 4 | There are too many core functions to feature here, but take a look at the 5 | [quickref](/quickref) to get a breakdown by conceptual arena. 6 | -------------------------------------------------------------------------------- /src/md/namespaces/clojure.core.server.md: -------------------------------------------------------------------------------- 1 | clojure.core.server contains functions to expose the Clojure environment through a socket connection and across network boundaries. If the property `-Dclojure.server.repl="{:port 8881 :accept clojure.core.server/repl}"` is present when the JDK starts, Clojure will start the related socket server (or more than one) on the specified port. The API in the namespace allows to do this programmatically. 2 | -------------------------------------------------------------------------------- /src/md/namespaces/clojure.edn.md: -------------------------------------------------------------------------------- 1 | Extensible Data Notation is a subset of the Clojure language used as a 2 | data transfer format, designed to be used in a similar way to JSON or 3 | XML. 4 | 5 | At some point in your adventures in Clojure land, you'll want to 6 | deserialize some clojure data structures from a string, and you'll 7 | want to use [clojure.edn/read](/clojure.edn/read) or 8 | [clojure.edn/read-string](/clojure.edn/read-string) for that. **Do not 9 | use** the `read-*` functions in `clojure.core` to deserialize untrusted Clojure code, as [they can be unsafe](http://www.learningclojure.com/2013/02/clojures-reader-is-unsafe.html). 10 | 11 | ##### Community Links 12 | 13 | * [Official EDN Spec](http://edn-format.org) 14 | * [Official documentation for clojure.edn](https://clojure.github.io/clojure/clojure.edn-api.html) 15 | * [EDN walkthrough by Mark Mandel](http://www.compoundtheory.com/clojure-edn-walkthrough) 16 | -------------------------------------------------------------------------------- /src/md/namespaces/clojure.pprint.md: -------------------------------------------------------------------------------- 1 | Pretty printing utility, really nice for looking at larger-ish data structures. See [pprint](/clojure.pprint/pprint). 2 | -------------------------------------------------------------------------------- /src/md/namespaces/clojure.set.md: -------------------------------------------------------------------------------- 1 | Functions for working on sets (`#{1 2 3 4}`) in all the ways you'd expect, e.g. calculating the [intersection](/clojure.set/intersection) of two sets, and testing if one set is a [subset](/clojure.set/subset_q) of another. 2 | -------------------------------------------------------------------------------- /src/md/namespaces/clojure.string.md: -------------------------------------------------------------------------------- 1 | Provides most standard string manipulation and processing function 2 | that you'd expect in any general-purpose programming language. 3 | 4 | In Clojure and ClojureScript strings are represented using the native 5 | platform implementation, and can be directly manipulated, 6 | e.g. `(.toLowerCase "FOO") ;=> "foo"`. The `clojure.string` namespace 7 | gives you the ability to manipulate strings in an idiomatic way: 8 | `(clojure.string/lower-case "FOO") ;=> "foo"`. 9 | 10 | Something to keep in mind is most (all?) of these functions take the 11 | string to act on as the first parameter, lending themselves well for 12 | use with the single-thrush operator (->) 13 | , as in this contrived example: 14 | 15 |
16 | (require '[clojure.string :as str])
17 | 
18 | (-> "  .LIRpa   ni  yAD    dloc thgIrb  a sAw Ti  "
19 |     str/reverse
20 |     str/trim
21 |     str/lower-case
22 |     (str/replace #"\s+" " ")
23 |     str/capitalize)
24 | 
25 | ;;=> "It was a bright cold day in april"
26 | 
27 | -------------------------------------------------------------------------------- /src/md/namespaces/clojure.test.md: -------------------------------------------------------------------------------- 1 | Provides basic facilities for unit testing Clojure code. 2 | 3 | * [Official Docs](http://clojure.github.io/clojure/clojure.test-api.html) 4 | * [Introduction to clojure.test by Jay Fields](http://blog.jayfields.com/2010/08/clojuretest-introduction.html) 5 | -------------------------------------------------------------------------------- /src/md/namespaces/clojure.zip.md: -------------------------------------------------------------------------------- 1 | Functional tree editing and manipulation. One of the core benefits of using Clojure is that you mostly work with immutable data structures. This, in turn, seems to make your programs easier to build and maintain. 2 | 3 | 4 | ##### Community Links 5 | 6 | * ["Editing" Trees in Clojure](http://www.exampler.com/blog/2010/09/01/editing-trees-in-clojure-with-clojurezip) 7 | * [Tim Baldrage's video walkthrough of Zippers](https://tbaldridge.pivotshare.com/media/zippers-episode-1/11348/feature) 8 | * ["The Art of Tree Shaping with Zippers" a talk by Arne Brasseur, at the Den of Clojure meetup in Denver](https://www.youtube.com/watch?v=5Nm56YvTKZY) 9 | * ["Clojure Zippers" blog post by Ivan Grishaev](https://grishaev.me/en/clojure-zippers/) 10 | -------------------------------------------------------------------------------- /system.properties: -------------------------------------------------------------------------------- 1 | java.runtime.version=1.7 -------------------------------------------------------------------------------- /test/clojuredocs/core_test.clj: -------------------------------------------------------------------------------- 1 | (ns clojuredocs.core-test 2 | (:require [clojure.test :refer :all] 3 | [clojuredocs.core :refer :all])) 4 | 5 | (deftest a-test 6 | (testing "FIXME, I fail." 7 | (is (= 0 1)))) 8 | -------------------------------------------------------------------------------- /tools/dev_export.clj: -------------------------------------------------------------------------------- 1 | (ns tools.dev-export 2 | (:require [somnium.congomongo :as mon] 3 | [clojure.java.shell :as sh])) 4 | -------------------------------------------------------------------------------- /tools/sanity_check.clj: -------------------------------------------------------------------------------- 1 | (ns tools.sanity-check 2 | (:require [clojure.java.jdbc :as j] 3 | [clojure.pprint :refer (pprint)] 4 | [clojure.string :as str] 5 | [somnium.congomongo :as mon] 6 | [clojuredocs.data :as data] 7 | [clojuredocs.util :as util] 8 | [clojure.set :as set])) 9 | 10 | (def mysql-db {:subprotocol "mysql" 11 | :subname "//127.0.0.1:3306/clojuredocs" 12 | :user "root" 13 | :password ""}) 14 | 15 | (defn all-users [] 16 | (j/query mysql-db ["SELECT * FROM users"])) 17 | 18 | (defn clojure-core-lib [] 19 | (first (j/query mysql-db ["SELECT * FROM libraries WHERE id=3"]))) 20 | 21 | (defn clojure-core-lib [] 22 | (first (j/query mysql-db ["SELECT * FROM libraries WHERE id=3"]))) 23 | 24 | (defn core-nss [] 25 | (j/query mysql-db ["SELECT * FROM namespaces WHERE library_id=3 OR library_id=15"])) 26 | 27 | (defn core-functions [] 28 | (let [ns-ids (map :id (core-nss))] 29 | (j/query mysql-db [(format "SELECT * FROM functions WHERE namespace_id IN (%s)" 30 | (->> ns-ids 31 | (interpose ",") 32 | (apply str)))]))) 33 | 34 | (defn core-examples [] 35 | (let [fids (map :id (core-functions))] 36 | (j/query mysql-db [(format "SELECT * FROM examples WHERE function_id IN (%s)" 37 | (->> fids 38 | (interpose ",") 39 | (apply str)))]))) 40 | 41 | (defn core-see-alsos [] 42 | (let [fs (core-functions) 43 | fids (map :id fs) 44 | comma-sep (->> fids 45 | (interpose ",") 46 | (apply str)) 47 | sas (j/query mysql-db 48 | [(format "SELECT * FROM see_alsos WHERE from_id IN (%s)" 49 | comma-sep 50 | comma-sep)])] 51 | sas)) 52 | 53 | (defn core-notes [] 54 | (let [fs (core-functions) 55 | fids (map :id fs) 56 | comma-sep (->> fids 57 | (interpose ",") 58 | (apply str)) 59 | notes (j/query mysql-db 60 | [(format "SELECT * FROM comments WHERE commentable_id IN (%s)" 61 | comma-sep)])] 62 | notes)) 63 | 64 | #_(->> (core-functions) 65 | (map :id) 66 | (mapcat #(j/query mysql-db 67 | [(format "SELECT * FROM comments WHERE commentable_id=%s" 68 | %)])) 69 | (remove empty?) 70 | count) 71 | 72 | (defn all-functions [] 73 | (j/query mysql-db ["SELECT * FROM functions"])) 74 | 75 | (defn all-notes [] 76 | (j/query mysql-db ["SELECT * FROM comments"])) 77 | 78 | (defn process-notes [lookup ns] 79 | (->> ns 80 | (map (fn [{:keys [id body commentable_id]}] 81 | (let [{:keys [name file] :as l} (lookup commentable_id)] 82 | (when l 83 | {:id id 84 | :body body 85 | :name name 86 | :file file})))) 87 | (sort-by :file) 88 | (remove nil?))) 89 | 90 | 91 | (defn render-note-overview [{:keys [file name body]}] 92 | (format "%s -- %s %s" 93 | file 94 | name 95 | (->> body 96 | (take 50) 97 | (apply str) 98 | pr-str))) 99 | 100 | #_(let [cfs (core-functions) 101 | afs (all-functions) 102 | lookup (reduce #(assoc %1 (:id %2) %2) {} afs) 103 | cnotes (core-notes) 104 | anotes (all-notes) 105 | cnotes-prime (process-notes lookup cnotes) 106 | anotes-prime (process-notes lookup anotes)] 107 | (doseq [n (set/difference (set anotes-prime) (set cnotes-prime))] 108 | (println (render-note-overview n))) 109 | (println "CORE NOTES:" (count cnotes-prime)) 110 | (println "ALL NOTES:" (count anotes-prime))) 111 | 112 | 113 | (do 114 | (println "-- Core Examples") 115 | (println "MySQL:" (count (core-examples))) 116 | (println "Mongo:" (count (mon/fetch :examples)))) 117 | 118 | 119 | (do 120 | (println "-- Users") 121 | (println "MySQL:" (count (all-users))) 122 | (println "Mongo:" (count (mon/fetch :users))) 123 | (println "Duplicate usernames:" (->> (all-users) 124 | (map :login) 125 | (reduce #(assoc %1 %2 (inc (%1 %2 0))) {}) 126 | (sort-by second) 127 | (filter #(> (second %) 1))))) 128 | 129 | (do 130 | (println "-- See Alsos") 131 | (println "MySQL:" (count (core-see-alsos))) 132 | (println "Mongo:" (count (mon/fetch :see-alsos)))) 133 | 134 | (do 135 | (println "-- Notes") 136 | (println "MySQL:" (count (core-notes))) 137 | (println "Mongo:" (count (mon/fetch :notes)))) 138 | -------------------------------------------------------------------------------- /tools/top_contribs.clj: -------------------------------------------------------------------------------- 1 | (ns tools.top-contribs 2 | (:require [somnium.congomongo :as mon] 3 | [clojure.pprint :refer (pprint)])) 4 | 5 | 6 | (def scores (atom {})) 7 | 8 | (time (doseq [{:keys [history]} (mon/fetch :examples)] 9 | (let [history (reverse history) 10 | first-user (-> history first :user)] 11 | (swap! scores update-in [first-user] #(+ 4 (or % 0))) 12 | (doseq [user (->> history rest (map :user))] 13 | (swap! scores update-in [user] #(inc (or % 0))))))) 14 | 15 | 16 | (->> @scores 17 | (sort-by second) 18 | reverse 19 | (take 24) 20 | (map #(assoc (first %) :score (second %))) 21 | pprint) 22 | --------------------------------------------------------------------------------