Skip to content
This repository has been archived by the owner on Mar 26, 2019. It is now read-only.

Create a new generator

Francesco Guardiani edited this page Aug 18, 2017 · 3 revisions

Before start writing a generator, give a look at how slush-vertx works

Create the directory tree

First thing to do is create the right directory tree. If you generator name is super_awesome_generator, add this directory tree to slush-vertx:

├── project_templates
│   └── super_awesome_generator
│       ├── language_a
│       │   └── template1.a
│       ├── language_b
│       │   └── template1.b
│       └── language_c
│           └── template1.c
└── src
    └── generators
        └── super_awesome_generator
            └── main.js

What type of generator are you creating?

If your generator creates only sources to put inside a defined sources directory, you can start from main.js of Vert.x Starter generator.

Otherwise if you are going to create a generator that renders sources, tests, docs and so on start from main.js of Vert.x Web Server generator

Exports of main.js

The main.js of your generator has to export an object containing at least the name of the generator and the Generation function inside generate field.

You can also add a description through description field.

Do the 99% of the work with languages metadata

Define metadata for every language, as described here. Try to take advantage as much as possible from variables templates and [[questions array|Slush-Vert.x-Structure#project-info-and-languages-metadata]]. Before defining new dependency, question or variable template, give a look at common_metadata.js to check if It already exists.

An example of a language metadata is the following:

{
    name: "java",
    build_tools: [ // Use build tools defined in common_metadata
        common_metadata.build_tools.gradle,
        common_metadata.build_tools.maven
    ],
    templates: { // Remember: this objects DEPENDS on the rendering function behaviour
        src: ["MainVerticle.java", path.join("users", "UsersRouter.java"), path.join("products", "ProductsRouter.java")],
        test: ["BaseTest.java", "ProductsTest.java", "UsersTest.java"]
    },
    dependencies: _.concat( // Create a project with vertx-core, junit, vertx-unit
        common_metadata.dependencies.java_dependencies,
        common_metadata.dependencies.java_test_dependencies,
        common_metadata.dependencies.vertx_test_dependencies
    ),
    questions: [
        common_metadata.questions.package
    ],
    var_templates: {
        package: common_metadata.var_templates.package, // Clean the package name
        main: common_metadata.var_templates.main_class, // Render the main class full name
        src_dir: common_metadata.var_templates.src_dir, // Render the src directory based on package name
        test_dir: common_metadata.var_templates.test_dir // Render the test src directory based on package name
    }
}

Rendering function

Give a look to Rendering function to understand how to create a rendering function with pre-built methods

Custom rendering function

If you need more complex metadata processing, create your own rendering function:

let super_awesome_rendering_function = function (project_info) {     
  let result = [];                                                                                                                                                                                                                                          
  // Load you super awesome templates!
  // Render your super awesome templates and put results of the rendering inside result as:
  // {"path": "/final/path/of/the/file.txt", "content": "Content of file"}
                       
  // Don't forget to render the build files!
  let buildFilesTemplatesFunctions = Utils
    .loadBuildFilesTemplates(project_info.build_tool.templates, project_info.build_tool.name);

  return _.concat(                                                                                                                              
    result,                                                                                                                                        
    _.zipWith( // Some lodash magic to render build files                                                                                                                                
        project_info.build_tool.templates,                                                                                                    
        buildFilesTemplatesFunctions.map(template => template(project_info)),                                                                 
        (path, content) => new Object({path: path, content: content})                                                                         
    )
  )
}                                                                                                                                             

Generation function

To create the generation function, use Utils.generateGenerationFunction(languagesMetadata, renderingFunction) and pass to it the languages metadata and the rendering function. For example:

let languagesMetadata = [...];
let renderingFunction = Utils.generateRenderingFunction("super_awesome_generator");
let generationFunction = Utils.generateGenerationFunction(languagesMetadata, renderingFunction);

// Now export the generator!
module.exports = {
  name: "Super Awesome Vert.x generator",
  description: "This generator does a lot of awesome things, like generate the code",
  generate: generationFunction,
  render: renderingFunction
}

Custom generation function

Sometimes you need to create a custom generation function, for example when you need to load an external Json. In this case you can write your own generation function:

let renderingFunction = Utils.generateRenderingFunction("super_awesome_generator");
let generationFunction = function (project_info, done) { // This function is async!
    // You can, of course, use the slush-vertx power of metadata processing, with the function below
    Utils.processLanguage(languagesMetadata, project_info).then(result => {
        // Utils.processLanguage() returns a Promise, so you are free to chain it
        return Promise.all([result, anotherSuperAwesomePromise])
    }).then(results => {
        // Build project_info object
        let project_info = results[0].project_info; // This is project_info rendered by Utils.processLanguage

        // Render files with your rendering function
        let files = renderingFunction(project_info);

        // Write files
        Utils.writeFilesArraySync(files);

        // Call slush callback
        done();
    }).catch(error => done(new gutil.PluginError('new', error.stack)));
}

Now write templates!

Now you are ready to write templates. Follow the (http://handlebarsjs.com/) guide to start writing the templates and remember that you have all the power of Handlebars helpers library.

Don't forget to add the templates to language metadata with [[the correct directory|Slush-Vert.x-Structure#rendering-function]].

Also remember that you can create sub directories inside the templates directory and they will reflected in generated code directory structure