Navigation Menu

Skip to content

Commit

Permalink
Merge branch 'release/1.1.1'
Browse files Browse the repository at this point in the history
  • Loading branch information
amalloy committed Jul 19, 2011
2 parents ed1301e + dac918c commit dcfb368
Show file tree
Hide file tree
Showing 7 changed files with 120 additions and 61 deletions.
1 change: 1 addition & 0 deletions README.md
Expand Up @@ -46,6 +46,7 @@ vaguely like this:
* [David Davis](https://github.com/daviddavis) (daviddavis) * [David Davis](https://github.com/daviddavis) (daviddavis)
* [Devin Walters](https://github.com/devn) (devn) * [Devin Walters](https://github.com/devn) (devn)
* [Michael Kohl](https://github.com/citizen428) (citizen428) * [Michael Kohl](https://github.com/citizen428) (citizen428)
* [Martin Sander](https://github.com/marvinthepa) (0x89)




Problem sources: Problem sources:
Expand Down
2 changes: 1 addition & 1 deletion project.clj
@@ -1,4 +1,4 @@
(defproject foreclojure "1.1.0" (defproject foreclojure "1.1.1"
:description "4clojure - a website for lisp beginners" :description "4clojure - a website for lisp beginners"
:dependencies [[clojure "1.2.1"] :dependencies [[clojure "1.2.1"]
[clojure-contrib "1.2.0"] [clojure-contrib "1.2.0"]
Expand Down
65 changes: 43 additions & 22 deletions resources/public/script/foreclojure.js
Expand Up @@ -60,6 +60,13 @@ function configureDataTables(){
} ); } );
} }


function setIconColor(element, color, timeOut) {
timeOut = (typeof timeOut == "undefined") ? 0 : timeOut
setTimeout (function() {
element.src = "/images/"+color+"light.png";
}, timeOut);
}

function configureCodeBox(){ function configureCodeBox(){
//For no javascript version we have the code-box text area //For no javascript version we have the code-box text area
//If we have javascript on then we remove it and replace it with //If we have javascript on then we remove it and replace it with
Expand All @@ -81,41 +88,55 @@ function configureCodeBox(){
images = $(".testcases").find("img"), images = $(".testcases").find("img"),
cont = true, cont = true,
high = false, high = false,
time = 800, animationTime = 800,
waitTimePerItem = 500,
waitTime = waitTimePerItem,


beforeSendCallback = function(data) { beforeSendCallback = function(data) {
$("#message-text").text("Executing unit tests..."); $("#message-text").text("Executing unit tests...");
images.each( function(index, element) {
setIconColor(element, "blue");
});
var anim = function() { var anim = function() {
if(cont) { if(cont) {
images.animate({ images.animate({
opacity: high ? 1.0 : 0.1, opacity: high ? 1.0 : 0.1,
}, time); }, animationTime);
high = !high; high = !high;
setTimeout(anim,time); setTimeout(anim,animationTime);
} }
}; };
anim(); anim();
}, },
successCallback = function(data) { successCallback = function(data) {
var failingTest = data.failingTest; var failingTest = data.failingTest
cont = false; getColorFor = function(index) {

return index === failingTest ? "red" : "green";
images.stop(true); },
images.css({ opacity: 1.0, }); testWasExecuted = function(index) {
images.each( function(index,element) { return index <= failingTest;
var color = "blue"; },
if (index < failingTest) { setColor = function(index,element) {
color = "green"; var color = getColorFor(index);
} else if (index === failingTest) { waitTime = waitTimePerItem * (index+1);
color = "red"; setIconColor(element, color, waitTime);
} },
element.src = "/images/"+color+"light.png"; setMessages = function() {
}); $("#message-text").html(data.message);

$("#golfgraph").html(data.golfChart);
$("#message-text").html(data.message); $("#golfscore").html(data.golfScore);
$("#golfgraph").html(data.golfChart); configureGolf();
$("#golfscore").html(data.golfScore); }
configureGolf(); stopAnimation = function() {
cont = false;
images.stop(true);
images.css({ opacity: 1.0, });
};

setTimeout(stopAnimation, waitTime);
images.filter( testWasExecuted ).
each(setColor);
setTimeout (setMessages, waitTime);
}; };


$.ajax({type: "POST", $.ajax({type: "POST",
Expand Down
1 change: 1 addition & 0 deletions src/foreclojure/core.clj
Expand Up @@ -34,6 +34,7 @@
session/wrap-stateful-session session/wrap-stateful-session
handler/site handler/site
wrap-uri-binding wrap-uri-binding
wrap-strip-trailing-slash
wrap-gzip)) wrap-gzip))


(defn run [] (defn run []
Expand Down
94 changes: 61 additions & 33 deletions src/foreclojure/problems.clj
Expand Up @@ -31,17 +31,33 @@
:where criteria :where criteria
:sort {:_id 1})))) :sort {:_id 1}))))


(defn next-unsolved-problem [solved-problems] (defn next-unsolved-problem [solved-problems just-solved-id]
(when-let [unsolved (->> (get-problem-list) (when-let [unsolved (seq
(remove (comp (set solved-problems) :_id)) (from-mongo
(seq))] (fetch :problems
(apply min-key :_id unsolved))) :only [:_id :title]
:where {:_id {:$nin solved-problems}}
:sort {:_id 1})))]
(let [[skipped not-yet-tried] (split-with #(< (:_id %) just-solved-id)
unsolved)]
(filter identity [(rand-nth (or (seq skipped)
[nil])) ; rand-nth barfs on empty seq
(first not-yet-tried)]))))

(letfn [(problem-link [{id :_id title :title}]
(str "<a href='/problem/" id "#prob-title'>" title "</a>"))]
(defn suggest-problems
([] "You've solved them all! Come back later for more!")
([problem]
(str "Now try " (problem-link problem) "!"))
([skipped not-tried]
(str "Now move on to " (problem-link not-tried)
", or go back and try " (problem-link skipped) " again!"))))


(defn next-problem-link [completed-problem-id] (defn next-problem-link [completed-problem-id]
(when-let [{:keys [solved]} (get-user (session/session-get :user))] (when-let [{:keys [solved]} (get-user (session/session-get :user))]
(if-let [{:keys [_id title]} (next-unsolved-problem solved)] (apply suggest-problems
(str "Now try <a href='/problem/" _id "'>" title "</a>!") (next-unsolved-problem solved completed-problem-id))))
"You've solved them all! Come back later for more!")))


(defn get-recent-problems [n] (defn get-recent-problems [n]
(map get-problem (map :_id (take-last n (get-problem-list))))) (map get-problem (map :_id (take-last n (get-problem-list)))))
Expand Down Expand Up @@ -122,7 +138,7 @@
:else (str "You've solved the problem! If you " :else (str "You've solved the problem! If you "
(login-link "log in" (str "/problem/" _id)) " we can track your progress."))] (login-link "log in" (str "/problem/" _id)) " we can track your progress."))]
(session/session-put! :code [_id code]) (session/session-put! :code [_id code])
[(str message " " gist-link) (str "/problem/" _id)] )) {:message (str message " " gist-link), :url (str "/problem/" _id)}))


(def restricted-list '[use require in-ns future agent send send-off pmap pcalls]) (def restricted-list '[use require in-ns future agent send send-off pmap pcalls])


Expand All @@ -138,30 +154,42 @@
(doall (take-while (complement #{end}) (doall (take-while (complement #{end})
(repeatedly #(read *in* false end)))))))) (repeatedly #(read *in* false end))))))))


(defn run-code [id raw-code] (defn run-code
(let [code (.trim raw-code) "Run the specified code-string against the test cases for the problem with the
{:keys [tests restricted] :as problem} (get-problem id) specified id.
sb-tester (get-tester restricted)]
(session/flash-put! :code code) Return a map, {:message, :url, :num-tests-passed}."
(try [id raw-code]
(let [user-forms (s/join " " (map pr-str (read-string-safely code)))] (try
(if (empty? user-forms) (let [code (.trim raw-code)
["Empty input is not allowed" *url*] {:keys [tests restricted] :as problem} (get-problem id)
(loop [[test & more] tests sb-tester (get-tester restricted)
i 0] user-forms (s/join " " (map pr-str (read-string-safely code)))
(session/flash-put! :failing-test i) results (if (empty? user-forms)
(if-not test ["Empty input is not allowed."]
(mark-completed problem code) (for [test tests]
(let [testcase (s/replace test "__" user-forms)] (try
(if (sb sb-tester (first (read-string-safely testcase))) (when-not (->> user-forms
(recur more (inc i)) (s/replace test "__")
["You failed the unit tests." *url*] read-string-safely
)))))) first
(catch Exception e (sb sb-tester))
[(.getMessage e) *url*])))) "You failed the unit tests")
(catch Throwable t (.getMessage t)))))
[passed [fail-msg]] (split-with nil? results)]
(assoc (if fail-msg
{:message fail-msg :url *url*}
(mark-completed problem code))
:num-tests-passed (count passed)))
(catch Throwable t {:message (.getMessage t), :url *url*
:num-tests-passed 0})))


(defn static-run-code [id raw-code] (defn static-run-code [id raw-code]
(apply flash-msg (run-code id raw-code))) (let [{:keys [message url num-tests-passed]}
(binding [*url* (str *url* "#prob-desc")]
(run-code id raw-code))]
(session/flash-put! :failing-test num-tests-passed)
(flash-msg message url)))


(defn render-test-cases [tests] (defn render-test-cases [tests]
[:table {:class "testcases"} [:table {:class "testcases"}
Expand Down Expand Up @@ -197,8 +225,8 @@
[:span#graph-link "View Chart"]]]))) [:span#graph-link "View Chart"]]])))


(defn rest-run-code [id raw-code] (defn rest-run-code [id raw-code]
(let [[message url] (run-code id raw-code)] (let [{:keys [message url num-tests-passed]} (run-code id raw-code)]
(json-str {:failingTest (session/flash-get :failing-test) (json-str {:failingTest num-tests-passed
:message message :message message
:golfScore (html (render-golf-score)) :golfScore (html (render-golf-score))
:golfChart (html (render-golf-chart))}))) :golfChart (html (render-golf-chart))})))
Expand Down
7 changes: 6 additions & 1 deletion src/foreclojure/ring.clj
@@ -1,7 +1,8 @@
(ns foreclojure.ring (ns foreclojure.ring
(:use [compojure.core :only [GET]] (:use [compojure.core :only [GET]]
[ring.util.response :only [response]]) [ring.util.response :only [response]])
(:require [clojure.java.io :as io]) (:require [clojure.java.io :as io]
[clojure.string :as s])
(:import (java.net URL))) (:import (java.net URL)))


;; copied from compojure.route, modified to use File instead of Stream ;; copied from compojure.route, modified to use File instead of Stream
Expand All @@ -22,3 +23,7 @@
(= "file" (.getProtocol ^URL body))) (= "file" (.getProtocol ^URL body)))
(update-in resp [:body] io/as-file) (update-in resp [:body] io/as-file)
resp)))) resp))))

(defn wrap-strip-trailing-slash [handler]
(fn [request]
(handler (update-in request [:uri] s/replace #"(?<=.)/$" ""))))
11 changes: 7 additions & 4 deletions src/foreclojure/utils.clj
Expand Up @@ -117,10 +117,10 @@


(defn html-doc [& body] (defn html-doc [& body]
(let [user (session/session-get :user)] (let [user (session/session-get :user)]
(html (html
(doctype :html5) (doctype :html5)
[:html [:html
[:head [:head
[:title "4Clojure"] [:title "4Clojure"]
[:link {:rel "alternate" :type "application/atom+xml" :title "Atom" :href "http://4clojure.com/problems/rss"}] [:link {:rel "alternate" :type "application/atom+xml" :title "Atom" :href "http://4clojure.com/problems/rss"}]
[:link {:rel "shortcut icon" :href "/favicon.ico"}] [:link {:rel "shortcut icon" :href "/favicon.ico"}]
Expand Down Expand Up @@ -171,7 +171,10 @@
[:div#content_body body] [:div#content_body body]
[:div#footer [:div#footer
"The content on 4clojure.com is available under the EPL v 1.0 license." "The content on 4clojure.com is available under the EPL v 1.0 license."
[:a#contact {:href "mailto:team@4clojure.com"} "Contact us!"]] (let [email "team@4clojure.com"]
[:span
[:a#contact {:href (str "mailto:" email)} "Contact us"]
(str `(~email))])]
(javascript-tag (javascript-tag
" var _gaq = _gaq || []; " var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-22844856-1']); _gaq.push(['_setAccount', 'UA-22844856-1']);
Expand Down

0 comments on commit dcfb368

Please sign in to comment.