Pallet cluster-spec and "lein pallet up" - clojure

I'm using pallet 0.8 and have several group-specs defined
(def group1 (group-spec ...))
(def group2 (group-spec ...))
;...etc
They are combibed to create several machines which will be my "test environment". There should be several such environments with different sets of group specs. Now i'm using groups in my pallet.clj:
(defproject my-project
:groups [group1 group2))
This is working fine, and when i run "lein pallet up" i have my nodes up. I also would like to add some common parameters to all groups inside specific "environment". For this i tried to use cluster-spec
(def my-cluster
(cluster-spec "my-cluster" :groups [group1, group2])
;...some other parameters)
Is this intended usage of cluster-spec? How do i use it with defproject? Or how do i merge cluster-spec map into pallet-project-map?

Related

Does test-kitchen support running multiple specific suites?

For example if my kitchen.yml contains these three suites (example is abbreviated):
suites:
- name: dogs
- name: cats
- name: horse
I would like to be able to run:
kitchen converge -c 2 dogs cats
Is this possible?
test-kitchen supports running multiple suites concurrently. You can use a regular expression "REGEXP" pattern to match on the suites you want to run.
$ kitchen help converge
Usage:
kitchen converge [INSTANCE|REGEXP|all]
Options:
-c, [--concurrency=N] # Run a converge against all matching instances concurrently. Only N instances will run at the same time if a number is given.
-p, [--parallel], [--no-parallel] # [Future DEPRECATION, use --concurrency] Run a converge against all matching instances concurrently.
-t, [--test-base-path=TEST_BASE_PATH] # Set the base path of the tests
-l, [--log-level=LOG_LEVEL] # Set the log level (debug, info, warn, error, fatal)
[--log-overwrite], [--no-log-overwrite] # Set to false to prevent log overwriting each time Test Kitchen runs
[--color], [--no-color] # Toggle color output for STDOUT logger
Description:
The instance states are in order: destroy, create, converge, setup, verify, destroy. Change one or more instances
from the current state to the converge state. Actions for all intermediate states will be executed. See
http://kitchen.ci for further explanation.
So you could use the following regex pattern to match on the "dogs" and "cats" suites and have kitchen run them. The "-c" option without a number following it will run all the suites that match the regex concurrently.
kitchen converge 'dogs|cats' -c
A "-p" option would also have the same behavior as "-c" without any number following it.
Hope that helps.

Associate result of operation to the hashmap value

I have 2 maps:
(def look {"directions" :look
"look" :look
"examine room" :look
})
(def quit {"exit game" :quit
"quit game" :quit
"exit" :quit
"quit" :quit
})
which are merged into one map by:
(defn actions [] (merge look quit))
and then I try to associate its result (which is a hash-map) into value of another map:
(assoc {} :1 actions)
but instead of expected result which should be:
{1: {"directions" :look, "look" :look ...
I receive
{:1 #object[fun_pro.core$actions 0x20a8027b "fun_pro.core$actions#20a8027b"]}
which is, as I understand reference to the action object.
What should I do to receive expected result? I tried also use of unquote-splicing but I'm not advanced enough to use macros yet and couldn't make it works.
EDIT1:
OK, It seem I found the solution. Instead of using (defn actions...) I should use (def actions...).
EDIT2:
To clarify why I use such a structure.
As I said in comment below, I will use this maps to compare it with the answer provided by user to find which command to use. For example if user type "show me directions", it will trigger function to display directions based on keyword "directions". The same result will be if user will ask "I look around the room" or "I examine room to find road", based on keywords "look" and "examine room".
It will be done by first splitting user input into set of strings and find if there is common word with keys from my map (turned into set). So input "show me directions" will be processed into set #{"show" "me" "directions"}. Then I will use clojure.set/intersection to find if there is common element with set of map keywords and trigger function accordingly to result (I have already coded algorithm for that).
Of course I'm open for any suggestions if there is better solution for it.
OK, It seem I found the solution on my own. Instead of using (defn actions...) I should use (def actions...).
This results of desired output.

Using the reconciler to test query in Om Next

Im sure I have read somwhere how it is possible to use the reconciler to test query expressions in Om Next directly but im not able to find the source again or figure out if this is possible based on the Om documentation. Is this possible to do so and if it is, how?
What I have right now to test is using the parser but I was hoping for a better way using the reconciler:
(parser {:state (atom state)} (om/get-query MyQuery))
This is how I currently find the value of top level keywords:
(defn query [kw]
(let [res (my-parser {:state my-reconciler} `[[~kw _]])]
(when (not-empty res) (apply val res))))
So in your case you could try:
(my-parser {:state my-reconciler} (om/get-query MyQuery))
It looks like the value for :state can either be a state you give it as in your example, or the reconciler itself as in my example.
It depends on what you mean by "test query expressions in Om Next directly"? The code you wrote above is the only way to check how the parser will interpret the query you give it.
If you're wanting to see how the app state will be normalized and denormalized using the queries you provide, maybe the documentation for idents and om/tree->db is closer to what you're looking for.

Dealing with database reads in Clojure

I am trying to 'purify' some of my Clojure functions. I would like to get to a point where all my side-effecting code is explicitly declared in one function. It's easy to get some data at the start and to write it to a db at the end and just have a pure function transforming in between. However, the usual situation is that the transformation function requires another DB read somewhere in the middle of the logic:
(defn transform-users
[users]
(let [ids (map :id users)
profiles (db/read :profiles ids)]
(profiles->something profiles)))
(->> (db/read :users)
(transform-users)
(db/write :something)
Obviously this is a very simple example but the point is, how do I get the side-effecting db/read function out of there, how can I make transform-users pure (and as a benefit, easily testable)?
One thing you could do here would be a dependency-injection-like approach of supplying the (potentially) side-effectful function as an optional parameter, e.g.:
(defn transform-users
[users & {:keys [ids->profiles]
:or {ids->profiles #(db/read :profiles %)}]
(let [ids (map :id users)
profiles (ids->profiles ids)]
(profiles->something profiles)))
This should be easily testable since you can mock the injected functions without a lot of effort. And as a bonus, by supplying the default value, you're documenting what you're expecting and making the function convenient to call.
Why couple the reading of the profiles with transforming profiles?
(->> (db/read :users)
(map :id)
(db/read :profiles)
(profile->something)
(db/write :something)
(This also exposes the fact that you are doing two round trips to the db. Where is db/read :user-profiles ?)
(->> (db/read :user-profiles)
(profile->something)
(db/write :something)
or perhaps:
(->> (read-profiles-from-users)
(profile->something)
(db/write :something)

Clojure order dependency when calling functions in -main

I am new to Clojure and am just trying to build some sample apps to get used to the syntax. I noticed the following order dependency behaviour.
I created a project called timex to calculate the time in weeks between two dates. I am using the clj-time functions for the date difference calculations.
If my core.clj looks as follows:
(ns timex.core
(:gen-class))
(defn -main
"Calculate weeks between dates."
[& args]
dp
)
(require '[clj-time.core :as t])
(def d2 (t/date-time 1989 01 07))
(def dw (t/in-weeks (t/interval d2 (t/now))))
(def dp (str "The number of weeks between Jan 7, 1989 and now is " dw "!"))
**
If I run lein repl I get the following error:
#CompilerException java.lang.RuntimeException: Unable to resolve symbol: dp in this context, compiling:(timex/core.clj:4:1)
But if I re-order the lines in the file and put the def's and the require statement before the main as such
(ns timex.core
(:gen-class))
(require '[clj-time.core :as t])
(def d2 (t/date-time 1989 01 07))
(def dw (t/in-weeks (t/interval d2 (t/now))))
(def dp (str "The number of weeks between Jan 7, 1989 and now is " dw "!"))
(defn -main
"Calculate weeks between dates."
[& args]
dp
)
**
Then when I run lein repl and the invoke the (-main) function, I get:
timex.core=> (-main)
"The number of weeks between Jan 7, 1989 and now is 1341!"
Is this apparent order-dependency normal or am I doing something wrong? If the latter, then I would appreciate any advice or documentation I should review. Thanks.
The unit of compilation in Clojure is the s-expression as opposed to a whole file like many other languages. When you load a file that file file is evaluated from top to bottom in a single pass. vars (what is created by calls to def) must be created above where they are used, or you can use declare if you need mutual recursion though this is rare.
It's worth spending some time getting used to the difference between compiling a file in the traditional sense, and loading a file in the lisp sense because it is fundamental to the macro system and many other aspects of the language. When you require a file from a namespace, or call load-file from the repl the clojure compiler is invoked and repeatedly reads, macro-expands, then evaluates each from in the file starting at the top. The first line tels it what namespace to define things in which is why the ns expression comes first. Then further forms define things in that namespace. If you then load the file again it does nothing more than read the file from the top all over again. It does not clear the namespace or any other magic, so if you evaluate the file, then change the order and evaluate it again it can continue to work because the required functions are already defined in the namespace (as stored in memeory) when the second pass is run. When getting used to this it helps to run lein check often to make sure things are well ordered.
PS: the vast majority of the time the call to require goes in the ns form at the top of the file.