Yeoman Basics by example: A Ctools Content Type generator for Drupal

generator-ctools-content-type example

A few weeks ago I wrote a blog post about Yeoman and the existing Yeoman generators for Drupal. As someone who loves and rants a lot about code generation, it was a tool that I had been wanting to try out for quite some time, and the experience after having spent a couple of hours with the tool, figuring out which generators could be useful for me, was rather satisfactory.

Now, beyond having some generators that I can benefit from, my interest in Yeoman was mostly in the APIish side of it. In other words, I wanted to see how easy it is to create my own generators for whatever tasks I find myself repeating a lot. The best way to find that out is, of course, to try and write a generator plugin for it, facing the usual challenges of being a total newcomer to a language or a framework. One of the most common pieces of code I have to write in my projects, are ctools plugins, in particular, Content Type plugins, so I decided to write a generator for just those type of plugins. This post will explain the basics of the tool and how to create a basic generator. If you want to get the most out of it, I’d recommend you to open your IDE or text editor of choice, and follow along, so that you can experiment with Yeoman at the same time.

Why Yeoman

Now, if you are a regular reader of the blog, you might remember that one of my first entries was about FTG, a code generator I wrote as a drupal module, to scaffold custom field types. Why don’t I just follow the same approach, and write a simple UI just for that? Well the reasons are simple: first, I wanted to look into Yeoman because it’s a promising tool from the Javascript community, and second, as much as I like the FTG module, it was just done in an inefficient manner which I don’t consider “the way to go” for new code generators.

Yes, it works perfectly, yes, it saves me (and hopefully you) a ton of time, but it’s just not clean. Plus, even if I end up not using Yeoman much, in favour of writing my own code generation framework (I am that stubborn sometimes), it’s still worth knowing how Yeoman does things, and maybe pick the aspects I like most -or, as some would prefer to say, paying homage to it-. Enough useless talk, let’s get into the plugin creation. I’m assuming you know what Node.js and npm are, and how they work. If not, you might want to check their documentation to get started, and then come back to this post.

 1.- Plugin structure, and writing the package.json file

Before diving straight into code, I’ll include a snippet with the folder structure that I put in place to create the generator. This will give you an eye bird view of everything needed.

As you can see, there isn’t much involved in here. Three are the key files to mention:

  1. The package.json file, in which we’ll declare the basic information of the generator.
  2. The index.js file, where we’ll add the logic of the generator.
  3. The template file(s), where our code templates will be transformed in the desired code, after performing the needed replacements based on the input provided by the user.

A Yeoman plugin is, at its heart, a simple Node.js module that follows a simple rule in the name to make it possible for Yeoman to find available generators out there. This means that as like any other Node.js module, it’ll need a package.json file to work. Let’s have a look at it:

The most important piece of information of the package.json, for a Yeoman plugin, is the name of the plugin. It has to start with “generator” if we want Yeoman to pick it up as an actual plugin, and show it in the generators page. The “main” attribute will specify the main file of our plugin. In this case, the “index.js” file. The “keywords” attribute allows us to specify some of the words through which our plugin could be found in the npm repository, if we decided to publish it there. Note that we can push our generator to github and anyone could use it, even if we choose not to publish it on the npm page.

That’s us done with the easy part. Now let’s move onto some of the more interesting bits: prompting the user for info via the command line. At this point, you might want to check out the generator repository in my github account, since it might be easier to follow along from there. Also, if you want to learn more about the package.json and the plugin structure, you can find more details in the official documentation.

 2.- Prompting the user for input. The index.js file

The core part of a Yeoman generator, is the index.js file. In this file, we define the type of Generator we are creating (we can extend a base class for it, or we could have our custom one, although the base provided by Yeoman will be enough most cases), we list the questions that the generator will ask the user to define what code will be created, and in last instance, we trigger the scaffolding of the code to be generated.

Let’s have a look at a simplified version of the index.js file. I’ve included comments in some sections to make it easy to understand:

All right, don’t panic. This one seems a bigger one, but I promise it’s a dead simple bit. Let’s divide this file into three parts:

2.1.- Declaration dependencies: There’s not much in here. Different generators might require different Node.js libraries to generate certain parts of the code. In most cases, though, you won’t need any other library aside of Yeoman itself, but if you do, you’d just require whatever modules you want to use. In this case, since I wanted my code to be created inside a new folder, I added the mkdirp module, whose purpose I don’t need to explain, do I? ;).

2.2.- Prompting the user for info: After declaring our dependencies, and specifying that we’ll be extending the base yeoman class, we just need to gather the data we’ll use to generate our code. To do so, we have to implement the prompting method in the class Prototype. We’ll do two things in this method:

  1. First, define the questions that we’ll ask the user via the command-line. The Yeoman docs have more info about this, but the main thing to grasp here, is that each question to ask the user, is a question object, with three main properties: the type of question (e.g: text string, boolean, value to choose from a list, etc), the name of the variable where the response will be stored, and the label for the question. We don’t need to worry about how to actually get this info from the user, Yeoman will do it through the Inquirer.js API. That’s the API that defines the question objects, and you can find great examples of the type of prompts available in this link. Also, in the code above, I left two different prompts, one for a simple text value, and the other that allows the user to select one or more values from a list.
  2. Second, get the values the user has submitted, and parse them for later use in the last part of the generator (the writing method). Like this:

All I’m doing here is reading the values entered by the user from the answers object, and storing them in the generator class, as properties. We’ll use them in the next section. Before moving on, I’d like to mention that I’ve gone just through the surface of what the prompting method really is. If you plan to write simple generators, you might not need to know anything else about it, but still, I’d recommend you read this page about the Run loop in Yeoman, which explains in detail how prototype methods work in a Yeoman plugin.

3.- Passing the info supplied to the code template

So, by now, we have listed the info we need from the user, we’ve asked him for that info, and we’ve stored it in our class. The only remaining thing to do, is to use that data and create our code. To do so, we need to implement the writing method. I’ve showed a simplified version of it before, so let’s look at the complete one now:

As you can see, it’s pretty straightforward to figure out what the code is doing:

  1. First, I declare the template_params variable. It will hold the contents of each variable that I’ll be using in the code template. Or in other words, the parts of the code template that match these variable names, will be replaced by the values of the variables.
  2. Then I create, through mkdirp, the folder that will contain the ctools plugin.
  3. Finally, I call “this.template()” to generate the code file. \o/! The first argument is the template file, which I mentioned at the beginning of this post (“ctools_content_type.inc”). The second argument is the destination file, and the third, are the template arguments.

Since the code template is a bit long and not very easy to read due to the presence of some conditional statements (not in the generated code, but conditionals used by Yeoman itself, as some parts are optional and might not be generated), I won’t include it in the post, but you can see it in github. Essentially, it’s the code of a simple ctools content type generator, but with the parts that need to change, enclosed within a specific markup to tell Yeoman that a line has to be replaced. For example:

In that line, the “<%= ct_description %>” bit, will be replaced with the value the user entered to the question that asks him for a description of the plugin.

And that’s it. A few questions, a code file that we’ve probably already written in one of our projects, a few replacements, and the next time we have to do the same thing we’ll probably save some time and avoid our good, old and precious copy & paste routine. In fact, in many cases we might be saving minutes instead of just seconds. Isn’t that lovely?

There are some more things I’d like to explore with Yeoman, such as using loops with the code template, and making some questions based on the value of previous questions. That would allow me to create more complex plugins to generate, for example, Entity modules for Drupal, with any arbitrary number of entity columns in the schema. But that’s one for another day.

Resources & links

Official Yeoman page – plugin creation: http://yeoman.io/authoring/index.html.

Working with the File System (Yeoman docs): http://yeoman.io/authoring/file-system.html.

Generator repository in github: https://github.com/salvamomo/generator-ctools-content-type.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">