Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Comparing changes

Choose two branches to see what's changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
base fork: marvinthepa/4clojure-client
base: master
...
head fork: marvinthepa/4clojure-client
compare: interactive
Checking mergeability… Don't worry, you can still create the pull request.
  • 8 commits
  • 2 files changed
  • 0 commit comments
  • 2 contributors
Commits on Oct 25, 2012
@mknoszlig mknoszlig 4clojure repl 6708f84
Commits on Nov 04, 2012
@mknoszlig mknoszlig - improved html2text rendering
- force output width to 80 cols
- view now with parameter that selects implicitly, reducing the
  effective command set to view and propose
- improved quoting for propose
51ee3d2
@mknoszlig mknoszlig force-cols was hard-coded to 80 cols. make output width configurable
as dynamic var.
abc9c7d
Commits on Nov 08, 2012
@marvinthepa merge client and repl namespaces c963622
@marvinthepa Merge mknoszlig/interactive
Conflicts:
	src/fourclojure/repl.clj
0ec0537
@marvinthepa update README a99cb05
@marvinthepa update doc string 0265505
@marvinthepa small cleanups dff8235
Showing with 144 additions and 40 deletions.
  1. +18 −16 README.md
  2. +126 −24 src/fourclojure/client.clj
View
34 README.md
@@ -11,25 +11,27 @@ You have been warned.
```clojure
(use 'fourclojure.client)
-;; just submit to the homepage without login
-(check 1
- {}
- true)
-
-;; run tests offline before submitting (just copy the tests from the website, and wrap them in a quoted seq)
-(check 5
- {:tests '[(= __ (conj '(2 3 4) 1))
- (= __ (conj '(3 4) 2 1))]}
- [1 2 3 4])
-
-;; submit to the homepage with "login" (copy the value of the "ring-session" cookie from your browser when logged in
-(check 1
- {:cookie "d0217053-6447-47c2-ab8d-8e9f436e18d9"}
- true)
+;; select problem 1
+(view 1)
+
+;; check your solution
+(check false)
+
+;; review the problem description and tests
+(view)
+
+;; try again
+(check true)
+
+;; heureka!
+
+;; set cookie to record your progress
+(set-cookie! "24b081f1-4d52-45db-7be8-4429b18ee6f7")
+(check true)
```
## License
-Copyright © 2012 Martin Sander
+Copyright © 2012 Martin Sander, Maximilian Karasz
Distributed under the Eclipse Public License, the same as Clojure.
View
150 src/fourclojure/client.clj
@@ -1,23 +1,66 @@
(ns fourclojure.client
(:require [clojure.string :as string]
- [clj-http.client :as http]
+ [clj-http.client :as http]
[cheshire.core :as json]))
+(def ^:dynamic *problem-id* nil)
+(def ^:dynamic *current-problem* nil)
+(def ^:dynamic *cookie* nil)
+(def ^:dynamic *width* 80)
+
+;; TODO does not support "See the solutions that the users you follow have submitted"
+(defn clean [s]
+ (-> s
+ (string/replace #"\r" "")
+ (string/replace #"<.?p>" "")
+ (string/replace #"<.?code>" "")
+ (string/replace #"<a.*?href=\"(.*?)\".*?>(.*?)<\/a>"
+ "$2 [$1]")
+ (string/replace #"<.?span.*?>" "")
+ (string/replace #"<.?br\s?.?>" "\n")
+ (string/replace #"\t\s" "")
+ (string/replace #"<.?ul>" "")
+ (string/replace #"<li>" "- ")
+ (string/replace #"<\/li>" "")
+ (string/replace #"<.?i>" "")
+ (string/replace #"&mdash;" "-")
+ (string/replace #"<.?strong>" "")
+ (string/replace #"\n{2,}" "\n\n")))
+
+(defn keywordize
+ ([source]
+ (keywordize source {}))
+ ([source seed]
+ (into seed
+ (map (fn [[k v]] [(keyword k) v])
+ source))))
+
+(defn parse-problem [problem-data id]
+ (keywordize problem-data {:id id}))
+
+(defn parse-response [response]
+ (keywordize (json/parse-string response)))
+
+
;; TODO parse the test, and output the expected vs the real result (like common test libraries do)
(defn run-test [body t]
- (->
- (pr-str t)
- (string/replace "__" body)
- read-string
- eval))
+ (let [code-string (string/replace t "__" body)
+ code (read-string code-string)
+ result (try (eval code)
+ (catch Exception e
+ (println "exception in test '" code-string "':")
+ (println (.getMessage e))
+ false))]
+ (or result
+ (do
+ (println "you failed the test '" code-string "'")))))
(defn run-tests [body tests]
- (every? identity
- (map
- (partial run-test body)
- tests)))
+ (let [results (map
+ (partial run-test body)
+ tests)]
+ (every? identity results)))
-;; TODO check result
(defn submit [cookie problem-id body]
(http/post
(str "http://www.4clojure.com/rest/problem/" problem-id)
@@ -27,27 +70,86 @@
:accept :json
:form-params {:id problem-id :code body}}))
+(defn remote-check [code]
+ (parse-response
+ (:body (submit *cookie*
+ (:id *current-problem*)
+ code))))
+
+(defn force-cols [cols s]
+ (let [pattern (re-pattern (format ".{0,%d}[\\s\\n]" (dec cols)))]
+ (string/replace (apply str
+ (interpose "\n"
+ (re-seq pattern (str s "\n"))))
+ #"\n{2}" "\n")))
+
+(defn get-problem [id]
+ (json/parse-string
+ (:body
+ (http/get
+ (str "http://www.4clojure.com/api/problem/" id)
+ {:accept :json}))))
(defn run-and-submit
[problem-id
- {:keys [tests cookie]
- :or {tests []}}
+ {:keys [tests cookie] :or {tests []}}
forms]
{:pre [problem-id]}
(let [body (string/join
" " (map pr-str forms))]
- (and (run-tests body tests)
- (json/parse-string
- (:body (submit cookie problem-id body))))))
+ (if-not (run-tests body tests)
+ (str "/o\\ Fail /o\\\n") ;; TODO which test failed?
+ (let [response (remote-check body)]
+ (if (string/blank? (:message response))
+ (str "/o\\ Error /o\\\n"
+ (:error response))
+ (str "\\o/ Success \\o/\n"
+ (clean (:message response))))))))
+
+(defn run-and-submit-current [forms]
+ (run-and-submit *problem-id*
+ {:tests (:tests *current-problem*)
+ :cookie *cookie*}
+ forms))
+
+(defn select [id]
+ (def ^:dynamic *problem-id* id)
+ (def ^:dynamic *current-problem*
+ (parse-problem (get-problem id) id))
+ (view))
+
+;;
+;; Commands
+;;
+
+(defn set-cookie! [c]
+ (def ^:dynamic *cookie* c))
+
+(defn view
+ ([]
+ (if *current-problem*
+ (let [{:keys [id title description tests
+ user difficulty restricted]} *current-problem*]
+ (println
+ (force-cols *width*
+ (str
+ id ". *** " title " ***\n"
+ "author: " user "\n"
+ "level: " difficulty "\n\n"
+ (clean description) "\n\n"
+ "Tests:" "\n"
+ (apply str (interpose "\n"
+ (map clean tests)))
+ (when (seq restricted)
+ (str "\n\n" "Restrictions: " (pr-str restricted)))))))
+ (println "No problem selected - try (view <id>).")))
+ ([id]
+ (do (select id)
+ (view))))
(defmacro check
"check your solution of a 4clojure problem
parameters
- - problem-id: the id of the problem to solve
- - body: your solution, exactly like you would enter it in the code box at 4clojure.com, plain code (not quoted, not as string)
- - config: map of optional config options,
- {:tests quoted seq of the tests from 4clojure.com, to be run locally before submitting. If any of the tests fail, they will not be submitted
- :cookie value of the \"ring-session\"-cookie (extract it from your browser when logged in), needed for persisting your results}"
- [problem-id config & body]
- `(run-and-submit
- ~problem-id ~config '~body))
+ - body: your solution, exactly like you would enter it in the code box at 4clojure.com, plain code (not quoted, not as string)"
+ [& body]
+ `(run-and-submit-current '~body))

No commit comments for this range

Something went wrong with that request. Please try again.