Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Kari Marttila
Kari Marttila

Posted on • Originally published atkarimarttila.fi on

     

Clojure Power Tools Part 2

IntelliJ IDEA and Cursive

Introduction

This is the second part of my Clojure Power Tools series (I’m a bit interested myself how many blog posts I will write to this series). If you haven’t read the first part I recommend you to read it first:Clojure Power Tools Part 1. In this second blog article, I list a couple of new power tool tricks for debugging. I first introduce a poor man’s debug repl, and then the real debug repl.

Poor Man’s Debug Repl

I was refactoring some Clojure tests for the second version of a complicated application. There were quite a few structural changes in the application logic, and the domain was rather complicated comprising a lot of various recursive data structures. In some tests, I was quite puzzled about what kind of state there was in several places of the application. The tests were also rather complicated and I wanted a simple way to record certain bindings in several places of the application when I was running a certain area of the test - and only that area. I quickly implemented a poor man’s debug recorder:

(nsrecorder)(defvalue(atom{}))(defcounter(atom0))(defflag(atomfalse))(defnnext-id[](swap!counterinc))(defnassoc-value-with-id![kv](let[id(next-id)new-k(keyword(str(namek)"-"id))](swap!valueassocnew-kv)))(defnadd-value-if-recording![kv](when@flag(assoc-value-with-id!kv)))(defnstart![](reset!value{})(reset!counter0)(reset!flagtrue))(defnstop![](reset!flagfalse))
Enter fullscreen modeExit fullscreen mode

The idea is to provide a way to add stuff to the value map with unique keys - but only when I’m recording.

(I’m using my own exercises as an example here, the data here is ridiculously simple - no need for a recorder here, but I guess you get my point if you change the data to several hundreds of lines of recursive maps and vectors and complex business logic…)

So, let’s add the start/stop recording to the test we are interested:

(deftestproduct-groups-test(log/debug"ENTER product-groups-test")(testing"GET: /api/product-groups"(let[_(re/start!)login-ret(ss-tc/-call-api:post"login"nil{:email"test-kari.karttinen@foo.com":password"Kari"})_(log/debug(str"Got login-ret: "login-ret))json-web-token(get-inlogin-ret[:body:json-web-token])params(-create-basic-authenticationjson-web-token)get-ret(ss-tc/-call-api:get"/product-groups"paramsnil)status(:statusget-ret)body(:bodyget-ret)right-body{:ret"ok",:product-groups{:1"Test-Books",:2"Test-Movies"}}_(re/stop!)](is(=(not(nil?json-web-token))true))(is(=status200))(is(=bodyright-body)))))
Enter fullscreen modeExit fullscreen mode

See the(re/start!) and(re/stop!) commands in the test - we are recording only during that time.

Then I can add stuff to my recorder where-ever I want:

(defn-valid-token?"Parses the token from the http authorization header and asks session ns to validate the token."[envreq](log/debug"ENTER -valid-token?")(let[basic(get-inreq[:headers"authorization"])_(re/add-value-if-recording!:valid-token-basicbasic)basic-str(andbasic(last(re-find#"^Basic (.*)$"basic)))...
Enter fullscreen modeExit fullscreen mode

I.e.(re/add-value-if-recording! :valid-token-basic basic).

Run the tests.

After the tests I can examine the recorder in my scratch file:

(require'[recorder])(keys@recorder/value)@recorder/value(require'[portal.api:asportal-api])(portal.api/clear)(portal.api/open)(portal.api/tap)(tap>@recorder/value)
Enter fullscreen modeExit fullscreen mode

You can also use the excellentportal visualization tool to examine your complicated data you recorded (well, in this example, not so complicated):

Panel

Real Debug-Repl

The example above was a simple trick but a real Clojurianstops the world if he is interested to see what is happening in a particular moment of time instead of just recording it. We are going to use Gary Fredericks’sdebug-repl library for it. You need the dependency - I have these tool dependencies in my~/.clojure/deps.edn file:

{:aliases{:kari{:extra-paths["scratch"]:extra-deps{hashp/hashp{:mvn/version"0.1.1"}com.gfredericks/debug-repl{:mvn/version"0.0.11"}djblue/portal{:mvn/version"0.6.1"}}}}}
Enter fullscreen modeExit fullscreen mode

Then we need to be able to start the backend REPL with this debug repl middleware, I have this in myJustfile as a Just recipe which I use to start my backend repl with various options:

# Start backend repl with my toolbox and with debug-repl capability.@backend-debug-kari:    clj -M:dev:test:common:backend:kari -m nrepl.cmdline -m com.gfredericks.debug-repl/wrap-debug-repl -i -C
Enter fullscreen modeExit fullscreen mode

Then you can add a debug-repl breakpoint in your code, run your code, stop the world and examine the snapshot context of your world just before the breakpoint. Let’s see this in action:

debug-repl

I have added_ (break! "Yihaa!") breakpoint in one of the tests. Then I run the test and the world stops at the breakpoint (Hijacking repl for breakpoint: Yihaa! output in the REPL output window). Then I have moved the cursor in various let-bindings:json-web-token,params andget-ret and evaluated the forms: you can see the evaluated values in the REPL output window on the right. If I try to evaluatestatus I get an error “Unable to resolve symbol: status in this context” - of course, because it’s outside the context.

Conclusions

There are a lot of other Clojure tricks and tools - maybe I’ll write “Clojure Power Tools Part 3” blog post in the future.

The writer is working at Metosin using Clojure in cloud projects. If you are interested to start a Clojure project in Finland or you are interested to get Clojure training in Finland you can contact me by sending email to my Metosin email address or contact me via LinkedIn.

Kari Marttila

Top comments(0)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

I’m a software architect and developer working at Metosin and using mostly Clojure and AWS.
  • Location
    Helsinki
  • Education
    M.Sc. (Software Engineering)
  • Work
    Programmer at Metosin
  • Joined

More fromKari Marttila

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp