Permalink
Browse files

Barebones structure, with dynamic class reloading.

  • Loading branch information...
0 parents commit 218363aabc5f6482266fd44044c2643ad345e6e0 @llasram committed Apr 3, 2012
Showing with 102 additions and 0 deletions.
  1. +10 −0 .gitignore
  2. +15 −0 README.md
  3. +6 −0 project.clj
  4. +64 −0 src/defclass/core.clj
  5. +7 −0 test/defclass/core_test.clj
10 .gitignore
@@ -0,0 +1,10 @@
+/target
+/lib
+/classes
+/checkouts
+pom.xml
+*.jar
+*.class
+.lein-deps-sum
+.lein-failures
+.lein-plugins
15 README.md
@@ -0,0 +1,15 @@
+# defclass
+
+I'm an app. Or maybe I'm a library? I haven't decided yet.
+
+The choice is up to you!
+
+## Usage
+
+FIXME
+
+## License
+
+Copyright © 2012 FIXME
+
+Distributed under the Eclipse Public License, the same as Clojure.
6 project.clj
@@ -0,0 +1,6 @@
+(defproject defclass "0.1.0-SNAPSHOT"
+ :description "FIXME: write description"
+ :url "http://example.com/FIXME"
+ :license {:name "Eclipse Public License"
+ :url "http://www.eclipse.org/legal/epl-v10.html"}
+ :dependencies [[org.clojure/clojure "1.3.0"]])
64 src/defclass/core.clj
@@ -0,0 +1,64 @@
+(ns defclass.core
+ (:require [clojure.java.io :as io]))
+
+(defn- class-name
+ [sym] (.getName ^Class (resolve sym)))
+
+(defn- method-sigs
+ [spec]
+ (let [mname (first spec)
+ spec (drop-while (comp not sequential?) spec)
+ maybe-params (first spec)
+ meta-tag #(get (meta %) :tag 'void)
+ sig-for #(vector mname (->> % rest (map meta-tag) vec) (meta-tag %))]
+ (if (vector? maybe-params)
+ [(sig-for maybe-params)]
+ (map (comp sig-for first) spec))))
+
+(defn- defn-bodies
+ [prefix spec]
+ (let [mname (first spec)
+ fname (with-meta (symbol (str prefix mname)) (meta mname))]
+ (list* `defn fname (rest spec))))
+
+(def ^:private generate-class
+ (deref (ns-resolve (the-ns 'clojure.core) 'generate-class)))
+
+(defn- reload-class
+ [pqname bytecode]
+ (doto (clojure.lang.RT/baseLoader)
+ (.defineClass pqname bytecode '())))
+
+(defn- generate-reload-class
+ [pqname options-map]
+ (let [[cname bytecode] (generate-class options-map)]
+ (if *compile-files*
+ (clojure.lang.Compiler/writeClassFile cname bytecode)
+ (reload-class pqname bytecode))))
+
+(defmacro defclass
+ [cname [& fields] & opts+specs]
+ (let [[opts specs] (->> (partition-all 2 opts+specs)
+ (split-with (comp keyword? first))
+ (map (partial apply concat)))
+ opts (apply hash-map opts)
+ specs (first (reduce (fn [[specs iface] form]
+ (if (sequential? form)
+ [(update-in specs [iface] conj form) iface]
+ [(assoc specs form []) form]))
+ [{} cname] specs))
+ pqname (str *ns* "." cname)
+ prefix (str "__" cname "-")
+ not-ifaces (hash-set 'Object (get opts :extends) cname)
+ impl-names (apply hash-set (keys specs))
+ implements (->> (apply disj impl-names not-ifaces) (map class-name) vec)
+ methods (->> (get specs cname []) (mapcat method-sigs) vec)
+ opts (assoc opts
+ :name pqname
+ :implements implements
+ :methods methods
+ :prefix prefix)]
+ (generate-reload-class pqname opts)
+ `(let [result# (import ~(symbol pqname))]
+ ~@(map (partial defn-bodies prefix) (apply concat (vals specs)))
+ result#)))
7 test/defclass/core_test.clj
@@ -0,0 +1,7 @@
+(ns defclass.core-test
+ (:use clojure.test
+ defclass.core))
+
+(deftest a-test
+ (testing "FIXME, I fail."
+ (is (= 0 1))))

0 comments on commit 218363a

Please sign in to comment.