Structuring an Archetype

If you are following along with the archetect-tutorial, you can switch to this section's examples from within the directory where it was check out:

    git checkout 02_structure

Using inline templates within our script is not very practical. For one, we can only pipe out a single file. This might prove convenient simple bits of JSON, YAML, or SQL, but not for entire projects. In addition, for some programming languages, the file names often correlate to the name of structures within the files themself. Take Java for instance; we have to enter in the name of our class twice (first when prompted by Archetect, and second when specifying the file name to pipe to), making this error-prone to ensure these match.

We can do a lot better.

Archetect provides a render action that allows us to specify a directory of templates to read files from, or another archetype being composed by this archetype. Not only can we use variables within the templates, but we can also use them in the directory and file names themselves.

Refactoring

So far, we've been rendering the script.yml file. However, Archetypes are usually contained within a directory or Git repository, and we can't always specify the name of a script to render.

Instead, we generally specify the path to a directory or the git URL. By convention, Archetect looks for either archetype.yml or archetype.yaml at the root of the directory. Let's fix this:

mv script.yml archetect.yml

Now, we will instead specify the directory to render instead of the YAML file:

archetect render ./

Now, let's create a directory to store our templates in, and move our Java template into a file within:

mkdir contents
touch "contents/{{ ClassName }}.java"

Copy and paste the Java class from our initial script into the new file, and format it appropriately:

public class {{ ClassName }} {
    private static {{ ClassName }} {{ className }} = new {{ ClassName }}();
    
    private {{ ClassName }}(){}
    
    public static {{ ClassName }} getInstance(){
        return {{ className }};
    }
    
    public void printMessage() {
        System.out.println("This is a contrived example. You can try this at home... but don't try it at work!");
    }
    
    public static void main(String[] args) {
        {{ ClassName }}.getInstance().printMessage();
    }
}

With these changes, we can reformat our archetype.yml file to make use of our new templates directory:

---
script:

  - set:
      class:
        prompt: "What is the name of your class?"

      ClassName:
        value: "{{ class | pascal_case }}"

      className:
        value: "{{ class | camel_case }}"

  - render:
      directory:
        source: contents

Our directory is now becoming an archetype, and something we'll eventually check into revision control. We don't want to pollute our archetype with generated files. Therefore, we should start rendering into a different directory. As an example, if we had ~/projects/ and~/archetypes/ directories, where the former is where we render our projects and the latter is where we author our archetypes, we would now render as follows:

cd ~/projects/
archetect render ~/archetypes/archetect-tutorial

We no longer need to pipe our output to a file, and whatever we supply as our class name will now be reflected within both the file name and file contents using the same variable. Easy!