среда, 14 июня 2017 г.

Event Sourcing part 1

Spacemacs + smartscan

Very handy package - Smartscan for Clojure code editing, renaming local vars in fn, jumping to usages & more.

Spacemacs + Cider + 2 repls

When we use EMACS/Spacemacs and want to use two repls for CLJ and CLJS then
1. just add (add-to-list 'auto-mode-alist '("\.cljs$" . clojurescript-mode)) to init.el to enable clojurescript-mode for cljs files (otherwise it will be clojure mode with some problems in CLJS).
2. Select CLJ file and run cider-jack-in to run Clojure REPL
3. Select CLJS file (check that EMACS in Clojurescript mode) and run cider-jack-in-clojurescript or run cider-jack-in and then  (require'[ figwheel-sidecar.repl-api :as f]) and
(f/start-figwheel!) and (f/cljs-repl)

If I need to work with two different Clojure REPLs and eval S-forms I do following.

Run two REPLs by cider-jack-in command. 

To switch between REPL connections:
1. M-x and choose cider-toogle-request-dispatch to set CIDER variable to static mode
2. choose appropriate repl window and run command: cider-make-connection-default

Now, all S-exp will be evaluated in a default repl process.

воскресенье, 2 апреля 2017 г.

clojure.test tutorial

(ns example.basic-types-tests
  (:require [clojure.test :refer [deftest testing is use-fixtures]]
            [example.basic-types :as basic]
            [clojure.spec :as s]
            [clojure.spec.gen :as gen]))

(defn once-fixture [f]
  (println "Prepare for for all tests...")
  (println "Clean up after all tests..."))

(defn before-any-test-fixture [f]
  (println "Next test preparation...")
  (println "Clean up after test..."))

(use-fixtures :once once-fixture)
(use-fixtures :each before-any-test-fixture)

(deftest basic-types-spec
  (testing "testing basic types spec"
    (is (= 1 1))))

(deftest basic-types-generator-test
  (testing "testing generators for basic types"
    (is (= 1000 (count (gen/sample (s/gen ::basic/string) 1000))))))

(deftest arithmetic-test
  (testing "this should throw exception"
    (is (thrown? ArithmeticException (/ 1 0)))))

;; This will make tests hook: only specified tests will be run.
;; Warining! if hook was installed then only specified tests will be run
;; to disable hook perform (ns-unmap *ns* 'test-ns-hook)
;(defn test-ns-hook []
;  (basic-types-generator-test))

;; To run particular test form some namespace use follwing command in repl:
;;(clojure.test/test-vars [#'example.basic-types-tests/arithmetic-test])
;; if everything ok it returns nil, otherwise returns report. This will run test with existing fixtures
;; or lein test :only my.namespace/my-test

суббота, 1 апреля 2017 г.

clojure.test with clojure.spec

First of all, add necessary dependency to project.clj
  [org.clojure/test.check "0.9.0"]

Then add following line to project.clj  :monkeypatch-clojure-test false due to bug https://github.com/technomancy/leiningen/issues/2173

Then, define fns and their spec

(require '[clojure.spec :as s])

(defn average [list-sum list-count]
  (/ list-sum list-count))

(s/fdef average
  :args (s/and (s/cat :list-sum float? :list-count integer?)
               #(not (zero? (:list-count %))))
  :ret number?)

Then in test folder make file with tests and put

(:require [clojure.test :as t]
            [clojure.string :as str]
            [example.core :refer :all]
            [clojure.spec :as spec]
            [clojure.pprint :as pprint]
            [clojure.spec.test :as stest])

(defmacro defspec-test
  ([name sym-or-syms] `(defspec-test ~name ~sym-or-syms nil))
  ([name sym-or-syms opts]
   (when t/*load-tests*
     `(def ~(vary-meta name assoc :test `(fn []
                                           (let [check-results# (clojure.spec.test/check ~sym-or-syms ~opts)
                                                 checks-passed?# (every? nil? (map :failure check-results#))]
                                             (if checks-passed?#
                                               (t/do-report {:type    :pass
                                                             :message (str "Generative tests pass for "
                                                                           (str/join ", " (map :sym check-results#)))})
                                               (doseq [failed-check# (filter :failure check-results#)
                                                       :let [r# (clojure.spec.test/abbrev-result failed-check#)
                                                             failure# (:failure r#)]]
                                                   {:type     :fail
                                                    :message  (with-out-str (clojure.spec/explain-out failure#))
                                                    :expected (->> r# :spec rest (apply hash-map) :ret)
                                                    :actual   (if (instance? Throwable failure#)
                                                                (:clojure.spec.test/val failure#))})))
        (fn [] (t/test-var (var ~name)))))))

(defspec-test average-test ['example.core/average] {:clojure.spec.test.check/opts {:num-tests 500000}})

Notice that testing fn is fully qualified and ' at the begining in vector.

That's it.


Also, we can write usual test. To test function example.core/average with input auto generation and output validation with spec we can do it followin way:

(t/deftest average-test2
  (t/testing "average test"  
    (let [r (stest/check `example.core/average {:clojure.spec.test.check/opts {:num-tests 1000}})]
        (t/is (= true (-> r first :clojure.spec.test.check/ret :result))))))

Update 2:

To test function example.core/average we need to require [clojure.test.check.clojure-test :as check.clj-test :refer [defspec]]

(defspec average-test3   10   (prop/for-all [r (gen/tuple gen/int gen/int)]
                                                    (number? (apply example.core/average r))))

Update 3:
To run reproducible test we need to require [clojure.test.check.clojure-test :as check.clj-test :refer [defspec]]

(defspec abcd {:seed 12345 :num-tests 1000}
         (prop/for-all [s (s/gen ::basic/blank-string)]
                       (s/valid? ::basic/blank-string s)))

Update 4:

In example.core namespace:

(defn my-div [x y]
  "divide two numbers"
  (/ x y))

(s/fdef my-div
  :args (s/and (s/cat :first number? :second number?)
               #(not (zero? (:second %))))
  :ret number?)

In tests namespace:

(deftest my-div-test
  (let [result (spec.test/check `example.core/my-div {:clojure.spec.test.check/opts {:num-tests 1000}})]
    (is (= true (-> result first :clojure.spec.test.check/ret :result)))
    (clojure.spec.test/instrument 'example.core/my-div)
    (is (thrown? clojure.lang.ExceptionInfo (example.core/my-div 1 0)))
    (clojure.spec.test/unstrument example.core/my-div)
    (is (thrown? ArithmeticException (example.core/my-div 1 0)))))

четверг, 16 марта 2017 г.

clojure queue FIFO

If you need simple FIFO queue you can use clojure.lang.PersistentQueue/EMPTY function with some wrappers on it.

;; ------------------------------------------------------------------
  (defn new-queue []
    (atom clojure.lang.PersistentQueue/EMPTY))

  (defn push-queue [queue value]
    (swap! queue conj value))

  (defn pop-queue [queue]
    (let [value (peek @queue)]
      (swap! queue pop)
;; ------------------------------------------------------------------

Now let's test it

  (def a (new-queue))
;; => nil

(push-queue a 1)
;; => #object...

(push-queue a 2)
;; => #object...

(push-queue a 3)
;; => #object...

(push-queue a "hello")
;; => #object...

Let's check a content of queue a:

(seq @a)
;; => (1 2 3 "hello")

Now time to take first item:

(pop-queue a)
;; => 1

(seq @a)
;; => (2 3 "hello")

понедельник, 13 марта 2017 г.

emacs interactive package install

(use-package company
:ensure t
:bind (("C-c /". company-complete))

четверг, 9 марта 2017 г.

воскресенье, 26 февраля 2017 г.

clojure reducers are fast

(def v1 (for [x (range 10000000)]
          (rand-int 10000000)))

(count v1)

(require '[clojure.core.reducers])
(defn x-in-coll? [x coll]
  (clojure.core.reducers/reduce (fn [res next-v] (if (= next-v x) (reduced true) false)) false coll))

(defn x-in-coll2? [x coll]
  (some #(= x %) coll))

(time (x-in-coll? 0 v1))
(time (x-in-coll2? 0 v1))

(time (x-in-coll? 0 v1))
"Elapsed time: 176.250871 msecs"
=> false
(time (x-in-coll2? 0 v1))
"Elapsed time: 559.220763 msecs"
=> nil

четверг, 23 февраля 2017 г.

netstat analog for mac os


lsof -iUDP

lsof -i4 -sTCP:LISTEN

lsof -i6 -sTCP:LISTEN

вторник, 21 февраля 2017 г.

clojure.spec for any namespace on the fly

(create-ns 'com.mycompany)
(alias 'a 'com.mycompany)
(s/def ::a/keyword keyword?)
(s/exercise ::a/keyword)

четверг, 9 февраля 2017 г.

java Date to LocalDate

(defn inst2local-date
  "convert java.util.Date to java.time.LocalDate"  [^Date date]
  (-> date .toInstant (.atZone (ZoneId/systemDefault)) .toLocalDate))

четверг, 2 февраля 2017 г.

Joda to LocalDate in Clojure

(def joda-local-date (t/local-date-time year month day))
(-> joda-local-date tc/to-long Instant/ofEpochMilli (.atZone (ZoneId/systemDefault)) .toLocalDate)

воскресенье, 29 января 2017 г.

clojurescript + reagent = track mouse example

Here is pretty simple example of mouse tracking app

(ns hello-world-cljs.core
  (:require [reagent.core :as r]))

(println "Hello App is started!")
(defonce app-state (r/atom {:mouse-trac? false :pointer {:x 0 :y 0}}))
(defn mouse-coords [e] {:x (.-pageX e) :y (.-pageY e)})
(defn mouse-handler [e] (swap! app-state assoc-in [:pointer] (mouse-coords e)))
(defn start-mouse-listen [handler] (js/document.addEventListener "mousemove" handler))
(defn stop-mouse-listen [handler] (js/document.removeEventListener "mousemove" handler))

(defn trac-mouse-button []
  [:div   [:button {:type "button" :class "btn btn-default" :on-click (fn [e] (swap! app-state update-in [:mouse-trac?] not))}
    "Mouse track?"]])

(defn trac-button-state-label [] [:div [:label (str (:mouse-trac? @app-state))]])

(defn mouse-coords-label [] [:div [:label (str "Pointer moved to: " (str (:pointer @app-state)))]])

(defn mouse-changer
  "this fn called every time when state of :mouse-trac? is changed"  []
  [:div   (if (:mouse-trac? @app-state)
     (start-mouse-listen mouse-handler)
     (stop-mouse-listen mouse-handler))])

(defn app-hello [] [:div [trac-mouse-button] [trac-button-state-label] [mouse-coords-label] [mouse-changer]])

(r/render-component [app-hello]
                    (.getElementById js/document "app"))

Here is result: