<commit_before>/*jslint browser: true */
/*globals Backbone, $, tangelo, vg, _, d3 */

var visfiles;

var app = {};

// Tables of Backbone objects.
app.models = {};
app.collections = {};
app.views = {};

// A model describing a file that contains a Lyra-edited visualization.
app.models.Vis = Backbone.Model.extend({
    initialize: function (options) {
        "use strict";

        options = options || {};
        this.girderApi = options.girderApi;
        //this.itemId = options.itemId;
    },

    idAttribute: "_id",

    save: function () {
        "use strict";

        var url = this.girderApi + "/file",
            saveObj;

        if (this.isNew()) {
            saveObj = {
                name: this.get("filename"),
                visName: this.get("visName"),
                timeline: this.get("timeline"),
                vega: this.get("vega")
            };

            Backbone.ajax({
                url: url,
                type: "POST",
                dataType: "json",
                data: {
                    parentType: "folder",
                    parentId: this.folderId,
                    name: this.get("filename")
                },
                success: function (upload) {
                    this._upload(upload._id, JSON.stringify(saveObj));
                }
            });
        } else {
        }
    },

/*    fetch: function () {*/
    //},

/*    destroy: function () {*/
    //},

    _upload: function (data) {
        "use strict";

        console.log("saving: " + this.get("filename"));
    },

    url: function () {
        "use strict";

        return this.girderApi + "/item/" + this.get("_id") + "/download";
    }
});

app.models.Data = Backbone.Model.extend({
    initialize: function (options) {
        "use strict";

        options = options || {};
        this.girderApi = options.girderApi;
        //this.itemId = options.itemId;
    },

    idAttribute: "_id",

    url: function () {
        "use strict";

        return this.girderApi + "/item/" + this.get("_id") + "/download";
    },

    parse: function (response) {
        return {
            data: response
        };
    }
});

// A model describing a file - either a visualization or data.
app.models.File = Backbone.Model.extend({});

// A collection describing all the Lyra vis files in a Girder instance.
app.collections.Folder = Backbone.Collection.extend({
    model: app.models.File,

    initialize: function (models, options) {
        "use strict";

        options = options || {};

        this.url = options.girderApi + "/item?folderId=" + options.folderId;
        this.fetch();
    }
});

// A view that renders a Vega visualization.
app.views.Vega = Backbone.View.extend({
    initialize: function (options) {
        "use strict";

/*        this.model = new app.models.Vis({*/
            //girderApi: options.girderApi
        /*});*/
        this.girderApi = options.girderApi;
        Backbone.on("select:vis", this.loadVis, this);
    },

    loadVis: function (file) {
        "use strict";

        if (file.has("_id")) {
            this.model = new app.models.Vis({
                girderApi: this.girderApi
            });
            this.model.set("_id", file.get("_id"));
            this.model.fetch({
                success: _.bind(function () {
                    this.render();
                }, this)
            });
        } else {
            this.model = file;
            this.render();
        }
    },

    render: function () {
        "use strict";

        var vega = this.model.get("vega");

        vg.parse.spec(vega, _.bind(function (chart) {
            chart({
                el: this.el,
                renderer: "svg"
            }).update();
        }, this));
    }
});

// A view to "render" a dataset (using a read-only ACE editor instance).
app.views.Data = Backbone.View.extend({
    initialize: function (options) {
        "use strict";

        this.model = new app.models.Data({
            girderApi: options.girderApi
        });

        Backbone.on("select:data", this.loadData, this);
    },

    loadData: function (file) {
        "use strict";

        this.model.set("_id", file.get("_id"));
        this.model.fetch({
            success: _.bind(function () {
                this.render();
            }, this)
        });
    },

    render: function () {
        "use strict";

        console.log(this.model);
    },

    getData: function () {
        return this.model.get("data");
    }
});

// A collection describing all the data files in a Girder instance.
app.collections.Data = Backbone.Collection.extend({
});

// A view that renders a single file as a list item.
app.views.File = Backbone.View.extend({
    initialize: function (options) {
        "use strict";

        this.selectedEvent = options.selectedEvent;
    },

    tagName: "li",

    events: {
        click: "selected"
    },

    render: function () {
        "use strict";

        d3.select(this.el)
            .append("a")
            .attr("href", "#")
            .html(this.model.get("name"));

        return this;
    },

    selected: function () {
        "use strict";

        Backbone.trigger(this.selectedEvent, this.model);
    }
});

// A view that renders a list of files.
app.views.FileMenu = Backbone.View.extend({
    tagName: "div",

    initialize: function (options) {
        "use strict";

        var template;

        //this.options = options || {};
        this.selectedEvent = options.selectedEvent;

        template = _.template($("#vis-files-view-template").html(), {});
        this.$el.html(template);

        // When the collection gains an item, add it to the dropdown menu.
        this.collection.on("add", this.addItem, this);

        // When a visualization is selected, change the dropdown menu text.
        Backbone.on(this.selectedEvent, this.setSelected, this);
    },

    addItem: function (file) {
        "use strict";

        var newitem = new app.views.File({
            className: "file",
            model: file,
            selectedEvent: this.selectedEvent
        });

        this.$el.find("ul")
            .append(newitem.render().el);
    },

    setSelected: function (f) {
        "use strict";

        this.selectedModel = f;

        var label = f.get("name");

        if (f.unsaved) {
            label = "" + label + "";
        }

        label += " ";

        this.$el.find("button")
            .html(label);
    },

    getSelected: function () {
        return this.selectedModel;
    }
});

$(function () {
    "use strict";

    tangelo.config("config.json", function (config) {
        var main;

        // Issue ajax calls to get the Lyra collection in girder, and then both
        // the visualization and data folders therein.
        $.ajax({
            url: config.girderApi + "/collection",
            type: "GET",
            dataType: "json",
            data: {
                text: config.collection
            },
            success: function (lyraCollection) {
                // Now find the visualizations folder.
                $.ajax({
                    url: config.girderApi + "/folder",
                    type: "GET",
                    dataType: "json",
                    data: {
                        parentType: "collection",
                        parentId: lyraCollection._id,
                        text: config.visFolder
                    },
                    success: function (visFolder) {
                        // Make sure there is a result.
                        if (visFolder.length === 0) {
                            console.warn("No folder '" + config.visFolder + "' found in collection '" + config.collection + "'");
                        }

                        // Find the data folder.
                        $.ajax({
                            url: config.girderApi + "/folder",
                            type: "GET",
                            dataType: "json",
                            data: {
                                parentType: "collection",
                                parentId: lyraCollection._id,
                                text: config.dataFolder
                            },
                            success: function (dataFolder) {
                                // Make sure there is a result.
                                if (dataFolder.length === 0) {
                                    console.warn("No folder '" + config.dataFolder + "' found in collection '" + config.collection + "'");
                                }

                                // Run the actual application.
                                main(config, visFolder[0]._id, dataFolder[0]._id);
                            }
                        });
                    }
                });
            }

        });

        // The main application.
        main = function (config, visFolderId, dataFolderId) {
            var //visfiles,
                visMenu,
                vis,
                datafiles,
                dataMenu,
                data,
                f;

            f = {};

            // A function that launches a Lyra editor window.
            f.launchLyra = function (qargs) {
                var lyra = window.open("/lyra/editor.html?editor=true", "_blank");
                lyra.onload = function () {
                    lyra.postMessage(qargs, window.location.origin);
                };
            };

            f.createNew = function () {
                f.launchLyra({
                    new: true,
                    timeline: null,
                    data: encodeURIComponent(JSON.stringify(data.getData()))
                });
            };

            f.edit = function () {
                f.launchLyra({
                    new: false,
                    timeline: encodeURIComponent(JSON.stringify(visMenu.getSelectedVis().timeline)),
                    data: encodeURIComponent(JSON.stringify(dataMenu.getSelectedData()))
                });
            };

            f.receiveMessage = function (e) {
                var model,
                    newname;

                if (e.data.new) {
                    newname = "Unsaved " + _.uniqueId();

                    // Construct a new Vis model to represent the newly created
                    // Vega visualization.
                    model = new app.models.Vis({
                        name: newname,
                        visName: newname,
                        timeline: e.data.timeline,
                        vega: e.data.vega
                    });

                    visfiles.add(model);
                } else {
                    console.log("incoming message");
                    console.log(e.data);
                }
            };

            // The "New" button.
            d3.select("#create")
                .on("click", f.createNew);

            // Set up to receive messages.
            window.addEventListener("message", f.receiveMessage, false);

            // A collection of visualization files residing on Girder, and a
            // dropdown menu to select them.
            visfiles = new app.collections.Folder([], {
                girderApi: config.girderApi,
                folderId: visFolderId
            });

            visMenu = new app.views.FileMenu({
                el: "#vis-files-view",
                collection: visfiles,
                selectedEvent: "select:vis"
            });

            // The same, but for the data files.
            datafiles = new app.collections.Folder([], {
                girderApi: config.girderApi,
                folderId: dataFolderId
            });

            dataMenu = new app.views.FileMenu({
                el: "#data-files-view",
                collection: datafiles,
                selectedEvent: "select:data"
            });

            // A Vega view.
            vis = new app.views.Vega({
                el: "#vega",
                girderApi: config.girderApi
            });

            // A data view.
            data = new app.views.Data({
                el: "#edit-area",
                girderApi: config.girderApi
            });
        };
    });
});
<commit_msg>New visualizations are automatically selected (i.e., rendered) upon creation<commit_after>/*jslint browser: true */
/*globals Backbone, $, tangelo, vg, _, d3 */

var visfiles;

var app = {};

// Tables of Backbone objects.
app.models = {};
app.collections = {};
app.views = {};

// A model describing a file that contains a Lyra-edited visualization.
app.models.Vis = Backbone.Model.extend({
    initialize: function (options) {
        "use strict";

        options = options || {};
        this.girderApi = options.girderApi;
        //this.itemId = options.itemId;
    },

    idAttribute: "_id",

    save: function () {
        "use strict";

        var url = this.girderApi + "/file",
            saveObj;

        if (this.isNew()) {
            saveObj = {
                name: this.get("filename"),
                visName: this.get("visName"),
                timeline: this.get("timeline"),
                vega: this.get("vega")
            };

            Backbone.ajax({
                url: url,
                type: "POST",
                dataType: "json",
                data: {
                    parentType: "folder",
                    parentId: this.folderId,
                    name: this.get("filename")
                },
                success: function (upload) {
                    this._upload(upload._id, JSON.stringify(saveObj));
                }
            });
        } else {
        }
    },

/*    fetch: function () {*/
    //},

/*    destroy: function () {*/
    //},

    _upload: function (data) {
        "use strict";

        console.log("saving: " + this.get("filename"));
    },

    url: function () {
        "use strict";

        return this.girderApi + "/item/" + this.get("_id") + "/download";
    }
});

app.models.Data = Backbone.Model.extend({
    initialize: function (options) {
        "use strict";

        options = options || {};
        this.girderApi = options.girderApi;
        //this.itemId = options.itemId;
    },

    idAttribute: "_id",

    url: function () {
        "use strict";

        return this.girderApi + "/item/" + this.get("_id") + "/download";
    },

    parse: function (response) {
        return {
            data: response
        };
    }
});

// A model describing a file - either a visualization or data.
app.models.File = Backbone.Model.extend({});

// A collection describing all the Lyra vis files in a Girder instance.
app.collections.Folder = Backbone.Collection.extend({
    model: app.models.File,

    initialize: function (models, options) {
        "use strict";

        options = options || {};

        this.url = options.girderApi + "/item?folderId=" + options.folderId;
        this.fetch();
    }
});

// A view that renders a Vega visualization.
app.views.Vega = Backbone.View.extend({
    initialize: function (options) {
        "use strict";

/*        this.model = new app.models.Vis({*/
            //girderApi: options.girderApi
        /*});*/
        this.girderApi = options.girderApi;
        Backbone.on("select:vis", this.loadVis, this);
    },

    loadVis: function (file) {
        "use strict";

        if (file.has("_id")) {
            this.model = new app.models.Vis({
                girderApi: this.girderApi
            });
            this.model.set("_id", file.get("_id"));
            this.model.fetch({
                success: _.bind(function () {
                    this.render();
                }, this)
            });
        } else {
            this.model = file;
            this.render();
        }
    },

    render: function () {
        "use strict";

        var vega = this.model.get("vega");

        vg.parse.spec(vega, _.bind(function (chart) {
            chart({
                el: this.el,
                renderer: "svg"
            }).update();
        }, this));
    }
});

// A view to "render" a dataset (using a read-only ACE editor instance).
app.views.Data = Backbone.View.extend({
    initialize: function (options) {
        "use strict";

        this.model = new app.models.Data({
            girderApi: options.girderApi
        });

        Backbone.on("select:data", this.loadData, this);
    },

    loadData: function (file) {
        "use strict";

        this.model.set("_id", file.get("_id"));
        this.model.fetch({
            success: _.bind(function () {
                this.render();
            }, this)
        });
    },

    render: function () {
        "use strict";

        console.log(this.model);
    },

    getData: function () {
        return this.model.get("data");
    }
});

// A collection describing all the data files in a Girder instance.
app.collections.Data = Backbone.Collection.extend({
});

// A view that renders a single file as a list item.
app.views.File = Backbone.View.extend({
    initialize: function (options) {
        "use strict";

        this.selectedEvent = options.selectedEvent;
    },

    tagName: "li",

    events: {
        click: "selected"
    },

    render: function () {
        "use strict";

        d3.select(this.el)
            .append("a")
            .attr("href", "#")
            .html(this.model.get("name"));

        return this;
    },

    selected: function () {
        "use strict";

        Backbone.trigger(this.selectedEvent, this.model);
    }
});

// A view that renders a list of files.
app.views.FileMenu = Backbone.View.extend({
    tagName: "div",

    initialize: function (options) {
        "use strict";

        var template;

        //this.options = options || {};
        this.selectedEvent = options.selectedEvent;

        template = _.template($("#vis-files-view-template").html(), {});
        this.$el.html(template);

        // When the collection gains an item, add it to the dropdown menu.
        this.collection.on("add", this.addItem, this);

        // When a visualization is selected, change the dropdown menu text.
        Backbone.on(this.selectedEvent, this.setSelected, this);
    },

    addItem: function (file) {
        "use strict";

        var newitem = new app.views.File({
            className: "file",
            model: file,
            selectedEvent: this.selectedEvent
        });

        this.$el.find("ul")
            .append(newitem.render().el);
    },

    setSelected: function (f) {
        "use strict";

        this.selectedModel = f;

        var label = f.get("name");

        if (f.unsaved) {
            label = "" + label + "";
        }

        label += " ";

        this.$el.find("button")
            .html(label);
    },

    getSelected: function () {
        return this.selectedModel;
    }
});

$(function () {
    "use strict";

    tangelo.config("config.json", function (config) {
        var main;

        // Issue ajax calls to get the Lyra collection in girder, and then both
        // the visualization and data folders therein.
        $.ajax({
            url: config.girderApi + "/collection",
            type: "GET",
            dataType: "json",
            data: {
                text: config.collection
            },
            success: function (lyraCollection) {
                // Now find the visualizations folder.
                $.ajax({
                    url: config.girderApi + "/folder",
                    type: "GET",
                    dataType: "json",
                    data: {
                        parentType: "collection",
                        parentId: lyraCollection._id,
                        text: config.visFolder
                    },
                    success: function (visFolder) {
                        // Make sure there is a result.
                        if (visFolder.length === 0) {
                            console.warn("No folder '" + config.visFolder + "' found in collection '" + config.collection + "'");
                        }

                        // Find the data folder.
                        $.ajax({
                            url: config.girderApi + "/folder",
                            type: "GET",
                            dataType: "json",
                            data: {
                                parentType: "collection",
                                parentId: lyraCollection._id,
                                text: config.dataFolder
                            },
                            success: function (dataFolder) {
                                // Make sure there is a result.
                                if (dataFolder.length === 0) {
                                    console.warn("No folder '" + config.dataFolder + "' found in collection '" + config.collection + "'");
                                }

                                // Run the actual application.
                                main(config, visFolder[0]._id, dataFolder[0]._id);
                            }
                        });
                    }
                });
            }

        });

        // The main application.
        main = function (config, visFolderId, dataFolderId) {
            var //visfiles,
                visMenu,
                vis,
                datafiles,
                dataMenu,
                data,
                f;

            f = {};

            // A function that launches a Lyra editor window.
            f.launchLyra = function (qargs) {
                var lyra = window.open("/lyra/editor.html?editor=true", "_blank");
                lyra.onload = function () {
                    lyra.postMessage(qargs, window.location.origin);
                };
            };

            f.createNew = function () {
                f.launchLyra({
                    new: true,
                    timeline: null,
                    data: encodeURIComponent(JSON.stringify(data.getData()))
                });
            };

            f.edit = function () {
                f.launchLyra({
                    new: false,
                    timeline: encodeURIComponent(JSON.stringify(visMenu.getSelectedVis().timeline)),
                    data: encodeURIComponent(JSON.stringify(dataMenu.getSelectedData()))
                });
            };

            f.receiveMessage = function (e) {
                var model,
                    newname;

                if (e.data.new) {
                    newname = "Unsaved " + _.uniqueId();

                    // Construct a new Vis model to represent the newly created
                    // Vega visualization.
                    model = new app.models.Vis({
                        name: newname,
                        visName: newname,
                        timeline: e.data.timeline,
                        vega: e.data.vega
                    });

                    // Add the model to the vis files list, and simulate its
                    // selection (so that the menu will show it as active, and
                    // it will be rendered to the Vega view).
                    visfiles.add(model);
                    Backbone.trigger("select:vis", model);
                } else {
                    console.log("incoming message");
                    console.log(e.data);
                }
            };

            // The "New" button.
            d3.select("#create")
                .on("click", f.createNew);

            // Set up to receive messages.
            window.addEventListener("message", f.receiveMessage, false);

            // A collection of visualization files residing on Girder, and a
            // dropdown menu to select them.
            visfiles = new app.collections.Folder([], {
                girderApi: config.girderApi,
                folderId: visFolderId
            });

            visMenu = new app.views.FileMenu({
                el: "#vis-files-view",
                collection: visfiles,
                selectedEvent: "select:vis"
            });

            // The same, but for the data files.
            datafiles = new app.collections.Folder([], {
                girderApi: config.girderApi,
                folderId: dataFolderId
            });

            dataMenu = new app.views.FileMenu({
                el: "#data-files-view",
                collection: datafiles,
                selectedEvent: "select:data"
            });

            // A Vega view.
            vis = new app.views.Vega({
                el: "#vega",
                girderApi: config.girderApi
            });

            // A data view.
            data = new app.views.Data({
                el: "#edit-area",
                girderApi: config.girderApi
            });
        };
    });
});
<|endoftext|>


<commit_before>'use strict';

var assert = require('yeoman-generator').assert;
var helpers = require('yeoman-generator').test;
var path = require('path');
var os = require('os');
var shell = require('shelljs');

describe('js-mocha kata stack', function (done) {

  var tempTestingDir = path.join(os.tmpdir(), './temp-test');

  it('installs the correct files', function (done) {

    helpers
      .run(path.join(__dirname, '../app'))
      .inDir(tempTestingDir)
      .withOptions({
        'skip-install': true
      })
      .withPrompt({
        name  : 'some_name',
        kata  : 'string-calculator',
        stack : 'js-mocha'
      })
      .on('end', function() {
        assert.file([
          'README.md',
          'gulpfile.js',
          'src/StringCalculator.js',
          'spec/StringCalculator.spec.js',
          'package.json',
          '.gitignore',
          '.jshintrc'
        ]);

        done();
      });
  });

  it('can properly run a failing test after installation', function(done) {

    this.timeout(20000);

    helpers
      .run(path.join(__dirname, '../app'))
      .inDir(tempTestingDir)
      .withOptions({
        'skip-install': true
      })
      .withPrompt({
        name  : 'some_name',
        kata  : 'string-calculator',
        stack : 'js-mocha'
      })
      .on('end', function() {
        shell.exec('npm install', {
          silent: true
        });

        var results = shell.exec('npm test', {
          silent: true
        });

        // failing initial test should return non-zero status
        assert.notEqual(results.code, 0, '`npm test` failed to return a non-zero status');
        assert(results.output.match(/StringCalculator variable should not be null/), '`npm test` output should complain that StringCalculator should not be null');

        done();
      });
  });

});
<commit_msg>Added assertions testing output of `gulp test` command<commit_after>'use strict';

var assert = require('yeoman-generator').assert;
var helpers = require('yeoman-generator').test;
var path = require('path');
var os = require('os');
var shell = require('shelljs');

describe('js-mocha kata stack', function (done) {

  var tempTestingDir = path.join(os.tmpdir(), './temp-test');

  it('installs the correct files', function (done) {

    helpers
      .run(path.join(__dirname, '../app'))
      .inDir(tempTestingDir)
      .withOptions({
        'skip-install': true
      })
      .withPrompt({
        name  : 'some_name',
        kata  : 'string-calculator',
        stack : 'js-mocha'
      })
      .on('end', function() {
        assert.file([
          'README.md',
          'gulpfile.js',
          'src/StringCalculator.js',
          'spec/StringCalculator.spec.js',
          'package.json',
          '.gitignore',
          '.jshintrc'
        ]);

        done();
      });
  });

  it('can properly run a failing test after installation', function(done) {

    this.timeout(20000);

    helpers
      .run(path.join(__dirname, '../app'))
      .inDir(tempTestingDir)
      .withOptions({
        'skip-install': true
      })
      .withPrompt({
        name  : 'some_name',
        kata  : 'string-calculator',
        stack : 'js-mocha'
      })
      .on('end', function() {
        shell.exec('npm install', {
          silent: true
        });

        var results = shell.exec('npm test', {
          silent: true
        });

        // failing initial test should return non-zero status
        assert.notEqual(results.code, 0, '`npm test` failed to return a non-zero status');
        assert(results.output.match(/StringCalculator variable should not be null/), '`npm test` output should complain that StringCalculator should not be null');

        results = shell.exec('gulp test', {
          silent: true
        });

        assert(results.output.match(/Using gulpfile /), '`gulp test` output should include text "Using glupfile"');
        assert(results.output.match(/0 passing/), '`gulp test` output should report 0 passing tests');
        assert(results.output.match(/1 failing/), '`gulp test` output should report 1 failing test');
        assert(results.output.match(/StringCalculator variable should not be null/), '`gulp test` output should complain that StringCalculator should not be null');

        done();
      });
  });

});
<|endoftext|>


<commit_before>/*global describe, it, beforeEach */
/**
 * observe-plus.js - https://github.com/podefr/observe-plus
 * Copyright(c) 2014-2015 Olivier Scherrer <pode.fr@gmail.com>
 * MIT Licensed
 */
"use strict";

var chai = require("chai");
var expect = chai.expect;
var asap = require("asap");
var sinon = require("sinon");

var observeObject = require("../src/observe-plus").observe;

describe("GIVEN an observed object", function () {

    var pojo,
        observer,
        aggregatedEvents;

    function resetAggregatedEvents() {
        aggregatedEvents = [];
    }

    beforeEach(function () {
        pojo = {};
        observer = observeObject(pojo);
    });

    describe("WHEN observing newly added properties", function () {

        var dispose, spy;

        beforeEach(function () {
            resetAggregatedEvents();
            spy = sinon.spy();
            dispose = observer.observe("add", spy);
        });

        afterEach(function () {
            spy.reset();
        });

        it("THEN shouldn't publish any event before a new property is added", function (done) {
            asap(function () {
                expect(spy.callCount).to.equal(0);
                done();
            });
        });

        describe("WHEN a new property is added", function () {
            beforeEach(function () {
                pojo.newProperty = "newValue";
            });

            it("THEN should publish a new event", function (done) {
                asap(function () {
                    expect(spy.firstCall.args[0]).to.eql({
                        type: "add",
                        name: "newProperty",
                        object: pojo
                    });
                    done();
                });
            });

            it("THEN only published one event", function (done) {
                asap(function () {
                    expect(spy.callCount).to.equal(1);
                    done();
                });
            });

            describe("WHEN destroying", function () {
                beforeEach(function () {
                    resetAggregatedEvents();
                    observer.destroy();

                    pojo.anotherProperty = "value";
                });

                it("THEN doesn't publish events anymore", function (done) {
                    asap(function () {
                        expect(spy.callCount).to.equal(1);
                        done();
                    });
                });
            });

            describe("WHEN the property is modified", function () {
                beforeEach(function () {
                    resetAggregatedEvents();
                    observer.observe("update", spy);
                    pojo.newProperty = "updatedValue";
                });

                it("THEN calls the observer with the updated event", function (done) {
                    asap(function () {
                        expect(spy.secondCall.args[0]).to.eql({
                            type: "update",
                            name: "newProperty",
                            object: pojo,
                            oldValue: "newValue"
                        });
                        done();
                    });
                });
            });

            describe("WHEN the property is deleted", function () {
                beforeEach(function () {
                    resetAggregatedEvents();
                    observer.observe("delete", spy);
                    delete pojo.newProperty;
                });

                it("THEN calls the observer with undefined and the old value", function (done) {
                    asap(function () {
                        expect(spy.secondCall.args[0]).to.eql({
                            type: "delete",
                            name: "newProperty",
                            object: pojo,
                            oldValue: "newValue"
                        });
                        done();
                    });
                });
            });
        });

        describe("WHEN a new observer is added", function () {
            var dispose2;

            beforeEach(function () {
                resetAggregatedEvents();
                dispose2 = observer.observe("add", spy);
            });

            describe("WHEN an observer is disposed of", function () {
                var isDisposedOf = false;

                beforeEach(function () {
                    isDisposedOf = dispose();
                });

                describe("WHEN a new property is added", function () {
                    beforeEach(function () {
                        resetAggregatedEvents();
                        pojo.newProperty = {};
                    });

                    it("THEN doesn't call the disposed observer anymore", function (done) {
                        asap(function () {
                            expect(spy.callCount).to.equal(1);

                            done();
                        });
                    });

                    it("THEN tells if the observer can be disposed of", function () {
                        expect(isDisposedOf).to.be.true;
                    });

                    describe("WHEN an observer has been disposed of", function () {
                        beforeEach(function () {
                            isDisposedOf = dispose();
                        });

                        it("THEN tells that it can't be disposed of anymore", function (done) {
                            expect(isDisposedOf).to.be.false;
                            done();
                        });
                    });
                });
            });
        });
    });

    describe("WHEN observing specific properties", function () {
        var dispose, spy;

        beforeEach(function () {
            resetAggregatedEvents();
            spy = sinon.spy();
            dispose = observer.observeValue("newProperty", spy);
            pojo.newProperty = "newValue";
        });

        it("THEN publishes an event with the new value and the old value", function (done) {
            asap(function () {
                expect(spy.firstCall.args[0]).to.eql({
                    type: "add",
                    name: "newProperty",
                    object: pojo,
                    oldValue: undefined
                });
                done();
            });
        });
    });

    describe("WHEN observing nested properties", function () {
        var spy;

        beforeEach(function () {
            spy = sinon.spy();
            resetAggregatedEvents();
            observer.observeValue("newProperty.nested.property", spy);
            pojo.newProperty = {
                nested: {
                    property: true
                }
            };
        });

        it("Then publishes an event", function (done) {
             asap(function () {
                 expect(spy.firstCall.args[0]).to.eql({
                     type: "add",
                     object: pojo,
                     name: "newProperty.nested.property",
                     oldValue: undefined
                 });
                 done();
             });
        });

        describe("WHEN the nested property is modified", function () {
            beforeEach(function () {
                resetAggregatedEvents();
                pojo.newProperty.nested.property = false;
            });

            it("THEN publishes an update event", function (done) {
                asap(function () {
                    expect(spy.secondCall.args[0]).to.eql({
                        type: "update",
                        object: pojo,
                        name: "newProperty.nested.property",
                        oldValue: true
                    });
                    done();
                });
            });

            describe("WHEN a parent object is replaced and the value of the observed nested property changes", function () {
                beforeEach(function () {
                    resetAggregatedEvents();
                    pojo.newProperty.nested = {
                        property: true
                    };
                });

                it("THEN triggers a new event", function (done) {
                    asap(function () {
                        expect(spy.secondCall.args[0]).to.eql({
                            type: "update",
                            object: pojo,
                            name: "newProperty.nested.property",
                            oldValue: true
                        });
                        done();
                    });
                });
            });

            describe("WHEN a parent object is replaced and the value of the observed nested property doesn't change", function () {
                beforeEach(function () {
                    resetAggregatedEvents();
                    pojo.newProperty.nested = {
                        property: false
                    };
                });

                it("THEN doesn't trigger an event", function (done) {
                    asap(function () {
                        expect(spy.callCount).to.equal(2);
                        done();
                    });
                });
            });
        });

        describe("WHEN the nested property is deleted ", function () {
            beforeEach(function () {
                resetAggregatedEvents();
                delete pojo.newProperty.nested;
            });

            it("THEN publishes a delete event", function (done) {
                asap(function () {
                    expect(spy.secondCall.args[0]).to.eql({
                        type: "delete",
                        name: "newProperty.nested.property",
                        object: pojo,
                        oldValue: true
                    });
                    done();
                });
            });
        });

        describe("WHEN a parent object of a nested property is deleted", function () {
            beforeEach(function () {
                resetAggregatedEvents();
                delete pojo.newProperty;
            });

            it("THEN publishes a delete event", function (done) {
                asap(function () {
                    expect(spy.secondCall.args[0]).to.eql({
                        type: "delete",
                        name: "newProperty.nested.property",
                        object: pojo,
                        oldValue: true
                    });
                    done();
                });
            });
        });
    });

    describe("WHEN observing only once", function () {
        var dispose;
        beforeEach(function () {
            resetAggregatedEvents();
            dispose = observer.observeOnce("add", function (ev) {
                aggregatedEvents.push([ev]);
            });
        });

        describe("WHEN the property is added", function () {
            beforeEach(function () {
                pojo.newProperty = "value";
            });

            it("THEN calls the observer", function (done) {
                asap(function () {
                    var firstEvent = aggregatedEvents[0][0];
                    expect(firstEvent.name).to.equal("newProperty");
                    expect(firstEvent.object.newProperty).to.equal("value");
                    done();
                });
            });

            it("THEN is disposed of", function (done) {
                asap(function () {
                    expect(dispose()).to.be.false;
                    done();
                });
            });
        });
    });

    describe("WHEN observing a property only once", function () {
        var dispose;
        beforeEach(function () {
            resetAggregatedEvents();
            dispose = observer.observeValueOnce("newProperty", function (ev) {
                aggregatedEvents.push([ev]);
            });
            pojo.newProperty = "value";
        });

        it("THEN calls the observer", function (done) {
            asap(function () {
                var firstEvent = aggregatedEvents[0][0];
                expect(firstEvent.name).to.equal("newProperty");
                expect(firstEvent.object.newProperty).to.equal("value");
                done();
            });
        });

        it("THEN is disposed of", function (done) {
            asap(function () {
                expect(dispose()).to.be.false;
                done();
            });
        });
    });

    describe("WHEN pausing the updates", function () {
        beforeEach(function () {
            observer.observeValue("newProperty", function (ev) {
                aggregatedEvents.push([ev]);
            });
            observer.pause();
        });

        describe("WHEN a property changes", function () {
            beforeEach(function () {
                resetAggregatedEvents();
                pojo.newProperty = "value";
                pojo.newProperty = "newValue";
                delete pojo.newProperty;
            });

            it("THEN the observers aren't called", function (done) {
                asap(function () {
                    expect(aggregatedEvents.length).to.equal(0);
                    done();
                });
            });

            describe("WHEN resuming publishing the updates", function () {
                beforeEach(function () {
                    observer.resume();
                });

                it("THEN calls all the observers in order", function (done) {
                    asap(function () {
                        var firstEvent = aggregatedEvents[0][0],
                            secondEvent = aggregatedEvents[1][0],
                            thirdEvent = aggregatedEvents[2][0];

                        expect(firstEvent.type).to.equal("add");
                        expect(secondEvent.type).to.equal("update");
                        expect(thirdEvent.type).to.equal("delete");
                        done();
                    });
                });

                describe("WHEN the updates are paused and resumed again", function () {
                    beforeEach(function () {
                        resetAggregatedEvents();
                        observer.pause();
                        pojo.newProperty = "lastValue";
                        observer.resume();
                    });

                    it("THEN only publishes the new event", function (done) {
                        asap(function () {
                            expect(aggregatedEvents.length).to.equal(1);
                            done();
                        });
                    });
                });
            });
        });
    });
});<commit_msg>removing aggregateEvents<commit_after>/*global describe, it, beforeEach */
/**
 * observe-plus.js - https://github.com/podefr/observe-plus
 * Copyright(c) 2014-2015 Olivier Scherrer <pode.fr@gmail.com>
 * MIT Licensed
 */
"use strict";

var chai = require("chai");
var expect = chai.expect;
var asap = require("asap");
var sinon = require("sinon");

var observeObject = require("../src/observe-plus").observe;

describe("GIVEN an observed object", function () {

    var pojo,
        observer,
        aggregatedEvents;

    function resetAggregatedEvents() {
        aggregatedEvents = [];
    }

    beforeEach(function () {
        pojo = {};
        observer = observeObject(pojo);
    });

    describe("WHEN observing newly added properties", function () {

        var dispose, spy;

        beforeEach(function () {
            resetAggregatedEvents();
            spy = sinon.spy();
            dispose = observer.observe("add", spy);
        });

        afterEach(function () {
            spy.reset();
        });

        it("THEN shouldn't publish any event before a new property is added", function (done) {
            asap(function () {
                expect(spy.callCount).to.equal(0);
                done();
            });
        });

        describe("WHEN a new property is added", function () {
            beforeEach(function () {
                pojo.newProperty = "newValue";
            });

            it("THEN should publish a new event", function (done) {
                asap(function () {
                    expect(spy.firstCall.args[0]).to.eql({
                        type: "add",
                        name: "newProperty",
                        object: pojo
                    });
                    done();
                });
            });

            it("THEN only published one event", function (done) {
                asap(function () {
                    expect(spy.callCount).to.equal(1);
                    done();
                });
            });

            describe("WHEN destroying", function () {
                beforeEach(function () {
                    resetAggregatedEvents();
                    observer.destroy();

                    pojo.anotherProperty = "value";
                });

                it("THEN doesn't publish events anymore", function (done) {
                    asap(function () {
                        expect(spy.callCount).to.equal(1);
                        done();
                    });
                });
            });

            describe("WHEN the property is modified", function () {
                beforeEach(function () {
                    resetAggregatedEvents();
                    observer.observe("update", spy);
                    pojo.newProperty = "updatedValue";
                });

                it("THEN calls the observer with the updated event", function (done) {
                    asap(function () {
                        expect(spy.secondCall.args[0]).to.eql({
                            type: "update",
                            name: "newProperty",
                            object: pojo,
                            oldValue: "newValue"
                        });
                        done();
                    });
                });
            });

            describe("WHEN the property is deleted", function () {
                beforeEach(function () {
                    resetAggregatedEvents();
                    observer.observe("delete", spy);
                    delete pojo.newProperty;
                });

                it("THEN calls the observer with undefined and the old value", function (done) {
                    asap(function () {
                        expect(spy.secondCall.args[0]).to.eql({
                            type: "delete",
                            name: "newProperty",
                            object: pojo,
                            oldValue: "newValue"
                        });
                        done();
                    });
                });
            });
        });

        describe("WHEN a new observer is added", function () {
            var dispose2;

            beforeEach(function () {
                resetAggregatedEvents();
                dispose2 = observer.observe("add", spy);
            });

            describe("WHEN an observer is disposed of", function () {
                var isDisposedOf = false;

                beforeEach(function () {
                    isDisposedOf = dispose();
                });

                describe("WHEN a new property is added", function () {
                    beforeEach(function () {
                        resetAggregatedEvents();
                        pojo.newProperty = {};
                    });

                    it("THEN doesn't call the disposed observer anymore", function (done) {
                        asap(function () {
                            expect(spy.callCount).to.equal(1);

                            done();
                        });
                    });

                    it("THEN tells if the observer can be disposed of", function () {
                        expect(isDisposedOf).to.be.true;
                    });

                    describe("WHEN an observer has been disposed of", function () {
                        beforeEach(function () {
                            isDisposedOf = dispose();
                        });

                        it("THEN tells that it can't be disposed of anymore", function (done) {
                            expect(isDisposedOf).to.be.false;
                            done();
                        });
                    });
                });
            });
        });
    });

    describe("WHEN observing specific properties", function () {
        var dispose, spy;

        beforeEach(function () {
            resetAggregatedEvents();
            spy = sinon.spy();
            dispose = observer.observeValue("newProperty", spy);
            pojo.newProperty = "newValue";
        });

        it("THEN publishes an event with the new value and the old value", function (done) {
            asap(function () {
                expect(spy.firstCall.args[0]).to.eql({
                    type: "add",
                    name: "newProperty",
                    object: pojo,
                    oldValue: undefined
                });
                done();
            });
        });
    });

    describe("WHEN observing nested properties", function () {
        var spy;

        beforeEach(function () {
            spy = sinon.spy();
            resetAggregatedEvents();
            observer.observeValue("newProperty.nested.property", spy);
            pojo.newProperty = {
                nested: {
                    property: true
                }
            };
        });

        it("Then publishes an event", function (done) {
             asap(function () {
                 expect(spy.firstCall.args[0]).to.eql({
                     type: "add",
                     object: pojo,
                     name: "newProperty.nested.property",
                     oldValue: undefined
                 });
                 done();
             });
        });

        describe("WHEN the nested property is modified", function () {
            beforeEach(function () {
                resetAggregatedEvents();
                pojo.newProperty.nested.property = false;
            });

            it("THEN publishes an update event", function (done) {
                asap(function () {
                    expect(spy.secondCall.args[0]).to.eql({
                        type: "update",
                        object: pojo,
                        name: "newProperty.nested.property",
                        oldValue: true
                    });
                    done();
                });
            });

            describe("WHEN a parent object is replaced and the value of the observed nested property changes", function () {
                beforeEach(function () {
                    resetAggregatedEvents();
                    pojo.newProperty.nested = {
                        property: true
                    };
                });

                it("THEN triggers a new event", function (done) {
                    asap(function () {
                        expect(spy.secondCall.args[0]).to.eql({
                            type: "update",
                            object: pojo,
                            name: "newProperty.nested.property",
                            oldValue: true
                        });
                        done();
                    });
                });
            });

            describe("WHEN a parent object is replaced and the value of the observed nested property doesn't change", function () {
                beforeEach(function () {
                    resetAggregatedEvents();
                    pojo.newProperty.nested = {
                        property: false
                    };
                });

                it("THEN doesn't trigger an event", function (done) {
                    asap(function () {
                        expect(spy.callCount).to.equal(2);
                        done();
                    });
                });
            });
        });

        describe("WHEN the nested property is deleted ", function () {
            beforeEach(function () {
                resetAggregatedEvents();
                delete pojo.newProperty.nested;
            });

            it("THEN publishes a delete event", function (done) {
                asap(function () {
                    expect(spy.secondCall.args[0]).to.eql({
                        type: "delete",
                        name: "newProperty.nested.property",
                        object: pojo,
                        oldValue: true
                    });
                    done();
                });
            });
        });

        describe("WHEN a parent object of a nested property is deleted", function () {
            beforeEach(function () {
                resetAggregatedEvents();
                delete pojo.newProperty;
            });

            it("THEN publishes a delete event", function (done) {
                asap(function () {
                    expect(spy.secondCall.args[0]).to.eql({
                        type: "delete",
                        name: "newProperty.nested.property",
                        object: pojo,
                        oldValue: true
                    });
                    done();
                });
            });
        });
    });

    describe("WHEN observing only once", function () {
        var dispose, spy;

        beforeEach(function () {
            resetAggregatedEvents();
            spy = sinon.spy();
            dispose = observer.observeOnce("add", spy);
        });

        afterEach(function () {
            spy.reset();
        });

        describe("WHEN the property is added", function () {
            beforeEach(function () {
                pojo.newProperty = "value";
            });

            it("THEN calls the observer", function (done) {
                asap(function () {
                    expect(spy.firstCall.args[0]).to.eql({
                        type: "add",
                        name: "newProperty",
                        object: pojo
                    });
                    done();
                });
            });

            it("THEN is disposed of", function (done) {
                asap(function () {
                    expect(dispose()).to.be.false;
                    done();
                });
            });
        });
    });

    describe("WHEN observing a property only once", function () {
        var dispose, spy;

        beforeEach(function () {
            resetAggregatedEvents();
            spy = sinon.spy();
            dispose = observer.observeValueOnce("newProperty", spy);
            pojo.newProperty = "value";
        });

        it("THEN calls the observer", function (done) {
            asap(function () {
                expect(spy.firstCall.args[0]).to.eql({
                    type: "add",
                    object: pojo,
                    name: "newProperty",
                    oldValue: undefined
                });
                done();
            });
        });

        it("THEN is disposed of", function (done) {
            asap(function () {
                expect(dispose()).to.be.false;
                done();
            });
        });
    });

    describe("WHEN pausing the updates", function () {
        var spy;

        beforeEach(function () {
            spy = sinon.spy();
            observer.observeValue("newProperty", spy);
            observer.pause();
        });

        describe("WHEN a property changes", function () {
            beforeEach(function () {
                resetAggregatedEvents();
                pojo.newProperty = "value";
                pojo.newProperty = "newValue";
                delete pojo.newProperty;
            });

            it("THEN the observers aren't called", function (done) {
                asap(function () {
                    expect(spy.callCount).to.equal(0);
                    done();
                });
            });

            describe("WHEN resuming publishing the updates", function () {
                beforeEach(function () {
                    observer.resume();
                });

                it("THEN calls all the observers in order", function (done) {
                    asap(function () {
                        expect(spy.firstCall.args[0].type).to.equal("add");
                        expect(spy.secondCall.args[0].type).to.equal("update");
                        expect(spy.thirdCall.args[0].type).to.equal("delete");
                        done();
                    });
                });

                describe("WHEN the updates are paused and resumed again", function () {
                    beforeEach(function () {
                        resetAggregatedEvents();
                        observer.pause();
                        pojo.newProperty = "lastValue";
                        observer.resume();
                    });

                    it("THEN only publishes the new event", function (done) {
                        asap(function () {
                            expect(spy.callCount).to.equal(4);
                            done();
                        });
                    });
                });
            });
        });
    });
});<|endoftext|>


<commit_before>var _ = require('lodash');
var OAuth = require('oauth');
var optimist = require('optimist');
var prompt = require('prompt');
var util = require('util');

var schema = {
  properties: {
    consumerKey: {
      required: true
    },
    consumerSecret: {
      required: true
    },
    accessToken: {
      required: true
    },
    accessSecret: {
      required: true
    },
    friendId: {
      required: true
    }
  }
};

prompt.override = optimist.argv;
prompt.start();

prompt.get(schema, function(err, input) {
  var oauth = new OAuth.OAuth(
  'https://secure.splitwise.com/api/v3.0/get_request_token',
  'https://secure.splitwise.com/api/v3.0/get_access_token',
  input.consumerKey,
  input.consumerSecret,
  '1.0',
  null,
  'HMAC-SHA1');

  var file = require(__dirname + '\\friend.json');
  var some = _.first(file.results, 1);
  _.each(some, function(tx) {
    console.log('----------------------------------')
    console.log('Who Paid: ' + tx.sender.name);
    console.log('Who Owes: ' + tx.receiver.name);
    console.log('Amount: ' + tx.amount);
    console.log('Description: ' + tx.message);
    console.log('Date: ' + tx.happenedAt.iso.substring(0, tx.happenedAt.iso.indexOf('T')));
    var natural = tx.receiver.name + ' owes ' + tx.sender.name + ' $' + tx.amount + ' for ' + tx.message + ' on ' + tx.happenedAt.iso.substring(0, tx.happenedAt.iso.indexOf('T'));
    console.log(natural);
    oauth.post(
      'https://secure.splitwise.com/api/v3.0/parse_sentence',
      input.accessToken,
      input.accessSecret,
      {
        input: natural,
        friend_id: input.friendId,
        //autosave: false,
        //creation_method: 'quickadd'
      },
      null,
      function(err, data, response) {
        console.log(util.inspect(data));
      }
    );
  });
});
<commit_msg>Not necessary.<commit_after>var _ = require('lodash');
var OAuth = require('oauth');
var optimist = require('optimist');
var prompt = require('prompt');

var schema = {
  properties: {
    consumerKey: {
      required: true
    },
    consumerSecret: {
      required: true
    },
    accessToken: {
      required: true
    },
    accessSecret: {
      required: true
    },
    friendId: {
      required: true
    }
  }
};

prompt.override = optimist.argv;
prompt.start();

prompt.get(schema, function(err, input) {
  var oauth = new OAuth.OAuth(
  'https://secure.splitwise.com/api/v3.0/get_request_token',
  'https://secure.splitwise.com/api/v3.0/get_access_token',
  input.consumerKey,
  input.consumerSecret,
  '1.0',
  null,
  'HMAC-SHA1');

  var file = require(__dirname + '\\friend.json');
  var some = _.first(file.results, 1);
  _.each(some, function(tx) {
    console.log('----------------------------------')
    console.log('Who Paid: ' + tx.sender.name);
    console.log('Who Owes: ' + tx.receiver.name);
    console.log('Amount: ' + tx.amount);
    console.log('Description: ' + tx.message);
    console.log('Date: ' + tx.happenedAt.iso.substring(0, tx.happenedAt.iso.indexOf('T')));
    var natural = tx.receiver.name + ' owes ' + tx.sender.name + ' $' + tx.amount + ' for ' + tx.message + ' on ' + tx.happenedAt.iso.substring(0, tx.happenedAt.iso.indexOf('T'));
    console.log(natural);
    oauth.post(
      'https://secure.splitwise.com/api/v3.0/parse_sentence',
      input.accessToken,
      input.accessSecret,
      {
        input: natural,
        friend_id: input.friendId,
        //autosave: false,
        //creation_method: 'quickadd'
      },
      null,
      function(err, data, response) {
        console.log(data);
      }
    );
  });
});
<|endoftext|>


<commit_before>var _ = require('lodash');
var optimist = require('optimist');
var prompt = require('prompt');

var schema = {
  properties: {
    file: {
      required: true,
      default: 'file.json'
    },
    friendId: {
      required: true
    }
  }
};
prompt.override = optimist.argv;
prompt.start();

prompt.get(schema, function(err, input) {
  var file = require(__dirname + '\\' + input.file);
  var some = _.first(file.results, 20);
  _.each(some, function(tx) {
    console.log('----------------------------------')
    console.log('Who Paid: ' + tx.sender.name);
    console.log('Who Owes: ' + tx.receiver.name);
    console.log('Amount: ' + tx.amount);
    console.log('Description: ' + tx.message);
    console.log('Date: ' + tx.happenedAt.iso);
    console.log(tx.receiver.name + ' owes ' + tx.sender.name + ' $' + tx.amount + ' for ' + tx.message + ' on ' + tx.happenedAt.iso);
  });
});
<commit_msg>Trim the time zone.<commit_after>var _ = require('lodash');
var optimist = require('optimist');
var prompt = require('prompt');

var schema = {
  properties: {
    file: {
      required: true,
      default: 'file.json'
    },
    friendId: {
      required: true
    }
  }
};
prompt.override = optimist.argv;
prompt.start();

prompt.get(schema, function(err, input) {
  var file = require(__dirname + '\\' + input.file);
  var some = _.first(file.results, 20);
  _.each(some, function(tx) {
    console.log('----------------------------------')
    console.log('Who Paid: ' + tx.sender.name);
    console.log('Who Owes: ' + tx.receiver.name);
    console.log('Amount: ' + tx.amount);
    console.log('Description: ' + tx.message);
    console.log('Date: ' + tx.happenedAt.iso.substring(0, tx.happenedAt.iso.indexOf('T')));
    console.log(tx.receiver.name + ' owes ' + tx.sender.name + ' $' + tx.amount + ' for ' + tx.message + ' on ' + tx.happenedAt.iso.substring(0, tx.happenedAt.iso.indexOf('T')));
  });
});
<|endoftext|>