Skip to content

Commit

Permalink
Refactor into separate namespaces.
Browse files Browse the repository at this point in the history
  • Loading branch information
mudge committed Mar 28, 2013
1 parent 444651a commit 293aa84
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 70 deletions.
2 changes: 1 addition & 1 deletion project.clj
Expand Up @@ -6,5 +6,5 @@
:dependencies [[org.clojure/clojure "1.5.1"] :dependencies [[org.clojure/clojure "1.5.1"]
[clj-http "0.7.0"] [clj-http "0.7.0"]
[cheshire "5.0.2"]] [cheshire "5.0.2"]]
:plugins [[lein-midje "3.0.0"]] :plugins [[lein-midje "3.0.0"] [lein-kibit "0.0.8"]]
:main foreclojure-downloader.core) :main foreclojure-downloader.core)
15 changes: 15 additions & 0 deletions src/foreclojure_downloader/api.clj
@@ -0,0 +1,15 @@
(ns foreclojure-downloader.api
(:require [clj-http.client :as client])
(:use [slingshot.slingshot :only [try+]]))

(defn- problem-url
"Return the 4clojure URL for a given problem number."
[n]
(str "http://www.4clojure.com/api/problem/" n))

(defn problem
"Returns a numbered problem from 4clojure as a map."
[n]
(try+ (assoc (:body (client/get (problem-url n) {:as :json})) :number n)
(catch [:status 404] _)))

78 changes: 9 additions & 69 deletions src/foreclojure_downloader/core.clj
@@ -1,73 +1,13 @@
(ns foreclojure-downloader.core (ns foreclojure-downloader.core
(:require [clojure.string :as string]) (:require [foreclojure-downloader.api :as api]
(:require [clojure.core.reducers :as reducers]) [foreclojure-downloader.writer :as writer]))
(:require [clj-http.client :as client])
(:require [cheshire.core :as cheshire]))


(defn download-problem [n] (defn- problems
"Download the given problem number from 4clojure." []
(let [url (str "http://www.4clojure.com/api/problem/" n) (pmap api/problem (range 1 200)))
response (client/get url {:throw-exceptions false})]
(when (= 200 (:status response))
(cheshire/parse-string (:body response) true))))

(defn convert-newlines [string]
(string/replace string "\r\n" "\n"))

(defn parse-test [test]
"Take a String test and parse out the equality operator and two sides.
Do this with a regular expression rather than read-string in an attempt
to preserve the original test code: e.g. reading #(* % 2) with
read-string produces (fn* [pn_1234#] (* pn_1234# 2))."
(if-let [matches (re-find #"^(?s)\((==?)\s+(#?\".+\"|'?\(.+\)|\[.+\]|#?\{.+\}|\S+)\s+(#?\".+\"|'?\(.+\)|\[.+\]|#?\{.+\}|\S+)\)$"
(convert-newlines test))]
matches
["" "=" (convert-newlines test) true]))

(defn arrow [test]
"Return the appropriate checker for the given test."
(let [equals (second (parse-test test))]
(str equals ">")))

(defn lhs [test]
"Return the left-hand side of the given test."
(nth (parse-test test) 2))

(defn rhs [test]
"Return the left-hand side of the given test."
(last (parse-test test)))

(defn checker [test]
"Return the full checker for the given test."
(str " " (lhs test) " " (arrow test) " " (rhs test)))

(defn checkers [problem]
"Return all checkers for the given problem."
(str (string/join "\n" (map checker (:tests problem))) ")"))

(defn fact [problem]
"Return a full fact block for a given problem including all checkers."
(str "(future-fact \"" (:title problem) "\"\n" (checkers problem) "\n"))

(defn problem-file-content [n problem]
"Return the full test file content for a given problem."
(str "(ns foreclojure-solutions.p" n "\n"
" (:use midje.sweet))\n"
"\n"
"(defn __ []\n"
" \"Implement me!\")\n"
"\n"
(fact problem)))

(defn problem-file-name [n]
(str "src/foreclojure_solutions/p" n ".clj"))

(defn write-problem [n]
(when-let [problem (download-problem n)]
(.mkdirs (java.io.File. "src/foreclojure_solutions"))
(spit (problem-file-name n) (problem-file-content n problem)))
n)


(defn -main [] (defn -main []
(into [] (reducers/map write-problem (range 1 200)))) (do
(println "Downloading problems...")
(pmap writer/write-problem (problems))
nil))
41 changes: 41 additions & 0 deletions src/foreclojure_downloader/translator.clj
@@ -0,0 +1,41 @@
(ns foreclojure-downloader.translator
(:require [clojure.string :as string]))

(defn- parse-simple-test
"Parse simple tests of the form (= x y) where possible."
[test]
(next (re-find #"^(?s)\((==?)\s+(#?\".+\"|'?\(.+\)|\[.+\]|#?\{.+\}|\S+)\s+(#?\".+\"|'?\(.+\)|\[.+\]|#?\{.+\}|\S+)\)$"
test)))

(defn- test->checker
"Translate a test into a Midje checker.
Tests of the form (= x y) will be translated to x => y but more complex tests
will be preserved and checked to be true, e.g. (and (= x y) (= z a)) => true"
[test]
(let [normalized-test (string/replace test "\r\n" "\n")]
(if-let [[equals left right] (parse-simple-test normalized-test)]
(str " " left " " equals "> " right)
(str normalized-test " => true"))))

(defn- checkers
"Return all checkers for the given problem."
[problem]
(str (string/join "\n" (map test->checker (:tests problem))) ")"))

(defn- fact
"Return a full fact block for a given problem including all checkers."
[problem]
(str "(future-fact \"" (:title problem) "\"\n"
(checkers problem) "\n"))

(defn problem-test
"Return the full test for a given problem as a String."
[problem]
(str "(ns foreclojure-solutions.p" (:number problem) "\n"
" (:use midje.sweet))\n"
"\n"
"(defn __ []\n"
" \"Implement me!\")\n"
"\n"
(fact problem)))
16 changes: 16 additions & 0 deletions src/foreclojure_downloader/writer.clj
@@ -0,0 +1,16 @@
(ns foreclojure-downloader.writer
(:require [foreclojure-downloader.translator :as translator]))

(defn- file-name
"Return the file name for the given problem."
[problem]
(str "src/foreclojure_solutions/p" (:number problem) ".clj"))

(defn write-problem
"Write the given problem to disk."
[problem]
(do
(.mkdirs (java.io.File. "src/foreclojure_solutions"))
(spit (file-name problem) (translator/problem-test problem)))
problem)

0 comments on commit 293aa84

Please sign in to comment.