Record of experiments, readings, links, videos and other things that I find on the long road.
Registro de experimentos, lecturas, links, vídeos y otras cosas que voy encontrando en el largo camino.
Saturday, April 30, 2016
Kata: Mars Rover in Java (recorded)
This is the recording of what I've done so far (all the possible movements of the rover): If you decide to watch it, please do it at 2x speed (I still write slowly).
These are the commits after each green and every refactoring step.
You can see the code in the using_java_enum branch of this GitHub repository.
Friday, April 29, 2016
Interesting Paper: "Public versus Published Interfaces"
Books I read (January - April 2016)
- El Eternauta, Héctor Germán Oesterheld and Francisco Solano López
- Barcelona. Los vagabundos de la chatarra, Jorge Carrión and Sagar Fornies
- Rip Van Winkle, Washington Irving
- La guerra interminable (The Forever War), Joe Haldeman
- Maintanable JavaScript, Nicholas C. Zakas
- Ve y pon un centinela (Go Set a Watchman), Harper Lee
- El nombre del viento (The Name of the Wind), Patrick Rothfuss
- You Don't Know JS: Async & Performance, Kyle Simpson
- Sapiens: A Brief History of Humankind, Yuval Noah Harari
- The Principles of Object Oriented JavaScript, Nicholas C. Zakas
February
- The Leprechauns of Software Engineering, Laurent Bossavit
- The Wise Man's Fear, Patrick Rothfuss
- Fundamentals of Object-Oriented Design in UML, Meilir Page-Jones
- Old Man's War, John Scalzi
- Cevdet Bey e hijos (Cevdet Bey ve Oğulları), Orhan Pamuk
- Ringworld, Larry Niven
- Why Nations Fail: The Origins of Power, Prosperity, and Poverty, Daron Acemoglu and James A. Robinson
March
- The Grumpy Programmer's Guide To Building Testable PHP Applications, Chris Hartjes
- The Grapes of Wrath, John Steinbeck
- The Coding Dojo Handbook, Emily Bache
- Fahrenheit 451, Ray Bradbury
April
- Implementation Patterns, Kent Beck (3rd time)
- The Power of Habit: Why We Do What We Do in Life and Business, Charles Duhigg
- How to Win Friends and Influence People, Dale Carnegie
- Understanding the Four Rules of Simple Design, Corey Haines (2nd time)
- La llamada de Cthulhu (The Call of Cthulhu), El horror de Dunwich (The Dunwich Horror) and Las ratas en las paredes (The Rats in the Walls), H. P. Lovecraft
- The Tales of Beedle the Bard, J. K. Rowling
Thursday, April 28, 2016
Interesting Talk: "Good Enough is the enemy of the Good"
Tuesday, April 26, 2016
Improving test failures feedback by extending IPrintWithWriter protocol in ClojureScript
(ns math-ops.operations | |
(:require | |
[math-ops.game-levels :as levels])) | |
(defrecord Operation [op1 operator op2 res]) | |
(defn- hide-sth [operation] | |
(assoc operation (rand-nth [:op1 :op2 :res]) :?)) | |
(defn- random-0-9 [] | |
(rand-int 10)) | |
(defn invertible? [level {:keys [op1 operator]}] | |
(and (levels/allowed-operator? level operator) | |
(not (and (= operator *) (= op1 0))))) | |
(defn- invert [level operation] | |
(if (invertible? level operation) | |
(->Operation (:res operation) | |
(levels/inverse-operator level (:operator operation)) | |
(:op1 operation) | |
(:op2 operation)) | |
operation)) | |
(defn- invert-may-be [level operation] | |
(if (rand-nth [true false]) | |
(invert level operation) | |
operation)) | |
(defn make [level] | |
(let [op1 (random-0-9) | |
op2 (random-0-9) | |
operator (levels/random-operator level) | |
res (operator op1 op2)] | |
(->> (->Operation op1 operator op2 res) | |
(invert-may-be level) | |
(hide-sth)))) | |
(defn- find-unknown [operation] | |
(ffirst (filter #(= :? (second %)) operation))) | |
(defn- substitute-unknown [operation guess] | |
(assoc operation (find-unknown operation) guess)) | |
(defn- correct? [{:keys [res op1 op2 operator]}] | |
(= res (operator op1 op2))) | |
(defn correct-guess? [operation guess] | |
(correct? (substitute-unknown operation guess))) |
$ lein doo node unit-tests | |
Building ... | |
Analyzing jar:file:/home/trikitrok/.m2/repository/org/clojure/clojurescript/1.7.170/clojurescript-1.7.170.jar!/cljs/core.cljs | |
... done. Elapsed 2.32174747 seconds | |
;; ====================================================================== | |
;; Testing with Node: | |
Testing math-ops.operations-test | |
FAIL in (operations-tests) (math_ops/operations_test.js:130:4) | |
expected: (= some-non-invertible-operation (invert :max-level some-non-invertible-operation)) | |
actual: (not (= #math-ops.operations.Operation{:op1 1, :operator #object[cljs$core$_STAR_ "function cljs$core$_STAR_(var_args){ | |
var args6895 = []; | |
var len__5318__auto___6901 = arguments.length; | |
var i__5319__auto___6902 = (0); | |
while(true){ | |
if((i__5319__auto___6902 < len__5318__auto___6901)){ | |
args6895.push((arguments[i__5319__auto___6902])); | |
var G__6903 = (i__5319__auto___6902 + (1)); | |
i__5319__auto___6902 = G__6903; | |
continue; | |
} else { | |
} | |
break; | |
} | |
var G__6900 = args6895.length; | |
switch (G__6900) { | |
case 0: | |
return cljs.core._STAR_.cljs$core$IFn$_invoke$arity$0(); | |
break; | |
case 1: | |
return cljs.core._STAR_.cljs$core$IFn$_invoke$arity$1((arguments[(0)])); | |
break; | |
case 2: | |
return cljs.core._STAR_.cljs$core$IFn$_invoke$arity$2((arguments[(0)]),(arguments[(1)])); | |
break; | |
default: | |
var argseq__5337__auto__ = (new cljs.core.IndexedSeq(args6895.slice((2)),(0))); | |
return cljs.core._STAR_.cljs$core$IFn$_invoke$arity$variadic((arguments[(0)]),(arguments[(1)]),argseq__5337__auto__); | |
} | |
}"], :op2 0, :res 0} #math-ops.operations.Operation{:op1 0, :operator #object[cljs$core$_SLASH_ "function cljs$core$_SLASH_(var_args){ | |
var args6905 = []; | |
var len__5318__auto___6911 = arguments.length; | |
var i__5319__auto___6912 = (0); | |
while(true){ | |
if((i__5319__auto___6912 < len__5318__auto___6911)){ | |
args6905.push((arguments[i__5319__auto___6912])); | |
var G__6913 = (i__5319__auto___6912 + (1)); | |
i__5319__auto___6912 = G__6913; | |
continue; | |
} else { | |
} | |
break; | |
} | |
var G__6910 = args6905.length; | |
switch (G__6910) { | |
case 1: | |
return cljs.core._SLASH_.cljs$core$IFn$_invoke$arity$1((arguments[(0)])); | |
break; | |
case 2: | |
return cljs.core._SLASH_.cljs$core$IFn$_invoke$arity$2((arguments[(0)]),(arguments[(1)])); | |
break; | |
default: | |
var argseq__5337__auto__ = (new cljs.core.IndexedSeq(args6905.slice((2)),(0))); | |
return cljs.core._SLASH_.cljs$core$IFn$_invoke$arity$variadic((arguments[(0)]),(arguments[(1)]),argseq__5337__auto__); | |
} | |
}"], :op2 1, :res 0})) | |
Testing math-ops.operations-guessing-test | |
Ran 2 tests containing 18 assertions. | |
1 failures, 0 errors. | |
Watching paths: /home/trikitrok/Clojure/0_MiClojure/math-ops/src/cljs, /home/trikitrok/Clojure/0_MiClojure/math-ops/test |
So we decided to extend the IPrintWithWriter protocol to improve the test failures feedback.
First, we tried to do it in the record declaration as we were doing for the Object protocol:
(defrecord Operation [op1 operator op2 res] | |
Object | |
(toString [this] (operation->string this)) | |
IPrintWithWriter | |
(-pr-writer [this writer _] | |
(-write writer (operation->string this)))) |
We were also getting this warning:
WARNING: Protocol IPrintWithWriter implemented multiple times at line 17 /home/trikitrok/Clojure/0_MiClojure/operations.cljs |
(ns math-ops.operations | |
(:require | |
[math-ops.game-levels :as levels] | |
[clojure.string :as string])) | |
(def symbols-description | |
{+ "+" | |
- "-" | |
* "x" | |
/ "/" | |
:? "?"}) | |
(defn- operation->string [{:keys [op1 operator op2 res]}] | |
(string/join " " ["#Operation [" op1 (symbols-description operator) op2 "=" res "]"])) | |
(defrecord Operation [op1 operator op2 res] | |
Object | |
(toString [this] (operation->string this))) | |
(extend-protocol IPrintWithWriter | |
Operation | |
(-pr-writer [this writer _] | |
(-write writer (operation->string this)))) | |
... | |
... |
$ lein doo node unit-tests | |
Building ... | |
Analyzing jar:file:/home/trikitrok/.m2/repository/org/clojure/clojurescript/1.7.170/clojurescript-1.7.170.jar!/cljs/core.cljs | |
... done. Elapsed 2.309172112 seconds | |
;; ====================================================================== | |
;; Testing with Node: | |
Testing math-ops.operations-test | |
FAIL in (operations-tests) (math_ops/operations_test.js:130:4) | |
expected: (= some-non-invertible-operation (invert :max-level some-non-invertible-operation)) | |
actual: (not (= #Operation [ 1 x 0 = 0 ] #Operation [ 0 / 1 = 0 ])) | |
Testing math-ops.operations-guessing-test | |
Ran 2 tests containing 18 assertions. | |
1 failures, 0 errors. | |
Watching paths: /home/trikitrok/Clojure/0_MiClojure/math-ops/src/cljs, /home/trikitrok/Clojure/0_MiClojure/math-ops/test |
... | |
... | |
(extend-type Operation | |
IPrintWithWriter | |
(-pr-writer [this writer _] | |
(-write writer (operation->string this)))) | |
... | |
... |
So we decided to ask it on the Clojurians slack.
There thanks to Alejandro Gómez and Nicolás Berger we understood what was happening. As Nicolás said:
That's the reason why it wasn't working and why we were getting that warning.
Check the code Nicolas is talking about in ClojureScript repository.
In the end, it was a nice problem to have, because we not only got a nicer and more useful failure feedback which was our initial goal, but also, learned more about extending core protocols in ClojureScript.
PS: Thank you very much to Alejandro Gómez and Nicolás Berger for their help.
Monday, April 25, 2016
Interesting Talk: "Declarative Thinking, Declarative Practice, Kevlin"
Friday, April 22, 2016
Running cljs.test tests on node.js using doo and karma
This is what we did to make it work.
First we installed Node.js, and then, Karma and its CLI following these instructions.
Once we had that, we added the doo plugin to the project.clj.
... | |
... | |
:plugins [[lein-cljsbuild "1.1.1"] | |
[lein-doo "0.1.6"]] | |
... | |
... |
Next, we created a new namespace for the unit tests in the tests directory, and set up the test build in project.clj (we called the new namespace unit-tests):
... | |
... | |
:cljsbuild {:builds [... | |
{:id "unit-tests" | |
:source-paths ["src/cljs" "test"] | |
:compiler {:output-to "out/unit_tests.js" | |
:main 'math-ops.unit-tests | |
:optimizations :none}}]} | |
... | |
... |
:main 'math-ops.unit-tests
In that namespace, we used doo to run the tests and register the namespaces containing unit tests:
(ns math-ops.unit-tests | |
(:require | |
[doo.runner :refer-macros [doo-tests]] | |
[math-ops.operations-test] | |
[math-ops.operations-guessing-test])) | |
(doo-tests | |
'math-ops.operations-test | |
'math-ops.operations-guessing-test) |
Finally, to run the tests on Node.js we added it as a target to the build setup in project.clj:
... | |
... | |
:cljsbuild {:builds [... | |
... | |
{:id "unit-tests" | |
:source-paths ["src/cljs" "test"] | |
:compiler {:output-to "out/unit_tests.js" | |
:main 'math-ops.unit-tests | |
:target :nodejs | |
:optimizations :none}}]} | |
... | |
... |
:target :nodejs
to the compiler options.
Then we could run our tests writing
lein doo node unit-tests
on the console:
trikitrok@trikitrok:/home/trikitrok/Clojure/0_MiClojure/math-ops$ lein doo node unit-tests | |
Building ... | |
Analyzing jar:file:/home/trikitrok/.m2/repository/org/clojure/clojurescript/1.7.170/clojurescript-1.7.170.jar!/cljs/core.cljs | |
... done. Elapsed 2.950936503 seconds | |
;; ====================================================================== | |
;; Testing with Node: | |
Testing math-ops.operations-test | |
Testing math-ops.operations-guessing-test | |
Ran 2 tests containing 18 assertions. | |
0 failures, 0 errors. | |
Watching paths: /home/trikitrok/Clojure/0_MiClojure/math-ops/src/cljs, /home/trikitrok/Clojure/0_MiClojure/math-ops/test |
:source-paths ["src/cljs" "test"]
, so any change to a file in them, makes the unit test run again.
You can check the code in this repository.
References:
Wednesday, April 13, 2016
Kata: FizzBuzz in Clojure using cond->
(ns fizz-buzz-bang.core) | |
(defn- divisible-by? [divisor number] | |
(zero? (mod number divisor))) | |
(defn say [n] | |
(cond-> nil | |
(divisible-by? 3 n) (str "Fizz") | |
(divisible-by? 5 n) (str "Buzz") | |
(divisible-by? 7 n) (str "Bang") | |
:always (or (str n)))) |
Interesting Podcast: "Dave Thomas on Innovating Legacy Systems"
Tuesday, April 12, 2016
The joy of a live interactive spiking and stabilization flow in ClojureScript using re-frame and figwheel
Francesc and I are working on a pet project to learn a bit of ClojureScript.
We have a very small amount of time to work on it during the week (two hours and a half at most), so we try to make that remote pairing time as productive and fun as possible.
figwheel and the re-frame framework are helping us a lot to achieve that. On one hand, the re-frame framework makes it beautifully simple to work with state and events. On the other hand, figwheel allows a Live interactive programming experience.
We work remotely in small spikes in which we usually start with a hardcoded view and then give it behavior step by step while, thanks to figwheel, we can instantly see the resulting view on the browser and interact with it.
Live interactive programming with figwheel is not only a lot of fun but it also gives you a very quick feedback on what you’re doing.
Once these spikes are working we extract all the new behavior that we consider non-view related from the view and put it in a separated name space. This makes our views as dumb as possible (à la Passive View pattern).
Then we stabilize and document the extracted behavior writing tests for it. This protects us against regressions in the code and makes us reflect on what we did during the spike. Many times we had found better names or different ways to arrange the code during this stabilization phase.
This flow of live interactive spiking with figwheel and the ClojureScript REPL, and then stabilization through dumbing views and testing the extracted logic is making us enjoy GUI programming a lot.