Finding Clojure




Kurt Harriger     @kurtharriger
The Challenge

Twitter search url filter
Project Folder
% lein new twitturl
Created new project in: /Users/kurtharriger/code/twitturl




├──   README
├──   project.clj
├──   src
│     └── twitturl
│          └── core.clj
└──   test
      └── twitturl
           └── test
               └── core.clj

 % git init && git add -A && git commit -m “create project”
project.clj
(defproject twitturl "1.0.0-SNAPSHOT"
  :description "Twitturl aggregator"
  :dependencies [[org.clojure/clojure "1.2.0"]
         [org.clojure/clojure-contrib "1.2.0"]
         [ring "0.3.7"]
         [clj-http "0.1.2"]]
  :dev-dependencies [[lein-ring "0.4.0"]]
  :ring {:handler twitturl.core/app})
Appleholics
% brew install leiningen


       Non Appleholics
Find Leiningen for Clojure on
https://github.com/technomancy/leiningen
% lein self-install
S-Expressions
 Clojure:
 (defn fn [arg1 & more] arg1)


defn is a macro used here with 3 parameters:
function name, arguments, and body.
Returns the function binding.

 Clojure:
 (println (fn value1 value2))

 Java:
 System.out.println(fn(value1, value2));
S-Expressions
 Clojure:
 (defn fn [arg1 & more] arg1)


defn is a macro used here with 3 parameters:
function name, arguments, and body.
Returns the function binding.

 Clojure:
 (println (fn value1 value2))

 Java:
 System.out.println(fn(value1, value2));
ring framework
(ns twitturl.core
  (:require [com.twinql.clojure.http :as http])
  (:use [ring.middleware.params])
  (:use [hiccup.core]))

                                       Import dependencies


(defn handler [] nil)      ;todo create handler

(def app (-> handler wrap-params))

                  -> operator used apply multiple
  Entry point          functions to handler
De-structuring
(defn format-tweets [json] nil) ; todo

(defn handler [{{q "q"} :params}]
  { :content-type "text/html"
    :body (html [:body
      (-> (search q) format-tweets)])})

same as:                                 Only need q

(defn handler [request]
  (let [params (request :params)        Local variable assignments
        q      (params "q")]               request and params
    {:content-type "text/html"                 unnecessary.
     :body (html [:body
       (-> (search q) format-tweets)])}))
The Search
(def url "http://search.twitter.com/search.json")

   define readonly variable named url



(defn search [query]
  (http/get url :query {:q query} :as :json))

  returns result of com.twinql.clojure.http.get
  using parameter list
  1)   url
  2)   :query
  3)   {:q query }
  4)   :as
  5)   :json
Enter the repl
% lein deps
Copying 22 files to /Users/kurtharriger/code/twitturl/lib
Copying 17 files to /Users/kurtharriger/code/twitturl/lib/dev

% lein repl
REPL started; server listening on localhost:24319.
user=> (use ‘twitturl.core)
nil
user=> (search “clojure”)

{:code 200,
  :reason "OK",
  :content
   {:results
     [{:from_user "planetclojure",
       :text "Removing defined tests in Clojure REPL
              http://goo.gl/fb/qeUty #clojure #SO",
      ...
     }, ...]
   }
}
The Handler
(defn format-tweets [json] nil) ; todo

(defn handler [{{q "q"} :params}]
  { :content-type "text/html"
    :body        hiccup library function

      (html [:body
        (-> (search q) format-tweets)])})

           same as (format-tweets (search q))


 returns a map containing
 :content-type and :body
functions
(defn aslink [url]
   (html [:a {:href url} url]))
user=> (aslink “http://url1”)
"<a href="http://url1">http://url1</a>"


(def urls (partial re-seq #"http://S+"))
same as:
(defn urls [text] (re-seq #“http://S+” text))

user=> (urls “blah blah http://url1 http://url2”)
("http://url1" "http://url2")
map reduce
(defn linkify [text]
  (reduce #(.replace %1 %2 (aslink %2))
     text (urls text)))
               (fn [newText url]
                  (.replace newText url (aslink url))


String linkify(String text) {
  String newText = text;
  for(String url : getUrls(text)) {
    newText = newText.replace(url, asLink(url));
  }
  return newText;
}
chaining
(defn format-tweet [tweet]   1         2
  [:li (:from_user tweet)
    [:blockquote (-> tweet :text linkify)]])

 Java:

 linkify(tweet.get(“text”))
chaining
(defn format-tweet [tweet]   1         2 2
  [:li (:from_user tweet)
    [:blockquote (-> tweet :text linkify)]])

 Java:

 linkify(tweet.get(“text”))
                                               4


           Java requires more(parentheses)?!
chaining
(defn format-tweets [json]
  [:ul (->> json :content :results
            (remove #(-> % :text urls nil?))
            (map format-tweet))])
ArrayList<String> formatTweets(JSONObject json) {
      StringBuilder tweets = new StringBuilder();
      tweets.append(“<ul>”);
      JSONObject content = json.getJSONObject(“content”);
      JSONArray results = json.getJSONArray(“results”);
      for(JSONObject tweet : results) {
         String[] urls = getUrls(tweet.getString(“text”))
         if(urls != null && urls.length > 0) {
           tweets.append(“<li>” + formatTweet(tweet) “</li>”);
         }
      }
      tweets.append(“</ul>”);
      return tweets;
}
chaining
(defn format-tweets [json]            5
  [:ul (->> json :content :results
            (remove #(-> % :text urls nil?))
            (map format-tweet))])
ArrayList<String> formatTweets(JSONObject json) {
      StringBuilder tweets = new StringBuilder();
      tweets.append(“<ul>”);
      JSONObject content = json.getJSONObject(“content”);
      JSONArray results = json.getJSONArray(“results”);
      for(JSONObject tweet : results) {
                                                                 12
         String[] urls = getUrls(tweet.getString(“text”))
         if(urls != null && urls.length > 0) {
           tweets.append(“<li>” + formatTweet(tweet) “</li>”);
         }
      }
            Java usually requires more(parentheses)?!
      tweets.append(“</ul>”);
      return tweets;
}
ring server
% lein ring server
2011-04-16 21:18:54.965:INFO::Logging to STDERR via org.mortbay.log.StdErrLog

2011-04-16 21:18:54.968:INFO::jetty-6.1.26

2011-04-16 21:18:54.998:INFO::Started SocketConnector@0.0.0.0:3000

Started server on port 3000



% lein ring war
Created /Users/kurtharriger/code/twitturl/twitturl-1.0.0-SNAPSHOT.war




Amazon Elastic Beanstalk plugin
https://github.com/weavejester/lein-beanstalk
src/twitturl/core.clj
 1   (ns twitturl.core
 2     (:use [ring.middleware.params])
 3     (:use [hiccup.core])
 4     (:require [com.twinql.clojure.http :as http]))
 5
 6   (def url "http://search.twitter.com/search.json")
 7
 8   (defn search [query] (http/get url :query {:q query} :as :json))
 9   (defn aslink [url] (html [:a {:href url} url]))
10
11   (def   urls (partial re-seq #"http://S+"))
12
13   (defn linkify [text]
14     (reduce #(.replace %1 %2 (aslink %2)) text (urls text)))
15
16   (defn format-tweet [tweet]
17     [:li (:from_user tweet)
18      [:blockquote (-> tweet :text linkify)]])
19
20   (defn format-tweets [json]
21     [:ul (->> json :content :results
22               (remove #(-> % :text urls nil?))
23               (map format-tweet))])
24
25   (defn handler [{{q "q"} :params}]
26     { :content-type "text/html"
27      :body (html [:body (-> (search q) format-tweets)])})
28
29   (def app (-> handler wrap-params ))
Finding Clojure
    https://github.com/kurtharriger/twitturl
     Leiningen                Ring framework

     DSLs                     Lists are awesome

     Less () than Java        De-structuring

     Method chaining          High-order functions

Kurt Harriger            @kurtharriger

Finding Clojure

  • 1.
  • 2.
  • 3.
    Project Folder % leinnew twitturl Created new project in: /Users/kurtharriger/code/twitturl ├── README ├── project.clj ├── src │   └── twitturl │   └── core.clj └── test └── twitturl └── test └── core.clj % git init && git add -A && git commit -m “create project”
  • 4.
    project.clj (defproject twitturl "1.0.0-SNAPSHOT" :description "Twitturl aggregator" :dependencies [[org.clojure/clojure "1.2.0"] [org.clojure/clojure-contrib "1.2.0"] [ring "0.3.7"] [clj-http "0.1.2"]] :dev-dependencies [[lein-ring "0.4.0"]] :ring {:handler twitturl.core/app})
  • 5.
    Appleholics % brew installleiningen Non Appleholics Find Leiningen for Clojure on https://github.com/technomancy/leiningen % lein self-install
  • 6.
    S-Expressions Clojure: (defnfn [arg1 & more] arg1) defn is a macro used here with 3 parameters: function name, arguments, and body. Returns the function binding. Clojure: (println (fn value1 value2)) Java: System.out.println(fn(value1, value2));
  • 7.
    S-Expressions Clojure: (defnfn [arg1 & more] arg1) defn is a macro used here with 3 parameters: function name, arguments, and body. Returns the function binding. Clojure: (println (fn value1 value2)) Java: System.out.println(fn(value1, value2));
  • 8.
    ring framework (ns twitturl.core (:require [com.twinql.clojure.http :as http]) (:use [ring.middleware.params]) (:use [hiccup.core])) Import dependencies (defn handler [] nil) ;todo create handler (def app (-> handler wrap-params)) -> operator used apply multiple Entry point functions to handler
  • 9.
    De-structuring (defn format-tweets [json]nil) ; todo (defn handler [{{q "q"} :params}] { :content-type "text/html" :body (html [:body (-> (search q) format-tweets)])}) same as: Only need q (defn handler [request] (let [params (request :params) Local variable assignments q (params "q")] request and params {:content-type "text/html" unnecessary. :body (html [:body (-> (search q) format-tweets)])}))
  • 10.
    The Search (def url"http://search.twitter.com/search.json") define readonly variable named url (defn search [query] (http/get url :query {:q query} :as :json)) returns result of com.twinql.clojure.http.get using parameter list 1) url 2) :query 3) {:q query } 4) :as 5) :json
  • 11.
    Enter the repl %lein deps Copying 22 files to /Users/kurtharriger/code/twitturl/lib Copying 17 files to /Users/kurtharriger/code/twitturl/lib/dev % lein repl REPL started; server listening on localhost:24319. user=> (use ‘twitturl.core) nil user=> (search “clojure”) {:code 200, :reason "OK", :content {:results [{:from_user "planetclojure", :text "Removing defined tests in Clojure REPL http://goo.gl/fb/qeUty #clojure #SO", ... }, ...] } }
  • 12.
    The Handler (defn format-tweets[json] nil) ; todo (defn handler [{{q "q"} :params}] { :content-type "text/html" :body hiccup library function (html [:body (-> (search q) format-tweets)])}) same as (format-tweets (search q)) returns a map containing :content-type and :body
  • 13.
    functions (defn aslink [url] (html [:a {:href url} url])) user=> (aslink “http://url1”) "<a href="http://url1">http://url1</a>" (def urls (partial re-seq #"http://S+")) same as: (defn urls [text] (re-seq #“http://S+” text)) user=> (urls “blah blah http://url1 http://url2”) ("http://url1" "http://url2")
  • 14.
    map reduce (defn linkify[text] (reduce #(.replace %1 %2 (aslink %2)) text (urls text))) (fn [newText url] (.replace newText url (aslink url)) String linkify(String text) { String newText = text; for(String url : getUrls(text)) { newText = newText.replace(url, asLink(url)); } return newText; }
  • 15.
    chaining (defn format-tweet [tweet] 1 2 [:li (:from_user tweet) [:blockquote (-> tweet :text linkify)]]) Java: linkify(tweet.get(“text”))
  • 16.
    chaining (defn format-tweet [tweet] 1 2 2 [:li (:from_user tweet) [:blockquote (-> tweet :text linkify)]]) Java: linkify(tweet.get(“text”)) 4 Java requires more(parentheses)?!
  • 17.
    chaining (defn format-tweets [json] [:ul (->> json :content :results (remove #(-> % :text urls nil?)) (map format-tweet))]) ArrayList<String> formatTweets(JSONObject json) { StringBuilder tweets = new StringBuilder(); tweets.append(“<ul>”); JSONObject content = json.getJSONObject(“content”); JSONArray results = json.getJSONArray(“results”); for(JSONObject tweet : results) { String[] urls = getUrls(tweet.getString(“text”)) if(urls != null && urls.length > 0) { tweets.append(“<li>” + formatTweet(tweet) “</li>”); } } tweets.append(“</ul>”); return tweets; }
  • 18.
    chaining (defn format-tweets [json] 5 [:ul (->> json :content :results (remove #(-> % :text urls nil?)) (map format-tweet))]) ArrayList<String> formatTweets(JSONObject json) { StringBuilder tweets = new StringBuilder(); tweets.append(“<ul>”); JSONObject content = json.getJSONObject(“content”); JSONArray results = json.getJSONArray(“results”); for(JSONObject tweet : results) { 12 String[] urls = getUrls(tweet.getString(“text”)) if(urls != null && urls.length > 0) { tweets.append(“<li>” + formatTweet(tweet) “</li>”); } } Java usually requires more(parentheses)?! tweets.append(“</ul>”); return tweets; }
  • 19.
    ring server % leinring server 2011-04-16 21:18:54.965:INFO::Logging to STDERR via org.mortbay.log.StdErrLog 2011-04-16 21:18:54.968:INFO::jetty-6.1.26 2011-04-16 21:18:54.998:INFO::Started SocketConnector@0.0.0.0:3000 Started server on port 3000 % lein ring war Created /Users/kurtharriger/code/twitturl/twitturl-1.0.0-SNAPSHOT.war Amazon Elastic Beanstalk plugin https://github.com/weavejester/lein-beanstalk
  • 21.
    src/twitturl/core.clj 1 (ns twitturl.core 2 (:use [ring.middleware.params]) 3 (:use [hiccup.core]) 4 (:require [com.twinql.clojure.http :as http])) 5 6 (def url "http://search.twitter.com/search.json") 7 8 (defn search [query] (http/get url :query {:q query} :as :json)) 9 (defn aslink [url] (html [:a {:href url} url])) 10 11 (def urls (partial re-seq #"http://S+")) 12 13 (defn linkify [text] 14 (reduce #(.replace %1 %2 (aslink %2)) text (urls text))) 15 16 (defn format-tweet [tweet] 17 [:li (:from_user tweet) 18 [:blockquote (-> tweet :text linkify)]]) 19 20 (defn format-tweets [json] 21 [:ul (->> json :content :results 22 (remove #(-> % :text urls nil?)) 23 (map format-tweet))]) 24 25 (defn handler [{{q "q"} :params}] 26 { :content-type "text/html" 27 :body (html [:body (-> (search q) format-tweets)])}) 28 29 (def app (-> handler wrap-params ))
  • 22.
    Finding Clojure https://github.com/kurtharriger/twitturl Leiningen Ring framework DSLs Lists are awesome Less () than Java De-structuring Method chaining High-order functions Kurt Harriger @kurtharriger