GithubHelp home page GithubHelp logo

karlmikko / debux Goto Github PK

View Code? Open in Web Editor NEW

This project forked from philoskim/debux

0.0 2.0 0.0 162 KB

A simple debugging library for Clojure and ClojureScript.

License: Eclipse Public License 1.0

Clojure 98.82% HTML 1.18%

debux's Introduction

debux

Debux is a simple but useful library for debugging Clojure and ClojureScript. I wrote this library to debug my own Clojure(Script) code and to analyze other developer’s Clojure(Script) code.

1. Prerequisites

  • clojure 1.7.0 or later

  • clojurescript 0.0-3308 or later

2. Installation

To include debux in your project, simply add the following to your project.clj dependencies:

[philoskim/debux "0.2.0"]

3. How to use

In Clojure, the following line has to be included in your file to use debux library.

(use 'debux.core)

In ClojureScript, the following (:require …​) line has to be included in your file to use debux library.

(ns example.core
  (:require [debux.cs.core :refer-macros [clog dbg]]))

4. New features added in version 0.2.0

4.1. Debugging thread macro -> or ->>

When debugging the thread-first macro -> or thread-last macro ->>, debux prints every expression in the thread macros.

This is an example of thread-first macro ->.

(dbg (-> "a b c d"
         .toUpperCase
         (.replace "A" "X")
         (.split " ")
         first))
;=> "X"
REPL output:
dbg: (-> "a b c d" .toUpperCase (.replace "A" "X") (.split " ") first)
  "a b c d" =>
    "a b c d"
  .toUpperCase =>
    "A B C D"
  (.replace "A" "X") =>
    "X B C D"
  (.split " ") =>
    ["X", "B", "C", "D"]
  first =>
    "X"
=>
  "X"

Another example.

(def person
  {:name "Mark Volkmann"
   :address {:street "644 Glen Summit"
             :city "St. Charles"
             :state "Missouri"
             :zip 63304}
   :employer {:name "Object Computing, Inc."
              :address {:street "12140 Woodcrest Dr."
                        :city "Creve Coeur"
                        :state "Missouri"
                        :zip 63141}}})

(dbg (-> person :employer :address :city))
; => "Creve Coeur"
REPL output:
dbg: (-> person :employer :address :city)
  person =>
    {:name "Mark Volkmann",
     :address
     {:street "644 Glen Summit",
      :city "St. Charles",
      :state "Missouri",
      :zip 63304},
     :employer
     {:name "Object Computing, Inc.",
      :address
      {:street "12140 Woodcrest Dr.",
       :city "Creve Coeur",
       :state "Missouri",
       :zip 63141}}}
  :employer =>
    {:name "Object Computing, Inc.",
     :address
     {:street "12140 Woodcrest Dr.",
      :city "Creve Coeur",
      :state "Missouri",
      :zip 63141}}
  :address =>
    {:street "12140 Woodcrest Dr.",
     :city "Creve Coeur",
     :state "Missouri",
     :zip 63141}
  :city =>
    "Creve Coeur"
=>
  "Creve Coeur"

This is an example of thread-last macro ->>.

(def c 5)

(dbg (->> c (+ 3) (/ 2) (- 1)))
; => 3/4
REPL output:
dbg: (->> c (+ 3) (/ 2) (- 1))
  c =>
    5
  (+ 3) =>
    8
  (/ 2) =>
    1/4
  (- 1) =>
    3/4
=>
  3/4

If you want to debug one of the expressions in the thread macro -> or ->>, don’t do it like this. You will have some exception.

(-> {:a [1 2]}
    (dbg (get :a))
    (conj 3))
; => java.lang.IllegalArgumentException
;    Don't know how to create ISeq from: java.lang.Long

Instead, do it like this.

(-> {:a [1 2]}
    (get :a)
    dbg
    (conj 3))
; => [1 2 3]
REPL output:
dbg: (get {:a [1 2]} :a) =>
  [1 2]

Another example.

(->> [-1 0 1 2]
     (filter pos?)
     (map inc)
     dbg
     (map str))
; => ("2" "3")
REPL output:
dbg: (map inc (filter pos? [-1 0 1 2])) =>
  (2 3)

4.2. Debugging let or comp form

When debugging let form,

(dbg (let [c (+ 1 2)
           [d e] [5 6]]
       (-> (+ d e) (- c))))
; => 8

each binding will be printed.

REPL output:
dbg: (let [c (+ 1 2) [d e] [5 6]] ...)
  c =>
    3
  [d e] =>
    [5 6]
=>
  8

When debugging comp form,

(def c (dbg (comp inc inc +)))

(c 10 20)
; => 32

the result of each function will be printed.

REPL output:
dbg: (comp inc inc +)
  + =>
    30
  inc =>
    31
  inc =>
    32
=>
  32

5. Usage in Clojure

In the first place, the following line has to be included in your file to use debux library in Clojure.

(use 'debux.core)

5.1. Basic usage

This is a simple example. The macro dbg prints an original form and pretty-prints the evaluated value on the REPL window. Then it returns the value without interfering with the code execution.

If you wrap the code with dbg like this,

(* 2 (dbg (+ 10 20)))
; => 60

the following will be printed in the REPL window.

REPL output:
dbg: (+ 10 20) =>
  30

5.1.1. Nested dbg

The dbg macro can be nested.

(dbg (* 2 (dbg (+ 10 20))))
; => 60
REPL output:
dbg: (+ 10 20) =>
  30

dbg: (* 2 (dbg (+ 10 20))) =>
  60

5.1.2. Debugging several forms

Sometimes you need to see several forms evaluated. To do so, a literal vector form can be used like this.

(let [a (take 5 (range))
      {:keys [b c d] :or {d 10 b 20 c 30}} {:c 50 :d 100}
      [e f g & h] ["a" "b" "c" "d" "e"]]
  (dbg [a b c d e f g h]))
; => [(0 1 2 3 4) 20 50 100 "a" "b" "c" ("d" "e")]
REPL output:
dbg: [a b c d e f g h] =>
  {:a (0 1 2 3 4),
   :b 20,
   :c 50,
   :d 100,
   :e "a",
   :f "b",
   :g "c",
   :h ("d" "e")}

Notice that the printed value is a map, not a vector and the form is prepended with colon to differenciate the form from the evaluated value.

Further examples:

(def a 10)
(def b 20)

(dbg [a b [a b] :c])
; => [10 20 [10 20] :c]
REPL output:
dbg: [a b [a b] :c] =>
  {:a 10, :b 20, :[a b] [10 20], ::c :c}

5.2. Various options

The various options can be added and combinated in any order after the form.

5.2.1. String option

You can add your own message in a string and it will be printed betwen less-than and more-than sign like this.

(dbg (repeat 5 (dbg (repeat 5 "x")
                    "inner repeat"))
     "outer repeat")
; => (("x" "x" "x" "x" "x")
;     ("x" "x" "x" "x" "x")
;     ("x" "x" "x" "x" "x")
;     ("x" "x" "x" "x" "x")
;     ("x" "x" "x" "x" "x"))
REPL output:
dbg: (repeat 5 "x")   <inner repeat> =>
  ("x" "x" "x" "x" "x")

dbg: (repeat 5 (dbg (repeat 5 "x") "inner repeat"))   <outer repeat> =>
  (("x" "x" "x" "x" "x")
   ("x" "x" "x" "x" "x")
   ("x" "x" "x" "x" "x")
   ("x" "x" "x" "x" "x")
   ("x" "x" "x" "x" "x"))

5.2.2. Number option

If you don’t specify the number after the form, debux will print only 100 items in each collection by default.

(dbg (range 200))
; => (0 1 2 ... 199)
REPL output:
dbg: (range 200) =>
  (0 1 2 3 4
   ......
   98 99 ...)

So, if you want to print more than 100 items, specify the number option explicitly.

(dbg (range 200) 200)
; => (0 1 2 ... 199)
REPL output:
dbg: (range 200) =>
  (0 1 2 3 4
   ......
   198 199)

Especially in the case of evaluating an infinite lazy-seq, you have to specify the number of the elements to print, to avoid the OutOfMemoryError.

(dbg (range) 5)
; => (0 1 2 3 4)
REPL output:
dbg: (range) =>
  (0 1 2 3 4)

If you omit the number in evaluating an infinite lazy-seq, it will print default 100 elements but cannnot avoid OutOfMemoryError.

(dbg (range))
; => Unhandled java.lang.OutOfMemoryError   Java heap space
REPL output:
dbg: (range) =>
  (0 1 2 3 4 5 6 7 8 9
   ......
   98 99 ...)

So Be careful! You have to limit the number of realized infinite lazy-seq explicitly by the number option.

5.2.3. :if expression option

You can set :if expression like this.

(for [i (range 10)]
  (dbg i :if (even? i)))
; => (0 1 2 3 4 5 6 7 8 9)
REPL output:
dbg: i =>
  0

dbg: i =>
  2

dbg: i =>
  4

dbg: i =>
  6

dbg: i =>
  8

6. Usage in ClojureScript on Browser Console

You can use dbg or clog macro in REPL window like weasel in ClojureScript. However, you should use clog instead of dbg, because clog macro uses the console.log fuction of browser’s developer tools to style the form. The evaluated result of dbg macro will go to the REPL window, and that of clog macro will go to the browser’s console.

The following (:require …​) line has to be included in your file to use debux library in ClojureScript.

(ns example.core
  (:require [debux.cs.core :as d :refer-macros [clog dbg break]]))


(clog (repeat 5 (clog (repeat 5 "x")
                      "inner repeat"))
      "outer repeat")
clog 1

Besides 'Usage in Clojure' features, you can use additional options in clog macro.

6.1. CSS Styling

6.1.1. Predefined style keywords

You can style the form, using the following predefined keywords.

keyword abbreviation

:style

:s

:error

:e

:warn

:w

:info

:i

:debug

:d

(clog (+ 10 20) :style :error "error style")
(clog (+ 10 20) :style :warn "warn style")
(clog (+ 10 20) :style :info "info style")
(clog (+ 10 20) :style :debug "debug style")
(clog (+ 10 20) "debug style is default")
clog 2

6.1.2. User-defined style

You can redefine the predefined styles or define your own new style like this.

(d/merge-style {:warn "background: #9400D3; color: white"
                :love "background: #FF1493; color: white"})

(clog (+ 10 20) :style :warn "warn style changed")
(clog (+ 10 20) :style :love "love style")

;; You can style the form directly in string format in any way you want.
(clog (+ 10 20) :style "color:orange; background:blue; font-size: 14pt")
clog 3

6.2. :once option

If you add :once (or :o in brief) option after the form, the same evaluated value will not be printed. This is a very useful feature, when you are debugging a game programming, where successive multiple frames usually have the same evaluated value.

(def a (atom 10))

;; This will be printed.
(clog @a :once)

;; This will not be printed,
;; because the evaluated value is the same as before.
(clog @a :once)


(reset! a 20)

;; This will be printed,
;; because the evaluated value is not the same as before.
(clog @a :once)

;; This will not be printed,
;; because the evaluated value is the same as before.
(clog @a :once)
clog 4

6.3. :js option

If :js option is added after the form, the JavaScript object will be printed as well, so you can inspect the internal structures of ClojureScript data types.

(clog {:a 10 :b 20} :js)
clog 5

7. Usage in ClojureScript on Browser REPL

You can use both dbg and clog macros on the browser REPL like weasel. The following is an example about runing the browser REPL weasel.

;; project.clj
(defproject example "0.2.0-SNAPSHOT"
  :dependencies [[org.clojure/clojure "1.8.0"]
                 [org.clojure/clojurescript "1.7.228"]
                 [com.cemerick/piggieback "0.2.1"]
                 [weasel "0.7.0"]
                 [philoskim/debux "0.2.0"]]
  :plugins [[lein-cljsbuild "1.0.5"]
            [lein-figwheel "0.3.7"]]
  :source-paths ["src/clj"]
  :clean-targets ^{:protect false}
                 ["resources/public/js/app.js"
                  "resources/public/js/app.js.map"]
  :repl-options {:nrepl-middleware [cemerick.piggieback/wrap-cljs-repl]}
  :cljsbuild {:builds [{:id "dev"
                        :source-paths ["src/cljs"]
                        :figwheel true
                        :compiler {:main "example.brepl"
                                   :asset-path "js/out"
                                   :output-to "resources/public/js/app.js"
                                   :output-dir "resources/public/js/out"
                                   :source-map true
                                   :optimizations :none} }]})
;; example/brepl.cljs
(ns example.brepl
  (:require [cljs.debux :refer-macros [dbg clog break]]
            [weasel.repl :as ws-repl] ))

(ws-repl/connect "ws://localhost:9001")

Aftr that, you have to evaluate the following forms in your REPL window to run the browser REPL weasel.

user> (require '[weasel.repl.websocket :as ws]
               '[cemerick.piggieback :as pback])
nil

user> (pback/cljs-repl (ws/repl-env :port 9001))
<< started Weasel server on ws://127.0.0.1:9001 >>
<< waiting for client to connect ...

Refresh your browser’s page, and then you will see the following in your REPL window.

<< waiting for client to connect ... connected! >>
To quit, type: :cljs/quit
cljs.user>

Now you can do anything in this REPL as in the Clojure REPL. When you evaluate dbg macro in your ClojureScript source code, the result will go to the REPL window and when you evaluate clog macro in your ClojureScript source code, the result will go to your browser’s console window.

8. License

Copyright © 2015—​2016 Young Tae Kim

Distributed under the Eclipse Public License either version 1.0 or any later version.

debux's People

Contributors

philoskim avatar

Watchers

 avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.