Practicalli Spacemacs

A practical guide to Spacemacs, a community driven Emacs configuration that provides all the power of Emacs and Vim. The guide contains a section on Clojure development, the majority of the book is applicable to any language.

Content for the book - Clojure Development with Spacemacs github site

YOUTUBE NDrpclY54E0 REPL driven development with Clojure using Spacemacs, CIDER and clj-kondo

A 30 minute video showing how to develop a simple random function generator project in Clojure using REPL driven development with Spacemacs, CIDER and clj-kondo. The project generates 500+ lines of output which is managed by the CIDER inspector.

Transcript

Hello and thank you for watching this practically screencast i will demonstrate a REPL driven development workflow for closure using spacemacs.

Spacemacs is a community configuration for Emacs and uses the CIDER project for its Clojure development environment.

This workflow uses the closure cli tools to configure a project and run the REPL. This workflow will use the aliases from practically closure depth to eden which is a user level configuration to provide a range of community tools on top of closure cli and clj condo is a static analysis tool which shows bugs in our closure code as we write them.

so we can fix them straight away working with closure projects we'll start by creating a new closure project space single quote opens the e shell pop-up buffer providing a terminal to run commands the shell buffer defaults to the evil insert mode ucd to change the directory in which to create the project a new project can be generated from the app template using this command minus x runs a function defined in the project new alias which is configured to run the clj new create function with default values this command overwrites the default values by providing a specific template name and the name of the project once the project has been created toggle the shell buffer visibility with space quote or type exit to close this e-shell now we'll create a layout for the project layouts help organize all the buffers for a project and run commands relative to those project buffers space ll to select an existing layout or create a layout by typing a new name our project is going to be called random functions so we'll create a layout the same name let's open the closure project space ff to open the helm find file and navigate to the file as we type then helm narrows down the files and directories that we can traverse to making it quicker to navigate tab to autocomplete a directory or file name control j control k will allow us to go and select from the list if we go down the wrong path we can use control h to go back up a level and when we find the right file just press return to open it this is the depth.eden file the configuration file for the project let's just format this to make it a little bit more readable the path key is showing us the directories that are going to be included in the class path depth is showing the libraries we're going to use the project and we have some additional aliases that are project specific we can use them when we want to test and also package the project up into an uber jar for deployment before starting our ripple we should add any libraries as dependencies although this project only needs closure so we're ready to start comma quote is the shortcut for running says man start which allows us to choose the type of rapple connection we want so to check in crj we'll start to close your ripple using the depth eden project configuration and connect the editor the wrapper once started there is an indicator in the mode line to show that we are running a closure replica you can also use comma m for the connection management menu and hit i to show that we've got a repel connected we go back to that menu and do browse says one browser it shows us all the side of sessions we've got running things are getting a little bit slow are you using up all your memory you can quickly come here and jump to the projects that are running a rebel and shut them down if you want to quit so rebel is running okay let's switch to the source code buffer so space find file and we narrow down to source practically random function and here we have the sample code that's generated by the template we use source code buffers are more effective than the ripple buffer as evaluation is always done in the context of the current namespace to work with slider effectively first evaluate the source code buffer and we use the evaluation menu comma e and b for buffer this loads in all the code from this particular file and now the closure call functions are loaded into the repl and sider features like help have the information that they need if some of the code in your buffer is not compiling evaluate the namespace instead comma e n n this is also enough to load in closure core into the ripple when i'm starting with a new project i create a rich comment block for reply experiments and i created a repel snippet for this which also includes metadata to tell clj condo lint tool to ignore duplicate names this is very useful when designing your own functions using a comma block means that if we go back and evaluate the whole buffer then the code that we write inside the comment is not evaluated does not write enclosure code we type the open paren unlike magic closure mode will automatically add the closing paren for us this keeps the structure of our code intact as we type names of functions suggestions of matching functions appear along with the documentation and we can use ctrl j and control k or the up and down arrows to navigate through the different functions the project being developed should return a public function chosen at random from a given namespace showing the documentation for that function here we can see we have the closure core ns publix which returns a map of the publicly interned mappings for the namespace that's going to give us all the function names from a particular namespace and we can press tab to auto complete the name we can also get help by doing comma h to look at the cider dock and it pops up another buffer with the documentation information when it was added to closure and where it's actually defined so we could actually go and look at the source code as well if we needed to see what it did then we've got all the details there let's delete that buffer and go back we just walk back to the documentation buffer we can see also shows other functions that are related to this one okay back to the code and we notice that there is a little squiggle underneath the start of this expression and a pop-up shows us that ns publix should be called with one argument but currently we're calling it with zero so this is clj condo giving us a quick hint that we haven't quite got our code right and so we can fix that straight away we do space e and capital l this shows us all the fly track errors that we have so far and in the list we can just press enter to jump to any of these errors and go and fix them we've also got a warning here of this unused binding argument because in main we're not using the args argument so we'll need to fix that at some point too let's go back and just fix this error now star ns star is a dynamic variable which points to the current namespace so we'll use that as an initial argument and you can see the squiggly line is now disappeared so our linter is happy with our code with crj condo providing live linting it's like having a friendly person constantly pairing with you and helping you avoid lots of little mistakes let's evaluate this expression to see what it does comma e f is going to uh cider evolve this means it's going to evaluate the top level expression and we can see the type of value that ns publix is returning it's a hash map with the key as the short name of the function and the value as the fully qualified name if we want to keep a record of what the expression evaluates to we can use the evaluation menu again this time evaluate the top level expression and return the result as a comment this is quite useful as you're exploring what your code is actually doing let's copy the code and then we can try with a new namespace capital d will delete the end of the line and leaves the closing parens in place and we can type the symbol name for closure core which should have over 500 functions in there with the cursor anywhere in or on the parentheses if i'll do it top level form again turning the results and it's capping the results to about 100 in the ui and it's showing the end of the results in the mini buffer the results are translated and it's giving us a suggestion of how we can inspect all the results so comma d is the debug and inspection menu and we can go and press v to inspect values and we want to go and have a look at the last result and it's showing us that the type of this result is all contained with inside a closureland persistent hash map with key value pairs now we use n and p to page through all of the results pressing return on either the key or the value will show more of its details so here we can see this is a symbol as a key with a value of plus and we can use shift l go back and do the same thing with the value here we see we've got some metadata when it was added the name space it's in the name and so on so this might be quite useful for us to investigate for our project when we're done we just press q to quit let's see if we can just get the function names from the hashmap and do y y to copy the line p to paste newline in and we want to pass this expression as an argument to another function so we can use the lisp state space k and use w to wrap the existing expression with an outer expression press escape to leave lisp state as we know that ns publix is returning a map we can use the keys function which is going to return a sequence of the keys so that gives us all the short form names of the functions and we can also use vowels to get the values so this is giving us the fully qualified names of those functions and that's probably going to be useful because that's where all the metadata was as we know the value is going to be large rather than simply evaluating the expression we can use the cider inspector to evaluate and show the result at the same time comma d v shows the slider inspect menu and we use f to evaluate the top level expression and show the results so we can see this is a sequence and we can scroll through we can also dynamically change the page size and this time it's showing us more information on each page if we keep the slider inspector open it will update the values as we evaluate different expressions let's just move it to the side using the window menu and capital to move it to the right and we can resize the window that it's using and we can use space zx to also scale up and down the size of the font it's using phase one will take us back to our code if we're just using e f to evaluate the top level form then it's going to also update the cider inspector we want to get a random function let's copy that expression repeat paste wrap that expression and we want something random so by typing around it's showing us the different functions that are already part of closure core and if we look at round nth let's return a random element of the sequence collection uh as we have a sequence from vals then that looks like the function that we want you have to evaluate the function rand int has returned a random function called satisfies and in the cider inspect buffer we can see the metadata for that function which we'll use very soon our replica experiments have given us enough insight to start designing our code let's create some tests to codify our design we can use treemax as a visual browser spacebt to show us the directory structure but also switch between the source code branch and the testcode branch press return it switches to the test namespace pressing space 0 jumps us back to treemax we can navigate back to the source in that way hpt will toggle it off again i prefer to use the projectile way of switching between the two so we can do space p for the projectile menu and a to alternate between the source code and test code the native base requires the closure test library i'm going to update by including the specific functions i'm going to use and we're including the source code file i'm going to change this to software under test as an alias using the as key this is a common naming convention used and it helps us identify specifically the functions we're actually testing you can see clj condo is underlined the practically random function namespace because we're not actually using this yet as soon as we start writing expressions that call functions from this namespace that underscore will go let's create our first test by simply just refactoring the test that's already there so the dev test function defines a test and we give a name based on the function that we're actually going to test which is going to be public functions and then we add the test to the end of the name testing gives us some context in which the tests are run the purpose of those tests and if we do get failures then testing helps organize the test results into groups so we can easily find them in the original test code the is expression is the assertion for the test and we could have multiple of these if we wish is is simply comparing whether something is true or false if it is true then the test passes if it's false then the test fails let's change this assertion we should be getting back a sequence when we call public functions with a given namespace let's save the file we use the test menu comma t and a to run all the tests oh we've got an error and we've got a message saying there were no tests let's try and fix these so in the error buffer it's saying it's going error compiling random function test and the description is saying no such var public functions and that's because we haven't created it yet so we need to go into the random function source code and create a public functions function space pa to switch back and let's write the shell of a function type in define choose the snippet which adds a definite expression that we can fill in and we can press tab to jump between the different sections and we'll leave the function body empty for now as this is enough to make the test compile save the buffer and evaluate the function definition so the function is now in the repl and inside the test should be able to find it run all the tests again so we don't get an error but it is saying that we can't find the tests did i forget to use is in my tests well no i didn't because iz is right there so what what is going on okay let's see if we can diagnose why we're getting this error to help us understand whether this is a cider error or a error with our code we can run an external test run space quote will open up our e-shell again and it's done so in our current location just cd up to the root of the project using the closure command and the test coucher alias from the practically closure depth eden project we can run the coucher test runner on our project let's just make that window large so we can see the results and we see it's actually running okay it's actually running the test we are getting a fail because we've got an empty body in our test so we can actually see that it's returning nil rather than a sequence but it is actually running our test so there is a difference between external test runners like capture they will run the files from the file space at the exciter test needs everything inside the repple if we switch to the project configuration file we can see that we've got the source and resources directories in the path but the test directory is only included when we use a particular alias so we need to tell cider how to add the test directory when running the ripple let's use the repel manager to quit the rebel we can use the universal argument space u to edit the command when we run sesame start and select cider jacking now we can use the arrow keys to go in and edit this command before it runs to include the test directory using the test alias that's defined in the project configuration and press enter and we can see that it's included that in the actual startup command we switch back to the tests using projectile evaluate the test buffer to load everything in and then run all the tests test summary which shows that we're actually running the test and we get our failure as to be expected because again we're not returning any value we're just returning nil so now we have a failing test we can go and fix it and create a passing test now we have a failing test let's go make the test pass switch back to the source code and we can take one of our reply experiments and copy that paste it in and just change the hard-coded namespace to the argument that we're passing to the function evaluate the function so it's in memory and we can also run the tests from the source code directory as well and we see down the bottom we've got green we've got a green bar we've run one assertion in one test and we don't have any failures or errors success to avoid editing the command line each time the wrapper is started we can create a max project configuration so space p e will create a projectile dir locals and if we know the names we can type in the variables that we want to set use the global options to add an alias into the command line and we specify the alias as a string that's the only variable we want so we can just do ctrl g and it writes our configuration for us it's recommended to specify the particular mode that we want to apply this variable in just to make clear where it's supposed to be used we don't need this again so we can close that buffer we do need emacs to read that file and the most effective way of doing that is reverting a buffer from the current project so we do space space revert buffer and say yes to confirm now if we quit the repel and start it again now we can see it's using the m test in there by default and everything is good so it's very useful to add a dir locals file before you get started with any project if you want to check the command that's used to start the ripple switch to the message buffer and you can see that there is a starting sider session entry followed by the actual command that's used to run the repo and we can see our alias is included in there let's continue developing the project by writing some more tests creating functions and maybe also a little bit more experimenting in the rebel okay we've seen that when we were using the cider inspector there's some more information we can get from each function so let's jump down to the rich comment block and experiment using the relative numbers down the side we can see that we want to jump to a line that's 20 lines away so we can type in 20 followed by j and it will jump as that line and press o and start a new line it's a very effective way of moving around using evil and the relative line numbers let's take one of our existing functions and we want to be able to wrap it so we can add another function to that and we want to use some function that will give us the metadata if we're not sure what that function is called we can use the help menu and use cider apropos to go and search all the functions based on a pattern we start typing meta we can see there is actually a meta function there and there's quite a few alternatives we press tab then we also get the documentation for that function and we see this returns the metadata of a particular object that looks promising we can also go and have a look some of the other functions and press tab and see their documentation as well so we'll use meta as our function and let's evaluate this again this time using the cidr inspector so this is giving us a persistent hashmap with keys that will help us retrieve the particular piece of information that we actually want to use is there anything else we can do that's interesting with namespaces let's use the help apropos start typing no space you can see there's quite a few functions there we're already using publix what if we want to have all of the current namespaces let's see if there's something in the closure call that will help us let's use the help apropos and we want something like all ns oh excellent there it is an all ns function pressing tab and this is going to return a sequence of all namespaces perfect we don't get error from our linter so it looks like we can actually use this without any arguments you can always go back and check if we can't remember and actually quick way of looking is when we do a space after the name we can see in the mini buffer that is actually showing us the function signature underneath and i assume this is going to be a fairly large result so i'm going to use the decider inspector menu and see the results and we can see that it's actually bringing in all of the middleware that cider is actually using as well as the closure core uh some n-replica cider and reple so when it says all namespaces it really does mean all name spaces and there are quite a lot 198. if we wrap that with ns publix we're going to get an error because we're passing in a closure lazy sequence and we're expecting a closure symbol the error buffer can show an awful lot of error messages but you can also hide irrelevant pieces of information click any of these names and make them underscore it actually hides their information so we can just see what the closure is doing or we can add in particular java aspects or apple aspects and we can also limit it to just using the project but the description of the error it's pretty straightforward so we don't need to worry about that so if we map ns publix over all ns it's giving us a lazy sequence and each one of those are a map of key value pairs so really we want to map ns publics and then vowels so we can write a little anonymous function in here let's wrap n is publix we'll slurp in the argument and then we can wrap again and create vowels and we can just use the syntax shortcut for an anonymous function so we've got an error which is not quite right we're actually included and this publix needs to call each of the name spaces in turn so we need to barf that out so we use the structural editing lisp state and we can just move that out twice so that we're mapping the whole val's expression over each of the namespaces and then go in and add the anonymous function placeholder this will be quite large so let's use the cider inspector and we get a lazy sequence of the fully qualified function names for each of the name spaces however it is still a sequence of sequences each value in the sequence is another sequence itself so we want to be able to concatenate those together let's switch back to the code and we can just use map cat instead of map evaluate and that will update the cider inspector and now we can see we have a flat sequence of all of the namespaces now we want to get a random function and then get the details of that function so we can have the doc string and other useful information we take the previous function and then we can add runs nth to get a random function and there we see we get an example of a random function pulled from all of the namespaces and then wrap that function and get the metadata and this returns a nice simple hashmap as it's not too big we can simply pretty print that we can do that as a separate buffer or as it's only small we can evaluate that to a comment and this gives us some useful documentation we might want to keep in a design journal just to show how certain functions and expressions are supposed to work which can help an awful lot when you're trying to debug and maintain code let's take our experiments and define tests for all our public functions switch to the test code and we can create a another def test to test the function public functions all ns and then add tests to the end add a test string and an assertion to check that we get a sequence back from the function that we're testing we can add more assertions to the public functions test to cover different name spaces let's just copy the is and i use the percent sign to jump to the end of that expression press a to append and then we paste in other assertions and provide specific name spaces let's evaluate this public test if we're not on the test we can evaluate the previous expression just by doing comma e and we can evaluate the last s expression we can do the same thing here this is not compiling because we haven't actually created the public functions all on so we need to go and create this and we just use a def for this uh because we're not passing any arguments if we jump down to our reply experiments it is just the results of mapcat and we weren't just the values that mapcat creates so we have a sequence of the fully qualified function names from all of the namespaces evaluate the def let's see if our tests run they do we've got three assertions in one test function oh that's only one test function it's not running out of the test function so back to the test so we either need to run the test from here which in space max will load in the buffer before it runs the tests or we can just simply evaluate the test start a test run now we've got two test functions we see we've got one assertion error so it's not quite right let's go and refactor our function now we can actually simplify this a little bit so we actually just want to map cut ns publics so we could just space k raise this up and raise it up again if you just want to see what that does without running the whole def we can go just after the closing map cat expression comma eval eval s expression and that will evaluate just that particular part of the code let's wrap this with vowels evaluate just the valves expression now we can see we've got the no spaces we want we've got a nice sequence make sure we evaluate the def switch to the tests we evaluate the test then it clears the results from the previous test run and now our test run and the good news is that they're all passing so now we've got five assertions in two test functions with no errors or failures so we also want a random function from the sequences of public functions that we've created so far so let's now create a test for our random function and call it random function details test create a testing context for running all our assertions in and we're going to return our function details as a hashmap so we can simply test that that's what we're returning and our test is going to call the random function details function and it could take a sequence of function names as an argument and we can use the functions we already have to generate that let's do space v a few times and we can select the expression we copy that use set to jump to the end create new line and print out a few more assertions and again specific namespaces for each of the assertions before we run the test let's go and create that function switch back to the source code let's use the snippet paste in the name let's evaluate that switch back to the tests namespace if we run the test from here it's going to evaluate the new def test and we see that the three assertions have failed because we haven't actually implemented the function yet let's jump to the end of our replica experiments and we see this is how we got the metadata and so we want to take this and create our random function we want meta get the details we want round nth to get a specific function name and we've already got the actual sequence which is in functions so that should give us the answer let's evaluate that function and now all our tests run and we've got eight assertions across three test functions and they're all working so now we've got our api of our project we can write a print function to give a nice output from the command line so we're going to place this function at the top as a help function i've got a little section divider snippet and this just helps me separate out logically the different sections of the code so if this project does grow to be quite large it becomes very easy to separate these things out into their own namespaces let's just add a print print function which takes in some metadata and print it out by pulling out the particular keys from the metadata and using string to join it up into a nice output as this is a helper function i'm not going to write a unit test for this let's update the main function to be able to call this code from the command line so we're going to add a meaningful doc string when you've got two cases it's either going to take no arguments or one argument so let's do the zero argument case and create a a single argument case and just to be safe we'll create a variable argument case as well but with one fixed argument if there's no arguments then we are going to call pretty print function which is going to format the random function details for all the public functions in all the namespaces the one argument branch can be the same as above except we're going to call public functions with the given namespace name and if somebody calls this more than one argument we're just going to call main with the namespace name and let's be friendly and just print out a little warning message if we look on line 45 our linter is showing that we've got an unused binding replace it with an underscore which is a common approach to having a name but you're not interested in the actual value and you can see the linter error has gone such teaching as idiomatic closure as well so there we go a project created by ripple driven development foreclosure using space max there are many more features of space max and closure development however i think we've covered many of the important ones take a look at the practically space max book for examples of how to use space max not just for closure development but also general usage of using this amazingly powerful editor and also being able to understand how to make the most out of vim and evil mode and visit the practically website to see all the latest live broadcasts and recorded videos and subscribe to the practically youtube channel to be notified when a new video is released thank you for watching and good luck with your own development projects.