Liking cljdoc? Tell your friends :D

Component

If you have been following our tutorial, you should now have:

You will soon create your first component. But before you do that, open workspace.edn in your editor and set :auto-add to true:

 :vcs {:name "git"
       :auto-add true}

The :auto-add option tells poly to automatically add files generated by the create command to git. This behavior will be helpful in the examples that follow.

When :auto-add is false, you have to either:

  • Manually git add files generated by create commands yourself.

  • Or specify the :git-add option on poly create commands.

How to Execute Commands

Before you create a component, let’s recap the two ways of executing poly commands:

  1. poly help - the stand-alone version of the tool.

  2. clojure -M:poly help - as a Clojure dep from the workspace directory.

Which forms are available depends on how you installed poly.

All the documentation refers to the first form, even though the other form is also valid.

A third way to execute poly commands is through the poly shell. You start the poly shell via the shell command:

  1. poly shell

  2. clojure -M:poly shell

The shell command is the default command:

  1. poly

  2. clojure -M:poly

start a shell

From the poly shell, it’s enough to enter the name of the command, e.g.:

shell info

In most cases, the poly shell is the most convenient way to execute commands. It gives you instant feedback, history, and autocomplete. Feel free to use the shell from now on. It will make you more productive and enhance your development experience!

Type quit to end your poly shell session.

Creating a Component

Let’s continue with our example by executing the create component command:

poly create component name:user

Your workspace directory structure will now look like this:

example
├── bases
├── components
│   └── user (1)
│       ├── deps.edn
│       ├── resources
│       │   └── user
│       ├── src
│       │   └── se
│       │       └── example
│       │           └── user
│       │               └── interface.clj
│       └── test
│           └── se
│               └── example
│                   └── user
│                       └── interface_test.clj
├── deps.edn
├── development
│   └── src
│       └── dev
│           └── lisa.clj
├── logo.png
├── projects
├── readme.md
└── workspace.edn
1new user component

You’ll notice the command also printed out this message:

  Remember to add :local/root dependencies to dev and project 'deps.edn' files.

This is a reminder to add the component to your ./deps.edn file. If you don’t, tools.deps and the development environment will not recognize your newly created component, which would be a pity!

The poly tool leaves this task to you as a developer. Our philosophy is to leave the editing of files to you. While we are happy to generate files for you, we want to give you complete control over changing your files.

Let’s listen to the helpful reminder. Add the user component to your ./deps.edn:

{:aliases  {:dev {:extra-paths ["development/src"]

                  :extra-deps {poly/user {:local/root "components/user"} (1)

                               org.clojure/clojure {:mvn/version "1.11.1"}}}

            :test {:extra-paths ["components/user/test"]}}} (2)
1Add the user component as a dependency
2Add the user component tests

All dependency keys must be unique. A good pattern is to prefix them with poly/ followed by the brick name, e.g., poly/user as in this example.

If you expose your code outside your workspace, e.g. as a library, then you should use a more unique prefix, e.g. maven-group-id.workspace-name/, see for example how these components are configured in the Kaocha test runner (You don’t need to include .bases and .components in the prefix, polylith-kaocha/kaocha-resource-config-loader` should be enough, because components and bases can’t share the same name).
Most IDEs now support adding components as a :local/root dependency, but if you are not using Cider, Calva, or a current version of Cursive, please review IDE Support for :local/root.

Cursive users: After creating a component, you may need to refresh your IDE by clicking the Import Changes link:

cursive import changes

The component has its own deps.edn file that looks like:

{:paths ["src" "resources"]
 :deps {}
 :aliases {:test {:extra-paths ["test"]
                  :extra-deps {}}}}

It specifies a src, resources, and test directory.

The create component command created the user component resources directory:

example
├── components
│   └── user
│       ├── resources
│       │   └── user

This directory contains a user directory, which is the name of the component’s interface and is where you put your resources, e.g.:

example
├── components
│   └── user
│       ├── resources
│       │   └── user
│       │       └── myimage.png

The reason you should put myimage.png under resources/user and not directly under resources is that you want to avoid name clashes. This would happen if the same filename existed in more than one component in a project.

If you don’t need the resources directory, you can delete it and remove it from the corresponding component deps.edn file. That said, you should consider keeping it around. It offers a deliberately clash-resistant naming convention for any resources you or your team might add in the future.

Let’s continue by executing the info command:

poly info
info

The output tells you you have one development project, one user component, and one user interface but no base (yet). We refer to components and bases as bricks (we will soon explain what a base is). The cryptic s-- and st- will be described in the flags section.

If your poly output does not look as nice and colorful, see colors.

Add an Implementation

Now, let’s add the core namespace to user:

add user namespaces

…​and change it to:

(ns se.example.user.core)

(defn hello [name]
  (str "Hello " name "!"))

…​and update the interface to:

(ns se.example.user.interface
  (:require [se.example.user.core :as core]))

(defn hello [name]
  (core/hello name))

The interface delegates the incoming call to the implementing core namespace, which is the most common way of structuring components in Polylith.

In this example, we had you put all your implementing code in one single namespace. As a codebase grows, more namespaces can be added to the component as needed. There is no rule that the implementing namespace must be named core, but this is what the create component command generates, and we stuck with this default for this example.

IDE Support for :local/root

Cider, Calva, and Cursive (v1.13.0 and later) all include support :local/root dependencies.

If your IDE doesn’t include support for :local/root dependencies, and you are unable to switch to an IDE that has this support, then you have to add your components as :extra-paths instead of :extra-deps in your ./deps.edn:

{:aliases {:dev {:extra-paths ["development/src"
                               "components/user/src"  (1)
                               "components/user/resources"]} (2)

           :test {:extra-paths ["components/user/test"]}}}
1user component src added as a path
2user component resources added as a path

If at all possible, we recommend you add components as dependencies instead of paths for these reasons:

  • It’s more readable.

  • It’s consistent with how projects are specified.

  • You won’t have to duplicate the bricks library dependencies in your ./deps.edn.

  • You can add or remove the resources directory from a brick without having to remember to update your ./deps.edn.

If you want to compare the difference, take a look at how article was elegantly added as a single dependency versus adding it as two paths in the RealWorld example app.

Can you improve this documentation?Edit on GitHub

cljdoc is a website building & hosting documentation for Clojure/Script libraries

× close