【问题标题】:Ember blueprint that injects environment config?注入环境配置的 Ember 蓝图?
【发布时间】:2015-05-16 04:56:11
【问题描述】:

我对 Ember 比较陌生,想知道是否有一种方法可以创建一个蓝图/生成器,在维护所有现有配置的同时将新值注入环境配置。是否有一些 Ember 魔法允许现有文件充当蓝图模板?我理想的实现应该是这样的:

ember g platform foo

// config/environment.js
module.exports = function(environment) {
var ENV = {
  // Existing config values here...
  APP: {
    platforms: {
      foo: 'abc123'  // Generator injects the 'foo' platform and a GUID
    }
  };
  // Existing environment-specific settings here...
  return ENV;
};

使用 Node 的 fs.readFile()fs.writeFile() 更容易实现这一点吗?如果是这样,我该如何解析environment.js

【问题讨论】:

  • 嘿马洛恩。 Meta 中有几处关于技术写作和简洁性建议的参考资料;我会将this one 视为规范文章。

标签: node.js ember.js ember-cli


【解决方案1】:

不,据我所知,Ember 中没有现成的魔法,抱歉。当您生成路线时,会发生与您正在谈论的非常相似的事情,但代码相当复杂。 ember generate route new_route 函数调用了这个函数

function addRouteToRouter(name, options) {
  var routerPath = path.join(options.root, 'app', 'router.js');
  var source = fs.readFileSync(routerPath, 'utf-8');

  var routes = new EmberRouterGenerator(source);
  var newRoutes = routes.add(name, options);

  fs.writeFileSync(routerPath, newRoutes.code());
}

然后执行解释器级别的代码以添加路由器并将其恢复为代码:

module.exports = EmberRouterGenerator;

var recast    = require('recast');
var traverse  = require('es-simpler-traverser');

var Scope     = require('./scope');
var DefineCallExpression = require('./visitors/define-call-expression.js');

var findFunctionExpression = require('./helpers/find-function-expression');
var hasRoute               = require('./helpers/has-route');
var newFunctionExpression  = require('./helpers/new-function-expression');
var resourceNode           = require('./helpers/resource-node');
var routeNode              = require('./helpers/route-node');

function EmberRouterGenerator(source, ast) {
  this.source = source;
  this.ast = ast;
  this.mapNode = null;
  this.scope = new Scope();

  this.visitors = {
    CallExpression: new DefineCallExpression(this.scope, this)
  };

  this._ast();
  this._walk();
}

EmberRouterGenerator.prototype.clone = function() {
  var route = new EmberRouterGenerator(this.source);

  return route;
};

EmberRouterGenerator.prototype._ast  = function() {
  this.ast = this.ast || recast.parse(this.source);
};

EmberRouterGenerator.prototype._walk  = function() {
  var scope = this.scope;
  var visitors = this.visitors;

  traverse(this.ast, {
    exit: function(node) {
      var visitor = visitors[node.type];

      if (visitor && typeof visitor.exit === 'function') {
        visitor.exit(node);
      }
    },

    enter: function(node) {
      var visitor = visitors[node.type];

      if (visitor && typeof visitor.enter === 'function') {
        visitor.enter(node);
      }
    }
  });
};

EmberRouterGenerator.prototype.add = function(routeName, options) {
  if (typeof this.mapNode === 'undefined') {
    throw new Error('Source doesn\'t include Ember.map');
  }

  var route = this.clone();
  var routes  = route.mapNode.arguments[0].body.body;

  route._add.call(
    route,
    routeName.split('/'),
    routes,
    options
  );

  return route;
};



EmberRouterGenerator.prototype._add = function(nameParts, routes, options) {
  options = options || {};
  var parent   =  nameParts[0];
  var name     = parent;
  var children = nameParts.slice(1);
  var route    = hasRoute(parent, routes);

  if (!route) {
    if (options.type === 'resource') {
      route = resourceNode(name, options);
      routes.push(route);
    } else {
      route = routeNode(name, options);
      routes.push(route);
    }
  }

  if (children.length > 0) {
    var routesFunction = findFunctionExpression(route.expression.arguments);

    if (!routesFunction) {
      routesFunction = newFunctionExpression();
      route.expression.arguments.push(routesFunction);
    }

    this._add(children, routesFunction.body.body, options);
  }
};

EmberRouterGenerator.prototype.remove = function(routeName) {
  if (typeof this.mapNode === 'undefined') {
    throw new Error('Source doesn\'t include Ember.map');
  }

  var route = this.clone();
  var routes  = route.mapNode.arguments[0].body.body;

  var newRoutes = route._remove.call(
    route,
    routeName.split('/'),
    routes
  );

  if (newRoutes) {
    route.mapNode.arguments[0].body.body = newRoutes;
  }

  return route;
};



EmberRouterGenerator.prototype._remove = function(nameParts, routes) {
  var parent   =  nameParts[0];
  var name     = parent;
  var children = nameParts.slice(1);
  var route    = hasRoute(parent, routes);
  var newRoutes;

  if (children.length > 0) {
    var routesFunction = route.expression && findFunctionExpression(route.expression.arguments);

    if (routesFunction) {
      newRoutes = this._remove(children, routesFunction.body.body);

      if (newRoutes) {
        routesFunction.body.body = newRoutes;
      }

      return routes;
    }
  } else {
    if (route) {
      routes = routes.filter(function(node) {
        return node !== route;
      });

      return routes;
    } else {
      return false;
    }
  }
};

EmberRouterGenerator.prototype.code = function(options) {
  options = options || { tabWidth: 2, quote: 'single' };

  return recast.print(this.ast, options).code;
};

那么还有另一种选择,它涉及读取文件,在正确解析文件后在正确的位置添加新环境,然后将流写回。您想要做的事情的复杂性可能超过手动执行此操作所需的时间 IMO。如果这是你经常做的事情,也许考虑用另一种语言编写一个脚本,在文本文件操作方面更好(阅读更多人使用它)

【讨论】:

  • “您想要做的事情的复杂性可能超过手动执行此操作所需的时间”——这也是我最终的结果。感谢您指出路线生成器。
猜你喜欢
  • 2015-12-13
  • 2014-12-22
  • 2013-05-24
  • 2014-01-30
  • 1970-01-01
  • 1970-01-01
  • 2013-07-26
  • 2019-07-04
  • 1970-01-01
相关资源
最近更新 更多