Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
Clojure Programming Cookbook

You're reading from   Clojure Programming Cookbook Handle every problem you come across in the world of Clojure programming with this expert collection of recipes

Arrow left icon
Product type Paperback
Published in Oct 2016
Publisher Packt
ISBN-13 9781785885037
Length 618 pages
Edition 1st Edition
Languages
Arrow right icon
Authors (2):
Arrow left icon
Nicolas Modrzyk Nicolas Modrzyk
Author Profile Icon Nicolas Modrzyk
Nicolas Modrzyk
Makoto Hashimoto Makoto Hashimoto
Author Profile Icon Makoto Hashimoto
Makoto Hashimoto
Arrow right icon
View More author details
Toc

Table of Contents (11) Chapters Close

Preface 1. Live Programming with Clojure 2. Interacting with Collections FREE CHAPTER 3. Clojure Next 4. File Access and the Network 5. Working with Other Languages 6. Concurrency and Parallelism 7. Advanced Tips 8. Web Applications 9. Testing 10. Deployment and DevOps

Using third-party libraries

So, you have found someone else's code that you would like to use, and you are trying to take their work and use it in your project, great! This is what this recipe is all about. There are a few ways to get the code closer to you.

Getting ready

This recipe will introduce you to the art of adding dependencies, packaged as JAR files, to your project and how to reference them, as well as using them from your Clojure code. We will go from downloading the file and starting Clojure REPL manually, to using dependency management tools. Lastly, we will also present how to add new dependencies at runtime, so you do not need to restart your live programming environment.

How to do it...

Each minor section in this recipe shows you how to add the dependency, using different ways in different scenarios.

Adding the JAR file manually to your classpath

Say we have found the following library, clj-tuples, and we want to add this to our REPL session. Most JARs for Clojure are available on either mvnrepository.com or clojars.org.

clj-tuples is on clojars, and since clojars is a regular Maven repository, we can navigate directly through the file. Download and save it (https://clojars.org/repo/ruiyun/tools.timer/1.0.1/tools.timer-1.0.1.jar), and now let's start a Clojure REPL:

java -cp .:clj-tuple-0.2.2.jar:clojure-1.8.0-beta1.jar  clojure.main

And let's quickly have fun with our new library code...mmmmm, tuples:

user=> (use 'ruiyun.tools.timer) 
; nil 
user=> (run-task!  
#(println "Say hello every 5 seconds.") :period 5000); #object[java.util.Timer 0x45a4b042 "java.util.Timer@45a4b042"] 
user=> Say hello every 5 seconds. 
   user=> (cancel! *1) 

Ok, great!

Using Leiningen and a project.clj file

So, that was fun, but maybe you have more than one person's code you want to steal, and also, you just noticed that some of the stolen code is also stealing code from somebody else's code...what do you do?

Leiningen is a command-line tool that will, among other things, help you maintain stolen code. This recipe will not look into installing Leiningen because there is documentation all around that does this, so we just want to make sure at this stage that you have a recent version:

NicolassMacBook:chapter01 niko$ lein version

Leiningen 2.5.1 on Java 1.8.0_45 Java HotSpot(TM) 64-Bit Server VM

To make Leiningen understand what we want, most of the time, we would give it a project.clj file with a DSL that looks mostly like a gigantic Clojure map. If we want to import the same dependency as we did previously, this is the way we would write it:

(defproject chapter01 "0.1.0" 
  :dependencies  
  [[org.clojure/clojure "1.8.0-beta1"] 
   [clj-tuple "0.2.2"]]) 

At its root, declaring a dependency on a third-party library is done through this mini DSL, where a two element vector points to a name and a version:

[name "version"] 

So here:

[clj-tuple "0.2.2"] 

Dependencies are declared as Clojure vectors in the project map, with :dependencies as its key. We now have access to billions of libraries of somewhat different quality depending on the author, but anyway, it's done. Leiningen also uses clojars by default, so there's no need to define repository definitions yet. The same code as before works:

(use 'clj-tuple) 

Viewing dependencies

Using the project.clj file, we can directly see what our code depends on, and in particular the version of things our code depends on. The project.clj file of clj-tuple is located at https://github.com/Ruiyun/tools.timer/blob/master/project.clj and the portion we are interested in is as follows:

(defproject clj-tuple "0.2.2" 
  :description "Efficient small collections." 
  :dependencies [] 
  ...) 

This means that clj-tuples does not depend on anything else and is a well behaved self-contained library.

This is obviously not always the case, and while we are at it we can look at another library, named puget.

Puget has the following dependencies defined:

:dependencies 
    [[fipp "0.6.2"] 
    [mvxcvi/arrangement "1.0.0"] 
    [org.clojure/clojure "1.8.0"]] 

But the great thing is that we don't need to write all those dependency lines ourselves. Transitive dependencies are pulled properly by Leiningen, so our project.clj file will now look simply like this:

(defproject chapter01 "0.1.0" 
  :dependencies [ 
                 [org.clojure/clojure "1.8.0-beta1"] 
                [clj-tuple "0.2.2"]                  
                 [mvxcvi/puget "0.9.1"]]) 

The first time we launch a new REPL we will notice all the dependencies being downloaded:

NicolassMacBook:chapter01 niko$ lein repl 
Retrieving mvxcvi/puget/0.9.1/puget-0.9.1.pom from clojars 
Retrieving fipp/fipp/0.6.2/fipp-0.6.2.pom from clojars 
Retrieving org/clojure/core.rrb-vector/0.0.11/core.rrb-vector-0.0.11.pom from central 
Retrieving mvxcvi/arrangement/1.0.0/arrangement-1.0.0.pom from clojars 
Retrieving org/clojure/core.rrb-vector/0.0.11/core.rrb-vector-0.0.11.jar from central 
Retrieving fipp/fipp/0.6.2/fipp-0.6.2.jar from clojars 
Retrieving mvxcvi/arrangement/1.0.0/arrangement-1.0.0.jar from clojars 
Retrieving mvxcvi/puget/0.9.1/puget-0.9.1.jar from clojars     

This only applies the first time. The second time it occurs without extra messages, and you can now check for yourself that the library is there, and you get the expected colorized output, but not in this book:

user=> (require '[puget.printer :as puget]) 
nil 
user=> (puget/pprint #{'x :a :z 3 1.0}) 
#{1.0 3 :a :z x} 
nil 
user=> (puget/cprint #{'x :a :z 3 1.0}) 
#{1.0 3 :a :z x} 

one-off

Sometimes it is great to just try things out, and have a one-off REPL to try out the new dependencies. This is where we can use a Leiningen plugin named try.

Plugins for Leiningen can be installed globally on your machine with a file named profiles.clj located here:

$HOME/.lein/profiles.clj

The file is a simple map with user-defined settings; here, we just want to add a plugin, so we add it to the vector:

{:user {:plugins [ [lein-try "0.4.3"] ]}} 

That's it. From anywhere on your computer you can now try dependencies. To make things new, we will look at a new useful dependency named env, which makes it easy to retrieve environment settings.

This is the usual Leiningen definition of env, and the following code would be used in project.clj, as seen before:

[adzerk/env "0.2.0"] 

Here, we just type the following:

lein try adzerk/env  

And we can see the dependencies coming along locally (provided you have an Internet connection):

Retrieving adzerk/env/0.2.0/env-0.2.0.pom from clojars 
Retrieving adzerk/env/0.2.0/env-0.2.0.jar from clojars 

And we can now use the require macro:

(require '[adzerk.env :as env]) 

And try it. env returns all the env variables available, whether through Java or Shell:

user=> (env/env) 
; ... 

If you have the chance to run the preceding command where project.clj was located, the dependencies from the project are also available, so we can combine dependencies:

user=> (require '[puget.printer :as puget]) 
 
user=> (puget/cprint (env/env))  
{"Apple_PubSub_Socket_Render" "/private/tmp/com.apple.launchd.jI7P2DRL6X/Render", 
 "HOME" "/Users/niko", 
 "JAVA_ARCH" "x86_64", 
 "JAVA_CMD" "java", 
 "JAVA_MAIN_CLASS_3927" "clojure.main", 
 "JVM_OPTS" "", 
 ... 

Voila! Now we have combined a temporary dependency with our main project dependencies.

New dependencies at runtime

So far we have seen how to add dependencies offline, in the sense that we need to stop our REPL in order for the new dependencies to be handled properly. Now we will see how to add a dependency on the fly.

The dependency we will look at now is named pomegranate and it does just that, add dependencies on the fly.

In the same way we added a Leiningen plugin earlier on, we will add a global dependency to our runtimes by adding a dependency to the same profiles.clj file:

{:user  
    {:dependencies  
    [[com.cemerick/pomegranate "0.3.0"]] }} 

Those dependencies will be ready to be loaded through all your Leiningen-based projects, so be careful not to add too many. With a new REPL loaded, we can now load pomegranate, and the only method we need is happily named add-dependencies:

(require '[cemerick.pomegranate :refer [add-dependencies]])  

The newly required function takes a vector of coordinates using the same pattern we have seen so far, and a map of repositories, with a name for the key and a URL to a Maven-like repository for the value:

(add-dependencies  
                 :coordinates '[[active-quickcheck "0.3.0"]] 
                 :repositories  {"clojars" "http://clojars.org/repo"}) 

The first time this is called, the dependency and all the underlying will be downloaded and be ready for use. We took active-quickcheck as a sample, a library that can be used to generate random tests based on some given predicates. The library still needs to be required in the current namespace:

(use 'active.quickcheck) 
 
; sample dependency test, we will not go in the details here 
(quickcheck (property [a integer 
                   b integer] 
           (= (+ a b) (+ b a)))) 

So we have pretty much seen all the different ways to add dependencies to our Clojure environment, whether through:

  • Direct JAR file
  • Leiningen's project.clj
  • Leiningen's profiles.clj
  • Leiningen's try plugin
  • Using pomegranate
lock icon The rest of the chapter is locked
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $19.99/month. Cancel anytime
Banner background image