There are several components to create a course such as manifest , course , chapter , sub-chapter , subject , instruction , sub-instruction and so on, now we are going to examine those components in detail.

"This course is for …​…​.."

*or you can override it with a string, such as:

"This course does not require any programming experience, it’s for total beginners to programming."

"This course requires some programming experience, Clojure experience is NOT required."

Defining who should enroll this course such as people with no programming experience or people with some Clojure experience and so on.There are 3 predefined keywords(that turn into some strings).

Long description for a course which is limited to 600 characters.

Short description for a course which is limited to 125 characters.

When it’s false that means users can’t jump into chapters randomly they need to go step by step from scratch, if it’s true they can jump into any chapter randomly and skip it whenever they want.

Unique identifier for your project if you change it you will be creating new course so keep it that way unless you want same course couple of times.

Email address or URL that related to the course(e.g. GitHub issue page URL) so users of your course can submit a bug report to you.

"It's designed to teach \"How to make a course on Clojurecademy?\""

Every Clojurecademy course needs a manifest which specifies main characteristics of a course such as a name, descriptions and so on.

Now we are going to construct a course, let’s define our course like this(don’t worry we will talk about every component in detail later on):

;;We name it like course-map because under the hood DSL creates huge map data structure basically ;;all components made from Clojure maps ( def course-map ( course my-manifest ( chapter 'ch-intro ;name identifier for chapter "Intro to Clojure" ;title for chapter ( sub-chapter 'sub-ch-basics ;name identifier for sub chapter "Basics" ;title for sub chapter ( subject 'sub-about-clojure ;name identifier for subject "Aobut Clojure" ;title for subject ( learn ;learn part for subject ( text ( p "Clojure is a functional programming language that runs on JVM." ))))))))

We created our simple course without any instructions(we will talk about it) so what do we have up there?:

Course takes one manifest and multiple chapters as parameters.

Chapter takes name identifier, title and multiple sub chapters as parameters.

Sub Chapter takes name identifier, title and multiple subjects as parameters.

Subject takes name identifier, title and one instruction(and optional other components as well).





We need to make sure that our course is valid and going to run this command:

lein clojurecademy test



Got the following error:

Missing :clojurecademy option in project.clj. You need to have a line in your project.clj file that looks like: :clojurecademy {:course-map your.ns/course-map}

That output means we need to define our course-map in project.clj file so let’s open our project.clj file and add this line:

:clojurecademy {:course-map clj.core/course-map}

Also we have to add this option as well:

:eval-in :leiningen

Then try again that lein clojurecademy test , you should get the following output:

Map is valid. There is no test var defined.Please add defcoursetest for testing.

Now we get this success output which means our course is valid and we need to have at least one subject which has instruction then we will be able to deploy to Clojurecademy.



Now we are going to extend our course with an instruction so we will be able to expect users to provide some input and validate their input that it’s valid or not.Here is the extended version that one subject( subj-hello-world ) added to sub chapter called sub-ch-basics :

( sub-chapter 'sub-ch-basics "Basics" ( subject 'subj-about-clojure "About Clojure" ( learn ( text ( p "Clojure is a functional programming language that runs on JVM." )))) ( subject 'subj-hello-world "Hello, World" ( learn ( text ( p "Now we are going to use Clojure's print functionality to see some output.Please follow the instructions" ))) ( instruction 'ins-clojure-helloworld ;name identifier ( run-pre-tests? false ) ( initial-code :none ) ( rule :no-rule? true ) ( sub-instruction 'sub-ins-hello-world ;name identifier ( text ( p "Please print \"Hello, World\" to console " "then click the Run button to see the result" )) ( testing ( is ( form-used? ( println "Hello, World" )))))) 'hello-world ))

As you can see in this instruction we are asking users to write (println "Hello, World") and click Run button, so we can validate their input. We are going to talk about components that used in here( run-pre-tests? , initial-code , etc.) in the following sections.

Instruction can have multiple sub-instruction components and we have one here, every sub-instruction can have one text (for telling the user what to do) and one testing component, since we can have multiple is components within testing it’s easy to write many is assertions to validate given input.

In the example is component takes a form which is supposed to return either true or false if it is true test passes if not it fails. form-used? is a predefined function in the platform basically it checks the given form that exists in the user’s code. You can check Predefined Functions.

At the bottom 'hello-world indicates namespace of our subject.

Let’s run our command to be sure that we are on the right track:

lein clojurecademy test

Here what we get(again):

Map is valid. There is no test var defined.Please add defcoursetest for testing.

Let’s focus on this message: There is no test var defined.Please add defcoursetest for testing. which means we need to write a test(not the test we know of) that validates our instruction so we will be sure that this instruction has a working solution, the thing is we are going to write user’s code(code that users provide to pass instruction).



Before writing test we need to add required ns:

[clojurecademy.dsl.test :refer [defcoursetest]]



Now, please add following code under the course-map, then run lein clojurecademy test :

( defcoursetest my-test [ ch-intro sub-ch-basics subj-hello-world ins-clojure-helloworld sub-ins-hello-world ] ( println "Hello, World" ))

Here is the output which indicates everything is fine and you are ready to deploy your course to Clojurecademy:

Map is valid.

Hello, World

1 routes passed.

Every sub-instruction needs defcoursetest and you can define your test where ever you like in clj files. Let’s examine defcoursetest in depth:

my-test is name identifier for defcoursetest which should be unique it works like def in Clojure.

[ch-intro sub-ch-basics subj-hello-world ins-clojure-helloworld sub-ins-hello-world] indicates route for this sub instruction. From chapter to sub-instruction (using name identifiers)

(println "Hello, World") it’s assumed that users code (code that provided by user)



If you want to see your course on Clojurecademy immediately you can check here(Deployment), you can regularly deploy your course as you add/change something in your course and see it visually all the time.



We said that instruction can have multiple sub-instruction components so let’s add new subject called subj-math-fns under the subj-hello-world :

( subject 'subj-math-fns "Let's write some math functions" ( learn ( text ( p "To understand Clojure comprehensively we are going to write some basic math functions in this section." ))) ( instruction 'ins-subj-math-fns ( run-pre-tests? false ) ( initial-code :none ) ( rule :no-rule? true ) ( sub-instruction 'sub-ins-my-add ( text ( p "Please write a function called " ( hi "my-add" ) " which adds given numbers" )) ( testing ( is ( = ( my-add 1 ) 1 )) ( is ( = ( my-add 1 2 ) 3 )) ( is ( = ( my-add 1 2 3 4 5 6 ) 21 )))) ( sub-instruction 'sub-ins-my-subs ( text ( p "Please write a function called " ( hi "my-subs" ) " which subtracts given numbers" )) ( testing ( is ( = ( my-subs 1 ) -1 )) ( is ( = ( my-subs 2 1 ) 1 )) ( is ( = ( my-subs 100 1 2 3 4 5 ) 85 ))))) 'subj-math-fns )

And it’s defcourtests would be like this: