I'd like to mock out a macro in a namespace.
For instance, clojure.tools.logging/error.
I tried with-redefs with no luck
(def logged false)
(defmacro testerror
{:arglists '([message & more] [throwable message & more])}
[& args]
`(def logged true))
(deftest foo
...
(with-redefs
[log/error testerror]
...
That gave this error:
CompilerException java.lang.RuntimeException: Can't take value of a macro
Amalloy provided you the answer for your direct question on how to mock a macro - you cannot.
However, you can solve your problem with other solutions (simpler than moving your whole application to component dependency injection). Let me suggest two alternative implementations (unfortunately, not very straightforward but still simpler than using component).
Mock the function called by logging macro
You cannot mock a macro but you can mock a function that will be used when the logging macro get expanded.
(require '[clojure.tools.logging :as log])
(require '[clojure.pprint :refer [pprint]])
(pprint (macroexpand `(log/error (Exception. "Boom") "There was a failure")))
Gives:
(let*
[logger__739__auto__
(clojure.tools.logging.impl/get-logger
clojure.tools.logging/*logger-factory*
#object[clojure.lang.Namespace 0x2c50fafc "boot.user"])]
(if
(clojure.tools.logging.impl/enabled? logger__739__auto__ :error)
(clojure.core/let
[x__740__auto__ (java.lang.Exception. "Boom")]
(if
(clojure.core/instance? java.lang.Throwable x__740__auto__)
(clojure.tools.logging/log*
logger__739__auto__
:error
x__740__auto__
(clojure.core/print-str "There was a failure"))
(clojure.tools.logging/log*
logger__739__auto__
:error
nil
(clojure.core/print-str x__740__auto__ "There was a failure"))))))
As you can see, the function that does actual logging (if a given level is enabled) is done with clojure.tools.logging/log* function.
We can mock it and write our test:
(require '[clojure.test :refer :all])
(def log-messages (atom []))
(defn log*-mock [logger level throwable message]
(swap! log-messages conj {:logger logger :level level :throwable throwable :message message}))
(with-redefs [clojure.tools.logging/log* log*-mock]
(let [ex (Exception. "Boom")]
(log/error ex "There was a failure")
(let [logged (first #log-messages)]
(is (= :error (:level logged)))
(is (= "There was a failure!" (:message logged)))
(is (= ex (:throwable logged))))))
Use your logging library API to collect and inspect log messages
Your logging library API might provide features that would allow you to plug into in your test to collect and assert logging events. For example with java.util.logging you can write your own implementation of Handler that would collect all logged log records and add it to a specific (or root) logger.
You cannot do this. The point of macros is that they are expanded when the code is compiled, and after that they are gone. The original code that included a call to the macro is unrecoverable. You cannot retroactively redefine a macro at runtime: you're too late already.
An alternative approach, if you want to have swappable logging implementations, would be to use something like Component for dependency injection, and use a different logging component depending on whether you are running tests or running your real program. Arguably that's a bit heavy-handed, and maybe there is a simpler approach, but I don't know it.
Related
I'm currently in the start of making a tool that should modify all the functions in another namespace, and the run the "main" function in that (other) namespace. Almost like mocking in unit-testing, but for another purpose. The first step to this is to redefine functions from another namespace. The first listing shows code that works, where I explicitly name the function to be replaced.
(ns one)
(defn a-fun []
(println "original"))
(defn caller []
(a-fun))
(ns two)
(with-redefs [one/a-fun #(println "replaced")]
(one/caller))
;; replaced
;; nil
The next step is getting the functions programmatically from the other namespace. This is where I have run into a wall. I've experimented much with ns-publics, but so far no luck. So I try to break the problem down. And the problem is that the replacement of the function is not working. The output should print "replaced" not "original".
(ns two)
(def target (ns-resolve 'one (symbol "a-fun")))
(def caller (ns-resolve 'one (symbol "caller")))
(with-redefs [target #(println "replaced")]
(caller))
;; original
;; nil
Now, the #'two/target shows #'one/a-fun when I evaluate it in the repl. I can call one/a-fun by entering ((ns-resolve 'one (symbol "a-fun"))) in the repl, so I don't understand what part is not working.
I've been through a lot of documentation today, and I'm not really getting any closer.
You could try using with-redefs-fn like so:
(defn p [] "old")
(with-redefs-fn
{(ns-resolve *ns* 'p) #(println "new")}
(fn []
(p)))
;; => new
This does mean that the body of with-redefs-fn must be a function that uses your redefined Vars.
I'm writing a small debugging library and I would like to let users choose how to display data structures. I was imagining that users could require it in this kind of way:
(ns some-user-namespace
(:require
[clojure.pprint]
[my.library :with-args {print-fn clojure.pprint/pprint}]))
Is something like this possible, and if not, how can I solve this problem?
It's not possible to do it this way. If you really to offer this kind of setup, you could provide a configuration function to be called by the user after the import:
(ns some-namespace
(:require [my.library]))
(my.library/print-with! clojure.pprint/pprint)
Ending function name with ! is an idiomatic way of indicating that it's causing some side effects.
In your library it could look like:
(ns my.library)
(def config (atom {:printer println}))
(defn print-with! [new-printer]
(swap! config assoc :printer new-printer))
(defn my-lib-print [foo]
((:printer #config) foo))
EDIT: For a solution that does not require global, mutable state you can use dynamic bindings.
Lib:
(ns my.library)
(def ^:dynamic *printer* println)
(defn my-lib-print [foo]
(*printer* foo))
Usage:
(binding [my.library/*printer* clojure.pprint/pprint]
(my.library/my-lib-print {:hello "World"}))
These are the only two ways for some kind of external, contextual configuration I can think of. The only alternative is pure higher order function:
(defn my-lib-print [printer foo]
(printer foo))
I'm learning about Hystrix and Clojure and don't understand how to (properly) set a timeout on a Hystrix command in Clojure.
I searched StackOverflow and the web more generally. I looked at Hystrix's Clojure wrapper source code (https://github.com/Netflix/Hystrix/blob/master/hystrix-contrib/hystrix-clj/src/main/clojure/com/netflix/hystrix/core.clj). There is a init-fn function parameter that looked promising, but the comments seem to suggest that this won't be a sustainable solution. But would this be a simple start?
I have a ridiculously simple Hystrix command running in Clojure and would appreciate help in extending this to set, say, a 200ms timeout:
(ns hystrix-timeout.core
(:require [clojure.string :as str])
(:require [com.netflix.hystrix.core :as hystrix])
(:gen-class))
(defn my-primary [a]
(if (= a true) (throw (Exception. "Primary failed")) (str "primary: " a)))
(defn my-fallback [a]
(str "fallback: " a))
(hystrix/defcommand my-command
{:hystrix/fallback-fn my-fallback}
[a]
(my-primary a))
(defn -main
"Executes a simple Hystrix command. Will use a timeout when I know how to do this in Clojure."
[& args]
(println (my-command false))
(println (my-command true))
(System/exit 0) ; Exit explicitly as Hystrix threads are still running.
)
I've put my lein project up at https://github.com/oliverbaier/learning/tree/master/hystrix-timeout in case this makes answering easier.
Thanks a lot,
Oliver
The simplest route is to just use System properties. You can set either a global default:
(System/setProperty "hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds" "200")
or a command specific value:
(System/setProperty "hystrix.command.my-command.execution.isolation.thread.timeoutInMilliseconds" "200")
You can perform this in your -main method. If you are running a real web-app sans -main, you can add a ring init in project.clj :ring {:handler your-handler :init your-config-method} your-config-method will be invoked on startup.
I think this is a bug in clojure/tools.logging. I have the following db.clj file. What it does is unimportant. The important part is that for safety I have disabled the *read-eval*. I invoke db/start with no problems. However, if I uncomment the #_(log/info "Failed to bootstrap") form, it throws an EvalReader not allowed error. I've tried all sorts of combinations for the log/info call. If it's outside of the try block, it's fine. Inside the try block anywhere, whether it's in the body, catch, or finally, it raises this exception. However, when I wrap a try around log/info elsewhere, it's fine.
What gives?
(ns extenium.db
(:require [clojure.tools.logging :as log]
[clojure.java.io :as io])
(:import com.thinkaurelius.titan.core.TitanGraph
com.thinkaurelius.titan.core.TitanFactory))
(def ^:private
sentinel- (Object.))
(def ^:private
db- (atom nil))
...
(defn start [^String path]
(locking sentinel-
(log/info "Starting database at path" path)
(let [exists (.exists (io/file path))
^TitanGraph db_ (TitanFactory/open path)]
(if exists
(log/info "Path" path "exists")
(log/info "Path" path "does not exist"))
(log/info "Starting database engine")
(swap! db- (constantly db_))
(log/info "Started database engine")
(if (not exists)
(try
(bootstrap-)
(catch Throwable t
#_(log/info "Failed to bootstrap")
(stop)
(.delete (io/file path))
(throw t)))))
(log/info "Started database")
true))
EDIT: Trimmed down code per #alex-taggart. bootstrap- implementation not shown. I had originally included everything because this seemed like a context-specific bug and I felt it was safer to provide as much context as possible.
EDIT: Per #chouser, added how I'm disabling *read-eval*. This is the template that is generated by lein new app.
(defn -main
"The main entry point into Extenium."
[& args]
;; Prevent arbitrary eval injection
(alter-var-root #'*read-eval* (constantly false))
;; Initialize system settings from the command line and configuration file
(init!- args)
;; Start the service
(start!-))
It's not a bug, really. The clojure.tools.logging library is just an abstraction upon other Java logging facilities. To discover which one is available, it uses eval expression. You're welcome to check for your own: here is a quick search result and a certain file where it gets used.
In your case, I believe it's not necessary to care about the global read-eval. This is an internal feature and who knows what other libraries depend on it. If you validate the user input and prevent it from being evaluated, it's fine to leave the flag as is. I would say, SQL injections and XSS are the things you should be worried about first.
How to achieve Aspect-Oriented Programming in Clojure? Do we need AOP in Clojure?
Let's say we want plain vanilla Clojure solution (no AspectJ).
Aspect-Oriented Programming is typically used to add cross-cutting functionality to code that would otherwise get hopelessly intertwined with business logic. A great example is logging - you don't really want logging code scattered everywhere in your code base.
You don't really need AOP in Clojure because it's easy to achieve this with other techniques in Clojure.
For example, you can use higher-order functions to "wrap" other functions with cross cutting functionality:
; a simple function - the "business logic"
(defn my-calculation [a b]
(+ a b))
; higher order function that adds logging to any other function
(defn wrap-with-logging [func]
(fn [& args]
(let [result (apply func args)]
(println "Log result: " result)
result)))
; create a wrapped version of the original function with logging added
(def my-logged-calculation (wrap-with-logging my-calculation))
(my-logged-calculation 7 9)
=> Log result: 16
=> 16
AOP IMHO is just an artifact of certain kinds of static programming languages. AFAIKS it's usually just a bunch of non-standard compiler extensions. I've not yet seen any application of AOP that can't be solved better & natively in more dynamic languages. Clojure is certainly dynamic enough, and that's without even considering macros.
I may be wrong, but if so, I'd need to see an actual AOP use case that can't be implemented just as well in pure clojure.
Edit: just to be clear: I refuse to see things like elisp's advice as aspect oriented. In dynamic languages, those are just techniques to be used whenever you need them, with no need for language support other than rebinding of function definitions - which all lisps support anyway.
There's no need to treat them as special - you can easily define your own defadvice-like function in clojure. See for example, compojure's wrap! macro, which is actually deprecated since you generally don't even need it.
Aspect oriented programming is a great way to achieve seperation of concernes in Java. Clojure's composable abstractions achieve this very well. See this question also. The topic is covered really well in The Joy Of Clojure.
as for an example of Aspect Oriented Clojure by another name check out the Ring web framework
Well you could be more AOP w/ Clojure easily. Just use metadata in functions to informe when you want logs:
(defn ^:log my-calculation
[a b]
(+ a b))
Then you can redefine all functions, wrapping them w/ logging automatically. Part of this code (together w/ unwrap functions bellow):
(defn logfn
[f topic severity error-severity]
(fn [& args]
(try
(if severity
(let [r (apply f args)]
(log* topic {:args args, :ret r} severity)
r)
(apply f args))
(catch Exception e
(if error-severity
(let [data {:args args, :error (treat-error e), :severity error-severity}]
(log* topic data error-severity)
(throw e))
(throw e))))))
(defn logfn-ns
"Wrap function calls for logging on call or on error.
By default, do nothing. When any :log or :log-error, enables logging. If ^:log,
only log on error (default severity error).
Can customize log severity w/ e.g. ^{:log info} or on error log severity likewise."
[ns alias]
(doseq [s (keys (ns-interns ns))
:let [v (ns-resolve ns s)
f #v
log (-> v meta :log)
log-error (-> v meta :log-error)]
:when (and (ifn? f)
(-> v meta :macro not)
(-> v meta :logged not) ;; make it idempotent
(or log log-error))]
(let [log (if (= log true) nil log)
log-error (or log-error "error")
f-with-log (logfn f
(str alias "/" s)
log
log-error)]
(alter-meta! (intern ns s f-with-log)
(fn [x]
(-> x
(assoc :logged true)
(assoc :unlogged #v)))))))
(defn unlogfn-ns
"Reverts logfn-ns."
[ns]
(doseq [s (keys (ns-interns ns))
:let [v (ns-resolve ns s)]
:when (-> v meta :logged)]
(let [f-without-log (-> v meta :unlogged)]
(alter-meta! (intern ns s f-without-log)
(fn [x]
(-> x
(dissoc :logged)
(dissoc :unlogged)))))))
You just call (log/logfn-ns 'my.namespace "some alias") and all is wrapped w/ logging (and some).
PS: My custom logger above have a topic which is "some alias/function name"
PS2: Also wrapped w/ try/catch.
PS3: Didn't like this so much. Reverted to have explicit logging.