Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

launched :D

Signed-off-by: Chris Granger <ibdknox@gmail.com>
  • Loading branch information...
commit 81747fea4300d6fb6b07b80be10bcc64de74bd15 1 parent 8f5f124
@ibdknox authored
View
3  _layouts/default.html
@@ -4,7 +4,7 @@
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>{{ page.title }}</title>
<meta name="author" content="Chris Granger" />
- <link href="http://feeds.feedburner.com/tom-preston-werner" rel="alternate" title="Tom Preston-Werner" type="application/atom+xml" />
+ <link href="http://feeds.feedburner.com/ChrisGranger" rel="alternate" title="Chris Granger" type="application/atom+xml" />
<!-- syntax highlighting CSS -->
<link rel="stylesheet" href="/css/syntax.css" type="text/css" />
@@ -48,6 +48,7 @@
</div>
</div>
+ <p id="copyright">© 2012 Chris Granger</p>
</div>
<!-- Google Analytics -->
View
79 _posts/2012-02-19-more-woot.md
@@ -1,84 +1,9 @@
---
layout: post
-title: "more woot"
+title: "A new leaf"
tags: []
---
-Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam pretium est sed risus pharetra sodales. Nulla facilisi. Donec faucibus nisl sed felis euismod bibendum. Etiam elementum auctor nisi, ut interdum ligula iaculis in. In ac dui sed justo tincidunt sollicitudin et nec felis. Aliquam eu elementum tellus. Morbi nec enim justo. Maecenas urna velit, tempor vitae consectetur nec, auctor in sem. Phasellus viverra gravida malesuada. Etiam volutpat lectus eget urna pretium sit amet consequat erat porta.
+I'm currently remodeling my site a bit, so mind the dust while I clean up today. :)
-{% highlight clojure %}
-(woot "cool")
-{% endhighlight %}
-Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam pretium est sed risus pharetra sodales. Nulla facilisi. Donec faucibus nisl sed felis euismod bibendum. Etiam elementum auctor nisi, ut interdum ligula iaculis in. In ac dui sed justo tincidunt sollicitudin et nec felis. Aliquam eu elementum tellus. Morbi nec enim justo. Maecenas urna velit, tempor vitae consectetur nec, auctor in sem. Phasellus viverra gravida malesuada. Etiam volutpat lectus eget urna pretium sit amet consequat erat porta.
-
-{% highlight clojure %}
-(ns ipadtest.client.main
- (:require [waltz.state :as state]
- [crate.core :as crate]
- [fetch.remotes :as remotes]
- [fetch.lazy-store :as store])
- (:use [jayq.core :only [append $ show hide delegate data bind
- inner add-class remove-class]]
- [jayq.util :only [log]]
- [waltz.state :only [transition]])
- (:use-macros [waltz.macros :only [in out defstate deftrans]]
- [fetch.macros :only [letrem]]
- [crate.macros :only [defpartial]]))
-
-(def $controls ($ :#controls))
-(def $freqs ($ :#freqs))
-(def $notes ($ :#notes))
-(def $piano ($ :#piano))
-
-(def controls {:start {:action "dub"
- :label "start"}
- :stop {:action "stop-dub"
- :label "stop"}})
-
-(def notes (into {} (for [n (range 40 80)
- :let [nname (keyword (str "note" n))]]
- [nname {:action "set-note"
- :label (str n)
- :params [n]}])))
-
-(def freqs (into {} (for [n (range 0 8)
- :let [nname (keyword (str "freq" n))]]
- [nname {:action "set-freq"
- :label (str "f" n)
- :params [n]}])))
-
-(def piano (into {} (for [n (range 40 62)
- :let [nname (keyword (str "piano" n))]]
- [nname {:action "play-note"
- :label (str n)
- :params [n]}])))
-
-
-(def all-actions (merge controls notes freqs piano))
-
-(defpartial button [[k {:keys [label]}]]
- [:a {:class "button" :href "#" :data-id k} label])
-
-(defn populate [$container actions]
- (doseq [kv actions]
- (append $container (button kv))))
-
-(populate $notes notes)
-(populate $controls controls)
-(populate $freqs freqs)
-(populate $piano piano)
-
-(defn handle-click [e]
- (.preventDefault e)
- (this-as me
- (let [$me ($ me)
- id (data $me :id)
- {:keys [action params]} (all-actions id)]
- (remotes/remote-callback action (or params [])))))
-
-;;(delegate ($ "body") button :click handle-click)
-(delegate ($ "body") button :touchstart handle-click)
-
-(bind ($ js/document) :touchmove (fn [e] (.preventDefault e)))
-{% endhighlight %}
View
319 _posts/2012-02-20-overtone-and-clojurescript.markdown
@@ -0,0 +1,319 @@
+---
+layout: post
+title: "Overtone and ClojureScript"
+tags: []
+---
+
+Lots of folks have been interested in ClojureScript lately, but have had a hard time figuring out what a CLJS app actually looks like. So today I [recorded] myself building an [Overtone] controller (that I use on an iPad) using [noir], [fetch], [jayq], and [crate]. In the end, it looks like this:
+
+![overtone controller](/images/overtoneController.png)
+
+Since I don't narrate in the video, I figured I'd give a breakdown of some of the main ideas below. If you want all the gory details though, you can watch the [screencast][recorded] or look at the [code]. Now to the fun part.
+
+##Getting started
+
+The first step is to generate a new noir project using lein-noir (if you're new to noir, check out [noir's website][noir])
+
+{% highlight bash %}
+lein noir new overtoneinterface
+{% endhighlight %}
+
+Now to set up our project we just need to include our dependencies, which with the wonderful [lein-cljsbuild] means you do what you always do - add them to your project.clj. ClojureScript dependencies don't really work any differently than Clojure ones do:
+
+{% highlight clojure %}
+(defproject overtoneinterface "0.1.0-SNAPSHOT"
+ :description "FIXME: write this!"
+ :dependencies [[org.clojure/clojure "1.3.0"]
+ [overtone "0.6.0"]
+ [jayq "0.1.0-SNAPSHOT"]
+ [crate "0.1.0-SNAPSHOT"]
+ [fetch "0.1.0-SNAPSHOT"]
+ [noir "1.3.0-alpha10"]]
+ :cljsbuild {:source-path "src"
+ :compiler
+ {:output-dir "resources/public/cljs/"
+ :output-to "resources/public/cljs/bootstrap.js"
+ :optimizations :simple
+ :pretty-print true}}
+ :main overtoneinterface.server)
+{% endhighlight %}
+
+You see here that we also added a cljsbuild key that defines some properties for how we want out ClojureScript to be generated. They simply tell lein-cljsbuild where to find our source and where to place the output. To then get that into our app, we just need to include the generated javascript file and jquery in our views.common/layout function.
+
+{% highlight clojure %}
+(defpartial layout [& content]
+ (html5
+ [:head
+ [:title "overtoneinterface"]
+ (include-css "/css/reset.css")
+ (include-css "/css/default.css")
+ (include-js "https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js")]
+ [:body
+ [:div#wrapper
+ content]
+ (include-js "/cljs/bootstrap.js")]))
+{% endhighlight %}
+
+Then just modify welcome.clj to get rid of the getting-started content, change /welcome to /, and add a div#piano so we have a container to put our buttons in.
+
+{% highlight clojure %}
+(ns overtoneinterface.views.welcome
+ (:require [overtoneinterface.views.common :as common])
+ (:use [noir.core :only [defpage]]
+ [hiccup.core :only [html]]))
+
+(defpage "/" []
+ (common/layout
+ [:div#piano]))
+{% endhighlight %}
+
+I always end up creating a src/myapp/client/ directory where I keep my CLJS. So if you put the following in a main.cljs in there and fire up lein-cljsbuild, you'll see a nice little alert box:
+
+{% highlight clojure %}
+(ns overtoneinterface.client.main)
+
+(js/alert "hey!")
+{% endhighlight %}
+
+Now we're off to the races.
+
+##Using crate and jayq
+
+[crate] is a ClojureScript implementation of the HTML generation library [Hiccup], which represents html as Clojure vectors and maps. We use a special macro called `(defpartial ..)` to create a function that will create dom objects for us.
+
+{% highlight clojure %}
+(ns overtoneinterface.client.main
+ (:require [crate.core :as crate])
+ (:use-macros [crate.macros :only [defpartial]]))
+
+(defpartial button [{:keys [label action param]}]
+ [:a.button {:href "#" :data-action action :data-param param} label])
+{% endhighlight %}
+
+One thing to note here is that there's a special directive for requiring macros in CLJS. Also, any namespace used by that macro must be required as well, or otherwise that code won't end up in the generated file. Now to do something with it, we'll use jayq.
+
+[jayq] is a simple ClojureScript jQuery wrapper that I wrote, which makes it easy to do all your standard dom manipulations like you're used to.
+
+{% highlight clojure %}
+(ns overtoneinterface.client.main
+ (:require [crate.core :as crate])
+ (:use [jayq.core :only [$ append delegate data]])
+ (:use-macros [crate.macros :only [defpartial]]))
+
+(def $piano ($ :#piano))
+
+(defpartial button [{:keys [label action param]}]
+ [:a.button {:href "#" :data-action action :data-param param} label])
+
+(append $piano (button {:label "play note"
+ :action "play-note"
+ :param ""))))
+{% endhighlight %}
+
+It does, however, add some interesting bits. One of which, is that dom elements created with crate can be referenced by the function that was used to create them. This is actually immensely useful, because it basically gives you named controls for free. For example, we end up adding a click handler for all our buttons like so:
+
+{% highlight clojure %}
+(def $body ($ :body))
+
+(delegate $body button :click
+ (fn [e]
+ (.preventDefault e)
+ (js/alert "clicked!")))
+{% endhighlight %}
+
+Time to make that handler a bit more interesting. We're here to make music afterall.
+
+##Interacting with the server - fetch
+
+[fetch] is the next piece of the puzzle which helps us by removing the barrier between the server and the client. In this case, we're going to use remotes, which are functions defined on the server that are then called by the client. Normally, these would look something like this:
+
+{% highlight clojure %}
+(letrem [result (some-remote-func 2 3 4)]
+ (.log js/console result))
+{% endhighlight %}
+
+But since we want to call these dynamically based on whatever action our button is created with, we'll need to drop down one level and use `(fetch.remotes/remote-callback remote-func params)`. To do this however, we also need to be able to get a reference to the dom element that was clicked. In jQuery, you usually use `this`, but in ClojureScript "this" is a symbol just like anything else. For us to get at the js "this", we'll simply use the macro `(this-as some-symbol-meaning-this ... )`
+
+{% highlight clojure %}
+(ns overtoneinterface.client.main
+ (:require [crate.core :as crate]
+ [fetch.remotes :as remotes]) ;; add fetch.remotes
+ (:use [jayq.core :only [$ append delegate data]])
+ (:use-macros [crate.macros :only [defpartial]]))
+
+...
+
+(delegate $body button :click
+ (fn [e]
+ (.preventDefault e)
+ (this-as me
+ (let [$me ($ me)
+ action (data $me :action)
+ param (data $me :param)
+ params (if (= param "")
+ []
+ [param])]
+ (remotes/remote-callback action params)))))
+{% endhighlight %}
+
+What that does is extract the action and param attributes from our button and then tells fetch to call the remote function whose name is the value of action with the params we give it. On the Noir side, you then just needs to do two things - add the wrap-remotes middleware in server.clj (make sure you restart the server!):
+
+{% highlight clojure %}
+(ns overtoneinterface.server
+ (:require [noir.server :as server]
+ [noir.fetch.remotes :as remotes]))
+
+(server/load-views "src/overtoneinterface/views/")
+(server/add-middleware remotes/wrap-remotes)
+
+(defn -main [& m]
+ (let [mode (keyword (or (first m) :dev))
+ port (Integer. (get (System/getenv) "PORT" "8080"))]
+ (server/start port {:mode mode
+ :ns 'overtoneinterface})))
+{% endhighlight %}
+
+And define a remote in views/welcome.clj
+
+{% highlight clojure %}
+(ns overtoneinterface.views.welcome
+ (:require [overtoneinterface.views.common :as common]
+ [overtoneinterface.models.dubstep :as dubstep])
+ (:use [noir.core :only [defpage]]
+ [overtone.live]
+ [overtone.inst.sampled-piano]
+ [noir.fetch.remotes :only [defremote]]
+ [hiccup.core :only [html]]))
+
+(defpage "/" []
+ (common/layout
+ [:div#controls]
+ [:div#wobble]
+ [:div#notes]
+ [:div#piano]))
+
+(defremote play-note [n]
+ (sampled-piano n))
+
+;;play-note is also just a regular function, meaning you could use
+;;it in your clj code like normal..
+;;(play-note 60)
+{% endhighlight %}
+
+If this is the first time your server has loaded overtone.live, it may take a few seconds for it to refresh as it has to startup supercollider and a few other things. Also, if this is your first time ever using the sampled piano, it has to download a pretty large set of samples (this can take an hour). Assuming you have both of those though, clicking the button will cause a tone to be played. In the video, this happens at [11:20](http://www.youtube.com/watch?v=lcRQFGtFiyE&feature=youtu.be&hd=1#t=11m20s).
+
+##Adding a bit more.
+
+At this point the fundamentals of the app are there, the rest is just icing on the cake. I clean up the code so that it's easy to add a bunch of buttons to a container and create more piano keys for us to click:
+
+{% highlight clojure %}
+(def piano-notes (for [note (range 40 60)]
+ {:label (str note) :action "play-note" :param note}))
+
+(defn populate [container buttons]
+ (doseq [b buttons]
+ (append container (button b))))
+
+(populate $piano piano-notes)
+{% endhighlight %}
+
+Then I grab the code from the dubstep example in the [Overtone] repository and drop it in, create a couple more remote functions and we then have the ability to fully control our little dubstep machine. The final welcome.clj looks like this:
+
+{% highlight clojure %}
+(ns overtoneinterface.views.welcome
+ (:require [overtoneinterface.views.common :as common]
+ [overtoneinterface.models.dubstep :as dubstep])
+ (:use [noir.core :only [defpage]]
+ [overtone.live]
+ [overtone.inst.sampled-piano]
+ [noir.fetch.remotes :only [defremote]]
+ [hiccup.core :only [html]]))
+
+(defpage "/" []
+ (common/layout
+ [:div#controls]
+ [:div#wobble]
+ [:div#notes]
+ [:div#piano]))
+
+(defremote play-note [n]
+ (sampled-piano n))
+
+(defremote start-dub []
+ (dubstep/start-dub))
+
+(defremote stop-dub []
+ (dubstep/stop-dub))
+
+(defremote dub-note [n]
+ (dubstep/alter-dub :note n))
+
+(defremote dub-wobble [n]
+ (dubstep/alter-dub :wobble-factor n))
+{% endhighlight %}
+
+And here's the final main.cljs:
+
+{% highlight clojure %}
+(ns overtoneinterface.client.main
+ (:require [crate.core :as crate]
+ [fetch.remotes :as remotes])
+ (:use [jayq.core :only [$ append delegate data]])
+ (:use-macros [crate.macros :only [defpartial]]))
+
+(def $body ($ :body))
+(def $piano ($ :#piano))
+(def $controls ($ :#controls))
+(def $notes ($ :#notes))
+(def $wobble ($ :#wobble))
+
+(defpartial button [{:keys [label action param]}]
+ [:a.button {:href "#" :data-action action :data-param param} label])
+
+(def piano-notes (for [note (range 40 60)]
+ {:label (str note) :action "play-note" :param note}))
+
+(def dub-notes (for [note (range 40 80)]
+ {:label (str note) :action "dub-note" :param note}))
+
+(def dub-wobble (for [w (range 0 8)]
+ {:label (str "w" w) :action "dub-wobble" :param w}))
+
+(def control-buttons [{:label "start" :action "start-dub" :param ""}
+ {:label "stop" :action "stop-dub" :param ""}])
+
+(defn populate [container buttons]
+ (doseq [b buttons]
+ (append container (button b))))
+
+(populate $piano piano-notes)
+(populate $controls control-buttons)
+(populate $notes dub-notes)
+(populate $wobble dub-wobble)
+
+(delegate $body button :click
+ (fn [e]
+ (.preventDefault e)
+ (this-as me
+ (let [$me ($ me)
+ action (data $me :action)
+ param (data $me :param)
+ param (if (= param "")
+ []
+ [param])]
+ (remotes/remote-callback action param)))))
+{% endhighlight %}
+
+And there you have it - a complete overtone controller in about 20 minutes.
+
+[Discuss this on HackerNews.](http://news.ycombinator.com/item?id=3615022)
+
+[lein-cljsbuild]: https://github.com/emezeske/lein-cljsbuild
+[Hiccup]: http://github.com/weavejester/hiccup
+[code]: http://github.com/ibdknox/overtoneCljs
+[recorded]: http://youtu.be/lcRQFGtFiyE
+[overtone]: http://github.com/overtone/overtone
+[jayq]: http://github.com/ibdknox/jayq
+[crate]: http://github.com/ibdknox/crate
+[fetch]: http://github.com/ibdknox/fetch
+[noir]: http://webnoir.org/
View
4 atom.xml
@@ -4,7 +4,7 @@ layout: nil
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
- <title>ibdknox</title>
+ <title>chris-granger.com</title>
<link href="http://chris-granger.com/atom.xml" rel="self"/>
<link href="http://chris-granger.com/"/>
<updated>{{ site.time | date_to_xmlschema }}</updated>
@@ -19,7 +19,7 @@ layout: nil
<title>{{ post.title }}</title>
<link href="http://chris-granger.com{{ post.url }}"/>
<updated>{{ post.date | date_to_xmlschema }}</updated>
- <id>http://chris-granger.com/id>
+ <id>http://chris-granger.com</id>
<content type="html">{{ post.content | xml_escape }}</content>
</entry>
{% endfor %}
View
16 css/screen.css
@@ -1,5 +1,5 @@
html {height:100%; }
-body { background:url("/images/bg.png"); height:100%; color:#5e5e5e; font-family:"Gill Sans","Gill Sans MT", sans-serif; font-size:12pt;}
+body { background:url("/images/bg.png"); height:100%; color:#535353; font-family:"Gill Sans","Gill Sans MT", sans-serif; font-size:12pt;}
a { text-decoration:none; color:#6e6e6e;}
#wrapper {height:100%; min-width:1280px; }
@@ -11,16 +11,22 @@ a { text-decoration:none; color:#6e6e6e;}
#nav .info { color: #9e9e9e; margin-top:100px; font-size:12pt; position:absolute; bottom:150px; }
+#copyright { z-index: -1; position:fixed; color: #9e9e9e; bottom:50px; right:50px;}
+
#contentwrapper { width: 700px; min-height:100%; position:relative; margin:0 auto; background:url("/images/middlebg.png"); border:1px solid #f0f0f0; border-width:0px 1px 0 1px; z-index:10; }
-#content { padding:50px; padding-top:35px; font-size:13pt; }
+#content { padding:50px; padding-top:35px; font-size:13pt; font-weight:lighter; }
#content h1 { font-size:24pt; font-weight:lighter; margin-bottom:20px; }
#content h1 a { color:#5e5e5e; }
+#content h2 { font-size: 18pt; }
#content ul {list-style:inside; }
-#content img { text-align:center; max-width:600px; box-shadow: 0 0 8px #666; border-radius:5px; border:none;}
+#content img { display:block; margin: 0 auto; max-width:600px; box-shadow: 0 0 8px #666; border-radius:5px; border:none;}
#content p {margin: 20px 0; font-weight:lighter; }
#content p a { text-decoration:underline; }
#content pre { font-family: "Courier New", Courier, monospace; font-size:10pt; background:#d8d8d8; box-shadow: 0 0 8px #aaa; border-radius:5px; padding:15px 20px; overflow:auto;}
-#content code { }
+#content p code { font-family: "Courier New", Courier, monospace; font-size:10pt; background:#d8d8d8; border-radius:2px; padding:2px; }
+#content blockquote p { font-size:11pt; background:#d8d8d8; border-radius:5px; padding:10px; box-shadow: 0 0 8px #aaa; }
+
+#content div + h1 { margin-top: 25px; }
#related {margin-top:80px; }
@@ -38,7 +44,7 @@ a { text-decoration:none; color:#6e6e6e;}
#resume { font-weight:lighter; }
#resume h1 { margin-bottom:0; line-height:1em; margin-top:10px; }
-#resume h2 { margin-bottom:30px; color:#8e8e8e; }
+#resume h2 { margin-bottom:30px; color:#8e8e8e; font-size:12pt; }
#resume dt { font-size:18pt; color:#8e8e8e; margin-bottom:10px;}
#resume dt span { color:#5e5e5e; }
#resume dd { margin-bottom: 20px; }
View
20 css/syntax.css
@@ -38,7 +38,6 @@
.highlight .sx { color: #00ee00 } /* Literal.String.Other */
.highlight .sr { color: #00ee00 } /* Literal.String.Regex */
.highlight .s1 { color: #00ee00 } /* Literal.String.Single */
-.highlight .ss { color: #ddd !important; } /* Literal.String.Symbol */
.highlight .na { color: #008080 } /* Name.Attribute */
.highlight .nc { color: #445588; font-weight: bold } /* Name.Class */
@@ -62,13 +61,14 @@
-.highlight .c1 { color:#aaa !important; font-style:normal; } /* Comment.Single */
-.highlight .mi { color: #969; } /* CHRIS: Literal.Number.Integer */
-.highlight .s { color: #969 !important; } /* CHRIS: Literal.String */
-.highlight .nb { color:#666 !important } /* CHRIS: clojure funcs */
-.highlight .nf { color: #666 !important; font-weight:normal !important; } /* CHRIS: function names */
-.highlight .nv { color: #478 !important; } /* CHRIS: clojure vars */
-.highlight .o { color:#478 !important; } /* CHRIS: . and # */
-.highlight .p { color:#888; } /* paren */
-.highlight .k { color:#666 !important; } /* CHRIS: clojure special forms */
+.highlight .ss { color: #4a4a4a !important; } /* CHRIS: symbols Literal.String.Symbol */
+.highlight .c1 { color:#777 !important; font-style:normal; } /* Comment.Single */
+.highlight .mi { color: #858; } /* CHRIS: Literal.Number.Integer */
+.highlight .s { color: #858 !important; } /* CHRIS: Literal.String */
+.highlight .nb { color:#4a4a4a !important } /* CHRIS: clojure funcs */
+.highlight .nf { color: #4a4a4a !important; font-weight:normal !important; } /* CHRIS: function names */
+.highlight .nv { color: #378 !important; } /* CHRIS: clojure vars */
+.highlight .o { color:#378 !important; } /* CHRIS: . and # */
+.highlight .p { color:#4a4a4a; } /* paren */
+.highlight .k { color:#4a4a4a !important; } /* CHRIS: clojure special forms */
View
BIN  images/overtoneController.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
2  index.html
@@ -1,6 +1,6 @@
---
layout: default
-title: ibdknox
+title: Chris Granger
---
<div id="post">
View
111 projects/cljs.markdown
@@ -0,0 +1,111 @@
+---
+layout: default
+title: "Cljs"
+tags: []
+---
+
+#clojurescript
+
+My first experiences with ClojureScript culminated in [Pinot] a library the provided the basic building blocks for creating websites in CLJS. It was, however, a monolithic conglomeration of all the random things I built along the way. To cure this, I broke Pinot apart into the following libraries:
+
+#[jayq]
+
+Pronounced "jake" is a jquery wrapper that makes jquery objects work just like normal clojure collections.
+
+{% highlight clojure %}
+(ns myapp
+ (:use [jayq.core :only [$ css inner]]))
+
+(def $interface ($ :#interface))
+
+(-> $interface
+ (css {:background "blue"})
+ (inner "Loading!"))
+{% endhighlight %}
+
+#[waltz]
+
+A state management library that lets you build very complex interactions without ending up in the tar pit.
+
+{% highlight clojure %}
+(def me (state/machine "cool")
+
+(defstate me :loading
+ (in [] (show $loading))
+ (out [] (hide $loading)))
+
+(defstate me :normal
+ (in [v]
+ (inner $value v)
+ (wait delay #(transition me :update))))
+
+(deftrans me :update []
+ (state/set me :loading)
+ (store/latest [:metrics (:metric params)]
+ #(transition me :set %)))
+
+(deftrans me :set [v]
+ (state/unset me :loading)
+ (state/set me :normal v))
+
+(transition me :update)
+{% endhighlight %}
+
+#[fetch]
+
+A library to make client/server interaction painless. Includes the pinot.remotes stuff as well as the start of a lazy-store implementation that works like a map where the values are fetched from the server lazily.
+
+{% highlight clojure %}
+(ns playground.client.test
+ (:require [fetch.remotes :as remotes])
+ (:require-macros [fetch.macros :as fm]))
+
+(fm/remote (adder 2 5 6) [result]
+ (js/alert result))
+
+(fm/remote (get-user 2) [{:keys [username age]}]
+ (js/alert (str "Name: " username ", Age: " age)))
+
+;; for a much nicer experience, use letrem
+(fm/letrem [a (adder 3 4)
+ b (adder 5 6)]
+ (js/alert (str "a: " a " b: " b)))
+{% endhighlight %}
+
+#[crate]
+
+An implementation of hiccup over dom objects.
+
+{% highlight clojure %}
+(ns overtoneinterface.client.main
+ (:require [crate.core :as crate])
+ (:use-macros [crate.macros :only [defpartial]]))
+
+(defpartial button [{:keys [label action param]}]
+ [:a.button {:href "#" :data-action action :data-param param} label])
+{% endhighlight %}
+
+#[monet]
+
+A visual library for ClojureScript to make it easier (and performant) to work with canvas/visuals.
+
+{% highlight clojure %}
+(ns game.core
+ (:require [monet.canvas :as canvas])
+
+(canvas/add-entity :background
+ (canvas/entity {:x 0 :y 0 :w 600 :h 600}
+ nil ;;update function
+ (fn [ctx box]
+ (-> ctx
+ (canvas/fill-style "#191d21")
+ (canvas/rect box)))))
+(canvas/init (.get ($ :#canvas) 0))
+{% endhighlight %}
+
+[pinot]: http://github.com/ibdknox/pinot
+[jayq]: http://github.com/ibdknox/jayq
+[crate]: http://github.com/ibdknox/crate
+[fetch]: http://github.com/ibdknox/fetch
+[monet]: http://github.com/ibdknox/monet
+[waltz]: http://github.com/ibdknox/waltz
View
12 projects/cljs.md
@@ -1,12 +0,0 @@
----
-layout: default
-title: "Cljs"
-tags: []
----
-
-#ClojureScript
-
-CLJS is awesome
-
-
-
View
2  projects/iwbyp.md
@@ -8,5 +8,5 @@ tags: []
<a href="http://iwbyp.chris-granger.com">iwbyp</a> is a service where I build a web prototype of your idea in two weeks using Clojure. If you have wireframes and an idea, I can build it for you at fixed cost.
-![iwbyp](/images/iwbyp.png)
+[![iwbyp](/images/iwbyp.png)](http://iwbyp.chris-granger.com)
View
45 projects/korma.markdown
@@ -0,0 +1,45 @@
+---
+layout: default
+title: "korma"
+tags: []
+---
+
+#korma
+
+[Korma] is a domain specific language for Clojure that takes the pain out of working with your favorite RDBMS. Here's a basic example:
+
+{% highlight clojure %}
+(defdb prod (postgres {:db "korma"
+ :username "db"
+ :password "dbpass"}))
+
+(defentity address)
+(defentity user
+ (has-many address))
+
+(select user
+ (with address)
+ (fields :firstName :lastName :address.state)
+ (where {:email "korma@sqlkorma.com"}))
+{% endhighlight %}
+
+Korma bring composability and reuse to SQL by turning queries into maps that can be built up over time. This make it much more expressive than SQL is naturally and gives you the full power of Clojure to construct queries in interesting ways.
+
+{% highlight clojure %}
+(def base (-> (select* "user")
+ (fields :id :username)
+ (where {:email [like "*@gmail.com"]})))
+
+(def ordered-and-active (-> base
+ (where {:active true})
+ (order :created)))
+
+(def constrained (-> ordered-and-active
+ (limit 20)))
+
+(exec constrained)
+{% endhighlight %}
+
+Checkout the [korma] website for more.
+
+[korma]: http://sqlkorma.com/
View
11 projects/korma.md
@@ -1,11 +0,0 @@
----
-layout: default
-title: "korma"
-tags: []
----
-
-#korma
-
-korma is awesome
-
-
View
38 projects/noir.md
@@ -6,5 +6,41 @@ tags: []
#noir
-Noir is awesome
+[Noir](http://webnoir.org) is a micro-framework that allows you to rapidly develop websites in Clojure. It looks like this:
+{% highlight clojure %}
+(ns my-app
+ (:use noir.core)
+ (:require [noir.server :as server]))
+
+(defpage "/welcome" []
+ "Welcome to Noir!")
+
+(server/start 8080)
+{% endhighlight %}
+
+By default, it uses an HTML generation library called Hiccup, which represents tags as clojure vectors. This allows you to compose your views in very interesting and powerful ways using the functions you know and love from Clojure:
+
+{% highlight clojure %}
+(defpartial todo-item [{:keys [id title due]}]
+ [:li {:id id} ;; maps define HTML attributes
+ [:h3 title]
+ [:span.due due]]) ;; add a class
+
+(defpartial todos-list [items]
+ [:ul#todoItems ;; set the id attribute
+ (map todo-item items)])
+
+(todos-list [{:id "todo1"
+ :title "Get Milk"
+ :due "today"}])
+;; =>
+;; <ul id="todoItems">
+;; <li id="todo1">
+;; <h3>Get Milk</h3>
+;; <span class="due">today</span>
+;; </li>
+;; </ul>
+{% endhighlight %}
+
+Learn more at [http://webnoir.org](http://webnoir.org)
View
11 resume.md → resume.markdown
@@ -37,15 +37,6 @@ title: ibdknox
<span>The next big thing?</span>
2010-Current
</dt>
- <dd>After about two years, I left Microsoft to start a company with my good friend <a href="http://nathanhammond.com">Nathan Hammond</a>. Nathan and I have known each other since high school, we both worked at Skookum and MODE together, and have always wanted to strike out on our own. Currently, we're working on several ideas, the first of which, a <a href="http://www.typewire.io">live-blogging service</a>, is now in beta. It's been an insane <a href="http://radian.org/notebook/wp-content/uploads/2008/04/20080404-img_2281.jpg">roller coaster ride</a> so far, but it's undoubtedly one of the best learning experiences I could ever have.</dd>
- <!--
- <dt>
- <span>Along the way, I learned.</span>
- 1987-Current
- </dt>
- <dd>
- Throughout all of this, I've developed a <a href="http://www.amazon.com/Linchpin-Are-Indispensable-Seth-Godin/dp/1591843162" target="_blank">unique skillset</a>. My eclectic background and general passion for solving problems and learning new things means I approach situations from angles most people haven't even dreamed of yet. I admit, I'm different (how many German majors do you know at Microsoft?), but I promise it's for the best.
- </dd>
- -->
+ <dd>After about two years, I left Microsoft to start a company with my good friend <a href="http://nathanhammond.com">Nathan Hammond</a>. Nathan and I have known each other since high school, we both worked at Skookum and MODE together, and had always wanted to strike out on our own. Together, we built a <a href="http://www.typewire.io">live-blogging service</a> called Typewire. It was an insane <a href="http://radian.org/notebook/wp-content/uploads/2008/04/20080404-img_2281.jpg">roller coaster ride</a> and undoubtedly one of the best learning experiences I've had, but in the end, it didn't work out. So thereafter, I joined up with the guys at <a href="https://www.readyforzero.com">ReadyForZero</a> to help combat the growing debt epidemic. As their first engineering hire, I've helped rewrite an old Django site into a new one built entirely on <a href="http://webnoir.org">Noir</a> and <a href="http://clojure.org">Clojure</a>.</dd>
</dl>
</div>
Please sign in to comment.
Something went wrong with that request. Please try again.