Permalink
Browse files

Refactor into separate namespaces.

  • Loading branch information...
1 parent 444651a commit 293aa84f19978e41a00d29bb3b0c8c4a5ce037ef @mudge committed Mar 28, 2013
View
@@ -6,5 +6,5 @@
:dependencies [[org.clojure/clojure "1.5.1"]
[clj-http "0.7.0"]
[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)
@@ -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] _)))
+
@@ -1,73 +1,13 @@
(ns foreclojure-downloader.core
- (:require [clojure.string :as string])
- (:require [clojure.core.reducers :as reducers])
- (:require [clj-http.client :as client])
- (:require [cheshire.core :as cheshire]))
+ (:require [foreclojure-downloader.api :as api]
+ [foreclojure-downloader.writer :as writer]))
-(defn download-problem [n]
- "Download the given problem number from 4clojure."
- (let [url (str "http://www.4clojure.com/api/problem/" n)
- 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- problems
+ []
+ (pmap api/problem (range 1 200)))
(defn -main []
- (into [] (reducers/map write-problem (range 1 200))))
+ (do
+ (println "Downloading problems...")
+ (pmap writer/write-problem (problems))
+ nil))
@@ -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)))
@@ -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.