自定义生成器

概述

要说Sails中哪个可以自动化重复工作来让你编程更加容易的话那非Generator莫属。Generators可以在你的Sails工程中使用命令行通过模板自动生成一些文件。实际上,Sails内核使用generators来创建Sails工程。所以当你输入:

~/ $ sails new myProject

...sails便会使用generators来构建一个有初始目录结构的Sails app,结构如下:

myProject
       |_api
       |_assets
       |_config
       |_node_modules
       |_tasks
       |_views
.gitignore
.sailsrc
app.js
Gruntfile.js
package.json
README.md

在Sails内核中generators的其他例子:

  • sails-generate-adapter
  • sails-generate-backend
  • sails-generate-controller
  • sails-generate-frontend
  • sails-generate-model
  • sails-generate-new
  • sails-generate-views
  • sails-generate-views-jade
  • 虽然不是一个独立的模块,但是可以通过sails generate api访问其他的generators

为了开始生成一个生成器的过程你可以使用sails-generate-generator

注意:通过调用generators来创建一个generators的想法看起来像是疯狂的无限循环但是请不要怀疑这种做法,因为它不会创建一个虫洞到另外一个邪恶的世界的。

创建一个生成器

首先我们需要一个Sails工程。如果你还没有创建那么可以在你的终端输入:

~/ $ sails new myProject

cdmyProject或者任何已经存在的Sails工程然后在命令行中创建一个名为awesomegenerator

~/ $ sails generate generator awesome

如果你看到提示:info: Created a new generator!,那说明generator已经创建成功了。

使能一个生成器

为了使能generator你需要通过\myProject\.sailsrc来告诉Sails。如果你正在使用一个已存在的生成器那么你将会在.sailsrc中链接到一个npm模块然后使用npm install安装它。因为你正在开发一个生成器,所以你可以直接地链接。为了创建链接请回到终端然后cd进入awesome generator文件夹并输入:

~/ $  pwd

pwd命令将会返回一个完整的generator的文件夹路径(比如:/Users/irl/sails_projects/myProject/awesome)。

拷贝该路径然后打开myProject/.sailsrc。在modules属性中添加awesome关键词并粘贴刚才复制的awesome generator路径。

注意:你可以命名generator为任意你想要的名字,现在我们只是假设为awesome

{
  "generators": {
    "modules": {
      "awesome": "/Users/irl/sails_projects/myProject/awesome"
    }
  }
}

注意:你在.sailsrc文件中命名的generator的名字将会用在终端命令行中执行。

最后,你需要在终端中做一个npm install,这是为了安装那些添加到生成器的package.json需要的模块。

使用生成器

回到终端并输入:sails generate awesome example。让我们看看这会生成些什么。

Generator会创建些什么?

使用文本编辑器打开你的工程,你会注意到有一个新创建的文件夹叫做hey_look_a_folder,并且还有一个名为example的文件:

/**
 * This is just an example file created at Wed Jun 04 2014 17:35:59 GMT-0500 (CDT).
 *
 * You can use underscore templates, see?
 */

module.exports = function () {
  // ...
};

这个文件夹和文件表明了generator的强大,因为它不仅创建了元素而且还使用命令行中的arguments来影响文件的内容。比如,文件名称--example,使用了命令行参数sails generate awesome example的一个元素。

基本的生成器配置

所有有关awesome generator的配置都包含在\myProjects\awesome\Generator.jsGenerator.js的主要部分是before()函数和targets目录。

注意:我们指的是使用{}作为一个目录的JavaScript对象。

配置before()函数

让我们仔细查看myProject/awesome/Generator.js文件:

...
before: function (scope, cb) {

    // scope.args are the raw command line arguments.
    if (!scope.args[0]) {
      return cb( new Error('Please provide a name for this awesome.') );
    }

    // scope.rootPath is the base path for this generator
    if (!scope.rootPath) {
      return cb( INVALID_SCOPE_VARIABLE('rootPath') );
    }

    // Attach defaults
    _.defaults(scope, {
      createdAt: new Date()
    });

    // Decide the output filename for use in targets below:
    scope.filename = scope.args[0];

    // Add other stuff to the scope for use in our templates:
    scope.whatIsThis = 'an example file created at '+scope.createdAt;

    // When finished, we trigger a callback with no error
    // to begin generating files/folders as specified by
    // the `targets` below.
    cb();
  },
  ...

每个generator都可以访问到scope目录,该目录在generator被执行的时候你想要获取参数的时候有用。

如果你的默认awesome generator是一个新的关键词,createdAt:在scope中被创建。我们临时看一下某个模板的这个字段:

...
// Attach defaults
    _.defaults(scope, {
      createdAt: new Date()
    });
...

接下去,当执行awesome generator(比如sails generate awesome <theargument>)的时候,scope.arg中参数数组是可用的。在我们默认的awesome generator,属性filename被添加到scope中并为scope.arg数组中的第一个元素的值赋值。比如:

...
scope.filename = scope.args[0];
...

最后,另外一个属性(比如scope.whatIsThis)也被添加到scope目录。

...
scope.whatIsThis = 'an example file created at '+scope.createdAt;
...

配置目标目录

现在我们仔细查看myProject\awesome\Generator.js中的targets字段来更好理解这个文件夹(比如hey_look_a_folder)和文件(比如example)是怎样生成的。

...
targets: {

    // Usage:
    // './path/to/destination.foo': { someHelper: opts }

    // Creates a dynamically-named file relative to `scope.rootPath`
    // (defined by the `filename` scope variable).
    //
    // The `template` helper reads the specified template, making the
    // entire scope available to it (uses underscore/JST/ejs syntax).
    // Then the file is copied into the specified destination (on the left).
    './:filename': { template: 'example.template.js' },

    // Creates a folder at a static path
    './hey_look_a_folder': { folder: {} }

  },
...

·templatefolder`辅助者看起来很像路由。这些辅助者执行的操作就像它们的名字一样。

template helper

template辅助者基于一个模板创建一些文件一点儿也不会感到惊讶。记住,模板中是可以访问到scope字段的。

...
'./:filename': { template: 'example.template.js' },
...

左手边的参数指定了路径和文件名称,而右边参数则是表明generator会使用哪个模板去创建文件。注意你在before函数中的scopre.args的第一个参数中的scope.filename中使用filename。这个模板可以在myProject\awesome\templates可以找到。在awesome generator你正在使用example.template.js

/**
 * This is just <%= whatIsThis %>.
 *
 * You can use underscore templates, see?
 */

module.exports = function () {
  // ...
};

注意:在属性scope中的whatIsThis是作为你重新调用使用的createdAt:属性在before函数中创建。

folder helper

folder辅助者用来生成文件夹。

...
'./hey_look_a_folder': { folder: {} }
...

左手边的参数指定了路径和文件名称,而右边参数指定任何可选地参数。比如,默认,如果在那个位置中已经存在一个文件夹,那么将会出现一个错误:Something else already exists at ::<path of folder>。如果你想要generator去重写覆盖一个已经存在的文件夹你有两种方法:你可以在选项参数中通过指定force:true来让folder辅助者覆盖一个已经存在的文件夹:

...
'./hey_look_a_folder': { folder: { force: true} }
...

你也可以在命令行中执行generator的时候使用--force参数来配置所有的辅助者覆盖已经存在的文件夹:

~/ $ sails generate awesome test --force

在一个生成器中使用一个生成器

为了利用其它编程者的劳动成果,generators可以被其它的generators使用。这就是传递给所有的generatorsscope字段的强大之处。

比如,Sails内核有一个叫做sails-generate-model的生成器。因为它是内建到Sails内核的,所以无需安装。将它添加到我们的awesome生成器是很容易的。在myProject\awesome\Generator.js通过插入./': ['model']来包含它:

...
targets: {

    // './:filename': { template: 'example.template.js' },

    './': ['model'],

    // './hey_look_a_folder': { folder: {} }

  },
...

注意:通过使用./作为路径,生成器被执行的任何文件夹中的的任何模型都会被放在在\api\models文件夹中。

就是这么简单!现在我们在awesome生成器中创建一个模型。在终端中输入:

~/ $ sails generate awesome user name:string email:email

如果你查看了myProject\api\models那么你将会看到一个新的文件叫做User.js已经被创建了并且包含了之前指定的模型属性:

/**
* User
*
* @description :: TODO: You might write a short summary of how this model works and what it represents here.
* @docs        :: http://sailsjs.org/#!documentation/models
*/

module.exports = {

  attributes: {

    name : { type: 'string' },

    email : { type: 'email' }
  }
};

福利:发布你的生成器到npmjs.org

为了发布awesome生成器到npmjs.org,请进入myProject\awesome\package.json文件然后改变它的文件名、作者和其他基本的信息(比如许可证)。

myProject\awesome文件夹中在终端输入:

~/ $ npm publish

注意:如果你还没有一个NPM账号,可以到(npmjs.org)[https://www.npmjs.org/]中创建一个

想要取消发布某个模块可以输入:

~/ $  npm unpublish` --force

改变myProject\.sailsrc为:

{
  "generators": {
    "modules": {
      "awesome": "whatever you named the module in package.json"
    }
  }
}

从awesome generator文件中在终端输入:

~/ $ npm install

results matching ""

    No results matching ""