четверг, 15 февраля 2018 г.

core.async tip from Tim Baldridge

If I'm understand the problem I think this is by design. Closing a channel results in a logical termination value being put into the channel.

This is so that, by default, core.async won't loose data.

Most of the time when I encounter this problem I fix it by draining the channel:

(defn drain [c]
  (go (while (not (nil? (<! c)))

And then:

(close! c)
(drain c)

среда, 14 февраля 2018 г.

clojure ring async example


[ring "1.6.3"]
[ring/ring-jetty-adapter "1.6.3"]
[org.clojure/core.async "0.4.474"]
[org.immutant/web "2.1.10"]


(ns test01.core
  (:require [ring.adapter.jetty :as jetty]
            [clojure.core.async :refer [go chan <! <!! >! >!! timeout close!]]))

(defn what-is-my-ip [request respond raise]
  (respond {:status  200
            :headers {"Content-Type" "text/plain"}
            :body    (:remote-addr request)}))

(defn what-is-my-ip2 [request respond raise]
  (let [ch (chan)]
      (respond (<! ch)))
      ;;(<! (timeout 2000))
      (>! ch {:status  200
              :headers {"Content-Type" "text/plain"}
              :body    (:remote-addr request)}))))

(defn start-server
  (jetty/run-jetty handler {:host "localhost" :async? true :port 8080 :path "/" :join? false}))

(defn -main
  "entry point"
  [& args]
  (println "Hello, world."))

;; (def s (start-server what-is-my-ip))
;; (.stop s)
;;(def s (start-server what-is-my-ip2))


(ns test01.imm
  (:require [immutant.web :as web]
            [immutant.web.async :as async]))

(defn app [request]
  (async/as-channel request
                    {:on-open (fn [stream]
                                (async/send! stream {:status  200
                                                     :headers {"Content-Type" "text/plain"}
                                                     :body    (:remote-addr request)}
                                             {:close? true}))}))

(defn start-server [h]
  (web/run h {:host "localhost" :port 8080}))

(def s (start-server app))

(web/stop s)

среда, 27 декабря 2017 г.

clojure cli and nrepl

Today, I will show you how to run nrepl server and connect emacs to it.

1. Create empty folder helloproject

2. Create file deps.edn in helloproject folder and put here the following content:
{:deps {clj-time {:mvn/version "0.14.2"}}
 :aliases {:repl {:extra-deps
                  {cider/cider-nrepl       {:mvn/version "0.16.0-SNAPSHOT"}
                   org.clojure/tools.nrepl {:mvn/version "0.2.12"}
                   refactor-nrepl          {:mvn/version "2.3.1"}}}}}

Here we import library clj-time from Maven repo, and  import extra deps under alias :repl

3. Now create src folder in helloproject folder,.i.e helloproject/src

4. Create file hello.clj in helloproject/src folder and put here the following content:
(ns hello
  (:require [clj-time.core :as t]
            [clj-time.format :as f]))

(defn time-str
  "Returns a string representation of a datetime in the local time zone."
   (f/with-zone (f/formatter "hh:mm aa") (t/default-time-zone))

(defn -main []
  (println "Hello world, the time is" (time-str (t/now))))

5. Now we are ready to run repl. Change dir to helloproject and run command: clj -R:repl
The clj parameter -R:repl is important to load extra deps. If you have several aliases then you should list them comma separated, i.e. clj -R:alias1,alias2

After run command clj -R:repl you should see the prompt:
Clojure 1.9.0

6. Now we may start nrepl server. Enter the following  two commands in repl:

(require '[clojure.tools.nrepl.server :refer [start-server]] '[cider.nrepl :refer [cider-nrepl-handler]])

(let [port (or (some-> (first *command-line-args*) (java.lang.Long/parseLong)) 7888)] (start-server :port port :handler cider-nrepl-handler) (println "Started nREPL on port" port))

7. After successful nrepl server start you may open helloproject/src/hello.clj in your favourite editor and connect to remote repl.

In Emacs: M-x cider-connect
then enter: localhost and 7888

воскресенье, 3 декабря 2017 г.

http/2 client example in clojure

1. Add [com.squareup.okhttp3/okhttp "3.9.1"] as dependecy in project.clj

2. Download  Jetty's ALPN boot JAR. from here

3. Add jvm options to boot ALPN and optionally truststore to JKS

:jvm-opts ["-Xbootclasspath/p:/opt/libs/alpn-boot-8.1.9.v20160720.jar"

4.  Simple http/2 client code looks like this.

(ns test01.core
  (:import (okhttp3 OkHttpClient Request Request$Builder Response)))

(defn -main [& args]
  (let [client (OkHttpClient.)
        request (-> (Request$Builder.)
                    (.url "https://localhost:50443")
        response (-> client (.newCall request) .execute)]
    (println (-> response .body .string))
    (println (-> response .protocol .toString))))


суббота, 2 декабря 2017 г.

java 9 http client in clojure


Here is a small example, how to use HttpClient from java 9.

1) add to project.clj section  :jvm-opts ["--add-modules" "jdk.incubator.httpclient"] cause HttpClient is available as separate module.

(import '(jdk.incubator.http HttpClient HttpRequest HttpResponse HttpResponse$BodyHandler))

(def http-client (HttpClient/newHttpClient))

(def request (-> (HttpRequest/newBuilder)
                 (.uri (java.net.URI. "https://www.javabullets.com"))

(def response (.send http-client request (HttpResponse$BodyHandler/asString)))

(.statusCode response)
(.body response)

понедельник, 6 ноября 2017 г.

clojure monorepo how-to

Monorepo is the way to manage several related projects from one point. In this note I'll show you how to organize monorepo using Amperity plugin lein-monolith .

0. Lets create root project and some subprojects
                    project.clj        ;;project file for root project
                            /app01     ;; project folder for app01 project
                            /lib01       ;; project folder for lib01 project which is needed for app01
                            /lib02       ;; some other lib

1. We should put section :plugins [[lein-monolith "1.0.1"]] to root project.clj and to project.clj of all subprojects.

(defproject rootproject/all "MONOLITH"
  :description "Mono repo"

  :plugins [[lein-monolith "1.0.1"]
                 [lein-cprint "1.3.0"]]

;; some repo which is used in all subprojects
  :repositories {"readytalk" {:url "https://dl.bintray.com/readytalk/maven/"}}

 ;; this is local deps that belongs only root project until section :dependencies is included in :inherit section. But for :inherit section we will use :managed-dependencies instead.
  :dependencies [[org.clojure/clojure "1.9.0-beta4"]]

;; this managed-deps will be delivered to all subprojects
  :managed-dependencies [[org.clojure/clojure "1.9.0-beta4"]]
  :monolith {
             ;; here we tell lein-monolith to merge :repositories and :managed-dependencies
             ;; to all subprojects.
             :inherit [:repositories :managed-dependencies]

            ;; here we tell that section profiles from root projects should be merged to all subprojects.
            ;; in this example we tell that in uberjar we should omit source files.
             :inherit-leaky [:profiles]

             ;; where is our subprojects located
             :project-dirs ["apps/*" "libs/*"]}

  :profiles {:uberjar {:omit-source true}}

  :env {:foo "bar"})

2. Every project.clj of all subprojects we prepare like this:

(defproject rootproject/app01 "2.0.0-SNAPSHOT"
  :description "FIXME: write description"

;; here we include lein-monolith for subproject
  :plugins [[lein-monolith "1.0.1"]
                 [lein-cprint "1.3.0"]]

 ;; here we tell that we need to inherit all sections from root project
  :monolith/inherit true
  :deployable true

  :dependencies [[rootproject/lib01 "0.1.0-SNAPSHOT"]]
  :main ^:skip-aot lib01.core
  :target-path "target/%s"
  :profiles {:uberjar {:aot :all}})

3. Now we can run commands from root project
lein monolith each do clean, uberjar 
This command will clean and make uberjars for all subprojects. Remember, that we should run
lein install
for lib01 to install to a local repo for app01 which use it. Also we can run command only for particular subproject
lein monolith each :start rootproject/app01 do uberjar 
 This command will run uberjar target only for particular project.

Interesting fact that we can point out clojure version only in root project - project.clj, in section :managed-dependencies and this version of clojure will be delivered to all subprojects. So we don't need to include clojure version in subprojects.

Also from root project we can run different tests for different subproject using selectors. First of all wee should put metadata to all deftest like here

In example above it is integrations test with ^:integration metadata. Then we should put section
{:unit (complement :integration)
:integration :integration}

in root project.clj and put :test-selectors to :inherit section.  

воскресенье, 15 октября 2017 г.

clojure debugging 2

(defmacro safe-eval
  "This macro is used to execute any function inside try-catch block.
  returns value or nil in case of Exception and prints debug info. "
  [& forms]
     (catch Exception e#
       (println "--------------")
       (println "file :" *file* \newline)
       (println "s-exp:" (quote ~@forms))
       (println "src  :" ~(meta &form))
       (println "=>" (.getMessage e#)))))