<commit_before>/**
 * Pimcore
 *
 * LICENSE
 *
 * This source file is subject to the new BSD license that is bundled
 * with this package in the file LICENSE.txt.
 * It is also available through the world-wide-web at this URL:
 * http://www.pimcore.org/license
 *
 * @copyright  Copyright (c) 2009-2014 pimcore GmbH (http://www.pimcore.org)
 * @license    http://www.pimcore.org/license     New BSD License
 */

pimcore.registerNS("pimcore.document.tags.href");
pimcore.document.tags.href = Class.create(pimcore.document.tag, {

    initialize: function(id, name, options, data, inherited) {

        this.id = id;
        this.name = name;
        this.options = this.parseOptions(options);

        this.data = {
            id: null,
            path: "",
            type: ""
        };

        if (!this.options.width) {
            this.options.width = Ext.get(id).getWidth() - 2;
        }

        if (data) {
            this.data = data;
            this.options.value = this.data.path;
        }

        this.setupWrapper();

        this.options.enableKeyEvents = true;

        if(typeof this.options.emptyText == "undefined") {
            this.options.emptyText = t("drop_element_here");
        }

        this.options.name = id + "_editable";
        this.element = new Ext.form.TextField(this.options);

        this.element.on("render", function (el) {
            // register at global DnD manager
            dndManager.addDropTarget(el.getEl(), this.onNodeOver.bind(this), this.onNodeDrop.bind(this));

            el.getEl().on("contextmenu", this.onContextMenu.bind(this));
        }.bind(this));

        // disable typing into the textfield
        this.element.on("keyup", function (element, event) {
            element.setValue(this.data.path);
        }.bind(this));

        this.element.render(id);
    },

    uploadDialog: function () {
        pimcore.helpers.assetSingleUploadDialog(this.options["uploadPath"], "path", function (res) {
            try {
                var data = Ext.decode(res.response.responseText);
                if(data["id"]) {

                    if (this.options["subtypes"]) {
                        var found = false;
                        var typeKeys = Object.keys(this.options.subtypes);
                        for (var st = 0; st < typeKeys.length; st++) {
                            for (var i = 0; i < this.options.subtypes[typeKeys[st]].length; i++) {
                                if (this.options.subtypes[typeKeys[st]][i] == data["type"]) {
                                    found = true;
                                    break;
                                }
                            }
                        }
                        if (!found) {
                            return false;
                        }
                    }

                    this.data.id = data["id"];
                    this.data.subtype = data["type"];
                    this.data.elementType = "asset";
                    this.data.path = data["fullpath"];
                    this.element.setValue(data["fullpath"]);
                }
            } catch (e) {
                console.log(e);
            }
        }.bind(this));
    },

    onNodeOver: function(target, dd, e, data) {
        data = this.getCustomPimcoreDropData(data);
        if (this.dndAllowed(data)) {
            return Ext.dd.DropZone.prototype.dropAllowed;
        }
        else {
            return Ext.dd.DropZone.prototype.dropNotAllowed;
        }
    },

    onNodeDrop: function (target, dd, e, data) {
        data = this.getCustomPimcoreDropData(data);

        if(!this.dndAllowed(data)){
            return false;
        }

        this.data.id = data.node.attributes.id;
        this.data.subtype = data.node.attributes.type;
        this.data.elementType = data.node.attributes.elementType;
        this.data.path = data.node.attributes.path;

        this.element.setValue(this.data.path);

        if (this.options.reload) {
            this.reloadDocument();
        }

        return true;
    },

    dndAllowed: function(data) {

        var i;
        var found;

        //only is legacy
        if (this.options.only && !this.options.types) {
            this.options.types = [this.options.only];
        }

        //type check   (asset,document,object)
        if (this.options.types) {
            found = false;
            for (i = 0; i < this.options.types.length; i++) {
                if (this.options.types[i] == data.node.attributes.elementType) {
                    found = true;
                    break;
                }
            }
            if (!found) {
                return false;
            }
        }

        //subtype check  (folder,page,snippet ... )
        if (this.options.subtypes) {
            found = false;
            var typeKeys = Object.keys(this.options.subtypes);
            for (var st = 0; st < typeKeys.length; st++) {
                for (i = 0; i < this.options.subtypes[typeKeys[st]].length; i++) {
                    if (this.options.subtypes[typeKeys[st]][i] == data.node.attributes.type) {
                        found = true;
                        break;
                    }
                }
            }
            if (!found) {
                return false;
            }
        }

        //object class check
        if (data.node.attributes.elementType == "object" && this.options.classes) {
            found = false;
            for (i = 0; i < this.options.classes.length; i++) {
                if (this.options.classes[i] == data.node.attributes.className) {
                    found = true;
                    break;
                }
            }
            if (!found) {
                return false;
            }
        }

        return true;
    },

    onContextMenu: function (e) {

        var menu = new Ext.menu.Menu();

        if(this.data["id"]) {
            menu.add(new Ext.menu.Item({
                text: t('empty'),
                iconCls: "pimcore_icon_delete",
                handler: function (item) {
                    item.parentMenu.destroy();
                    this.data = {};
                    this.element.setValue(this.data.path);
                    if (this.options.reload) {
                        this.reloadDocument();
                    }

                }.bind(this)
            }));

            menu.add(new Ext.menu.Item({
                text: t('open'),
                iconCls: "pimcore_icon_open",
                handler: function (item) {
                    item.parentMenu.destroy();
                    if (this.data.elementType == "document") {
                        pimcore.helpers.openDocument(this.data.id, this.data.subtype);
                    }
                    else if (this.data.elementType == "asset") {
                        pimcore.helpers.openAsset(this.data.id, this.data.subtype);
                    }
                    else if (this.data.elementType == "object") {
                        pimcore.helpers.openObject(this.data.id, this.data.subtype);
                    }
                }.bind(this)
            }));

            menu.add(new Ext.menu.Item({
                text: t('show_in_tree'),
                iconCls: "pimcore_icon_fileexplorer",
                handler: function (item) {
                    item.parentMenu.destroy();
                    pimcore.helpers.selectElementInTree(this.data.elementType, this.data.id);
                }.bind(this)
            }));
        }

        menu.add(new Ext.menu.Item({
            text: t('search'),
            iconCls: "pimcore_icon_search",
            handler: function (item) {
                item.parentMenu.destroy();

                this.openSearchEditor();
            }.bind(this)
        }));

        if((this.options["types"] && in_array("asset",this.options.types)) || !this.options["types"]) {
            menu.add(new Ext.menu.Item({
                text: t('upload'),
                cls: "pimcore_inline_upload",
                iconCls: "pimcore_icon_upload_single",
                handler: function (item) {
                    item.parentMenu.destroy();
                    this.uploadDialog();
                }.bind(this)
            }));
        }

        menu.showAt(e.getXY());

        e.stopEvent();
    },

    openSearchEditor: function () {

        //only is legacy
        if (this.options.only && !this.options.types) {
            this.options.types = [this.options.only];
        }

        pimcore.helpers.itemselector(false, this.addDataFromSelector.bind(this), {
            type: this.options.types,
            subtype: this.options.subtypes,
            specific: {
                classes: this.options["classes"]
            }
        });
    },

    addDataFromSelector: function (item) {
        if (item) {
            this.data.id = item.id;
            this.data.subtype = item.subtype;
            this.data.elementType = item.type;
            this.data.path = item.fullpath;

            this.element.setValue(this.data.path);
        }
    },

    getValue: function () {
        return {
            id: this.data.id,
            type: this.data.elementType,
            subtype: this.data.subtype
        };
    },

    getType: function () {
        return "href";
    }
});<commit_msg>document href: fix drag&drop<commit_after>/**
 * Pimcore
 *
 * LICENSE
 *
 * This source file is subject to the new BSD license that is bundled
 * with this package in the file LICENSE.txt.
 * It is also available through the world-wide-web at this URL:
 * http://www.pimcore.org/license
 *
 * @copyright  Copyright (c) 2009-2014 pimcore GmbH (http://www.pimcore.org)
 * @license    http://www.pimcore.org/license     New BSD License
 */

pimcore.registerNS("pimcore.document.tags.href");
pimcore.document.tags.href = Class.create(pimcore.document.tag, {

    initialize: function(id, name, options, data, inherited) {

        this.id = id;
        this.name = name;
        this.options = this.parseOptions(options);

        this.data = {
            id: null,
            path: "",
            type: ""
        };

        if (!this.options.width) {
            this.options.width = Ext.get(id).getWidth() - 2;
        }

        if (data) {
            this.data = data;
            this.options.value = this.data.path;
        }

        this.setupWrapper();

        this.options.enableKeyEvents = true;

        if(typeof this.options.emptyText == "undefined") {
            this.options.emptyText = t("drop_element_here");
        }

        this.options.name = id + "_editable";
        this.element = new Ext.form.TextField(this.options);

        this.element.on("render", function (el) {
            // register at global DnD manager
            dndManager.addDropTarget(el.getEl(), this.onNodeOver.bind(this), this.onNodeDrop.bind(this));

            el.getEl().on("contextmenu", this.onContextMenu.bind(this));
        }.bind(this));

        // disable typing into the textfield
        this.element.on("keyup", function (element, event) {
            element.setValue(this.data.path);
        }.bind(this));

        this.element.render(id);
    },

    uploadDialog: function () {
        pimcore.helpers.assetSingleUploadDialog(this.options["uploadPath"], "path", function (res) {
            try {
                var data = Ext.decode(res.response.responseText);
                if(data["id"]) {

                    if (this.options["subtypes"]) {
                        var found = false;
                        var typeKeys = Object.keys(this.options.subtypes);
                        for (var st = 0; st < typeKeys.length; st++) {
                            for (var i = 0; i < this.options.subtypes[typeKeys[st]].length; i++) {
                                if (this.options.subtypes[typeKeys[st]][i] == data["type"]) {
                                    found = true;
                                    break;
                                }
                            }
                        }
                        if (!found) {
                            return false;
                        }
                    }

                    this.data.id = data["id"];
                    this.data.subtype = data["type"];
                    this.data.elementType = "asset";
                    this.data.path = data["fullpath"];
                    this.element.setValue(data["fullpath"]);
                }
            } catch (e) {
                console.log(e);
            }
        }.bind(this));
    },

    onNodeOver: function(target, dd, e, data) {
        var record = data.records[0];

        record = this.getCustomPimcoreDropData(record);
        if (this.dndAllowed(record)) {
            return Ext.dd.DropZone.prototype.dropAllowed;
        }
        else {
            return Ext.dd.DropZone.prototype.dropNotAllowed;
        }
    },

    onNodeDrop: function (target, dd, e, data) {
        var record = data.records[0];

        record = this.getCustomPimcoreDropData(record);

        if(!this.dndAllowed(record)){
            return false;
        }

        this.data.id = record.data.id;
        this.data.subtype = record.data.type;
        this.data.elementType = record.data.elementType;
        this.data.path = record.data.path;

        this.element.setValue(record.data.path);

        if (this.options.reload) {
            this.reloadDocument();
        }

        return true;
    },

    dndAllowed: function(data) {

        var i;
        var found;

        //only is legacy
        if (this.options.only && !this.options.types) {
            this.options.types = [this.options.only];
        }

        //type check   (asset,document,object)
        if (this.options.types) {
            found = false;
            for (i = 0; i < this.options.types.length; i++) {
                if (this.options.types[i] == data.data.elementType) {
                    found = true;
                    break;
                }
            }
            if (!found) {
                return false;
            }
        }

        //subtype check  (folder,page,snippet ... )
        if (this.options.subtypes) {
            found = false;
            var typeKeys = Object.keys(this.options.subtypes);
            for (var st = 0; st < typeKeys.length; st++) {
                for (i = 0; i < this.options.subtypes[typeKeys[st]].length; i++) {
                    if (this.options.subtypes[typeKeys[st]][i] == data.data.type) {
                        found = true;
                        break;
                    }
                }
            }
            if (!found) {
                return false;
            }
        }

        //object class check
        if (data.data.elementType == "object" && this.options.classes) {
            found = false;
            for (i = 0; i < this.options.classes.length; i++) {
                if (this.options.classes[i] == data.data.className) {
                    found = true;
                    break;
                }
            }
            if (!found) {
                return false;
            }
        }

        return true;
    },

    onContextMenu: function (e) {

        var menu = new Ext.menu.Menu();

        if(this.data["id"]) {
            menu.add(new Ext.menu.Item({
                text: t('empty'),
                iconCls: "pimcore_icon_delete",
                handler: function (item) {
                    item.parentMenu.destroy();
                    this.data = {};
                    this.element.setValue(this.data.path);
                    if (this.options.reload) {
                        this.reloadDocument();
                    }

                }.bind(this)
            }));

            menu.add(new Ext.menu.Item({
                text: t('open'),
                iconCls: "pimcore_icon_open",
                handler: function (item) {
                    item.parentMenu.destroy();
                    if (this.data.elementType == "document") {
                        pimcore.helpers.openDocument(this.data.id, this.data.subtype);
                    }
                    else if (this.data.elementType == "asset") {
                        pimcore.helpers.openAsset(this.data.id, this.data.subtype);
                    }
                    else if (this.data.elementType == "object") {
                        pimcore.helpers.openObject(this.data.id, this.data.subtype);
                    }
                }.bind(this)
            }));

            menu.add(new Ext.menu.Item({
                text: t('show_in_tree'),
                iconCls: "pimcore_icon_fileexplorer",
                handler: function (item) {
                    item.parentMenu.destroy();
                    pimcore.helpers.selectElementInTree(this.data.elementType, this.data.id);
                }.bind(this)
            }));
        }

        menu.add(new Ext.menu.Item({
            text: t('search'),
            iconCls: "pimcore_icon_search",
            handler: function (item) {
                item.parentMenu.destroy();

                this.openSearchEditor();
            }.bind(this)
        }));

        if((this.options["types"] && in_array("asset",this.options.types)) || !this.options["types"]) {
            menu.add(new Ext.menu.Item({
                text: t('upload'),
                cls: "pimcore_inline_upload",
                iconCls: "pimcore_icon_upload_single",
                handler: function (item) {
                    item.parentMenu.destroy();
                    this.uploadDialog();
                }.bind(this)
            }));
        }

        menu.showAt(e.getXY());

        e.stopEvent();
    },

    openSearchEditor: function () {

        //only is legacy
        if (this.options.only && !this.options.types) {
            this.options.types = [this.options.only];
        }

        pimcore.helpers.itemselector(false, this.addDataFromSelector.bind(this), {
            type: this.options.types,
            subtype: this.options.subtypes,
            specific: {
                classes: this.options["classes"]
            }
        });
    },

    addDataFromSelector: function (item) {
        if (item) {
            this.data.id = item.id;
            this.data.subtype = item.subtype;
            this.data.elementType = item.type;
            this.data.path = item.fullpath;

            this.element.setValue(this.data.path);
        }
    },

    getValue: function () {
        return {
            id: this.data.id,
            type: this.data.elementType,
            subtype: this.data.subtype
        };
    },

    getType: function () {
        return "href";
    }
});<|endoftext|>


<commit_before>const Book = require('../../db/models/book');
const exchange = require('../amqp')();

const key = 'books.detail.get';

(async () => {
  let books = await Book.whereNeedsUpdate().exec();
  while (books != null && books.length > 0) {
    const asins = books.map(book => book.asin);
    console.log('processing to [%s]... %s', key, asins.join());
    await Book.updateMany({ asin: { '$in': asins } }, { processing: true });
    exchange.publish({ asins }, { key });
    books = await Book.whereNeedsUpdate().exec();
  }
})().then(() => process.exit(0));

process.on('unhandledRejection', (...errors) => {
  console.dir(...errors);
  process.exit(1);
});
<commit_msg>clean log<commit_after>const Book = require('../../db/models/book');
const exchange = require('../amqp')();

const key = 'books.detail.get';

(async () => {
  let books = await Book.whereNeedsUpdate().exec();
  while (books != null && books.length > 0) {
    const asins = books.map(book => book.asin);
    console.log('Processing to [%s]... %s', key, asins.join());
    await Book.updateMany({ asin: { '$in': asins } }, { processing: true });
    exchange.publish({ asins }, { key });
    books = await Book.whereNeedsUpdate().exec();
  }
})().then(() => process.exit(0));

process.on('unhandledRejection', (...errors) => {
  console.dir(...errors);
  process.exit(1);
});
<|endoftext|>


<commit_before>'use strict';

var fs = require('fs'),
    soap = require('..'),
    http = require('http'),
    assert = require('assert'),
  duplexer = require('duplexer'),
  req = require('request'),
  httpClient = require('../lib/http.js'),
 // stream = require('stream'),
  stream = require('readable-stream'),
  util = require('util'),
  events = require('events'),
  semver = require('semver'),
  should = require('should');

it('should allow customization of httpClient and the wsdl file download should pass through it', function(done) {

//Make a custom http agent to use streams instead on net socket
  function CustomAgent(options, socket){
    var self = this;
    events.EventEmitter.call(this);
    self.requests = [];
    self.maxSockets = 1;
    self.proxyStream = socket;
    self.options = options || {};
    self.proxyOptions = {};
  }

  util.inherits(CustomAgent, events.EventEmitter);

  CustomAgent.prototype.addRequest = function(req, options) {
    req.onSocket(this.proxyStream);
  };

  //Create a duplex stream

  var httpReqStream = new stream.PassThrough();
  var httpResStream = new stream.PassThrough();
  var socketStream = duplexer(httpReqStream, httpResStream);

  //Custom httpClient
  function MyHttpClient (options, socket){
    httpClient.call(this,options);
    this.agent = new CustomAgent(options, socket);
  }

  util.inherits(MyHttpClient, httpClient);

  MyHttpClient.prototype.request = function(rurl, data, callback, exheaders, exoptions) {
    var self = this;
    var options = self.buildRequest(rurl, data, exheaders, exoptions);
    //Specify agent to use
    options.agent = this.agent;
    var headers = options.headers;
    var req = self._request(options, function(err, res, body) {
      if (err) {
        return callback(err);
      }
      body = self.handleResponse(req, res, body);
      callback(null, res, body);
    });
    if (headers.Connection !== 'keep-alive') {
      req.end(data);
    }
    return req;
  };

  var wsdl = fs.readFileSync('./test/wsdl/default_namespace.wsdl').toString('utf8');
  //Should be able to read from stream the request
  httpReqStream.once('readable', function readRequest() {
    var chunk = httpReqStream.read();
    should.exist(chunk);

    //This is for compatibility with old node releases <= 0.10
    //Hackish
    if(semver.lt(process.version, '0.11.0'))
    {
      socketStream.on('data', function(data) {
        socketStream.ondata(data,0,1984);
      });
    }
    //Now write the response with the wsdl
    var state = httpResStream.write('HTTP/1.1 200 OK\r\nContent-Type: text/xml; charset=utf-8\r\nContent-Length: 1904\r\n\r\n'+wsdl);
  });

  var httpCustomClient = new MyHttpClient({}, socketStream);
  var url = 'http://localhost:50000/Platform.asmx?wsdl';
  soap.createClient(url,
    {httpClient: httpCustomClient},
    function(err, client) {
      assert.ok(client);
      assert.ok(!err);
      assert.equal(client.httpClient, httpCustomClient);
      var description = (client.describe());
      assert.deepEqual(client.describe(), {
        MyService: {
          MyServicePort: {
            MyOperation: {
              input: {
              },
              output: {
              }
            }
          }
        }
      });
      done();
    });
});<commit_msg>Mock up cork/uncork to avoid test failure (#829)<commit_after>'use strict';

var fs = require('fs'),
    soap = require('..'),
    http = require('http'),
    assert = require('assert'),
  duplexer = require('duplexer'),
  req = require('request'),
  httpClient = require('../lib/http.js'),
 // stream = require('stream'),
  stream = require('readable-stream'),
  util = require('util'),
  events = require('events'),
  semver = require('semver'),
  should = require('should');

it('should allow customization of httpClient and the wsdl file download should pass through it', function(done) {

//Make a custom http agent to use streams instead on net socket
  function CustomAgent(options, socket){
    var self = this;
    events.EventEmitter.call(this);
    self.requests = [];
    self.maxSockets = 1;
    self.proxyStream = socket;
    self.options = options || {};
    self.proxyOptions = {};
  }

  util.inherits(CustomAgent, events.EventEmitter);

  CustomAgent.prototype.addRequest = function(req, options) {
    req.onSocket(this.proxyStream);
  };

  //Create a duplex stream

  var httpReqStream = new stream.PassThrough();
  var httpResStream = new stream.PassThrough();
  var socketStream = duplexer(httpReqStream, httpResStream);

  // Node 4.x requires cork/uncork
  socketStream.cork = function() {
  };

  socketStream.uncork = function() {
  };

  //Custom httpClient
  function MyHttpClient (options, socket){
    httpClient.call(this,options);
    this.agent = new CustomAgent(options, socket);
  }

  util.inherits(MyHttpClient, httpClient);

  MyHttpClient.prototype.request = function(rurl, data, callback, exheaders, exoptions) {
    var self = this;
    var options = self.buildRequest(rurl, data, exheaders, exoptions);
    //Specify agent to use
    options.agent = this.agent;
    var headers = options.headers;
    var req = self._request(options, function(err, res, body) {
      if (err) {
        return callback(err);
      }
      body = self.handleResponse(req, res, body);
      callback(null, res, body);
    });
    if (headers.Connection !== 'keep-alive') {
      req.end(data);
    }
    return req;
  };

  var wsdl = fs.readFileSync('./test/wsdl/default_namespace.wsdl').toString('utf8');
  //Should be able to read from stream the request
  httpReqStream.once('readable', function readRequest() {
    var chunk = httpReqStream.read();
    should.exist(chunk);

    //This is for compatibility with old node releases <= 0.10
    //Hackish
    if(semver.lt(process.version, '0.11.0'))
    {
      socketStream.on('data', function(data) {
        socketStream.ondata(data,0,1984);
      });
    }
    //Now write the response with the wsdl
    var state = httpResStream.write('HTTP/1.1 200 OK\r\nContent-Type: text/xml; charset=utf-8\r\nContent-Length: 1904\r\n\r\n'+wsdl);
  });

  var httpCustomClient = new MyHttpClient({}, socketStream);
  var url = 'http://localhost:50000/Platform.asmx?wsdl';
  soap.createClient(url,
    {httpClient: httpCustomClient},
    function(err, client) {
      assert.ok(client);
      assert.ok(!err);
      assert.equal(client.httpClient, httpCustomClient);
      var description = (client.describe());
      assert.deepEqual(client.describe(), {
        MyService: {
          MyServicePort: {
            MyOperation: {
              input: {
              },
              output: {
              }
            }
          }
        }
      });
      done();
    });
});
<|endoftext|>


<commit_before>// ==========================================================================
// Project:   SproutCore - JavaScript Application Framework
// Copyright: ©2006-2009 Sprout Systems, Inc. and contributors.
//            Portions ©2008-2009 Apple, Inc. All rights reserved.
// License:   Licened under MIT license (see license.js)
// ==========================================================================

sc_require('models/record');

/** @class

  A RecordAttribute describes a single attribute on a record.  It is used to
  generate computed properties on records that can automatically convert data
  types and verify data.

  In addition to having predefined transform types, there is also a way to
  set a computed relationship on an attribute. A typical example of this would
  be if you have record with a parentGuid attribute, but are not able to
  determine which record type to map to before looking at the guid (or any
  other attributes). To set up such a computed property, you can attach a
  function in the attribute definition of the SC.Record subclass:

  {{{
    relatedToComputed: SC.Record.toOne(function() {
      return (this.readAttribute('relatedToComputed').indexOf("foo")==0) ? MyApp.Foo : MyApp.Bar;
    })
  }}}

  Notice that we are not using .get() to avoid another transform which would
  trigger an infinite loop.

  You usually will not work with RecordAttribute objects directly, though you
  may extend the class in any way that you like to create a custom attribute.

  A number of default RecordAttribute types are defined on the SC.Record.

  @extends SC.Object
  @since SproutCore 1.0
*/
SC.RecordAttribute = SC.Object.extend(
  /** @scope SC.RecordAttribute.prototype */ {

  /**
    The default value.  If attribute is null or undefined, this default value
    will be substituted instead.  Note that defaultValues are not converted
    so the value should be in the output type expected by the attribute.

    @property {Object}
  */
  defaultValue: null,

  /**
    The attribute type.  Must be either an object class or a property path
    naming a class.  The built in handler allows all native types to pass
    through, converts records to ids and dates to UTF strings.

    If you use the attr() helper method to create a RecordAttribute instance,
    it will set this property to the first parameter you pass.

    @property {Object|String}
  */
  type: String,

  /**
    The underlying attribute key name this attribute should manage.  If this
    property is left empty, then the key will be whatever property name this
    attribute assigned to on the record.  If you need to provide some kind
    of alternate mapping, this provides you a way to override it.

    @property {String}
  */
  key: null,

  /**
    If YES, then the attribute is required and will fail validation unless
    the property is set to a non-null or undefined value.

    @property {Boolean}
  */
  isRequired: NO,

  /**
    If NO then attempts to edit the attribute will be ignored.

    @property {Boolean}
  */
  isEditable: YES,

  /**
    If set when using the Date format, expect the ISO8601 date format.
    This is the default.

    @property {Boolean}
  */
  useIsoDate: YES,

  // ..........................................................
  // HELPER PROPERTIES
  //

  /**
    Returns the type, resolved to a class.  If the type property is a regular
    class, returns the type unchanged.  Otherwise attempts to lookup the
    type as a property path.

    @property {Object}
  */
  typeClass: function() {
    var ret = this.get('type');
    if (SC.typeOf(ret) === SC.T_STRING) ret = SC.objectForPropertyPath(ret);
    return ret ;
  }.property('type').cacheable(),

  /**
    Finds the transform handler.
  */
  transform: function() {
    var klass      = this.get('typeClass') || String,
        transforms = SC.RecordAttribute.transforms,
        ret ;

    // walk up class hierarchy looking for a transform handler
    while(klass && !(ret = transforms[SC.guidFor(klass)])) {
      // check if super has create property to detect SC.Object's
      if(klass.superclass.hasOwnProperty('create')) klass = klass.superclass ;
      // otherwise return the function transform handler
      else klass = SC.T_FUNCTION ;
    }

    return ret ;
  }.property('typeClass').cacheable(),

  // ..........................................................
  // LOW-LEVEL METHODS
  //

  /**
    Converts the passed value into the core attribute value.  This will apply
    any format transforms.  You can install standard transforms by adding to
    the SC.RecordAttribute.transforms hash.  See
    SC.RecordAttribute.registerTransform() for more.

    @param {SC.Record} record the record instance
    @param {String} key the key used to access this attribute on the record
    @param {Object} value the property value
    @returns {Object} attribute value
  */
  toType: function(record, key, value) {
    var transform = this.get('transform'),
        type      = this.get('typeClass');

    if (transform && transform.to) {
      value = transform.to(value, this, type, record, key) ;
    }
    return value ;
  },

  /**
    Converts the passed value from the core attribute value.  This will apply
    any format transforms.  You can install standard transforms by adding to
    the SC.RecordAttribute.transforms hash.  See
    SC.RecordAttribute.registerTransform() for more.

    @param {SC.Record} record the record instance
    @param {String} key the key used to access this attribute on the record
    @param {Object} value the property value
    @returns {Object} attribute value
  */
  fromType: function(record, key, value) {
    var transform = this.get('transform'),
        type      = this.get('typeClass');

    if (transform && transform.from) {
      value = transform.from(value, this, type, record, key);
    }
    return value;
  },

  /**
    The core handler.  Called from the property.

    @param {SC.Record} record the record instance
    @param {String} key the key used to access this attribute on the record
    @param {Object} value the property value if called as a setter
    @returns {Object} property value
  */
  call: function(record, key, value) {
    var attrKey = this.get('key') || key;

    if (value !== undefined) {
      value = this.fromType(record, key, value) ; // convert to attribute.
      record.writeAttribute(attrKey, value);
    } else {
      value = record.readAttribute(attrKey);
      if (SC.none(value) && (value = this.get('defaultValue'))) {
        if (typeof value === SC.T_FUNCTION) {
          value = this.defaultValue(record, key, this);
          // write default value so it doesn't have to be executed again
          if(record.attributes()) record.writeAttribute(attrKey, value, true);
        }
      } else value = this.toType(record, key, value);
    }

    return value ;
  },

  // ..........................................................
  // INTERNAL SUPPORT
  //

  /** @private - Make this look like a property so that get() will call it. */
  isProperty: YES,

  /** @private - Make this look cacheable */
  isCacheable: YES,

  /** @private - needed for KVO property() support */
  dependentKeys: [],

  /** @private */
  init: function() {
    sc_super();
    // setup some internal properties needed for KVO - faking 'cacheable'
    this.cacheKey = "__cache__" + SC.guidFor(this) ;
    this.lastSetValueKey = "__lastValue__" + SC.guidFor(this) ;
  }

}) ;

// ..........................................................
// CLASS METHODS
//

/**
  The default method used to create a record attribute instance.  Unlike
  create(), takes an attributeType as the first parameter which will be set
  on the attribute itself.  You can pass a string naming a class or a class
  itself.

  @param {Object|String} attributeType the assumed attribute type
  @param {Hash} opts optional additional config options
  @returns {SC.RecordAttribute} new instance
*/
SC.RecordAttribute.attr = function(attributeType, opts) {
  if (!opts) opts = {} ;
  if (!opts.type) opts.type = attributeType || String ;
  return this.create(opts);
};

/** @private
  Hash of registered transforms by class guid.
*/
SC.RecordAttribute.transforms = {};

/**
  Call to register a transform handler for a specific type of object.  The
  object you pass can be of any type as long as it responds to the following
  methods:

  | *to(value, attr, klass, record, key)* | converts the passed value (which will be of the class expected by the attribute) into the underlying attribute value |
  | *from(value, attr, klass, record, key)* | converts the underyling attribute value into a value of the class |

  @param {Object} klass the type of object you convert
  @param {Object} transform the transform object
  @returns {SC.RecordAttribute} receiver
*/
SC.RecordAttribute.registerTransform = function(klass, transform) {
  SC.RecordAttribute.transforms[SC.guidFor(klass)] = transform;
};

// ..........................................................
// STANDARD ATTRIBUTE TRANSFORMS
//

// Object, String, Number just pass through.

/** @private - generic converter for Boolean records */
SC.RecordAttribute.registerTransform(Boolean, {
  /** @private - convert an arbitrary object value to a boolean */
  to: function(obj) {
    return SC.none(obj) ? null : !!obj;
  }
});

/** @private - generic converter for Numbers */
SC.RecordAttribute.registerTransform(Number, {
  /** @private - convert an arbitrary object value to a Number */
  to: function(obj) {
    return SC.none(obj) ? null : Number(obj) ;
  }
});

/** @private - generic converter for Strings */
SC.RecordAttribute.registerTransform(String, {
  /** @private -
    convert an arbitrary object value to a String
    allow null through as that will be checked separately
  */
  to: function(obj) {
    if (!(typeof obj === SC.T_STRING) && !SC.none(obj) && obj.toString) {
      obj = obj.toString();
    }
    return obj;
  }
});

/** @private - generic converter for Array */
SC.RecordAttribute.registerTransform(Array, {
  /** @private - check if obj is an array
  */
  to: function(obj) {
    if (!SC.isArray(obj) && !SC.none(obj)) {
      obj = [];
    }
    return obj;
  }
});

/** @private - generic converter for Object */
SC.RecordAttribute.registerTransform(Object, {
  /** @private - check if obj is an object */
  to: function(obj) {
    if (!(typeof obj === 'object') && !SC.none(obj)) {
      obj = {};
    }
    return obj;
  }
});

/** @private - generic converter for SC.Record-type records */
SC.RecordAttribute.registerTransform(SC.Record, {

  /** @private - convert a record id to a record instance */
  to: function(id, attr, recordType, parentRecord) {
    var store = parentRecord.get('store');
    return store.find(recordType, id);
  },

  /** @private - convert a record instance to a record id */
  from: function(record) { return record.get('id'); }
});

/** @private - generic converter for transforming computed record attributes */
SC.RecordAttribute.registerTransform(SC.T_FUNCTION, {

  /** @private - convert a record id to a record instance */
  to: function(id, attr, recordType, parentRecord) {
    recordType = recordType.apply(parentRecord);
    var store = parentRecord.get('store');
    return store.find(recordType, id);
  },

  /** @private - convert a record instance to a record id */
  from: function(record) { return record.get('id'); }
});

/** @private - generic converter for Date records */
SC.RecordAttribute.registerTransform(Date, {

  /** @private - convert a string to a Date */
  to: function(str, attr) {
    var ret ;

    if (attr.get('useIsoDate')) {
      var regexp = "([0-9]{4})(-([0-9]{2})(-([0-9]{2})" +
             "(T([0-9]{2}):([0-9]{2})(:([0-9]{2})(\\.([0-9]+))?)?" +
             "(Z|(([-+])([0-9]{2}):([0-9]{2})))?)?)?)?",
          d      = str.match(new RegExp(regexp)),
          offset = 0,
          date   = new Date(d[1], 0, 1),
          time ;

      if (d[3]) { date.setMonth(d[3] - 1); }
      if (d[5]) { date.setDate(d[5]); }
      if (d[7]) { date.setHours(d[7]); }
      if (d[8]) { date.setMinutes(d[8]); }
      if (d[10]) { date.setSeconds(d[10]); }
      if (d[12]) { date.setMilliseconds(Number("0." + d[12]) * 1000); }
      if (d[14]) {
         offset = (Number(d[16]) * 60) + Number(d[17]);
         offset *= ((d[15] == '-') ? 1 : -1);
      }

      offset -= date.getTimezoneOffset();
      time = (Number(date) + (offset * 60 * 1000));

      ret = new Date();
      ret.setTime(Number(time));
    } else ret = Date.parse(str);
    return ret ;
  },

  _dates: {},

  _zeropad: function(num) {
    return ((num<0) ? '-' : '') + ((num<10) ? '0' : '') + Math.abs(num);
  },

  /** @private - convert a date to a string */
  from: function(date) {
    var ret = this._dates[date.getTime()];
    if (ret) return ret ;

    // figure timezone
    var zp = this._zeropad,
        tz = 0-date.getTimezoneOffset()/60;

    tz = (tz === 0) ? 'Z' : '%@:00'.fmt(zp(tz));

    this._dates[date.getTime()] = ret = "%@-%@-%@T%@:%@:%@%@".fmt(
      zp(date.getFullYear()),
      zp(date.getMonth()+1),
      zp(date.getDate()),
      zp(date.getHours()),
      zp(date.getMinutes()),
      zp(date.getSeconds()),
      tz) ;

    return ret ;
  }
});

<commit_msg>SC.RecordAttribute should return null for empty toOne<commit_after>// ==========================================================================
// Project:   SproutCore - JavaScript Application Framework
// Copyright: ©2006-2009 Sprout Systems, Inc. and contributors.
//            Portions ©2008-2009 Apple, Inc. All rights reserved.
// License:   Licened under MIT license (see license.js)
// ==========================================================================

sc_require('models/record');

/** @class

  A RecordAttribute describes a single attribute on a record.  It is used to
  generate computed properties on records that can automatically convert data
  types and verify data.

  In addition to having predefined transform types, there is also a way to
  set a computed relationship on an attribute. A typical example of this would
  be if you have record with a parentGuid attribute, but are not able to
  determine which record type to map to before looking at the guid (or any
  other attributes). To set up such a computed property, you can attach a
  function in the attribute definition of the SC.Record subclass:

  {{{
    relatedToComputed: SC.Record.toOne(function() {
      return (this.readAttribute('relatedToComputed').indexOf("foo")==0) ? MyApp.Foo : MyApp.Bar;
    })
  }}}

  Notice that we are not using .get() to avoid another transform which would
  trigger an infinite loop.

  You usually will not work with RecordAttribute objects directly, though you
  may extend the class in any way that you like to create a custom attribute.

  A number of default RecordAttribute types are defined on the SC.Record.

  @extends SC.Object
  @since SproutCore 1.0
*/
SC.RecordAttribute = SC.Object.extend(
  /** @scope SC.RecordAttribute.prototype */ {

  /**
    The default value.  If attribute is null or undefined, this default value
    will be substituted instead.  Note that defaultValues are not converted
    so the value should be in the output type expected by the attribute.

    @property {Object}
  */
  defaultValue: null,

  /**
    The attribute type.  Must be either an object class or a property path
    naming a class.  The built in handler allows all native types to pass
    through, converts records to ids and dates to UTF strings.

    If you use the attr() helper method to create a RecordAttribute instance,
    it will set this property to the first parameter you pass.

    @property {Object|String}
  */
  type: String,

  /**
    The underlying attribute key name this attribute should manage.  If this
    property is left empty, then the key will be whatever property name this
    attribute assigned to on the record.  If you need to provide some kind
    of alternate mapping, this provides you a way to override it.

    @property {String}
  */
  key: null,

  /**
    If YES, then the attribute is required and will fail validation unless
    the property is set to a non-null or undefined value.

    @property {Boolean}
  */
  isRequired: NO,

  /**
    If NO then attempts to edit the attribute will be ignored.

    @property {Boolean}
  */
  isEditable: YES,

  /**
    If set when using the Date format, expect the ISO8601 date format.
    This is the default.

    @property {Boolean}
  */
  useIsoDate: YES,

  // ..........................................................
  // HELPER PROPERTIES
  //

  /**
    Returns the type, resolved to a class.  If the type property is a regular
    class, returns the type unchanged.  Otherwise attempts to lookup the
    type as a property path.

    @property {Object}
  */
  typeClass: function() {
    var ret = this.get('type');
    if (SC.typeOf(ret) === SC.T_STRING) ret = SC.objectForPropertyPath(ret);
    return ret ;
  }.property('type').cacheable(),

  /**
    Finds the transform handler.
  */
  transform: function() {
    var klass      = this.get('typeClass') || String,
        transforms = SC.RecordAttribute.transforms,
        ret ;

    // walk up class hierarchy looking for a transform handler
    while(klass && !(ret = transforms[SC.guidFor(klass)])) {
      // check if super has create property to detect SC.Object's
      if(klass.superclass.hasOwnProperty('create')) klass = klass.superclass ;
      // otherwise return the function transform handler
      else klass = SC.T_FUNCTION ;
    }

    return ret ;
  }.property('typeClass').cacheable(),

  // ..........................................................
  // LOW-LEVEL METHODS
  //

  /**
    Converts the passed value into the core attribute value.  This will apply
    any format transforms.  You can install standard transforms by adding to
    the SC.RecordAttribute.transforms hash.  See
    SC.RecordAttribute.registerTransform() for more.

    @param {SC.Record} record the record instance
    @param {String} key the key used to access this attribute on the record
    @param {Object} value the property value
    @returns {Object} attribute value
  */
  toType: function(record, key, value) {
    var transform = this.get('transform'),
        type      = this.get('typeClass');

    if (transform && transform.to) {
      value = transform.to(value, this, type, record, key) ;
    }
    return value ;
  },

  /**
    Converts the passed value from the core attribute value.  This will apply
    any format transforms.  You can install standard transforms by adding to
    the SC.RecordAttribute.transforms hash.  See
    SC.RecordAttribute.registerTransform() for more.

    @param {SC.Record} record the record instance
    @param {String} key the key used to access this attribute on the record
    @param {Object} value the property value
    @returns {Object} attribute value
  */
  fromType: function(record, key, value) {
    var transform = this.get('transform'),
        type      = this.get('typeClass');

    if (transform && transform.from) {
      value = transform.from(value, this, type, record, key);
    }
    return value;
  },

  /**
    The core handler.  Called from the property.

    @param {SC.Record} record the record instance
    @param {String} key the key used to access this attribute on the record
    @param {Object} value the property value if called as a setter
    @returns {Object} property value
  */
  call: function(record, key, value) {
    var attrKey = this.get('key') || key;

    if (value !== undefined) {
      value = this.fromType(record, key, value) ; // convert to attribute.
      record.writeAttribute(attrKey, value);
    } else {
      value = record.readAttribute(attrKey);
      if (SC.none(value) && (value = this.get('defaultValue'))) {
        if (typeof value === SC.T_FUNCTION) {
          value = this.defaultValue(record, key, this);
          // write default value so it doesn't have to be executed again
          if(record.attributes()) record.writeAttribute(attrKey, value, true);
        }
      } else value = this.toType(record, key, value);
    }

    return value ;
  },

  // ..........................................................
  // INTERNAL SUPPORT
  //

  /** @private - Make this look like a property so that get() will call it. */
  isProperty: YES,

  /** @private - Make this look cacheable */
  isCacheable: YES,

  /** @private - needed for KVO property() support */
  dependentKeys: [],

  /** @private */
  init: function() {
    sc_super();
    // setup some internal properties needed for KVO - faking 'cacheable'
    this.cacheKey = "__cache__" + SC.guidFor(this) ;
    this.lastSetValueKey = "__lastValue__" + SC.guidFor(this) ;
  }

}) ;

// ..........................................................
// CLASS METHODS
//

/**
  The default method used to create a record attribute instance.  Unlike
  create(), takes an attributeType as the first parameter which will be set
  on the attribute itself.  You can pass a string naming a class or a class
  itself.

  @param {Object|String} attributeType the assumed attribute type
  @param {Hash} opts optional additional config options
  @returns {SC.RecordAttribute} new instance
*/
SC.RecordAttribute.attr = function(attributeType, opts) {
  if (!opts) opts = {} ;
  if (!opts.type) opts.type = attributeType || String ;
  return this.create(opts);
};

/** @private
  Hash of registered transforms by class guid.
*/
SC.RecordAttribute.transforms = {};

/**
  Call to register a transform handler for a specific type of object.  The
  object you pass can be of any type as long as it responds to the following
  methods:

  | *to(value, attr, klass, record, key)* | converts the passed value (which will be of the class expected by the attribute) into the underlying attribute value |
  | *from(value, attr, klass, record, key)* | converts the underyling attribute value into a value of the class |

  @param {Object} klass the type of object you convert
  @param {Object} transform the transform object
  @returns {SC.RecordAttribute} receiver
*/
SC.RecordAttribute.registerTransform = function(klass, transform) {
  SC.RecordAttribute.transforms[SC.guidFor(klass)] = transform;
};

// ..........................................................
// STANDARD ATTRIBUTE TRANSFORMS
//

// Object, String, Number just pass through.

/** @private - generic converter for Boolean records */
SC.RecordAttribute.registerTransform(Boolean, {
  /** @private - convert an arbitrary object value to a boolean */
  to: function(obj) {
    return SC.none(obj) ? null : !!obj;
  }
});

/** @private - generic converter for Numbers */
SC.RecordAttribute.registerTransform(Number, {
  /** @private - convert an arbitrary object value to a Number */
  to: function(obj) {
    return SC.none(obj) ? null : Number(obj) ;
  }
});

/** @private - generic converter for Strings */
SC.RecordAttribute.registerTransform(String, {
  /** @private -
    convert an arbitrary object value to a String
    allow null through as that will be checked separately
  */
  to: function(obj) {
    if (!(typeof obj === SC.T_STRING) && !SC.none(obj) && obj.toString) {
      obj = obj.toString();
    }
    return obj;
  }
});

/** @private - generic converter for Array */
SC.RecordAttribute.registerTransform(Array, {
  /** @private - check if obj is an array
  */
  to: function(obj) {
    if (!SC.isArray(obj) && !SC.none(obj)) {
      obj = [];
    }
    return obj;
  }
});

/** @private - generic converter for Object */
SC.RecordAttribute.registerTransform(Object, {
  /** @private - check if obj is an object */
  to: function(obj) {
    if (!(typeof obj === 'object') && !SC.none(obj)) {
      obj = {};
    }
    return obj;
  }
});

/** @private - generic converter for SC.Record-type records */
SC.RecordAttribute.registerTransform(SC.Record, {

  /** @private - convert a record id to a record instance */
  to: function(id, attr, recordType, parentRecord) {
    var store = parentRecord.get('store');
    if (SC.none(id) || (id==="")) return null;
    else return store.find(recordType, id);
  },

  /** @private - convert a record instance to a record id */
  from: function(record) { return record ? record.get('id') : null; }
});

/** @private - generic converter for transforming computed record attributes */
SC.RecordAttribute.registerTransform(SC.T_FUNCTION, {

  /** @private - convert a record id to a record instance */
  to: function(id, attr, recordType, parentRecord) {
    recordType = recordType.apply(parentRecord);
    var store = parentRecord.get('store');
    return store.find(recordType, id);
  },

  /** @private - convert a record instance to a record id */
  from: function(record) { return record.get('id'); }
});

/** @private - generic converter for Date records */
SC.RecordAttribute.registerTransform(Date, {

  /** @private - convert a string to a Date */
  to: function(str, attr) {
    var ret ;

    if (attr.get('useIsoDate')) {
      var regexp = "([0-9]{4})(-([0-9]{2})(-([0-9]{2})" +
             "(T([0-9]{2}):([0-9]{2})(:([0-9]{2})(\\.([0-9]+))?)?" +
             "(Z|(([-+])([0-9]{2}):([0-9]{2})))?)?)?)?",
          d      = str.match(new RegExp(regexp)),
          offset = 0,
          date   = new Date(d[1], 0, 1),
          time ;

      if (d[3]) { date.setMonth(d[3] - 1); }
      if (d[5]) { date.setDate(d[5]); }
      if (d[7]) { date.setHours(d[7]); }
      if (d[8]) { date.setMinutes(d[8]); }
      if (d[10]) { date.setSeconds(d[10]); }
      if (d[12]) { date.setMilliseconds(Number("0." + d[12]) * 1000); }
      if (d[14]) {
         offset = (Number(d[16]) * 60) + Number(d[17]);
         offset *= ((d[15] == '-') ? 1 : -1);
      }

      offset -= date.getTimezoneOffset();
      time = (Number(date) + (offset * 60 * 1000));

      ret = new Date();
      ret.setTime(Number(time));
    } else ret = Date.parse(str);
    return ret ;
  },

  _dates: {},

  _zeropad: function(num) {
    return ((num<0) ? '-' : '') + ((num<10) ? '0' : '') + Math.abs(num);
  },

  /** @private - convert a date to a string */
  from: function(date) {
    var ret = this._dates[date.getTime()];
    if (ret) return ret ;

    // figure timezone
    var zp = this._zeropad,
        tz = 0-date.getTimezoneOffset()/60;

    tz = (tz === 0) ? 'Z' : '%@:00'.fmt(zp(tz));

    this._dates[date.getTime()] = ret = "%@-%@-%@T%@:%@:%@%@".fmt(
      zp(date.getFullYear()),
      zp(date.getMonth()+1),
      zp(date.getDate()),
      zp(date.getHours()),
      zp(date.getMinutes()),
      zp(date.getSeconds()),
      tz) ;

    return ret ;
  }
});

<|endoftext|>


<commit_before>var fs = require('fs');
var grunt = require('grunt');
var opts = {
  stdout: true,
  stderr: true,
  failOnError: true
};

var isCardDir = function (dir){
  return dir[0] != ".";
};

function cardGruntCommand(dirname) {
  var cmd = "(cd cards/" + dirname + " && grunt)";
  return cmd;
}

function cardGruntDeployCommand(dirname) {
  var cmd = "(cd cards/" + dirname + " && grunt deploy)";
  return cmd;
}

function cardNpmInstallCommand(dirname) {
  var cmd = "(cd cards/" + dirname + " && npm install)";
  return cmd;
}

function cardNpmRefreshCommand() {
  var cmd = "rm -rf cards/*/node_modules";
  return cmd;
}

function pkgAt(path) {
  return grunt.file.readJSON(path + '/package.json' );
}

function cardIngestManifestCommand(dirname) {
  var name = pkgAt('cards/' + dirname).name;
  var manifestPath = 'cards/' + dirname + '/dist/dev/' + name + '/manifest.json';
  var cmd = '(cd glazier-server && bundle exec rake "glazier:card:ingest[../' + manifestPath + ']")';
  return cmd;
}

function herokuIngestCommand(dirname) {
  var deployJSON = pkgAt('cards/' + dirname);
  var glazierConfig = deployJSON.glazierConfig;
  var url = glazierConfig.assetHost + '/assets/cards/' + glazierConfig.repositoryName + '/manifest.json';
  var cmd = "(cd glazier-server && heroku surrogate rails runner \"PaneType.ingest('" + url + "')\" --app glazier)";
  return cmd;
}

herokuIngestIndexCommand = "(cd glazier-server && heroku surrogate rake 'glazier:ingest_as_current[../tmp/public/index.html]' --app glazier)";

module.exports = {
  updateAllTheThings: {
    command: [
      'git pull --ff-only',
      'npm install',
      '(cd glazier-server && bundle && bundle exec rake db:reset)',
      'for card in `ls cards`; do (cd cards/$card && git pull --ff-only && rm -rf node_modules && npm install); done',
      'grunt ingest',
      'grunt ingestCards'
    ].join('&&')
  },
  glazierServer: {
    command: [
      "cd glazier-server",
      "PORT=3040 foreman start"
    ].join(' && '),
    options: opts
  },
  ingest: {
    command: [
      "cd glazier-server",
      "bundle exec rake 'glazier:ingest_as_current[../tmp/public/index.html]'"
    ].join(' && '),
    options: opts
  },
  ingestIndex: {
    command: herokuIngestIndexCommand,
    options: opts
  },
  npmRefreshForCards: {
    command: fs.readdirSync('cards').filter(isCardDir).map(cardNpmRefreshCommand).join(' && '),
    options: opts
  },
  npmInstallForCards: {
    command: fs.readdirSync('cards').filter(isCardDir).map(cardNpmInstallCommand).join(' && '),
    options: opts
  },
  buildCards: {
    command: fs.readdirSync('cards').filter(isCardDir).map(cardGruntCommand).join(' && '),
    options: opts
  },
  ingestCardManifests: {
    command: fs.readdirSync('cards').filter(isCardDir).map(cardIngestManifestCommand).join(' && '),
    options: opts
  },
  deployCards: {
    command: fs.readdirSync('cards').filter(isCardDir).map(cardGruntDeployCommand).join(' && '),
    options: opts
  },
  herokuIngestCards: {
    command: fs.readdirSync('cards').filter(isCardDir).map(herokuIngestCommand).join(' && '),
    options: opts
  }
};
<commit_msg>continue fixup card deployment<commit_after>var fs = require('fs');
var grunt = require('grunt');
var opts = {
  stdout: true,
  stderr: true,
  failOnError: true
};

var cards = fs.readdirSync('cards').filter(isCardDir);

function isCardDir(dir){
  return dir[0] != ".";
}

function cardGruntCommand(dirname) {
  var cmd = "(cd cards/" + dirname + " && grunt)";
  return cmd;
}

function cardGruntDeployCommand(dirname) {
  var cmd = "(cd cards/" + dirname + " && grunt deploy)";
  return cmd;
}

function cardNpmInstallCommand(dirname) {
  var cmd = "(cd cards/" + dirname + " && npm install)";
  return cmd;
}

function cardNpmRefreshCommand() {
  var cmd = "rm -rf cards/*/node_modules";
  return cmd;
}

function pkgAt(path) {
  return grunt.file.readJSON(path + '/package.json' );
}

function cardIngestManifestCommand(dirname) {
  var name = pkgAt('cards/' + dirname).name;
  var manifestPath = 'cards/' + dirname + '/dist/dev/' + name + '/manifest.json';
  var cmd = '(cd glazier-server && bundle exec rake "glazier:card:ingest[../' + manifestPath + ']")';
  return cmd;
}

function herokuIngestCommand(dirname) {
  var pkg = pkgAt('cards/' + dirname);
  var glazierConfig = pkg.glazierConfig;

  if (!glazierConfig.assetHost) {
    throw new Error("Missing property: glazierConfig.assetHost");
  }

  if (!pkg.name) {
    throw new Error("Missing property: pkg.name");
  }

  var url = glazierConfig.assetHost + '/assets/cards/' + pkg.name + '/manifest.json';
  var cmd = "(cd glazier-server && heroku surrogate rails runner \"PaneType.ingest('" + url + "')\" --app glazier)";
  return cmd;
}

herokuIngestIndexCommand = "(cd glazier-server && heroku surrogate rake 'glazier:ingest_as_current[../tmp/public/index.html]' --app glazier)";

module.exports = {
  updateAllTheThings: {
    command: [
      'git pull --ff-only',
      'npm install',
      '(cd glazier-server && bundle && bundle exec rake db:reset)',
      'for card in `ls cards`; do (cd cards/$card && git pull --ff-only && rm -rf node_modules && npm install); done',
      'grunt ingest',
      'grunt ingestCards'
    ].join('&&')
  },
  glazierServer: {
    command: [
      "cd glazier-server",
      "PORT=3040 foreman start"
    ].join(' && '),
    options: opts
  },
  ingest: {
    command: [
      "cd glazier-server",
      "bundle exec rake 'glazier:ingest_as_current[../tmp/public/index.html]'"
    ].join(' && '),
    options: opts
  },
  ingestIndex: {
    command: herokuIngestIndexCommand,
    options: opts
  },
  npmRefreshForCards: {
    command: cards.map(cardNpmRefreshCommand).join(' && '),
    options: opts
  },
  npmInstallForCards: {
    command: cards.map(cardNpmInstallCommand).join(' && '),
    options: opts
  },
  buildCards: {
    command: cards.map(cardGruntCommand).join(' && '),
    options: opts
  },
  ingestCardManifests: {
    command: cards.map(cardIngestManifestCommand).join(' && '),
    options: opts
  },
  deployCards: {
    command: cards.map(cardGruntDeployCommand).join(' && '),
    options: opts
  },
  herokuIngestCards: {
    command: cards.filter(isCardDir).map(herokuIngestCommand).join(' && '),
    options: opts
  }
};
<|endoftext|>


<commit_before>require("requirish")._(module);

import assert from "better-assert";
import AddressSpace from "lib/address_space/AddressSpace";
import UADataType from "lib/address_space/UADataType";
import path from "path";
import fs from "fs";
import { normalize_require_file } from "lib/misc/utils";
import _ from "underscore";
import { resolveNodeId } from "lib/datamodel/nodeid";
import { LineFile } from "lib/misc/linefile";
import crypto from "crypto";
import {
    getEnumeration,
    hasEnumeration
} from "lib/misc/factories_enumerations";

import { hasConstructor } from "lib/misc/factories_factories";
import { getConstructor } from "lib/misc/factories_factories";
import createObject from 'lib/misc/create-factory';
import { QualifiedName } from "lib/datamodel/qualified_name";
import { lowerFirstLetter } from "lib/misc/utils";

function hashNamespace(namespaceUri) {
    const hash = crypto.createHash('sha1').update(namespaceUri).digest("hex");
    return hash;
}

/**
 * returns the location of the  javascript version of the schema  corresponding to schemaName
 * @method getSchemaSourceFile
 * @param namespace {String}
 * @param schemaName {String}
 * @param schema_type {String}  "enum" | "schema"
 * @return {string}
 * @private
 */
function getSchemaSourceFile(namespace, schemaName, schema_type) {
    assert(schemaName.match(/[a-zA-Z]+/));

    if (!(schema_type === "enum" || schema_type === "schema" || schema_type === "")) {
        throw new Error(` unexpected schema_type${schema_type}`);
    }
    const subfolder = hashNamespace(namespace);

    const root = path.join(__dirname, "/../../schemas");
    const folder = path.normalize(path.join(root, subfolder));

    if (!fs.existsSync(folder)) {
        fs.mkdirSync(folder);
    }
    if (schema_type === "") {
        return path.join(folder, `${schemaName}.js`);
    }
    return path.join(folder, `${schemaName}_${schema_type}.js`);
}

/**
 * convert a nodeset enumeration into a javascript script enumeration code
 * @method generateEnumerationCode
 * @param dataType
 * @param filename {string} the output filename
 *
 */
function generateEnumerationCode(dataType, filename) {
    assert(typeof filename === "string");

    const dataTypeName = dataType.browseName.name.toString();
    assert(!hasEnumeration(dataTypeName));

    // create the enumeration file
    const f = new LineFile();
    f.write(`// namespace ${dataType.namespaceUri.toString()}`);
    f.write("require(\"requirish\")._(module);");
    f.write("var factories  = require(\"lib/misc/factories\");");
    f.write("var makeNodeId = require(\"lib/datamodel/nodeid\").makeNodeId;");

    f.write(`var ${dataTypeName}_Schema = {`);
    f.write(`  id:  makeNodeId(${dataType.nodeId.value},${dataType.nodeId.namespace}),`);
    f.write(`  name: '${dataTypeName}',`);
    f.write(`  namespace: '${dataType.nodeId.namespace}',`);

    f.write("  enumValues: {");
    dataType.definition.forEach((pair) => {
        f.write(`     ${pair.name}: ${pair.value},`);
    });
    f.write("  }");
    f.write("};");
    f.write(`exports.${dataTypeName}_Schema = ${dataTypeName}_Schema;`);
    f.write(`exports.${dataTypeName} = factories.registerEnumeration(${dataTypeName}_Schema);`);
    f.save(filename);
}

/**
 * var dataType = {
 *    browseName: "Color",
 *    definition: [
 *      { name: "Red",  value: 12},
 *      { name: "Blue", value: 11}
 *    ]
 * };
 *
 * makeEnumeration(dataType);
 *
 * @method makeEnumeration
 *
 * @param dataType {Object}
 * @return {*}
 */
function makeEnumeration(dataType, bForce) {
    assert(dataType);
    assert(dataType.hasOwnProperty("browseName"));
    assert(dataType.browseName instanceof QualifiedName);
    assert(_.isArray(dataType.definition));

    const dataTypeName = dataType.browseName.name.toString();
    if (hasEnumeration(dataTypeName)) {
        return getEnumeration(dataTypeName).typedEnum;
    }
    // var Enumeration_Schema = {
    //    id: dataType.nodeId,
    //    name: dataType.browseName.toString(),
    //    enumValues: {}
    // };
    //
    // dataType.definition.forEach(function (pair) {
    //    Enumeration_Schema.enumValues[pair.name] = parseInt(pair.value, 10);
    // });

    const namespace = dataType.namespaceUri;
    const filename = getSchemaSourceFile(namespace, dataType.browseName.toString(), "enum");

    generateEnumerationCode(dataType, filename);

    const relative_filename = normalizeRequireFile(__dirname, filename);

    return require(relative_filename)[dataType.browseName.toString()];
}

function generateStructureCode(namespace, schema) {
    const name = schema.name;

    const f = new LineFile();

    f.write("require(\"requirish\")._(module);");
    f.write("var factories  = require(\"lib/misc/factories\");");
    f.write("var coerceNodeId = require(\"lib/datamodel/nodeid\").coerceNodeId;");
    f.write(`var ${schema.name}_Schema = {`);
    f.write(`    id:  coerceNodeId('${schema.id.toString()}'),`);
    f.write(`    name: "${name}",`);
    f.write("    fields: [");
    schema.fields.forEach((field) => {
        f.write("       {");
        f.write(`           name: "${field.name}",`);
        f.write(`           fieldType: "${field.fieldType}"`);
        if (field.isArray) {
            f.write(`         ,   isArray:${field.isArray ? "true" : false}`);
        }
        if (field.description) {
            f.write(`          , documentation: "${field.description}" `);
        }
        f.write("       },");
    });
    f.write("        ]");
    f.write("    };");
    f.write(`exports.${name}_Schema = ${name}_Schema;`);
    // xx write("exports."+name+" = factories.registerObject(" + name+"_Schema);");

    const filename = getSchemaSourceFile(namespace, name, "schema");
    f.save(filename);
}

function generateFileCode(namespace, schema) {
    assert(typeof namespace === "string");

    const f = new LineFile();

    const name = schema.name;
    const hint = `schemas/${hashNamespace(namespace)}`;

    f.write(`// namespace ${namespace.toString()}`);
    f.write("require(\"requirish\")._(module);");
    f.write('import createObject from "lib/misc/create-factory"');
    // f.write("var  registerObject = require(\"lib/misc/factories\").registerObject;");
    // f.write("registerObject('_generated_schemas|"+ name + "','_generated_schemas');");
    f.write(`createObject('${hint}|${name}');`);

    //
    f.write(`require("${hint}/${name}_schema");`);

    // var filename = "../_generated_schemas/_auto_generated_"+ name;
    let filename = `_generated_/_auto_generated_${name}`;
    f.write(`var ${name} = require("${filename}").${name};`);
    f.write(`exports.${name} = ${name};`);

    filename = getSchemaSourceFile(namespace, name, "");
    f.save(filename);
}

function generateFile(namespace, schema) {
    assert(typeof namespace === "string");

    const name = schema.name;
    const hint = `schemas/${hashNamespace(namespace)}`;

    createObject(`${hint}|${name}`);
}

/*= private
 *
 * @example:
 * @example:
 *    var dataType =  {
 *       browseName: "ServerStatusDataType",
 *       definition: [
 *           { name "timeout", dataType: "UInt32" }
 *       ]
 *    };
 * @param dataType {Object}
 * @return {*}
 */
function constructSchema(addressSpace, dataType) {
    let dataTypeName = dataType.browseName.name.toString();
    // remove DataType to get the name of the class
    dataTypeName = dataTypeName.replace(/DataType/, "");

    const schema = {
        id: dataType.binaryEncodingNodeId,
        name: dataTypeName,
        namespace: dataType.nodeId.namespace,
        fields: [
            // { name: "title", fieldType: "UAString" , isArray: false , documentation: "some text"},
        ]
    };
    const enumeration = addressSpace.findDataType("Enumeration");
    assert(enumeration, "Enumeration Type not found: please check your nodeset file");
    const structure = addressSpace.findDataType("Structure");
    assert(structure, "Structure Type not found: please check your nodeset file");

    // construct the fields
    dataType.definition.forEach((pair) => {
        const dataTypeId = resolveNodeId(pair.dataType);

        const fieldDataType = addressSpace.findNode(dataTypeId);

        if (!fieldDataType) {
            throw new Error(` cannot find description for object ${dataTypeId}. Check that this node exists in the nodeset.xml file`);
        }

        // xx console.log("xxxxx dataType",dataType.toString());

        // check if  dataType is an enumeration or a structure or  a basic type
        if (fieldDataType.isSupertypeOf(enumeration)) {
            makeEnumeration(fieldDataType);
        }
        if (fieldDataType.isSupertypeOf(structure)) {
            makeStructure(fieldDataType);
        }

        let dataTypeName = fieldDataType.browseName.toString();
        dataTypeName = dataTypeName.replace(/DataType/, "");

        schema.fields.push({
            name: lowerFirstLetter(pair.name),
            fieldType: dataTypeName,
            isArray: false,
            description: (pair.description ? pair.description.text : "")
        });
    });
    return schema;
}
function makeStructure(dataType, bForce) {
    bForce = !!bForce;

    assert(dataType instanceof UADataType);

    const addressSpace = dataType.addressSpace;
    assert(addressSpace.constructor.name === "AddressSpace");
    assert(addressSpace instanceof AddressSpace);

    const namespaceUri = addressSpace.getNamespaceUri(dataType.nodeId.namespace);

    // istanbul ignore next
    if (!dataType.binaryEncodingNodeId) {
        throw new Error(`DataType with name ${dataType.browseName.toString()} has no binaryEncoding node\nplease check your nodeset file`);
    }

    // if binaryEncodingNodeId is in the standard factory => no need to overwrite

    if (!bForce && (hasConstructor(dataType.binaryEncodingNodeId) || dataType.binaryEncodingNodeId.namespace === 0)) {
        // xx console.log("Skipping standard constructor".bgYellow ," for dataType" ,dataType.browseName.toString());
        return getConstructor(dataType.binaryEncodingNodeId);
    }

    const schema = constructSchema(addressSpace, dataType);

    generateFileCode(namespaceUri, schema);

    generateStructureCode(namespaceUri, schema);

    const filename = getSchemaSourceFile(namespaceUri, schema.name, "");

    const relative_filename = normalizeRequireFile(__dirname, filename);

    // xx console.log("xxxxxxxxxxxxxxxxxx => ".green,schema.name,filename.cyan,relative_filename.yellow);

    const constructor = require(relative_filename)[schema.name];
    assert(_.isFunction(constructor), "expecting a constructor here");

    return constructor;
}

const nodeset = {
    ServerState: null,
    ServerStatus: null,
    ServiceCounter: null,
    SessionDiagnostics: null
};

function registerDataTypeEnum(addressSpace, dataTypeName, bForce) {
    const dataType = addressSpace.findDataType(dataTypeName);
    assert(dataType);
    const superType = addressSpace.findNode(dataType.subtypeOf);
    assert(superType.browseName.toString() === "Enumeration");
    return makeEnumeration(dataType, bForce);
}

function registerDataType(addressSpace, dataTypeName, bForce) {
    let dataType = addressSpace.findDataType(`${dataTypeName}DataType`);
    if (!dataType) {
        dataType = addressSpace.findDataType(dataTypeName);
    }

    // istanbul ignore next
    if (!dataType) {
        console.log(`registerDataType: warning : Cannot find DataType ${dataTypeName}`);
        return null;
        // xx throw new Error(" Cannot find DataType " + dataTypeName);
    }

    const superType = addressSpace.findNode(dataType.subtypeOf);
    assert(superType.browseName.toString() === "Structure");

    // finding object with encoding
    //
    //   <UAObject NodeId="i=864" BrowseName="Default Binary" SymbolicName="DefaultBinary">
    //   <DisplayName>Default Binary</DisplayName>
    //   <References>
    //       <Reference ReferenceType="HasEncoding" IsForward="false">i=862</Reference>
    return makeStructure(dataType, bForce);
}

/**
 * creates the requested data structure and javascript objects for the OPCU objects
 * @method createExtensionObjectDefinition
 * @param addressSpace {AddressSpace}
 */
const createExtensionObjectDefinition = (addressSpace) => {
    assert(addressSpace instanceof AddressSpace);
    const force = true;
    // nodeset.ApplicationDescription = nodeset.ApplicationDescription || registerDataType(addressSpace, "ApplicationDescription",force);
    nodeset.ServerState = nodeset.ServerState || registerDataTypeEnum(addressSpace, "ServerState", force);
    nodeset.ServerStatus = nodeset.ServerStatus || registerDataType(addressSpace, "ServerStatus", force);
    nodeset.ServiceCounter = nodeset.ServiceCounter || registerDataType(addressSpace, "ServiceCounter", force);
    nodeset.SessionDiagnostics = nodeset.SessionDiagnostics || registerDataType(addressSpace, "SessionDiagnostics", force);
    nodeset.ServerDiagnosticsSummary = nodeset.ServerDiagnosticsSummary || registerDataType(addressSpace, "ServerDiagnosticsSummary", force);
    nodeset.SubscriptionDiagnostics = nodeset.SubscriptionDiagnostics || registerDataType(addressSpace, "SubscriptionDiagnostics", force);

    nodeset.ModelChangeStructure = nodeset.ModelChangeStructure || registerDataType(addressSpace, "ModelChangeStructure", force);
    nodeset.SemanticChangeStructure = nodeset.SemanticChangeStructure || registerDataType(addressSpace, "SemanticChangeStructure", force);
    nodeset.RedundantServer = nodeset.RedundantServer || registerDataType(addressSpace, "RedundantServer", force);
    nodeset.SamplingIntervalDiagnostics = nodeset.SamplingIntervalDiagnostics || registerDataType(addressSpace, "SamplingIntervalDiagnostics", force);
    nodeset.SessionSecurityDiagnostics = nodeset.SessionSecurityDiagnostics || registerDataType(addressSpace, "SessionSecurityDiagnostics", force);

    /**
     *  This Structured DataType defines the local time that may or may not take daylight saving time
     *  into account. Its elements are described in Table 24.
     *  Table 24 – TimeZoneDataType Definition
     *  Name                       Type       Description
     *  TimeZoneDataType structure
     *  offset                     Int16     The offset in minutes from UtcTime
     *  daylightSavingInOffset     Boolean   If TRUE, then daylight saving time (DST) is in effect and offset
     *                                       includes the DST correction. If FALSE then the offset does not
     *                                       include the DST correction and DST may or may not have
     *                                       been in effect.
     */
    nodeset.TimeZoneDataType = nodeset.TimeZoneDataType || registerDataType(addressSpace, "TimeZoneDataType", force);
};

export {
    createExtensionObjectDefinition,
    makeEnumeration,
    makeStructure,
    nodeset
};
<commit_msg>fiix import<commit_after>require("requirish")._(module);

import assert from "better-assert";
import AddressSpace from "lib/address_space/AddressSpace";
import UADataType from "lib/address_space/UADataType";
import path from "path";
import fs from "fs";
import {normalizeRequireFile} from "lib/misc/utils";
import _ from "underscore";
import { resolveNodeId } from "lib/datamodel/nodeid";
import { LineFile } from "lib/misc/linefile";
import crypto from "crypto";
import {
    getEnumeration,
    hasEnumeration
} from "lib/misc/factories_enumerations";

import { hasConstructor } from "lib/misc/factories_factories";
import { getConstructor } from "lib/misc/factories_factories";
import createObject from 'lib/misc/create-factory';
import { QualifiedName } from "lib/datamodel/qualified_name";
import { lowerFirstLetter } from "lib/misc/utils";

function hashNamespace(namespaceUri) {
    const hash = crypto.createHash('sha1').update(namespaceUri).digest("hex");
    return hash;
}

/**
 * returns the location of the  javascript version of the schema  corresponding to schemaName
 * @method getSchemaSourceFile
 * @param namespace {String}
 * @param schemaName {String}
 * @param schema_type {String}  "enum" | "schema"
 * @return {string}
 * @private
 */
function getSchemaSourceFile(namespace, schemaName, schema_type) {
    assert(schemaName.match(/[a-zA-Z]+/));

    if (!(schema_type === "enum" || schema_type === "schema" || schema_type === "")) {
        throw new Error(` unexpected schema_type${schema_type}`);
    }
    const subfolder = hashNamespace(namespace);

    const root = path.join(__dirname, "/../../schemas");
    const folder = path.normalize(path.join(root, subfolder));

    if (!fs.existsSync(folder)) {
        fs.mkdirSync(folder);
    }
    if (schema_type === "") {
        return path.join(folder, `${schemaName}.js`);
    }
    return path.join(folder, `${schemaName}_${schema_type}.js`);
}

/**
 * convert a nodeset enumeration into a javascript script enumeration code
 * @method generateEnumerationCode
 * @param dataType
 * @param filename {string} the output filename
 *
 */
function generateEnumerationCode(dataType, filename) {
    assert(typeof filename === "string");

    const dataTypeName = dataType.browseName.name.toString();
    assert(!hasEnumeration(dataTypeName));

    // create the enumeration file
    const f = new LineFile();
    f.write(`// namespace ${dataType.namespaceUri.toString()}`);
    f.write("require(\"requirish\")._(module);");
    f.write("var factories  = require(\"lib/misc/factories\");");
    f.write("var makeNodeId = require(\"lib/datamodel/nodeid\").makeNodeId;");

    f.write(`var ${dataTypeName}_Schema = {`);
    f.write(`  id:  makeNodeId(${dataType.nodeId.value},${dataType.nodeId.namespace}),`);
    f.write(`  name: '${dataTypeName}',`);
    f.write(`  namespace: '${dataType.nodeId.namespace}',`);

    f.write("  enumValues: {");
    dataType.definition.forEach((pair) => {
        f.write(`     ${pair.name}: ${pair.value},`);
    });
    f.write("  }");
    f.write("};");
    f.write(`exports.${dataTypeName}_Schema = ${dataTypeName}_Schema;`);
    f.write(`exports.${dataTypeName} = factories.registerEnumeration(${dataTypeName}_Schema);`);
    f.save(filename);
}

/**
 * var dataType = {
 *    browseName: "Color",
 *    definition: [
 *      { name: "Red",  value: 12},
 *      { name: "Blue", value: 11}
 *    ]
 * };
 *
 * makeEnumeration(dataType);
 *
 * @method makeEnumeration
 *
 * @param dataType {Object}
 * @return {*}
 */
function makeEnumeration(dataType, bForce) {
    assert(dataType);
    assert(dataType.hasOwnProperty("browseName"));
    assert(dataType.browseName instanceof QualifiedName);
    assert(_.isArray(dataType.definition));

    const dataTypeName = dataType.browseName.name.toString();
    if (hasEnumeration(dataTypeName)) {
        return getEnumeration(dataTypeName).typedEnum;
    }
    // var Enumeration_Schema = {
    //    id: dataType.nodeId,
    //    name: dataType.browseName.toString(),
    //    enumValues: {}
    // };
    //
    // dataType.definition.forEach(function (pair) {
    //    Enumeration_Schema.enumValues[pair.name] = parseInt(pair.value, 10);
    // });

    const namespace = dataType.namespaceUri;
    const filename = getSchemaSourceFile(namespace, dataType.browseName.toString(), "enum");

    generateEnumerationCode(dataType, filename);

    const relative_filename = normalizeRequireFile(__dirname, filename);

    return require(relative_filename)[dataType.browseName.toString()];
}

function generateStructureCode(namespace, schema) {
    const name = schema.name;

    const f = new LineFile();

    f.write("require(\"requirish\")._(module);");
    f.write("var factories  = require(\"lib/misc/factories\");");
    f.write("var coerceNodeId = require(\"lib/datamodel/nodeid\").coerceNodeId;");
    f.write(`var ${schema.name}_Schema = {`);
    f.write(`    id:  coerceNodeId('${schema.id.toString()}'),`);
    f.write(`    name: "${name}",`);
    f.write("    fields: [");
    schema.fields.forEach((field) => {
        f.write("       {");
        f.write(`           name: "${field.name}",`);
        f.write(`           fieldType: "${field.fieldType}"`);
        if (field.isArray) {
            f.write(`         ,   isArray:${field.isArray ? "true" : false}`);
        }
        if (field.description) {
            f.write(`          , documentation: "${field.description}" `);
        }
        f.write("       },");
    });
    f.write("        ]");
    f.write("    };");
    f.write(`exports.${name}_Schema = ${name}_Schema;`);
    // xx write("exports."+name+" = factories.registerObject(" + name+"_Schema);");

    const filename = getSchemaSourceFile(namespace, name, "schema");
    f.save(filename);
}

function generateFileCode(namespace, schema) {
    assert(typeof namespace === "string");

    const f = new LineFile();

    const name = schema.name;
    const hint = `schemas/${hashNamespace(namespace)}`;

    f.write(`// namespace ${namespace.toString()}`);
    f.write("require(\"requirish\")._(module);");
    f.write('import createObject from "lib/misc/create-factory"');
    // f.write("var  registerObject = require(\"lib/misc/factories\").registerObject;");
    // f.write("registerObject('_generated_schemas|"+ name + "','_generated_schemas');");
    f.write(`createObject('${hint}|${name}');`);

    //
    f.write(`require("${hint}/${name}_schema");`);

    // var filename = "../_generated_schemas/_auto_generated_"+ name;
    let filename = `_generated_/_auto_generated_${name}`;
    f.write(`var ${name} = require("${filename}").${name};`);
    f.write(`exports.${name} = ${name};`);

    filename = getSchemaSourceFile(namespace, name, "");
    f.save(filename);
}

function generateFile(namespace, schema) {
    assert(typeof namespace === "string");

    const name = schema.name;
    const hint = `schemas/${hashNamespace(namespace)}`;

    createObject(`${hint}|${name}`);
}

/*= private
 *
 * @example:
 * @example:
 *    var dataType =  {
 *       browseName: "ServerStatusDataType",
 *       definition: [
 *           { name "timeout", dataType: "UInt32" }
 *       ]
 *    };
 * @param dataType {Object}
 * @return {*}
 */
function constructSchema(addressSpace, dataType) {
    let dataTypeName = dataType.browseName.name.toString();
    // remove DataType to get the name of the class
    dataTypeName = dataTypeName.replace(/DataType/, "");

    const schema = {
        id: dataType.binaryEncodingNodeId,
        name: dataTypeName,
        namespace: dataType.nodeId.namespace,
        fields: [
            // { name: "title", fieldType: "UAString" , isArray: false , documentation: "some text"},
        ]
    };
    const enumeration = addressSpace.findDataType("Enumeration");
    assert(enumeration, "Enumeration Type not found: please check your nodeset file");
    const structure = addressSpace.findDataType("Structure");
    assert(structure, "Structure Type not found: please check your nodeset file");

    // construct the fields
    dataType.definition.forEach((pair) => {
        const dataTypeId = resolveNodeId(pair.dataType);

        const fieldDataType = addressSpace.findNode(dataTypeId);

        if (!fieldDataType) {
            throw new Error(` cannot find description for object ${dataTypeId}. Check that this node exists in the nodeset.xml file`);
        }

        // xx console.log("xxxxx dataType",dataType.toString());

        // check if  dataType is an enumeration or a structure or  a basic type
        if (fieldDataType.isSupertypeOf(enumeration)) {
            makeEnumeration(fieldDataType);
        }
        if (fieldDataType.isSupertypeOf(structure)) {
            makeStructure(fieldDataType);
        }

        let dataTypeName = fieldDataType.browseName.toString();
        dataTypeName = dataTypeName.replace(/DataType/, "");

        schema.fields.push({
            name: lowerFirstLetter(pair.name),
            fieldType: dataTypeName,
            isArray: false,
            description: (pair.description ? pair.description.text : "")
        });
    });
    return schema;
}
function makeStructure(dataType, bForce) {
    bForce = !!bForce;

    assert(dataType instanceof UADataType);

    const addressSpace = dataType.addressSpace;
    assert(addressSpace.constructor.name === "AddressSpace");
    assert(addressSpace instanceof AddressSpace);

    const namespaceUri = addressSpace.getNamespaceUri(dataType.nodeId.namespace);

    // istanbul ignore next
    if (!dataType.binaryEncodingNodeId) {
        throw new Error(`DataType with name ${dataType.browseName.toString()} has no binaryEncoding node\nplease check your nodeset file`);
    }

    // if binaryEncodingNodeId is in the standard factory => no need to overwrite

    if (!bForce && (hasConstructor(dataType.binaryEncodingNodeId) || dataType.binaryEncodingNodeId.namespace === 0)) {
        // xx console.log("Skipping standard constructor".bgYellow ," for dataType" ,dataType.browseName.toString());
        return getConstructor(dataType.binaryEncodingNodeId);
    }

    const schema = constructSchema(addressSpace, dataType);

    generateFileCode(namespaceUri, schema);

    generateStructureCode(namespaceUri, schema);

    const filename = getSchemaSourceFile(namespaceUri, schema.name, "");

    const relative_filename = normalizeRequireFile(__dirname, filename);

    // xx console.log("xxxxxxxxxxxxxxxxxx => ".green,schema.name,filename.cyan,relative_filename.yellow);

    const constructor = require(relative_filename)[schema.name];
    assert(_.isFunction(constructor), "expecting a constructor here");

    return constructor;
}

const nodeset = {
    ServerState: null,
    ServerStatus: null,
    ServiceCounter: null,
    SessionDiagnostics: null
};

function registerDataTypeEnum(addressSpace, dataTypeName, bForce) {
    const dataType = addressSpace.findDataType(dataTypeName);
    assert(dataType);
    const superType = addressSpace.findNode(dataType.subtypeOf);
    assert(superType.browseName.toString() === "Enumeration");
    return makeEnumeration(dataType, bForce);
}

function registerDataType(addressSpace, dataTypeName, bForce) {
    let dataType = addressSpace.findDataType(`${dataTypeName}DataType`);
    if (!dataType) {
        dataType = addressSpace.findDataType(dataTypeName);
    }

    // istanbul ignore next
    if (!dataType) {
        console.log(`registerDataType: warning : Cannot find DataType ${dataTypeName}`);
        return null;
        // xx throw new Error(" Cannot find DataType " + dataTypeName);
    }

    const superType = addressSpace.findNode(dataType.subtypeOf);
    assert(superType.browseName.toString() === "Structure");

    // finding object with encoding
    //
    //   <UAObject NodeId="i=864" BrowseName="Default Binary" SymbolicName="DefaultBinary">
    //   <DisplayName>Default Binary</DisplayName>
    //   <References>
    //       <Reference ReferenceType="HasEncoding" IsForward="false">i=862</Reference>
    return makeStructure(dataType, bForce);
}

/**
 * creates the requested data structure and javascript objects for the OPCU objects
 * @method createExtensionObjectDefinition
 * @param addressSpace {AddressSpace}
 */
const createExtensionObjectDefinition = (addressSpace) => {
    assert(addressSpace instanceof AddressSpace);
    const force = true;
    // nodeset.ApplicationDescription = nodeset.ApplicationDescription || registerDataType(addressSpace, "ApplicationDescription",force);
    nodeset.ServerState = nodeset.ServerState || registerDataTypeEnum(addressSpace, "ServerState", force);
    nodeset.ServerStatus = nodeset.ServerStatus || registerDataType(addressSpace, "ServerStatus", force);
    nodeset.ServiceCounter = nodeset.ServiceCounter || registerDataType(addressSpace, "ServiceCounter", force);
    nodeset.SessionDiagnostics = nodeset.SessionDiagnostics || registerDataType(addressSpace, "SessionDiagnostics", force);
    nodeset.ServerDiagnosticsSummary = nodeset.ServerDiagnosticsSummary || registerDataType(addressSpace, "ServerDiagnosticsSummary", force);
    nodeset.SubscriptionDiagnostics = nodeset.SubscriptionDiagnostics || registerDataType(addressSpace, "SubscriptionDiagnostics", force);

    nodeset.ModelChangeStructure = nodeset.ModelChangeStructure || registerDataType(addressSpace, "ModelChangeStructure", force);
    nodeset.SemanticChangeStructure = nodeset.SemanticChangeStructure || registerDataType(addressSpace, "SemanticChangeStructure", force);
    nodeset.RedundantServer = nodeset.RedundantServer || registerDataType(addressSpace, "RedundantServer", force);
    nodeset.SamplingIntervalDiagnostics = nodeset.SamplingIntervalDiagnostics || registerDataType(addressSpace, "SamplingIntervalDiagnostics", force);
    nodeset.SessionSecurityDiagnostics = nodeset.SessionSecurityDiagnostics || registerDataType(addressSpace, "SessionSecurityDiagnostics", force);

    /**
     *  This Structured DataType defines the local time that may or may not take daylight saving time
     *  into account. Its elements are described in Table 24.
     *  Table 24 – TimeZoneDataType Definition
     *  Name                       Type       Description
     *  TimeZoneDataType structure
     *  offset                     Int16     The offset in minutes from UtcTime
     *  daylightSavingInOffset     Boolean   If TRUE, then daylight saving time (DST) is in effect and offset
     *                                       includes the DST correction. If FALSE then the offset does not
     *                                       include the DST correction and DST may or may not have
     *                                       been in effect.
     */
    nodeset.TimeZoneDataType = nodeset.TimeZoneDataType || registerDataType(addressSpace, "TimeZoneDataType", force);
};

export {
    createExtensionObjectDefinition,
    makeEnumeration,
    makeStructure,
    nodeset
};
<|endoftext|>