Skip to content
Browse files

Merge branch 'release/0.0.2'

  • Loading branch information...
2 parents ff779bd + dead4e0 commit eba3ce41c4dd6970b0b6ee76b333fdf7c7102dc1 @amalloy amalloy committed Apr 19, 2011
View
40 README.md
@@ -0,0 +1,40 @@
+# 4Clojure
+
+An interactive problem website for Clojure beginners
+[https://www.4clojure.com](https://www.4clojure.com).
+
+## State of the Art
+
+This site is in a very early stage of development, so there is not a
+lot of polish yet. Anyone interested in contributing should check out
+the [Issues](https://github.com/dbyrne/4clojure/issues) page for ideas
+on what to work on.
+
+## Setup instructions for running locally
+
+* Download and install [leiningen](https://github.com/technomancy/leiningen).
+* Download and install [mongodb](http://www.mongodb.org/).
+* The project uses
+[clojail](https://github.com/cognitivedissonance/clojail), which
+requires a security policy setup in your home directory (because
+Clojure's `eval` is unsafe if used improperly). Set up a file called
+`.java.policy` in your home directory. The contents should look
+vaguely like this:
+
+ grant { permission java.security.AllPermission; };
+
+ but see the readme of that project for more details.
+
+* cd to the 4clojure project directory and run `lein deps`.
+* Start up your mongodb, if you don't have autostart:
+
+ mongod
+* For the first time use, you will need to load the problem data. Run the script `load-data.sh`:
+
+ ./load-data.sh
+* Run `lein ring server` and the browser should open for you.
+
+ lein ring server
+
+
+
View
12 README.txt
@@ -1,12 +0,0 @@
-4Clojure is a website to help beginners learn to program in Clojure through interactive problems. Users are asked to fill-in-the-blanks for progressively harder problems.
-
-This site is a very early stage of development, so there is not a lot of polish yet. Any interested in contributing should check out the "Issues" page for ideas on what to work on.
-
-Installation should be pretty simple:
-
-1) Install Leiningen
-2) Install MongoDB
-3) lein repl
-4) (use 'foreclojure.data-set)
-5) (load-problems)
-6) (run)
View
3 load-data.sh
@@ -0,0 +1,3 @@
+project_classpath=`lein classpath`
+echo $project_classpath
+java -cp $project_classpath clojure.main ./src/foreclojure/data_set.clj
View
11 project.clj
@@ -1,4 +1,4 @@
-(defproject foreclojure "0.1.0-SNAPSHOT"
+(defproject foreclojure "0.0.2"
:description "4clojure - a website for lisp beginners"
:dependencies [[org.clojure/clojure "1.2.1"]
[org.clojure/clojure-contrib "1.2.0"]
@@ -8,6 +8,9 @@
[sandbar "0.4.0-SNAPSHOT"]
[congomongo "0.1.3-SNAPSHOT"]
[org.jasypt/jasypt "1.7"]
- [amalloy/utils "[0.3.7,)"]]
- :dev-dependencies [[lein-ring "0.4.0"]]
- :main foreclojure.core)
+ [amalloy/utils "[0.3.7,)"]
+ [clj-github "1.0.0-SNAPSHOT"]]
+ :dev-dependencies [[lein-ring "0.4.0"]
+ [swank-clojure "1.2.1"]]
+ :main foreclojure.core
+ :ring {:handler foreclojure.core/app})
View
24 src/foreclojure/data_set.clj
@@ -3,7 +3,7 @@
(defn load-problems []
(do
-
+ (mongo! :db :mydb)
(insert! :problems
{:_id 1
:title "Nothing but the Truth"
@@ -341,4 +341,24 @@
:tags ["easy" "seqs" "core-functions"]
:tests ["(= (__ 1 4) '(1 2 3))"
"(= (__ -2 2) '(-2 -1 0 1))"]
- :secret-tests ["(= (__ 5 8) '(5 6 7))"]})))
+ :secret-tests ["(= (__ 5 8) '(5 6 7))"]})
+
+ (insert! :problems
+ {:_id 35
+ :title "Local bindings"
+ :times-solved 0
+ :description "Clojure lets you give local names to values using the special let-form."
+ :tags ["elementary" "syntax"]
+ :tests ["(= __ (let [x 5] (+ 2 x)))"
+ "(= __ (let [x 3, y 10] (- y x)))"
+ "(= __ (let [x 21] (let [y 3] (/ x y))))"]})
+
+ (insert! :problems
+ {:_id 36
+ :title "Let it Be"
+ :times-solved 0
+ :description "Can you bind x, y, and z so that these are all true?"
+ :tags ["elementary" "math" "syntax"]
+ :tests ["(= 10 (let __ (+ x y)))"
+ "(= 4 (let __ (+ y z)))"
+ "(= 1 (let __ z))"]})))
View
42 src/foreclojure/problems.clj
@@ -1,8 +1,10 @@
(ns foreclojure.problems
(:use [foreclojure.utils]
+ [foreclojure.social :only [tweet-link gist!]]
[clojail core testers]
- [somnium.congomongo]
- [hiccup form-helpers])
+ [somnium.congomongo]
+ [hiccup form-helpers]
+ [amalloy.utils.debug :only [?]])
(:require [sandbar.stateful-session :as session]
[clojure.string :as s]))
@@ -23,14 +25,32 @@
:only [:_id :title :tags :times-solved]
:sort {:id 1})))
-(defn mark-completed [id]
- (if-let [user (session/session-get :user)]
- (do
- (when (not-any? #{id} (get-solved user))
- (update! :users {:user user} {:$push {:solved id}})
- (update! :problems {:_id id} {:$inc {:times-solved 1}}))
- (flash-msg "Congratulations, you've solved the problem!" "/problems"))
- (flash-msg "You've solved the problem! If you log in we can track your progress." "/problems")))
+(defn tweet-solution [id gist-url & [link-text]]
+ (let [status-msg (str "Check out how I solved http://4clojure.com/problem/"
+ id " - " gist-url " #clojure #4clojure")]
+ (tweet-link status-msg link-text)))
+
+(defn mark-completed [id code & [user]]
+ (let [user (or user (session/session-get :user))
+ gist-url (gist! user id code)
+ gist-link (if gist-url
+ (str "<div class='share'>"
+ "Share this "
+ "<a href='" gist-url "'>solution</a>"
+ " on " (tweet-solution id gist-url) "!"
+ "</div>")
+ (str "<div class='error'>Failed to create gist of "
+ "your solution</div>"))
+
+ message
+ (if user
+ (do
+ (when (not-any? #{id} (get-solved user))
+ (update! :users {:user user} {:$push {:solved id}})
+ (update! :problems {:_id id} {:$inc {:times-solved 1}}))
+ "Congratulations, you've solved the problem!")
+ "You've solved the problem! If you log in we can track your progress.")]
+ (flash-msg (str message " " gist-link) "/problems")))
(defn get-tester [restricted]
(into secure-tester (map symbol restricted)))
@@ -51,7 +71,7 @@
(try
(loop [[test & more] tests]
(if-not test
- (mark-completed id)
+ (mark-completed id code)
(let [testcase (s/replace test "__" (str code))]
(if (sb sb-tester (read-string testcase))
(recur more)
View
7 src/foreclojure/register.clj
@@ -28,8 +28,11 @@
"User already exists",
(< 3 (.length user) 13)
"Username must be 4-12 characters long",
- (< 6 (.length pwd) 13)
- "Password must be 7-12 characters long",
+ (= user
+ (first (re-seq #"[A-Za-z0-9_]+" user)))
+ "Username must be alphanumeric"
+ (< 6 (.length pwd) 13)
+ "Password must be 7-12 characters long",
(= pwd repeat-pwd)
"Passwords don't match",
(not (empty? email))
View
26 src/foreclojure/social.clj
@@ -0,0 +1,26 @@
+(ns foreclojure.social
+ (:use [foreclojure.utils])
+ (:require [clj-github.gists :as gist])
+ (:import (java.net URLEncoder)))
+
+(defn tweet-link [status & [anchor-text]]
+ (str "<a href=\"http://twitter.com/home?status="
+ (URLEncoder/encode status) "\">"
+ (or anchor-text "Twitter")
+ "</a>"))
+
+(defn gist!
+ "Create a new gist containing a user's solution to a problem and
+ return its url."
+ [user-name problem-num solution]
+ (let [user-name (or user-name "anonymous")
+ filename (str user-name "-4clojure-solution" problem-num ".clj")
+ text (str ";; " user-name
+ "'s solution to http://4clojure.com/problem/" problem-num
+ "\n\n"
+ solution)]
+ (try
+ (->> (gist/new-gist {} filename text)
+ :repo
+ (str "https://gist.github.com/"))
+ (catch Throwable _ nil))))
View
4 src/foreclojure/static.clj
@@ -18,7 +18,9 @@
[:div
[:h3 "Is this site written in Clojure?"]
- "Absolutely! This site was created using a variety of open source Clojure (and Java) libraries. In fact, the code for this site is itself open source. Once you've mastered the language, feel free to contribute something back to the community."]
+ "Absolutely! This site was created using a variety of open source Clojure (and Java) libraries. In fact, the "
+ [:a {:href "https://github.com/dbyrne/4clojure"} "code for this site"]
+ " is itself open source. Once you've mastered the language, feel free to contribute something back to the community."]
[:div
[:h3 "So wait, I can't buy cheap real estate here?"]
"At this time, 4clojure.com does not provide information regarding the sale of foreclosed homes, and has no plans of doing so in the future."]])
View
3 src/foreclojure/users.clj
@@ -5,7 +5,8 @@
(defn get-users []
(from-mongo
(fetch :users
- :only [:user :solved])))
+ :only [:user :solved]
+ :sort {:solved -1})))
(def-page users-page []
[:table {:class "my-table" :width "50%"}

0 comments on commit eba3ce4

Please sign in to comment.
Something went wrong with that request. Please try again.