Clojure: How to initialize a def from a file at startup? - clojure

I have the following clojure code to initialize my config structure Config.
I noticed that the file is actually read when compiling the file, not at runtime.
For me, the config structure Config should be immutable, however, I do not want to have the configuration inside the JAR file.
How should I do this? Do I have to use an atom? It is okay if the application crashes if my.config is missing.
(def Config
(read-string (slurp "my.config")))

When you don't want it at compile time you have to wrap it in a function.
(defn def-my-conf []
(def Conf (blub)))
But you the cleaner way would be:
(declare Config)
(defn load-Config []
(alter-var-root (var Config) (blub)))
This function should be called inside your main.
EDIT: Of course an atom is also a solution!

Write a function for reading your config:
(defn read-config
[]
(read-string
(slurp "my.config")))
Then you can call this function from -main, and either 1) pass the config on to any functions that will need it, or 2) store it in a dynamic variable and let them read it directly:
(def ^:dynamic *config* nil)
(defn some-function-using-config
[]
(println *config*))
(defn -main
[]
(binding [*config* (read-config)]
(some-function-using-config)))
Which of the two to choose is a matter of taste and situation. With direct passing you make it explicit that a function is receiving config, with the dynamic variable you avoid having to include config as an argument to every single function you write, most of whom will just pass it on.
Both of these solutions work well for unit tests, since you can just rebind the dynamic variable to whatever config you want to use for each test.

TheQuickBrownFox had the answer, the file is read both at run-time, and a compile-time.
Fine for me! That is actually really cool!

Related

Is it possible to create writeable bean from Clojure that I can manage from jconsole?

I was exploring Clojure java.jmx API Reference and trying the examples mentioned there e.g.
;; Can I serve my own beans? Sure, just drop a Clojure ref
;; into an instance of clojure.java.jmx.Bean, and the bean
;; will expose read-only attributes for every key/value pair
;; in the ref:
(jmx/register-mbean
(create-bean
(ref {:string-attribute "a-string"}))
"my.namespace:name=Value")
It works great, the bean's attribute value is visible in the console but it is read-only.
Is there a way to create a writeable bean(so that it is listed in "Operations" folder and manageable from console)?
It looks like the clojure.java.jmx code supports setAttribute.
(see (deftype Bean ...) in https://github.com/clojure/java.jmx/blob/master/src/main/clojure/clojure/java/jmx.clj
The easiest way seems to be just using an atom instead of a ref.
You could then have atom watchers to run some code if JMX changes it.
Maybe give that a try. I've forgotten most of JMX ;)
EDIT: Quickly tried it. It looks like the attribute is still read-only :(
I gotta look deeper. Nevertheless, the source code is pretty nice and hopefully easy to understand.
EDIT2:
The issue is with build-attribute-info which passes false to the writeable? flag in the `MBeanAttributeInfo.
You can monkey patch that:
(import 'java.jmx.MBeanAttributeInfo)
(require '[clojure.java.jmx :as jmx])
(defn build-my-attribute-info
"Construct an MBeanAttributeInfo. Normally called with a key/value pair from a Clojure map."
([attr-name attr-value]
(build-my-attribute-info
(name attr-name)
(.getName (class attr-value)) ;; you might have to map primitive types here
(name attr-name) true true false)) ;; false -> true compared to orig code
([name type desc readable? writable? is?] (println "Build info:" name type readable? writable?) (MBeanAttributeInfo. name type desc readable? writable? is? )))
;; the evil trick one should only use in tests, maybe
;; overwrite the original definition of build-attribute-info with yours
(with-redefs [jmx/build-attribute-info build-my-attribute-info]
(jmx/register-mbean (jmx/create-bean (atom {:foo "bar"})) "my.bean:name=Foo"))
;; write to it
(jmx/write! "my.bean:name=Foo" "foo" "hello world")
;; read it
(jmx/read "my.bean:name=Foo" "foo")
=> "hello world"
Now, unfortunately, Java Mission Control still can't change the value, but I'm not sure why. The MBean info is correct.
Must be a permission thing.

How do you make a var set!-able in the Leiningen user repl?

I've got a dynamic var in a namespace defined in a source file, like this:
(ns mystuff.log ...)
(def ^:dynamic *logging* #{})
I'd like to be able to set! this var from the REPL, so that code in that same source file can look at it. In this example, the mystuff.log/log macro looks at *logging* to decide whether to print a given expression. At the REPL, it would be convenient to (set! *logging* #{:whatever}), changing its value multiple times during the session.
How can I get Leiningen's REPL to allow this? By default, set!ing such a var produces an IllegalStateException because set! can't change the root binding of a var. The var must be thread-local to be changeable by set!.
Is there a way to tell Leiningen to wrap its REPL something like this, to create a thread-local binding for a var?
(binding [mystuff.log/*logging* mystuff.log/*logging*]
(the-leiningen-repl ...))
The :init option of :repl-options, briefly explained here, seems like it offers something close. Apparently, though, the REPL calls :init, which would make it too late to establish a thread-local binding for the expressions typed into the REPL.
You probably want alter-var-root, not set!. With no special set-up or modifications to the REPL, here's what you can do:
user> (def logging #{})
#'user/logging
user> (alter-var-root #'logging conj :my-new-logger)
#{:my-new-logger}
user> (alter-var-root #'logging conj :another-new-logger)
#{:my-new-logger :another-new-logger}
user> logging
#{:my-new-logger :another-new-logger}
#{:my-new-logger :another-new-logger}
set! modifies only a var's current thread binding. alter-var-root modifies the var's root binding: the value that's shared across all threads where it's not overridden by a per-thread binding.*
*By the way, that's why alter-var-root doesn't have an exclamation point. It follows the same convention as other forms that modify root bindings, like def.

a binding in a macro isn't resolved

I'm creating a library for an API server, here's a simpliefied version of I have:
(defonce ^:dynamic *my-token* nil)
(defmacro def-my-token
[token1 & body]
`(binding [*my-token* ~token1] ~#body))
And the main "post" method:
(defn my-post-request [url1]
(try
(let [res (client/post (str "api/url/base" url1)
{:body (json/write-str (:secret my-token)) ; my-token should come from the macro
;......
And here's how I want to use it:
(defn -main [& args]
(def-my-token "fdsfdsfdsfds"
; now "my-token" should be created and visible in "my-post-request", shouldn't it?
(print
(my-post-request "/some_end_point"))))
But it says "Unable to resolve symbol: my-token in this context"
I wonder why? doens't def-my-token, being a macros, define it? why not? And how to fix that?
UPDATE:
Also without (defonce ^:dynamic *token* nil) it doesn't work. Why not?
Why isn't defining the macro enough?
Answer to your UPDATE:
According to the documentation for binding, you can only override already existing vars. That's why your solution doesn't work without establishing a root binding to your dynamic var.
Sidenote:
I would recommend doing what jmargolisvt said and use a plain def instead of defonce, as I've never seen any dynamic var definition in the wild using defonce.
EDIT:
doens't def-my-token, being a macros, define it? why not? And how to fix that?
Macros by themselves don't define things, they are small programs transforming your source code in the macro-expansion step of most Lisp REPL's. It could define anything you want it to, but then you should've wrote the def special form. What you used instead was binding which deals with already existing vars.
You might get more insight by toying with it in the REPL and/or reading the answer of this stackoverflow answer.
If you need some further explanation why overriding is needed:
It's practical to conceptualize vars as stacks. The root binding that you establish with using def is the first layer. Everything in your program will see this value unless you put "something" over it. As you can imagine in your example having *my-token* seen as nil from your functions would cause issues.
binding to the resuce!
It allows you put anything "on top" of the root binding (in your case nil) thread-locally inside of the body it, like so:
you bound *my-token*, not my-token. Try:
{:body (json/write-str (:secret *my-token*))
The asterisks are just a naming convention for dynamic vars, they are still part of the actual var name.

Use function in macro

I have this function which load correctly my namespace :
(defn load-module [module-name]
(use module-name)
)
And my "equivalent" macro that doesn't work :
(defmacro load-module-macro [module-name]
`(
(use '~module-name)
)
)
I don't understand the problem.
Moreover, I want to use this macro for load a module choose in configuration. In my config.clj I define a var with the namespace of my logger module which contains "save-data" function. Then I want to load the specified logger in my core program. So I can choose the logger to use directly in my configuration file (logger on disk, logger in database...). Is it the best way to do that ?
EDIT :
Error message
IllegalArgumentException Don't know how to create ISeq from: java.lang.Character clojure.lang.RT.seqFrom (RT.java:505)
No, in fact you don't want to use "use" directly in code at all. Use modifies the entire namespace it is called in and that could break your code in ways that are hardly predictable.
Instead what you should do is:
Implement a logging interface (Protocol), write a "meta-constructor" that dispatches whatever you set in config.clj as keyword. Code example
(defprotocol ILog
(save-data [this msg] "Logs message in msg."))
(defn create-file-log
"Returns an object implementing ILog, opens and flushes java.io.File file."
[file]
(let [f ... ;; create file writer here
]
(reify ILog
(save-data [this msg] ;; Write code that writes data to file here
))))
;; create other implementations like database here or elsewhere
(defn create-log
"Creates a log of of the type passed in type-kw."
[type-kw]
(case type-kw
:file (create-file-log "./app-log.txt")
;; other types
))
Now you would simply invoke create-log with whatever keyword is set in your config file and pass the returned object around to functions that need to do logging. Obviously, you could also def it as a global object but I don't recommend to do that.
Eventually you don't just want to set a keyword (type-kw) for the desired logging method in your config, but also other parameters like the file-name or a database uri so that you can pass something like
{:log-method :file
:data {:fname "app-log.txt"}}
or
{:log-method :db
:data {:uri "....
...to your create-log function that uses this structure to get the parameters for the reify constructors create-file-log, create-db-log, etc.
EDIT:
Because you don't like the switch statement, here is how to do it with multi-methods:
(defmulti create-log :logging-method)
(defmethod create-log :file
[arg-map]
(let [file (java.io.File. (:fname arg-map))]
(if (.exists file)
...
Then you simply have an entry in your config.clj
{...
:log {:logging-method :file
:fname "./log-file.txt"}}
To create a new logging type all you have to do now is to imagine an argument map like the one above and a constructor method for create-log.
I think you can also use multimethods for this. It's clojure's other polymorphism strategy, and might work well for your use case as you're only looking to implement a single method (save-data).
;; set up a config map
(def config {:logger :db-logger)
;; set up the dispatch function to read the logger from the config map
(defmulti save-data (fn [] (:logger config))
;; define methods as required - database logging
(defmethod save-data :db-logger []
(println "Save to database"))
;; some other logging - can be in another file
(defmethod save-data :other-logger []
(println "Save to other thing"))
Note: I'm still quite new to Clojure - so I'm not sure if this is a 'proper' way to use multimethods. Most of the examples I've seen dispatch on the type of the arguments, not on a config setting. Any experts, please correct me if I've got the wrong idea.

Setting Clojure "constants" at runtime

I have a Clojure program that I build as a JAR file using Maven. Embedded in the JAR Manifest is a build-version number, including the build timestamp.
I can easily read this at runtime from the JAR Manifest using the following code:
(defn set-version
"Set the version variable to the build number."
[]
(def version
(-> (str "jar:" (-> my.ns.name (.getProtectionDomain)
(.getCodeSource)
(.getLocation))
"!/META-INF/MANIFEST.MF")
(URL.)
(.openStream)
(Manifest.)
(.. getMainAttributes)
(.getValue "Build-number"))))
but I've been told that it is bad karma to use def inside defn.
What is the Clojure-idiomatic way to set a constant at runtime? I obviously do not have the build-version information to embed in my code as a def, but I would like it set once (and for all) from the main function when the program starts. It should then be available as a def to the rest of the running code.
UPDATE: BTW, Clojure has to be one of the coolest languages I have come across in quite a while. Kudos to Rich Hickey!
I still think the cleanest way is to use alter-var-root in the main method of your application.
(declare version)
(defn -main
[& args]
(alter-var-root #'version (constantly (-> ...)))
(do-stuff))
It declares the Var at compile time, sets its root value at runtime once, doesn't require deref and is not bound to the main thread. You didn't respond to this suggestion in your previous question. Did you try this approach?
You could use dynamic binding.
(declare *version*)
(defn start-my-program []
(binding [*version* (read-version-from-file)]
(main))
Now main and every function it calls will see the value of *version*.
While kotarak's solution works very well, here is an alternative approach: turn your code into a memoized function that returns the version. Like so:
(def get-version
(memoize
(fn []
(-> (str "jar:" (-> my.ns.name (.getProtectionDomain)
(.getCodeSource)
(.getLocation))
"!/META-INF/MANIFEST.MF")
(URL.)
(.openStream)
(Manifest.)
(.. getMainAttributes)
(.getValue "Build-number")))))
I hope i dont miss something this time.
If version is a constant, it's going to be defined one time and is not going to be changed you can simple remove the defn and keep the (def version ... ) alone. I suppose you dont want this for some reason.
If you want to change global variables in a fn i think the more idiomatic way is to use some of concurrency constructions to store the data and access and change it in a secure way
For example:
(def *version* (atom ""))
(defn set-version! [] (swap! *version* ...))