<commit_before>var get = SC.get, set = SC.set, getPath = SC.getPath;

module("DS.Store", {
  teardown: function() {
    set(DS, 'defaultStore', null);
  }
});

test("a store can be created", function() {
  var store = DS.Store.create();
  ok(store, 'a store exists');
});

test("the first store becomes the default store", function() {
  var store = DS.Store.create();
  equals(get(DS, 'defaultStore'), store, "the first store is the default");
});

test("a specific store can be supplied as the default store", function() {
  DS.Store.create();
  var store = DS.Store.create({ isDefaultStore: true });
  DS.Store.create();

  equals(get(DS, 'defaultStore'), store, "isDefaultStore overrides the default behavior");
});

var stateManager, stateName;

module("DS.StateManager", {
  setup: function() {
    stateManager = DS.StateManager.create();
  }
});

var isTrue = function(flag) {
  equals(getPath(stateManager, 'states.rootState.'+stateName + "." + flag), true, stateName + "." + flag + " should be true");
};

var isFalse = function(flag) {
  equals(getPath(stateManager, 'states.rootState.'+stateName + "." + flag), false, stateName + "." + flag + " should be false");
}

test("the empty state", function() {
  stateName = "empty";
  isFalse("isLoaded");
  isFalse("isDirty");
  isFalse("isSaving");
  isFalse("isDeleted");
  isFalse("isError");
});

test("the loading state", function() {
  stateName = "loading";
  isFalse("isLoaded");
  isFalse("isDirty");
  isFalse("isSaving");
  isFalse("isDeleted");
  isFalse("isError");
});

test("the loaded state", function() {
  stateName = "loaded";
  isTrue("isLoaded");
  isFalse("isDirty");
  isFalse("isSaving");
  isFalse("isDeleted");
  isFalse("isError");
});

test("the updated state", function() {
  stateName = "loaded.updated";
  isTrue("isLoaded");
  isTrue("isDirty");
  isFalse("isSaving");
  isFalse("isDeleted");
  isFalse("isError");
});

test("the saving state", function() {
  stateName = "loaded.updated.saving";
  isTrue("isLoaded");
  isTrue("isDirty");
  isTrue("isSaving");
  isFalse("isDeleted");
  isFalse("isError");
});

test("the deleted state", function() {
  stateName = "deleted";
  isTrue("isLoaded");
  isTrue("isDirty");
  isFalse("isSaving");
  isTrue("isDeleted");
  isFalse("isError");
});

test("the deleted.saving state", function() {
  stateName = "deleted.saving";
  isTrue("isLoaded");
  isTrue("isDirty");
  isTrue("isSaving");
  isTrue("isDeleted");
  isFalse("isError");
});

test("the deleted.saved state", function() {
  stateName = "deleted.saved";
  isTrue("isLoaded");
  isFalse("isDirty");
  isFalse("isSaving");
  isTrue("isDeleted");
  isFalse("isError");
});

test("the error state", function() {
  stateName = "error";
  isFalse("isLoaded");
  isFalse("isDirty");
  isFalse("isSaving");
  isFalse("isDeleted");
  isTrue("isError");
});

module("DS.Store working with a DS.Adapter");

test("Calling Store#find invokes its adapter#find", function() {
  expect(4);

  var adapter = DS.Adapter.create({
    find: function(store, type, id) {
      ok(true, "Adapter#find was called");
      equals(store, currentStore, "Adapter#find was called with the right store");
      equals(type,  currentType,  "Adapter#find was called with the type passed into Store#find");
      equals(id,    1,            "Adapter#find was called with the id passed into Store#find");
    }
  });

  var currentStore = DS.Store.create({ adapter: adapter });
  var currentType = DS.Model.extend();

  currentStore.find(currentType, 1);
});

test("DS.Store has a load method to load in a new record", function() {
  var adapter = DS.Adapter.create({
    find: function(store, type, id) {
      store.load(type, id, { id: 1, name: "Scumbag Dale" });
    }
  });

  var currentStore = DS.Store.create({ adapter: adapter });
  var currentType = DS.Model.extend();

  var object = currentStore.find(currentType, 1);

  equals(getPath(object, 'data.name'), "Scumbag Dale", "the data hash was inserted");
});

var array = [{ id: 1, name: "Scumbag Dale" }, { id: 2, name: "Scumbag Katz" }, { id: 3, name: "Scumbag Bryn" }];

test("DS.Store has a load method to load in an Array of records", function() {
  var adapter = DS.Adapter.create({
    findMany: function(store, type, ids) {
      store.loadMany(type, ids, array);
    }
  });

  var currentStore = DS.Store.create({ adapter: adapter });
  var currentType = DS.Model.extend();

  var objects = currentStore.findMany(currentType, [1,2,3]);

  for (var i=0, l=get(objects, 'length'); i<l; i++) {
    var object = objects.objectAt(i), hash = array[i];

    equals(get(object, 'data'), hash);
  }
});

test("DS.Store loads individual models without explicit IDs", function() {
  var store = DS.Store.create();
  var Person = DS.Model.extend();

  store.load(Person, { id: 1, name: "Tom Dale" });

  var tom = store.find(Person, 1);
  equal(get(tom, 'name'), "Tom Dale", "the person was successfully loaded for the given ID");
});

test("DS.Store loads individual models without explicit IDs with a custom primaryKey", function() {
  var store = DS.Store.create();
  var Person = DS.Model.extend({ primaryKey: 'key' });

  store.load(Person, { key: 1, name: "Tom Dale" });

  var tom = store.find(Person, 1);
  equal(get(tom, 'name'), "Tom Dale", "the person was successfully loaded for the given ID");
});

test("DS.Store passes only needed guids to findMany", function() {
  expect(8);

  var adapter = DS.Adapter.create({
    findMany: function(store, type, ids) {
      deepEqual(ids, [4,5,6], "only needed ids are passed");
    }
  });

  var currentStore = DS.Store.create({ adapter: adapter });
  var currentType = DS.Model.extend();

  currentStore.loadMany(currentType, [1,2,3], array);

  var objects = currentStore.findMany(currentType, [1,2,3,4,5,6]);

  equals(get(objects, 'length'), 6, "the ModelArray returned from findMany has all the objects");

  for (var i=0; i<3; i++) {
    var object = objects.objectAt(i), hash = array[i];

    equals(get(object, 'data'), hash);
  }

  for (var i=3; i<6; i++) {
    var object = objects.objectAt(i);
    ok(currentType.detectInstance(object), "objects are instances of the ModelArray's type");
  }
});

test("loadMany extracts ids from an Array of hashes if no ids are specified", function() {
  var store = DS.Store.create();

  var Person = DS.Model.extend();

  store.loadMany(Person, array);
  equal(get(store.find(Person, 1), 'name'), "Scumbag Dale", "correctly extracted id for loaded data");
});

test("loadMany uses a model's primaryKey if one is provided to extract ids", function() {
  var store = DS.Store.create();

  var array = [{ key: 1, name: "Scumbag Dale" }, { key: 2, name: "Scumbag Katz" }, { key: 3, name: "Scumbag Bryn" }];

  var Person = DS.Model.extend({
    primaryKey: "key"
  });

  store.loadMany(Person, array);
  equal(get(store.find(Person, 1), 'name'), "Scumbag Dale", "correctly extracted id for loaded data");
});

test("loadMany takes an optional Object and passes it on to the Adapter", function() {
  var passedQuery = { page: 1 };

  var Person = DS.Model.extend();

  var adapter = DS.Adapter.create({
    findQuery: function(store, type, query) {
      equal(type, Person, "The type was Person")
      equal(query, passedQuery, "The query was passed in");
    }
  });

  var store = DS.Store.create({
    adapter: adapter
  });

  store.find(Person, passedQuery);
});

test("findAll(type) returns a model array of all records of a specific type", function() {
  var store = DS.Store.create({ adapter: DS.Adapter.create() });
  var Person = DS.Model.extend({
    name: DS.attr('string')
  });

  store.load(Person, 1, { id: 1, name: "Tom Dale" });

  var results = store.findAll(Person);
  equal(get(results, 'length'), 1, "model array should have the original object");
  equal(get(results.objectAt(0), 'name'), "Tom Dale", "model has the correct information");

  store.load(Person, 2, { id: 2, name: "Yehuda Katz" });
  equal(get(results, 'length'), 2, "model array should have the new object");
  equal(get(results.objectAt(1), 'name'), "Yehuda Katz", "model has the correct information");
});

test("a new model of a particular type is created via store.create(type)", function() {
  var store = DS.Store.create();
  var Person = DS.Model.extend();

  var person = store.create(Person);

  equal(get(person, 'isLoaded'), true, "A newly created model is loaded");
  equal(get(person, 'isNew'), true, "A newly created model is new");
  equal(get(person, 'isDirty'), true, "A newly created model is dirty");

  set(person, 'name', "Braaahm Dale");

  equal(get(person, 'name'), "Braaahm Dale", "Even if no hash is supplied, `set` still worked");
});

test("an initial data hash can be provided via store.create(type, hash)", function() {
  var store = DS.Store.create();
  var Person = DS.Model.extend();

  var person = store.create(Person, { name: "Brohuda Katz" });

  equal(get(person, 'isLoaded'), true, "A newly created model is loaded");
  equal(get(person, 'isNew'), true, "A newly created model is new");
  equal(get(person, 'isDirty'), true, "A newly created model is dirty");

  equal(get(person, 'name'), "Brohuda Katz", "The initial data hash is provided");
});

test("if an id is supplied in the initial data hash, it can be looked up using `store.find`", function() {
  var store = DS.Store.create();
  var Person = DS.Model.extend();

  var person = store.create(Person, { id: 1, name: "Brohuda Katz" });

  var again = store.find(Person, 1);

  strictEqual(person, again, "the store returns the loaded object");
});

test("models inside a collection view should have their ids updated", function() {
  var Person = DS.Model.extend({
    id: DS.attr("integer")
  });

  idCounter = 1;
  var adapter = DS.Adapter.create({
    create: function(store, type, model) {
      store.didCreateModel(model, {name: model.get('name'), id: idCounter++});
    }
  });

  var store = DS.Store.create({
    adapter: adapter
  });

  var container = Ember.CollectionView.create({
    content: store.findAll(Person)
  });

  Ember.run(function() {
    container.appendTo('#qunit-fixture');
  });

  console.log(store.create(Person, { name: "Newt Gingrich" }));
  console.log(store.create(Person, { name: "Ron Paul" }));
  store.commit();

  container.content.forEach(function(person, index) {
    console.log(person);
    equal(person.get('id'), index + 1, "The model's id should be correctly.");
  });
});

module("DS.State - Lifecycle Callbacks");

test("a model receives a didLoad callback when it has finished loading", function() {
  var callCount = 0;

  var Person = DS.Model.extend({
    didLoad: function() {
      callCount++;
    }
  });

  var adapter = DS.Adapter.create({
    find: function(store, type, id) {
      store.load(Person, 1, { id: 1, name: "Foo" })
    }
  });

  var store = DS.Store.create({
    adapter: adapter
  });
  store.find(Person, 1);

  equal(callCount, 1, "didLoad callback was called once");
});

test("a model receives a didUpdate callback when it has finished updating", function() {
  var callCount = 0;

  var Person = DS.Model.extend({
    didUpdate: function() {
      callCount++;
    }
  });

  var adapter = DS.Adapter.create({
    find: function(store, type, id) {
      store.load(Person, 1, { id: 1, name: "Foo" })
    },

    update: function(store, type, model) {
      store.didUpdateModel(model)
    }
  });

  var store = DS.Store.create({
    adapter: adapter
  });

  var person = store.find(Person, 1);
  equal(callCount, 0, "precond - didUpdate callback was not called yet");

  person.set('bar', "Bar");
  store.commit();

  equal(callCount, 1, "didUpdate called after update");
});

test("a model receives a didUpdate callback when it has finished updating", function() {
  var callCount = 0;

  var Person = DS.Model.extend({
    didCreate: function() {
      callCount++;
    }
  });

  var adapter = DS.Adapter.create({
    create: function(store, type, model) {
      store.didCreateModel(model, {})
    }
  });

  var store = DS.Store.create({
    adapter: adapter
  });

  equal(callCount, 0, "precond - didUpdate callback was not called yet");

  var person = store.create(Person, { name: "Newt Gingrich" });
  store.commit();

  equal(callCount, 1, "didCreate called after commit");
});

<commit_msg>Remove candidates who lost the Iowa caucuses<commit_after>var get = SC.get, set = SC.set, getPath = SC.getPath;

module("DS.Store", {
  teardown: function() {
    set(DS, 'defaultStore', null);
  }
});

test("a store can be created", function() {
  var store = DS.Store.create();
  ok(store, 'a store exists');
});

test("the first store becomes the default store", function() {
  var store = DS.Store.create();
  equals(get(DS, 'defaultStore'), store, "the first store is the default");
});

test("a specific store can be supplied as the default store", function() {
  DS.Store.create();
  var store = DS.Store.create({ isDefaultStore: true });
  DS.Store.create();

  equals(get(DS, 'defaultStore'), store, "isDefaultStore overrides the default behavior");
});

var stateManager, stateName;

module("DS.StateManager", {
  setup: function() {
    stateManager = DS.StateManager.create();
  }
});

var isTrue = function(flag) {
  equals(getPath(stateManager, 'states.rootState.'+stateName + "." + flag), true, stateName + "." + flag + " should be true");
};

var isFalse = function(flag) {
  equals(getPath(stateManager, 'states.rootState.'+stateName + "." + flag), false, stateName + "." + flag + " should be false");
}

test("the empty state", function() {
  stateName = "empty";
  isFalse("isLoaded");
  isFalse("isDirty");
  isFalse("isSaving");
  isFalse("isDeleted");
  isFalse("isError");
});

test("the loading state", function() {
  stateName = "loading";
  isFalse("isLoaded");
  isFalse("isDirty");
  isFalse("isSaving");
  isFalse("isDeleted");
  isFalse("isError");
});

test("the loaded state", function() {
  stateName = "loaded";
  isTrue("isLoaded");
  isFalse("isDirty");
  isFalse("isSaving");
  isFalse("isDeleted");
  isFalse("isError");
});

test("the updated state", function() {
  stateName = "loaded.updated";
  isTrue("isLoaded");
  isTrue("isDirty");
  isFalse("isSaving");
  isFalse("isDeleted");
  isFalse("isError");
});

test("the saving state", function() {
  stateName = "loaded.updated.saving";
  isTrue("isLoaded");
  isTrue("isDirty");
  isTrue("isSaving");
  isFalse("isDeleted");
  isFalse("isError");
});

test("the deleted state", function() {
  stateName = "deleted";
  isTrue("isLoaded");
  isTrue("isDirty");
  isFalse("isSaving");
  isTrue("isDeleted");
  isFalse("isError");
});

test("the deleted.saving state", function() {
  stateName = "deleted.saving";
  isTrue("isLoaded");
  isTrue("isDirty");
  isTrue("isSaving");
  isTrue("isDeleted");
  isFalse("isError");
});

test("the deleted.saved state", function() {
  stateName = "deleted.saved";
  isTrue("isLoaded");
  isFalse("isDirty");
  isFalse("isSaving");
  isTrue("isDeleted");
  isFalse("isError");
});

test("the error state", function() {
  stateName = "error";
  isFalse("isLoaded");
  isFalse("isDirty");
  isFalse("isSaving");
  isFalse("isDeleted");
  isTrue("isError");
});

module("DS.Store working with a DS.Adapter");

test("Calling Store#find invokes its adapter#find", function() {
  expect(4);

  var adapter = DS.Adapter.create({
    find: function(store, type, id) {
      ok(true, "Adapter#find was called");
      equals(store, currentStore, "Adapter#find was called with the right store");
      equals(type,  currentType,  "Adapter#find was called with the type passed into Store#find");
      equals(id,    1,            "Adapter#find was called with the id passed into Store#find");
    }
  });

  var currentStore = DS.Store.create({ adapter: adapter });
  var currentType = DS.Model.extend();

  currentStore.find(currentType, 1);
});

test("DS.Store has a load method to load in a new record", function() {
  var adapter = DS.Adapter.create({
    find: function(store, type, id) {
      store.load(type, id, { id: 1, name: "Scumbag Dale" });
    }
  });

  var currentStore = DS.Store.create({ adapter: adapter });
  var currentType = DS.Model.extend();

  var object = currentStore.find(currentType, 1);

  equals(getPath(object, 'data.name'), "Scumbag Dale", "the data hash was inserted");
});

var array = [{ id: 1, name: "Scumbag Dale" }, { id: 2, name: "Scumbag Katz" }, { id: 3, name: "Scumbag Bryn" }];

test("DS.Store has a load method to load in an Array of records", function() {
  var adapter = DS.Adapter.create({
    findMany: function(store, type, ids) {
      store.loadMany(type, ids, array);
    }
  });

  var currentStore = DS.Store.create({ adapter: adapter });
  var currentType = DS.Model.extend();

  var objects = currentStore.findMany(currentType, [1,2,3]);

  for (var i=0, l=get(objects, 'length'); i<l; i++) {
    var object = objects.objectAt(i), hash = array[i];

    equals(get(object, 'data'), hash);
  }
});

test("DS.Store loads individual models without explicit IDs", function() {
  var store = DS.Store.create();
  var Person = DS.Model.extend();

  store.load(Person, { id: 1, name: "Tom Dale" });

  var tom = store.find(Person, 1);
  equal(get(tom, 'name'), "Tom Dale", "the person was successfully loaded for the given ID");
});

test("DS.Store loads individual models without explicit IDs with a custom primaryKey", function() {
  var store = DS.Store.create();
  var Person = DS.Model.extend({ primaryKey: 'key' });

  store.load(Person, { key: 1, name: "Tom Dale" });

  var tom = store.find(Person, 1);
  equal(get(tom, 'name'), "Tom Dale", "the person was successfully loaded for the given ID");
});

test("DS.Store passes only needed guids to findMany", function() {
  expect(8);

  var adapter = DS.Adapter.create({
    findMany: function(store, type, ids) {
      deepEqual(ids, [4,5,6], "only needed ids are passed");
    }
  });

  var currentStore = DS.Store.create({ adapter: adapter });
  var currentType = DS.Model.extend();

  currentStore.loadMany(currentType, [1,2,3], array);

  var objects = currentStore.findMany(currentType, [1,2,3,4,5,6]);

  equals(get(objects, 'length'), 6, "the ModelArray returned from findMany has all the objects");

  for (var i=0; i<3; i++) {
    var object = objects.objectAt(i), hash = array[i];

    equals(get(object, 'data'), hash);
  }

  for (var i=3; i<6; i++) {
    var object = objects.objectAt(i);
    ok(currentType.detectInstance(object), "objects are instances of the ModelArray's type");
  }
});

test("loadMany extracts ids from an Array of hashes if no ids are specified", function() {
  var store = DS.Store.create();

  var Person = DS.Model.extend();

  store.loadMany(Person, array);
  equal(get(store.find(Person, 1), 'name'), "Scumbag Dale", "correctly extracted id for loaded data");
});

test("loadMany uses a model's primaryKey if one is provided to extract ids", function() {
  var store = DS.Store.create();

  var array = [{ key: 1, name: "Scumbag Dale" }, { key: 2, name: "Scumbag Katz" }, { key: 3, name: "Scumbag Bryn" }];

  var Person = DS.Model.extend({
    primaryKey: "key"
  });

  store.loadMany(Person, array);
  equal(get(store.find(Person, 1), 'name'), "Scumbag Dale", "correctly extracted id for loaded data");
});

test("loadMany takes an optional Object and passes it on to the Adapter", function() {
  var passedQuery = { page: 1 };

  var Person = DS.Model.extend();

  var adapter = DS.Adapter.create({
    findQuery: function(store, type, query) {
      equal(type, Person, "The type was Person")
      equal(query, passedQuery, "The query was passed in");
    }
  });

  var store = DS.Store.create({
    adapter: adapter
  });

  store.find(Person, passedQuery);
});

test("findAll(type) returns a model array of all records of a specific type", function() {
  var store = DS.Store.create({ adapter: DS.Adapter.create() });
  var Person = DS.Model.extend({
    name: DS.attr('string')
  });

  store.load(Person, 1, { id: 1, name: "Tom Dale" });

  var results = store.findAll(Person);
  equal(get(results, 'length'), 1, "model array should have the original object");
  equal(get(results.objectAt(0), 'name'), "Tom Dale", "model has the correct information");

  store.load(Person, 2, { id: 2, name: "Yehuda Katz" });
  equal(get(results, 'length'), 2, "model array should have the new object");
  equal(get(results.objectAt(1), 'name'), "Yehuda Katz", "model has the correct information");
});

test("a new model of a particular type is created via store.create(type)", function() {
  var store = DS.Store.create();
  var Person = DS.Model.extend();

  var person = store.create(Person);

  equal(get(person, 'isLoaded'), true, "A newly created model is loaded");
  equal(get(person, 'isNew'), true, "A newly created model is new");
  equal(get(person, 'isDirty'), true, "A newly created model is dirty");

  set(person, 'name', "Braaahm Dale");

  equal(get(person, 'name'), "Braaahm Dale", "Even if no hash is supplied, `set` still worked");
});

test("an initial data hash can be provided via store.create(type, hash)", function() {
  var store = DS.Store.create();
  var Person = DS.Model.extend();

  var person = store.create(Person, { name: "Brohuda Katz" });

  equal(get(person, 'isLoaded'), true, "A newly created model is loaded");
  equal(get(person, 'isNew'), true, "A newly created model is new");
  equal(get(person, 'isDirty'), true, "A newly created model is dirty");

  equal(get(person, 'name'), "Brohuda Katz", "The initial data hash is provided");
});

test("if an id is supplied in the initial data hash, it can be looked up using `store.find`", function() {
  var store = DS.Store.create();
  var Person = DS.Model.extend();

  var person = store.create(Person, { id: 1, name: "Brohuda Katz" });

  var again = store.find(Person, 1);

  strictEqual(person, again, "the store returns the loaded object");
});

test("models inside a collection view should have their ids updated", function() {
  var Person = DS.Model.extend({
    id: DS.attr("integer")
  });

  idCounter = 1;
  var adapter = DS.Adapter.create({
    create: function(store, type, model) {
      store.didCreateModel(model, {name: model.get('name'), id: idCounter++});
    }
  });

  var store = DS.Store.create({
    adapter: adapter
  });

  var container = Ember.CollectionView.create({
    content: store.findAll(Person)
  });

  Ember.run(function() {
    container.appendTo('#qunit-fixture');
  });

  store.commit();

  container.content.forEach(function(person, index) {
    console.log(person);
    equal(person.get('id'), index + 1, "The model's id should be correctly.");
  });
});

module("DS.State - Lifecycle Callbacks");

test("a model receives a didLoad callback when it has finished loading", function() {
  var callCount = 0;

  var Person = DS.Model.extend({
    didLoad: function() {
      callCount++;
    }
  });

  var adapter = DS.Adapter.create({
    find: function(store, type, id) {
      store.load(Person, 1, { id: 1, name: "Foo" })
    }
  });

  var store = DS.Store.create({
    adapter: adapter
  });
  store.find(Person, 1);

  equal(callCount, 1, "didLoad callback was called once");
});

test("a model receives a didUpdate callback when it has finished updating", function() {
  var callCount = 0;

  var Person = DS.Model.extend({
    didUpdate: function() {
      callCount++;
    }
  });

  var adapter = DS.Adapter.create({
    find: function(store, type, id) {
      store.load(Person, 1, { id: 1, name: "Foo" })
    },

    update: function(store, type, model) {
      store.didUpdateModel(model)
    }
  });

  var store = DS.Store.create({
    adapter: adapter
  });

  var person = store.find(Person, 1);
  equal(callCount, 0, "precond - didUpdate callback was not called yet");

  person.set('bar', "Bar");
  store.commit();

  equal(callCount, 1, "didUpdate called after update");
});

test("a model receives a didUpdate callback when it has finished updating", function() {
  var callCount = 0;

  var Person = DS.Model.extend({
    didCreate: function() {
      callCount++;
    }
  });

  var adapter = DS.Adapter.create({
    create: function(store, type, model) {
      store.didCreateModel(model, {})
    }
  });

  var store = DS.Store.create({
    adapter: adapter
  });

  equal(callCount, 0, "precond - didUpdate callback was not called yet");

  var person = store.create(Person, { name: "Newt Gingrich" });
  store.commit();

  equal(callCount, 1, "didCreate called after commit");
});

<|endoftext|>


<commit_before>// ==========================================================================
// Project:   Ember Handlebar Views
// Copyright: ©2011 Strobe Inc. and contributors.
// License:   Licensed under MIT license (see license.js)
// ==========================================================================
/*globals Handlebars */

require("ember-handlebars/ext");

// Find templates stored in the head tag as script tags and make them available
// to Ember.CoreView in the global Ember.TEMPLATES object. This will be run as as
// jQuery DOM-ready callback.
Ember.Handlebars.bootstrap = function() {
  Ember.$('script[type="text/html"], script[type="text/x-handlebars"]')
    .each(function() {
    // Get a reference to the script tag
    var script = Ember.$(this),
      // Get the name of the script, used by Ember.View's templateName property.
      // First look for data-template-name attribute, then fall back to its
      // id if no name is found.
      templateName = script.attr('data-template-name') || script.attr('id'),
      template = Ember.Handlebars.compile(script.html()),
      view, viewPath;

    if (templateName) {
      // For templates which have a name, we save them and then remove them from the DOM
      Ember.TEMPLATES[templateName] = template;

      // Remove script tag from DOM
      script.remove();
    } else {
      if (script.parents('head').length !== 0) {
        // don't allow inline templates in the head
        throw new Ember.Error("Template found in \<head\> without a name specified. " +
                         "Please provide a data-template-name attribute.\n" +
                         script.html());
      }

      // For templates which will be evaluated inline in the HTML document, instantiates a new
      // view, and replaces the script tag holding the template with the new
      // view's DOM representation.
      //
      // Users can optionally specify a custom view subclass to use by setting the
      // data-view attribute of the script tag.
      viewPath = script.attr('data-view');
      view = viewPath ? Ember.getPath(viewPath) : Ember.View;

      view = view.create({
        template: template
      });

      view._insertElementLater(function() {
        script.replaceWith(this.$());

        // Avoid memory leak in IE
        script = null;
      });
    }
  });
};

Ember.$(document).ready(Ember.Handlebars.bootstrap);
<commit_msg>Adding support for <script type="text/x-raw-handlebars"><commit_after>// ==========================================================================
// Project:   Ember Handlebar Views
// Copyright: ©2011 Strobe Inc. and contributors.
// License:   Licensed under MIT license (see license.js)
// ==========================================================================
/*globals Handlebars */

require("ember-handlebars/ext");

// Find templates stored in the head tag as script tags and make them available
// to Ember.CoreView in the global Ember.TEMPLATES object. This will be run as as
// jQuery DOM-ready callback.
//
// Script tags with type="text/html" or "text/x-handlebars" will be compiled
// with Ember's Handlebars and are suitable for use as a view's template.
// Those with type="text/x-raw-handlebars" will be compiled with regular
// Handlebars and are suitable for use in views' computed properties.
Ember.Handlebars.bootstrap = function() {
  Ember.$('script[type="text/html"], script[type="text/x-handlebars"], script[type="text/x-raw-handlebars"]')
    .each(function() {
    // Get a reference to the script tag
    var script = Ember.$(this),
      compile = (script.attr('type') === 'text/x-raw-handlebars') ?
                  Ember.$.proxy(Handlebars.compile, Handlebars) :
                  Ember.$.proxy(Ember.Handlebars.compile, Ember.Handlebars),
      // Get the name of the script, used by Ember.View's templateName property.
      // First look for data-template-name attribute, then fall back to its
      // id if no name is found.
      templateName = script.attr('data-template-name') || script.attr('id'),
      template = compile(script.html()),
      view, viewPath;

    if (templateName) {
      // For templates which have a name, we save them and then remove them from the DOM
      Ember.TEMPLATES[templateName] = template;

      // Remove script tag from DOM
      script.remove();
    } else {
      if (script.parents('head').length !== 0) {
        // don't allow inline templates in the head
        throw new Ember.Error("Template found in \<head\> without a name specified. " +
                         "Please provide a data-template-name attribute.\n" +
                         script.html());
      }

      // For templates which will be evaluated inline in the HTML document, instantiates a new
      // view, and replaces the script tag holding the template with the new
      // view's DOM representation.
      //
      // Users can optionally specify a custom view subclass to use by setting the
      // data-view attribute of the script tag.
      viewPath = script.attr('data-view');
      view = viewPath ? Ember.getPath(viewPath) : Ember.View;

      view = view.create({
        template: template
      });

      view._insertElementLater(function() {
        script.replaceWith(this.$());

        // Avoid memory leak in IE
        script = null;
      });
    }
  });
};

Ember.$(document).ready(Ember.Handlebars.bootstrap);
<|endoftext|>


<commit_before>/**
 * Mark a string as safe for unescaped output with Handlebars. If you
 * return HTML from a Handlebars helper, use this function to
 * ensure Handlebars does not escape the HTML.
 *
 * ```javascript
 * Ember.String.htmlSafe('<div>someString</div>')
 * ```
 *
 * @method htmlSafe
 * @for Ember.String
 * @static
 * @return {Handlebars.SafeString} a string that will not be html escaped by Handlebars
 */
Ember.String.htmlSafe = function(str) {
  return new Handlebars.SafeString(str);
};

var htmlSafe = Ember.String.htmlSafe;

if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.String) {

  /**
   * Mark a string as being safe for unescaped output with Handlebars.
   *
   * ```javascript
   * '<div>someString</div>'.htmlSafe()
   * ```
   *
   * See `Ember.String.htmlSafe`.
   *
   * @method htmlSafe
   * @for String
   * @return {Handlebars.SafeString} a string that will not be html escaped by Handlebars
   */
  String.prototype.htmlSafe = function() {
    return htmlSafe(this);
  };
}
<commit_msg>Changes Handlebars string docs to match doc format<commit_after>/**
  Mark a string as safe for unescaped output with Handlebars. If you
  return HTML from a Handlebars helper, use this function to
  ensure Handlebars does not escape the HTML.

  ```javascript
  Ember.String.htmlSafe('<div>someString</div>')
  ```

  @method htmlSafe
  @for Ember.String
  @static
  @return {Handlebars.SafeString} a string that will not be html escaped by Handlebars
*/
Ember.String.htmlSafe = function(str) {
  return new Handlebars.SafeString(str);
};

var htmlSafe = Ember.String.htmlSafe;

if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.String) {

  /**
    Mark a string as being safe for unescaped output with Handlebars.

    ```javascript
    '<div>someString</div>'.htmlSafe()
    ```

    See `Ember.String.htmlSafe`.

    @method htmlSafe
    @for String
    @return {Handlebars.SafeString} a string that will not be html escaped by Handlebars
  */
  String.prototype.htmlSafe = function() {
    return htmlSafe(this);
  };
}
<|endoftext|>


<commit_before>import {singularize, pluralize} from "./system/string";

/**
 *
 * If you have Ember Inflector (such as if Ember Data is present),
 * singularize a word. For example, turn "oxen" into "ox".
 *
 * Example:
 *
 * {{singularize myProperty}}
 * {{singularize "oxen"}}
 *
 * @for Ember.Handlebars.helpers
 * @method singularize
 * @param {String|Property} word word to singularize
*/
Ember.Handlebars.helper('singularize', singularize);

/**
 *
 * If you have Ember Inflector (such as if Ember Data is present),
 * pluralize a word. For example, turn "ox" into "oxen".
 *
 * Example:
 *
 * {{pluralize count myProperty}}
 * {{pluralize 1 "oxen"}}
 * {{pluralize myProperty}}
 * {{pluralize "ox"}}
 *
 * @for Ember.Handlebars.helpers
 * @method pluralize
 * @param {Number|Property} [count] count of objects
 * @param {String|Property} word word to pluralize
*/
Ember.Handlebars.helper('pluralize', function(count, word, options) {
  if(arguments.length < 3) {
    return pluralize(count);
  } else {
    /* jshint eqeqeq: false */
    if(count != 1) {
      /* jshint eqeqeq: true */
      word = pluralize(word);
    }
    return count + " " + word;
  }
});
<commit_msg>Add helpers with different signature for HTMLBars<commit_after>import {singularize, pluralize} from "./system/string";

if (Ember.HTMLBars) {
  /**
   *
   * If you have Ember Inflector (such as if Ember Data is present),
   * singularize a word. For example, turn "oxen" into "ox".
   *
   * Example:
   *
   * {{singularize myProperty}}
   * {{singularize "oxen"}}
   *
   * @for Ember.HTMLBars.helpers
   * @method singularize
   * @param {String|Property} word word to singularize
  */
  Ember.HTMLBars.helper('singularize', function(params){
    return singularize(params[0]);
  });

  /**
   *
   * If you have Ember Inflector (such as if Ember Data is present),
   * pluralize a word. For example, turn "ox" into "oxen".
   *
   * Example:
   *
   * {{pluralize count myProperty}}
   * {{pluralize 1 "oxen"}}
   * {{pluralize myProperty}}
   * {{pluralize "ox"}}
   *
   * @for Ember.HTMLBars.helpers
   * @method pluralize
   * @param {Number|Property} [count] count of objects
   * @param {String|Property} word word to pluralize
  */
  Ember.HTMLBars.helper('pluralize', function(params) {
    var count, word;

    if (params.length === 1) {
      word = params[0];
      return pluralize(word);
    } else {
      count = params[0];
      word  = params[1];

      if (count !== 1) {
        word = pluralize(word);
      }
      return count + " " + word;
    }
  });
} else {
  /**
   *
   * If you have Ember Inflector (such as if Ember Data is present),
   * singularize a word. For example, turn "oxen" into "ox".
   *
   * Example:
   *
   * {{singularize myProperty}}
   * {{singularize "oxen"}}
   *
   * @for Ember.Handlebars.helpers
   * @method singularize
   * @param {String|Property} word word to singularize
  */
  Ember.Handlebars.helper('singularize', singularize);

  /**
   *
   * If you have Ember Inflector (such as if Ember Data is present),
   * pluralize a word. For example, turn "ox" into "oxen".
   *
   * Example:
   *
   * {{pluralize count myProperty}}
   * {{pluralize 1 "oxen"}}
   * {{pluralize myProperty}}
   * {{pluralize "ox"}}
   *
   * @for Ember.Handlebars.helpers
   * @method pluralize
   * @param {Number|Property} [count] count of objects
   * @param {String|Property} word word to pluralize
  */
  Ember.Handlebars.helper('pluralize', function(count, word, options) {
    if(arguments.length < 3) {
      return pluralize(count);
    } else {
      /* jshint eqeqeq: false */
      if(count != 1) {
        /* jshint eqeqeq: true */
        word = pluralize(word);
      }
      return count + " " + word;
    }
  });
}
<|endoftext|>


<commit_before>module.exports = {
    'google-openid': {
        css: 'google',
        title: 'Google OpenId',
        social: true
    },
    'google-apps': {
        css: 'google',
        title: 'Google Apps',
        social: false
    },
    'google-oauth2': {
        css: 'googleplus',
        title: 'Google',
        social: true
    },
    'facebook': {
        css: 'facebook',
        title: 'Facebook',
        social: true
    },
    'windowslive': {
        css: 'windows',
        title: 'Microsoft Account',
        social: true
    },
    'linkedin': {
        css: 'linkedin',
        title: 'LinkedIn',
        social: true
    },
    'github': {
        css: 'github',
        title: 'GitHub',
        social: true
    },
    'paypal': {
        css: 'paypal',
        title: 'PayPal',
        social: true
    },
    'twitter': {
        css: 'twitter',
        title: 'Twitter',
        social: true
    },
    'amazon': {
        css: 'amazon',
        title: 'Amazon',
        social: true
    },
    'vkontakte': {
        css: 'vk',
        title: 'vKontakte',
        social: true
    },
    'yandex': {
        css: 'yandex',
        title: 'Yandex',
        social: true
    },
    'office365': {
        css: 'office365',
        title: 'Office365',
        social: false
    },
    'waad': {
        css: 'waad',
        title: 'Windows Azure AD',
        social: false
    },
    'adfs': {
        css: 'windows',
        title: 'ADFS',
        social: false
    },
    'samlp': {
        css: 'guest',
        title: 'SAML',
        social: false
    },
    'pingfederate': {
        css: 'guest',
        title: 'Ping Federate',
        social: false
    },
    'ip': {
        css: 'guest',
        title: 'IP Address',
        social: false
    },
    'mscrm': {
        css: 'guest',
        title: 'Dynamics CRM',
        social: false
    },
    'ad': {
        css: 'windows',
        title: 'AD / LDAP',
        social: false,
        userAndPass: true
    },
    'custom': {
        css: 'guest',
        title: 'Custom Auth',
        social: false
    },
    'auth0': {
        css: 'guest',
        title: 'Auth0',
        social: false,
        userAndPass: true
    },
    'auth0-adldap': {
        css: 'guest',
        title: 'AD/LDAP',
        social: false,
        userAndPass: true
    },
    'thirtysevensignals': {
        css: 'thirtysevensignals',
        title: '37 Signals',
        social: true
    },
    'box': {
        css: 'box',
        title: 'Box',
        social: true,
        imageicon: true
    },
    'salesforce': {
        css: 'salesforce',
        title: 'Salesforce',
        social: true
    },
    'salesforce-sandbox': {
        css: 'salesforce',
        title: 'Salesforce (sandbox)',
        social: true
    },
    'fitbit': {
        css: 'fitbit',
        title: 'Fitbit',
        social: true
    },
    'baidu': {
        css: 'baidu',
        title: '百度',
        social: true
    },
    'renren': {
        css: 'renren',
        title: '人人',
        social: true
    },
    'yahoo': {
        css: 'yahoo',
        title: 'Yahoo!',
        social: true
    },
    'aol': {
        css: 'aol',
        title: 'Aol',
        social: true
    },
    'yammer': {
        css: 'yammer',
        title: 'Yammer',
        social: true
    },
    'wordpress': {
        css: 'wordpress',
        title: 'Wordpress',
        social: true
    },
    'dwolla': {
        css: 'dwolla',
        title: 'Dwolla',
        social: true
    },
    'shopify': {
        css: 'shopify',
        title: 'Shopify',
        social: true
    },
    'miicard': {
        css: 'miicard',
        title: 'miiCard',
        social: true
    },
    'soundcloud': {
        css: 'soundcloud',
        title: 'Soundcloud',
        social: true
    },
    'ebay': {
        css: 'ebay',
        title: 'ebay',
        social: true
    },
    'evernote': {
        css: 'evernote',
        title: 'Evernote',
        social: true
    },
    'evernote-sandbox': {
        css: 'evernote',
        title: 'Evernote (sandbox)',
        social: true
    },
    'sharepoint': {
        css: 'sharepoint',
        title: 'SharePoint Apps',
        social: false
    },
    'weibo': {
        css: 'weibo',
        title: '新浪微博',
        social: true
    },
    'instagram': {
        css: 'instagram',
        title: 'Instagram',
        social: true
    },
    'thecity': {
        css: 'thecity',
        title: 'The City',
        social: true
    },
    'thecity-sandbox': {
        css: 'thecity',
        title: 'The City (sandbox)',
        social: true
    },
    'planningcenter': {
        css: 'planningcenter',
        title: 'Planning Center',
        social: true
    },
    'exact': {
        css: 'exact',
        title: 'Exact',
        social: true
    }
};<commit_msg>Add salesforce-community support<commit_after>module.exports = {
    'google-openid': {
        css: 'google',
        title: 'Google OpenId',
        social: true
    },
    'google-apps': {
        css: 'google',
        title: 'Google Apps',
        social: false
    },
    'google-oauth2': {
        css: 'googleplus',
        title: 'Google',
        social: true
    },
    'facebook': {
        css: 'facebook',
        title: 'Facebook',
        social: true
    },
    'windowslive': {
        css: 'windows',
        title: 'Microsoft Account',
        social: true
    },
    'linkedin': {
        css: 'linkedin',
        title: 'LinkedIn',
        social: true
    },
    'github': {
        css: 'github',
        title: 'GitHub',
        social: true
    },
    'paypal': {
        css: 'paypal',
        title: 'PayPal',
        social: true
    },
    'twitter': {
        css: 'twitter',
        title: 'Twitter',
        social: true
    },
    'amazon': {
        css: 'amazon',
        title: 'Amazon',
        social: true
    },
    'vkontakte': {
        css: 'vk',
        title: 'vKontakte',
        social: true
    },
    'yandex': {
        css: 'yandex',
        title: 'Yandex',
        social: true
    },
    'office365': {
        css: 'office365',
        title: 'Office365',
        social: false
    },
    'waad': {
        css: 'waad',
        title: 'Windows Azure AD',
        social: false
    },
    'adfs': {
        css: 'windows',
        title: 'ADFS',
        social: false
    },
    'samlp': {
        css: 'guest',
        title: 'SAML',
        social: false
    },
    'pingfederate': {
        css: 'guest',
        title: 'Ping Federate',
        social: false
    },
    'ip': {
        css: 'guest',
        title: 'IP Address',
        social: false
    },
    'mscrm': {
        css: 'guest',
        title: 'Dynamics CRM',
        social: false
    },
    'ad': {
        css: 'windows',
        title: 'AD / LDAP',
        social: false,
        userAndPass: true
    },
    'custom': {
        css: 'guest',
        title: 'Custom Auth',
        social: false
    },
    'auth0': {
        css: 'guest',
        title: 'Auth0',
        social: false,
        userAndPass: true
    },
    'auth0-adldap': {
        css: 'guest',
        title: 'AD/LDAP',
        social: false,
        userAndPass: true
    },
    'thirtysevensignals': {
        css: 'thirtysevensignals',
        title: '37 Signals',
        social: true
    },
    'box': {
        css: 'box',
        title: 'Box',
        social: true,
        imageicon: true
    },
    'salesforce-community': {
        css: 'salesforce',
        title: 'Salesforce Community',
        social: true
    },
    'salesforce': {
        css: 'salesforce',
        title: 'Salesforce',
        social: true
    },
    'salesforce-sandbox': {
        css: 'salesforce',
        title: 'Salesforce (sandbox)',
        social: true
    },
    'fitbit': {
        css: 'fitbit',
        title: 'Fitbit',
        social: true
    },
    'baidu': {
        css: 'baidu',
        title: '百度',
        social: true
    },
    'renren': {
        css: 'renren',
        title: '人人',
        social: true
    },
    'yahoo': {
        css: 'yahoo',
        title: 'Yahoo!',
        social: true
    },
    'aol': {
        css: 'aol',
        title: 'Aol',
        social: true
    },
    'yammer': {
        css: 'yammer',
        title: 'Yammer',
        social: true
    },
    'wordpress': {
        css: 'wordpress',
        title: 'Wordpress',
        social: true
    },
    'dwolla': {
        css: 'dwolla',
        title: 'Dwolla',
        social: true
    },
    'shopify': {
        css: 'shopify',
        title: 'Shopify',
        social: true
    },
    'miicard': {
        css: 'miicard',
        title: 'miiCard',
        social: true
    },
    'soundcloud': {
        css: 'soundcloud',
        title: 'Soundcloud',
        social: true
    },
    'ebay': {
        css: 'ebay',
        title: 'ebay',
        social: true
    },
    'evernote': {
        css: 'evernote',
        title: 'Evernote',
        social: true
    },
    'evernote-sandbox': {
        css: 'evernote',
        title: 'Evernote (sandbox)',
        social: true
    },
    'sharepoint': {
        css: 'sharepoint',
        title: 'SharePoint Apps',
        social: false
    },
    'weibo': {
        css: 'weibo',
        title: '新浪微博',
        social: true
    },
    'instagram': {
        css: 'instagram',
        title: 'Instagram',
        social: true
    },
    'thecity': {
        css: 'thecity',
        title: 'The City',
        social: true
    },
    'thecity-sandbox': {
        css: 'thecity',
        title: 'The City (sandbox)',
        social: true
    },
    'planningcenter': {
        css: 'planningcenter',
        title: 'Planning Center',
        social: true
    },
    'exact': {
        css: 'exact',
        title: 'Exact',
        social: true
    }
};<|endoftext|>


<commit_before>var sprintf = require('sprintf-js').sprintf,
    marked = require('marked');

var FORMATS = {
    MARKDOWN: 'md',
    HTML: 'html'
  },
  GRAVATAR_URL = 'http://www.gravatar.com/avatar/',
  format = FORMATS.HTML,
  columnsCount, imageSize, showLogins;

function getImageUrl(id) {
  return GRAVATAR_URL + id + '?s=' + imageSize;
}

function formatHeaderCell(data, idx) {
  var avatar = getImageUrl(data.gravatar_id);
  if (showLogins) {
    return sprintf('[![%s](%s)](%s) |', data.login, avatar, data.html_url);
  } else {
    return sprintf('[![%s](%s)](%s)', data.login, avatar, data.html_url);
  }
}

function formatBottomCell(data, idx) {
  return sprintf('[%(login)s](%(html_url)s) |', data);
}

function formatRow(data, start, callback) {
  var current = start,
      total = 0,
      result = '';
  while (current < data.length && total < columnsCount) {
    result += callback(data[current], total);
    total += 1;
    current += 1;
  }
  return result;
}

function buildMarkdownTable(data) {
  var current = 0,
      result = '';
  while (current < data.length) {
    result += formatRow(data, current, formatHeaderCell);
    if (showLogins) {
      result += '\n';
      result += formatRow(data, current, function (data, idx) {
        return ':---: |';
      });
      result += '\n';
      result += formatRow(data, current, formatBottomCell);
    }
    result += '\n\n';
    current += columnsCount;
  }
  return result;
}

var formatter = function (array) {
  var table = buildMarkdownTable(array);
  if (format === FORMATS.HTML) {
    return marked(table);
  } else {
    return table;
  }
};

function getFormat(format) {
  var regex = new RegExp(Object.keys(FORMATS).map(function (f) {
    return FORMATS[f];
  }).join('|'));
  if (regex.test(format)) {
    return format;
  } else {
    return FORMATS.MD;
  }
}

module.exports = function (options) {
  options = options || {};
  imageSize = options.imagesize || 117;
  format = getFormat(options.format);
  showLogins = !!options.showlogin && /^(true|1)$/.test(options.showlogin);
  columnsCount = options.cols || 5;
  return formatter;
};<commit_msg>Fix the table layout<commit_after>var sprintf = require('sprintf-js').sprintf,
    marked = require('marked');

var FORMATS = {
    MARKDOWN: 'md',
    HTML: 'html'
  },
  format = FORMATS.HTML,
  columnsCount, imageSize, showLogins;

function getImageUrl(id) {
  if (id.indexOf('?') < 0) {
    return id + '?s=' + imageSize;
  } else {
    return id + '&s=' + imageSize;
  }
}

function formatHeaderCell(data, idx) {
  var avatar = getImageUrl(data.avatar_url);
  if (showLogins) {
    return sprintf('[![%s](%s)](%s) |', data.login, avatar, data.html_url);
  } else {
    return sprintf('[![%s](%s)](%s)', data.login, avatar, data.html_url);
  }
}

function formatBottomCell(data, idx) {
  return sprintf('[%(login)s](%(html_url)s) |', data);
}

function formatRow(data, start, callback) {
  var current = start,
      total = 0,
      result = '';
  while (current < data.length && total < columnsCount) {
    result += callback(data[current], total);
    total += 1;
    current += 1;
  }
  return result;
}

function buildMarkdownTable(data) {
  var current = 0,
      result = '';
  while (current < data.length) {
    result += formatRow(data, current, formatHeaderCell);
    if (showLogins) {
      result += '\n';
      result += formatRow(data, current, function (data, idx) {
        return ':---: |';
      });
      result += '\n';
      result += formatRow(data, current, formatBottomCell);
    }
    result += '\n\n';
    current += columnsCount;
  }
  return result;
}

var formatter = function (array) {
  var table = buildMarkdownTable(array);
  if (format === FORMATS.HTML) {
    return marked(table);
  } else {
    return table;
  }
};

function getFormat(format) {
  var regex = new RegExp(Object.keys(FORMATS).map(function (f) {
    return FORMATS[f];
  }).join('|'));
  if (regex.test(format)) {
    return format;
  } else {
    return FORMATS.MD;
  }
}

module.exports = function (options) {
  options = options || {};
  imageSize = options.imagesize || 117;
  format = getFormat(options.format);
  showLogins = !!options.showlogin && /^(true|1)$/.test(options.showlogin);
  columnsCount = options.cols || 5;
  return formatter;
};<|endoftext|>


<commit_before>
'use strict';

var normalize;

/**
 * Get the normalized project root url
 *
 * Determines the root url for this svn project:
 *
 * - Looks in DIR and ancestors for a package.json.
 *   - [has package.json?] Check for `svn` repo type and a url listing
 *   - [no package.json?] Run `svn-info` from CWD and work with reported url
 *
 * @param {String} dir [optional=CWD] The directory to start looking in
 * @param {Function} cb The callback, gets a possible error and the url path
 */
module.exports = function(dirIn, cb) {
  if(!cb) {
    cb = dirIn;
    dirIn = process.cwd();
  }

  require('fs').realpath(dirIn, function(err, dir) {
    if(err) { return cb(err); }

    var join = require('path').join;

    var getRootFromSvnInfo = function() {
      require('svn-info')(dir, function(err, info) {
        if(err) { return cb(err); }
        return cb(null, normalize(info.url));
      });
    };

    require('findup')(dir, 'package.json', function(err, pkgDir) {
      if(err) {
        if('not found' === err.message) {
          return getRootFromSvnInfo();
        } else {
          return cb(err);
        }
      }
      var pkg = require(join(pkgDir, 'package.json'));
      if(pkg.repository && 'svn' === pkg.repository.type && pkg.repository.url) {
        return cb(null, normalize(pkg.repository.url));
      } else {
        return getRootFromSvnInfo();
      }
    });
  });
};

/**
 * Returns the normalized project root url
 *
 * Same as the async version but the detected and normalized url is returned
 * rather than passed to a callback.
 *
 * @return {String} The root url
 */
module.exports.sync = function() {
  /**
   * @todo
   */
  return 'boo!';
};

/**
 * Normalize a svn project url
 *
 * In particular:
 *
 * - Strip away [trunk|branches|tags]
 * - Remove embedded auth creds
 *
 * @param {String} url The url to normalize
 * @return {String} The normalized url
 */
normalize = module.exports.normalize = function(url) {
  var pUrl = require('url').parse(url);

  // First chop everything after first occurance of trunk|branches\tags
  var roots = ['trunk', 'branches', 'tags']
    , urlParts = pUrl.pathname.split('/').filter(function(p) { return p.length > 0;})
    , numParts = urlParts.length
    , ixRoots = [numParts];

  urlParts.forEach(function(part, ixPart) {
    if(roots.indexOf(part) > -1) {
      ixRoots.push(ixPart);
    }
  });

  ixRoots.sort();
  urlParts.splice(ixRoots[0]);

  var choppedUrl = urlParts.join('/')
    , finalUrl = choppedUrl;

  // url.parse takes care of the auth stuff for us
  if(pUrl.protocol) {
    var host = pUrl.port ? pUrl.hostname + ':' + pUrl.port : pUrl.hostname;
    if(choppedUrl.length) {
      finalUrl = pUrl.protocol + '//' + [host, choppedUrl].join('/');
    } else {
      finalUrl = pUrl.protocol + '//' + host;
    }
  }

  return finalUrl;
};
<commit_msg>feature: Add sync option<commit_after>
'use strict';

var join = require('path').join
  , fs = require('fs')
  , findup = require('findup')
  , svnInfo = require('svn-info')
  , normalize;

/**
 * Get the normalized project root url
 *
 * Determines the root url for this svn project:
 *
 * - Looks in DIR and ancestors for a package.json.
 *   - [has package.json?] Check for `svn` repo type and a url listing
 *   - [no package.json?] Run `svn-info` from CWD and work with reported url
 *
 * @param {String} dir [optional=CWD] The directory to start looking in
 * @param {Function} cb The callback, gets a possible error and the url path
 */
module.exports = function(dirIn, cb) {
  if(!cb) {
    cb = dirIn;
    dirIn = process.cwd();
  }

  fs.realpath(dirIn, function(err, dir) {
    if(err) { return cb(err); }

    var getRootFromSvnInfo = function() {
      svnInfo(dir, function(err, info) {
        if(err) { return cb(err); }
        return cb(null, normalize(info.url));
      });
    };

    findup(dir, 'package.json', function(err, pkgDir) {
      if(err) {
        if('not found' === err.message) {
          return getRootFromSvnInfo();
        } else {
          return cb(err);
        }
      }
      var pkg = require(join(pkgDir, 'package.json'));
      if(pkg.repository && 'svn' === pkg.repository.type && pkg.repository.url) {
        return cb(null, normalize(pkg.repository.url));
      } else {
        return getRootFromSvnInfo();
      }
    });
  });
};

/**
 * Returns the normalized project root url
 *
 * Same as the async version but the detected and normalized url is returned
 * rather than passed to a callback.
 *
 * @return {String} The root url
 */
module.exports.sync = function(dirIn) {
  dirIn =  dirIn || process.cwd();

  var dir = fs.realpathSync(dirIn)
    , pkgDir
    , findupErr;

  try {
    pkgDir = findup.sync(dir, 'package.json');
  } catch(err) {
    // Could not find package.json?
    if('not found' !== err.message) {
      findupErr = err;
    }
  }

  // Not finding package.json is OK... other errors should be reported
  if(findupErr) { throw findupErr; }

  // Maybe the package.json file has the svn repo endpoints?
  if(pkgDir) {
    var pkg = require(join(pkgDir, 'package.json'));
    if(pkg.repository && 'svn' === pkg.repository.type && pkg.repository.url) {
      return normalize(pkg.repository.url);
    }
  }

  // Not info in package.json? Welp. Ask svn!
  var info = svnInfo.sync(dir);

  // svnInfo.sync should throw if this is not a working copy
  return normalize(info.url);
};

/**
 * Normalize a svn project url
 *
 * In particular:
 *
 * - Strip away [trunk|branches|tags]
 * - Remove embedded auth creds
 *
 * @param {String} url The url to normalize
 * @return {String} The normalized url
 */
normalize = module.exports.normalize = function(url) {
  var pUrl = require('url').parse(url);

  // First chop everything after first occurance of trunk|branches\tags
  var roots = ['trunk', 'branches', 'tags']
    , urlParts = pUrl.pathname.split('/').filter(function(p) { return p.length > 0;})
    , numParts = urlParts.length
    , ixRoots = [numParts];

  urlParts.forEach(function(part, ixPart) {
    if(roots.indexOf(part) > -1) {
      ixRoots.push(ixPart);
    }
  });

  ixRoots.sort();
  urlParts.splice(ixRoots[0]);

  var choppedUrl = urlParts.join('/')
    , finalUrl = choppedUrl;

  // url.parse takes care of the auth stuff for us
  if(pUrl.protocol) {
    var host = pUrl.port ? pUrl.hostname + ':' + pUrl.port : pUrl.hostname;
    if(choppedUrl.length) {
      finalUrl = pUrl.protocol + '//' + [host, choppedUrl].join('/');
    } else {
      finalUrl = pUrl.protocol + '//' + host;
    }
  }

  return finalUrl;
};
<|endoftext|>


<commit_before>/**
 * @overview The PostgreSQL sync state checker. Checks whether the current
 * replication state is async or synchronous.
 * @copyright Copyright (c) 2013, Joyent, Inc. All rights reserved.
 *
 *                   _.---.._
 *      _        _.-' \  \    ''-.
 *    .'  '-,_.-'   /  /  /       '''.
 *   (       _                     o  :
 *    '._ .-'  '-._         \  \-  ---]
 *                  '-.___.-')  )..-'
 *                           (_/
 */
var assert = require('assert-plus');
var pg = require('pg');
var Client = pg.Client;
var fs = require('fs');
var vasync = require('vasync');
var verror = require('verror');

/**
 * Checks and persists the current replication state. We do this by querying the
 * leader about our replication state -- since Postgresql does not present a
 * method of finding a slave's current replication status on the slave itself.
 *
 * @constructor
 *
 * @param {object} options Options object.
 * @param {Bunyan} options.log Bunyan logger.
 * @param {string} options.cookieLocation File on disk where the state is stored
 * @param {number} options.interval Period in ms to check the sync state.
 */
function SyncStateChecker(options) {
    assert.object(options, 'options');
    assert.number(options.interval, 'options.interval');
    assert.string(options.cookieLocation, 'options.cookieLocation');
    assert.object(options.log, 'options.log');

    /** @type {Bunyan} The bunyan log object */
    this._log = options.log.child({component: 'SyncStateChecker'}, true);
    /** @type {number} sync state check interval in ms */
    this._interval = options.interval;
    /** @type {string} location of the stored sync state cookie */
    this._cookieLocation = options.cookieLocation;
    /** @type {pg.Client} postgres client */
    this._pgClient = null;
    /** @type {string} URL of the this instance's primary */
    this._primaryUrl = null;
    /** @type {object} intervalId of the sync check */
    this._intervalId = null;
}

module.exports = SyncStateChecker;

/**
 * @const
 * @type {string} PostgreSQL sync state query string.
 * @default
 */
SyncStateChecker.prototype.SYNC_STATE_PG_QUERY =
    'select sync_state from pg_stat_replication where client_addr = \'%s\'';

/**
 * @constant
 * @type {string} sync state checker async flag
 * @default
 */
SyncStateChecker.prototype.ASYNC = 'async';

/**
 * @callback SyncStateChecker-cb
 * @param {Error} error
 */

/**
 * Start the sync checker.
 *
 * @param {string} primaryUrl looks like:
 * tcp://postgres@10.77.77.34:5432/postgres
 * @param {Url} myUrl looks like: tcp://postgres@10.77.77.34:5432/postgres
 * @param {SyncStateChecker-cb} cb
 */
SyncStateChecker.prototype.start = function start(primaryUrl, myUrl, cb) {
    var self = this;
    var log = self._log;
    log.info({
        primaryUrl: primaryUrl,
        currPrimaryUrl: self._primaryUrl,
        myUrl: myUrl
    }, 'SyncStateChecker.start: entering');

    assert.string(primaryUrl, 'primaryUrl');
    assert.object(myUrl, 'myUrl');
    assert.func(cb, 'cb');

    self._primaryUrl = primaryUrl;
    var queryString = sprintf(self.SYNC_STATE_PG_QUERY, myUrl.hostname);

    function createPgClient(pgUrl, _cb) {
        self._pgClient = new pg.Client(pgUrl);

        /*
         * do nothing on errors since this is querying state from a remote pg,
         * many things can cause this to fail, and we don't want the client
         * error to barf and emit error all the way up the stack, causing the
         * manatee process to fail.
         */
        self._pgClient.on('error', function () {});

        self._pgClient.connect(function (err) {
            // try again next time
            if (err) {
                self._pgClient.end();
                self._pgClient = null;
                err = new verror.VError(err);
            }
            return _cb(err);
        });

    }

    function querySyncState() {
        if (!self._pgClient) {
            log.info({primaryUrl: self._primaryUrl},
                'SyncStateChecker.start.querySyncState: ' +
                'pgClient not created yet, creating pgClient');

            createPgClient(self._primaryUrl, function (err) {
                log.debug({err: err},
                    'SyncStateChecker.start.querySyncState: ' +
                    'returned from createPgClient');
                return;
            });
        }
        self._pgClient.query(queryString, function (err, result) {
            if (err) {
                log.warn({err: err}, 'unable to query replication status');
            } else {
                log.debug({
                    file: self._cookieLocation,
                    state: result
                }, 'writing sync_state cookie');
                if (result.rows[0] && result.rows[0]['sync_state']) {
                    var state = {
                        role: result.rows[0]['sync_state'],
                        leader: self._primaryUrl
                    };
                    fs.writeFile(self._cookieLocation, JSON.stringify(state),
                        'utf8', function (_err)
                    {
                        if (_err) {
                            log.warn({
                                err: _err,
                                file: self._cookieLocation,
                                state: result
                            }, 'unable to write replication status');
                        }
                    });
                } else {
                    log.warn({
                        state: result
                    }, 'replication result does not have valid sync state');
                }
            }
        });
    }

    function checkWriteSyncState() {
        log.debug({url: self._primaryUrl, queryString: queryString},
                  'querying sync state from pg');

        querySyncState();
    }

    vasync.pipeline({
        funcs: [
            function stopInterval(_, _cb) {
                if (self._intervalId) {
                    clearInterval(self._intervalId);
                    self._intervalId = null;
                }
                return _cb();
            },
            function dcOldPgClient(_, _cb) {
                if (self._pgClient) {
                    try {
                        self._pgClient.end();
                    } catch (e) {}
                    self._pgClient = null;
                }
                return _cb();
            },
            function createNewPgClient(_, _cb) {
                /*
                 * ignore any errors whilst creating pg client, since we always
                 * try to create a new one if this client doesn't exist.
                 */
                createPgClient(self._primaryUrl, function () {
                    return _cb();
                });
            },
            function startInterval(_, _cb) {
                self._intervalId =
                    setInterval(checkWriteSyncState, self._interval);
                // manually invoke the function the first time.
                checkWriteSyncState();
                return _cb();
            }
        ]
    }, function (err, results) {
        return cb(err);
    });
};

/**
 * Stop checking the sync state of my primary. We only want to delete the
 * cookie if we are becoming the primary, otherwise we would like to preserve
 * the cookie information.
 *
 * @param {bool} delCookie Whether to delete the cookie.
 * @param {SyncStateChecker-cb} cb
 */
SyncStateChecker.prototype.stop = function (delCookie, cb) {
    assert.func(cb, 'cb');

    var self = this;
    self._log.info({
        currPrimaryUrl: self._primaryUrl,
        delCookie: delCookie
    }, 'SyncStateChecker.stop: entering');

    assert.func(cb, 'cb');

    vasync.pipeline({
        funcs: [
            function stopInterval(_, _cb) {
                if (self._intervalId) {
                    clearInterval(self._intervalId);
                    self._intervalId = null;
                }
                return _cb();
            },
            function dcOldPgClient(_, _cb) {
                if (self._pgClient) {
                    try {
                        self._pgClient.end();
                    } catch (e) {}
                    self._pgClient = null;
                }
                return _cb();
            },
            function deleteCookie(_, _cb) {
                if (delCookie) {
                    fs.unlink(self._cookieLocation, function (err) {
                        self._log.warn({err: err},
                            'SyncStateChecker.stop: unable to del cookie');
                        return _cb();
                    });
                } else {
                    return _cb();
                }
            }
        ]
    }, function (err, results) {
        self._log.info({err: err}, 'SyncStateChecker.stop: exiting');
        return cb(err);
    });
};

/**
 * @callback SyncStateChecker-lastRoleCb
 * @param {Error} error
 * @param {string} state
 */

/**
 * Get the last role of this instance.
 *
 * @param {SyncStateChecker-lastRoleCb} cb
 */
SyncStateChecker.prototype.getLastRole = function (cb) {
    var self = this;
    self._log.info({
        cookieLocaltion: self._cookieLocation
    }, 'SyncStateChecker.getLastRole: entering');

    assert.func(cb, 'cb');
    fs.readFile(self._cookieLocation, 'utf8', function (err, data) {
        if (err) {
            // do nothing if we can't get the cookie
            self._log.warn({err: err}, 'unable to get last role');
            return cb();
        }
        var state;
        if (data) {
            state = JSON.parse(data);
        }

        self._log.info({
            role: state
        }, 'SyncStateChecker.getLastRole: exiting');
        return cb(null, state);
    });
};

/**
 * Write the primary cookie.
 *
 * @param {SyncStateChecker-cb} cb
 */
SyncStateChecker.prototype.writePrimaryCookie = function writePrimaryCookie(cb)
{
    var self = this;
    self._log.info({myUrl: self._url},
        'SyncStateChecker.writePrimaryCookie: entering');

    var state = {
        role: 'primary'
    };

    fs.writeFile(self._cookieLocation, JSON.stringify(state), function (err) {
        if (err) {
            err = new verror.VError(err);
        }

        self._log.info({err: err, cookie: state},
            'SyncStateChecker.writePrimaryCookie: exiting');
        return cb(err);
    });
};

/**
 * @callback SyncStateChecker-masterCb
 * @param {boolean} canStart
 */

/**
 * Check whether this instance can start as a master.
 *
 * @param {SyncStateChecker-masterCb} cb
 */
SyncStateChecker.prototype.canStartAsMaster = function (cb) {
    var self = this;
    self._log.info({
        cookieLocaltion: self._cookieLocation
    }, 'SyncStateChecker.canStartAsMaster: entering');

    assert.func(cb, 'cb');

    fs.readFile(self._cookieLocation, 'utf8', function (err, data) {
        var can = true;
        var state;
        if (data) {
            state = JSON.parse(data);
        }
        if (state && state.role === self.ASYNC) {
            can = false;
        }

        self._log.info({
            cookieLocaltion: self._cookieLocation,
            data: data,
            canStart: can
        }, 'SyncStateChecker.canStartAsMaster: exiting');
        return cb(can);
    });
};
<commit_msg>add missing sprintf module to syncstatechecker<commit_after>/**
 * @overview The PostgreSQL sync state checker. Checks whether the current
 * replication state is async or synchronous.
 * @copyright Copyright (c) 2013, Joyent, Inc. All rights reserved.
 *
 *                   _.---.._
 *      _        _.-' \  \    ''-.
 *    .'  '-,_.-'   /  /  /       '''.
 *   (       _                     o  :
 *    '._ .-'  '-._         \  \-  ---]
 *                  '-.___.-')  )..-'
 *                           (_/
 */
var assert = require('assert-plus');
var pg = require('pg');
var Client = pg.Client;
var fs = require('fs');
var sprintf = require('util').format;
var vasync = require('vasync');
var verror = require('verror');

/**
 * Checks and persists the current replication state. We do this by querying the
 * leader about our replication state -- since Postgresql does not present a
 * method of finding a slave's current replication status on the slave itself.
 *
 * @constructor
 *
 * @param {object} options Options object.
 * @param {Bunyan} options.log Bunyan logger.
 * @param {string} options.cookieLocation File on disk where the state is stored
 * @param {number} options.interval Period in ms to check the sync state.
 */
function SyncStateChecker(options) {
    assert.object(options, 'options');
    assert.number(options.interval, 'options.interval');
    assert.string(options.cookieLocation, 'options.cookieLocation');
    assert.object(options.log, 'options.log');

    /** @type {Bunyan} The bunyan log object */
    this._log = options.log.child({component: 'SyncStateChecker'}, true);
    /** @type {number} sync state check interval in ms */
    this._interval = options.interval;
    /** @type {string} location of the stored sync state cookie */
    this._cookieLocation = options.cookieLocation;
    /** @type {pg.Client} postgres client */
    this._pgClient = null;
    /** @type {string} URL of the this instance's primary */
    this._primaryUrl = null;
    /** @type {object} intervalId of the sync check */
    this._intervalId = null;
}

module.exports = SyncStateChecker;

/**
 * @const
 * @type {string} PostgreSQL sync state query string.
 * @default
 */
SyncStateChecker.prototype.SYNC_STATE_PG_QUERY =
    'select sync_state from pg_stat_replication where client_addr = \'%s\'';

/**
 * @constant
 * @type {string} sync state checker async flag
 * @default
 */
SyncStateChecker.prototype.ASYNC = 'async';

/**
 * @callback SyncStateChecker-cb
 * @param {Error} error
 */

/**
 * Start the sync checker.
 *
 * @param {string} primaryUrl looks like:
 * tcp://postgres@10.77.77.34:5432/postgres
 * @param {Url} myUrl looks like: tcp://postgres@10.77.77.34:5432/postgres
 * @param {SyncStateChecker-cb} cb
 */
SyncStateChecker.prototype.start = function start(primaryUrl, myUrl, cb) {
    var self = this;
    var log = self._log;
    log.info({
        primaryUrl: primaryUrl,
        currPrimaryUrl: self._primaryUrl,
        myUrl: myUrl
    }, 'SyncStateChecker.start: entering');

    assert.string(primaryUrl, 'primaryUrl');
    assert.object(myUrl, 'myUrl');
    assert.func(cb, 'cb');

    self._primaryUrl = primaryUrl;
    var queryString = sprintf(self.SYNC_STATE_PG_QUERY, myUrl.hostname);

    function createPgClient(pgUrl, _cb) {
        self._pgClient = new pg.Client(pgUrl);

        /*
         * do nothing on errors since this is querying state from a remote pg,
         * many things can cause this to fail, and we don't want the client
         * error to barf and emit error all the way up the stack, causing the
         * manatee process to fail.
         */
        self._pgClient.on('error', function () {});

        self._pgClient.connect(function (err) {
            // try again next time
            if (err) {
                self._pgClient.end();
                self._pgClient = null;
                err = new verror.VError(err);
            }
            return _cb(err);
        });

    }

    function querySyncState() {
        if (!self._pgClient) {
            log.info({primaryUrl: self._primaryUrl},
                'SyncStateChecker.start.querySyncState: ' +
                'pgClient not created yet, creating pgClient');

            createPgClient(self._primaryUrl, function (err) {
                log.debug({err: err},
                    'SyncStateChecker.start.querySyncState: ' +
                    'returned from createPgClient');
                return;
            });
        }
        self._pgClient.query(queryString, function (err, result) {
            if (err) {
                log.warn({err: err}, 'unable to query replication status');
            } else {
                log.debug({
                    file: self._cookieLocation,
                    state: result
                }, 'writing sync_state cookie');
                if (result.rows[0] && result.rows[0]['sync_state']) {
                    var state = {
                        role: result.rows[0]['sync_state'],
                        leader: self._primaryUrl
                    };
                    fs.writeFile(self._cookieLocation, JSON.stringify(state),
                        'utf8', function (_err)
                    {
                        if (_err) {
                            log.warn({
                                err: _err,
                                file: self._cookieLocation,
                                state: result
                            }, 'unable to write replication status');
                        }
                    });
                } else {
                    log.warn({
                        state: result
                    }, 'replication result does not have valid sync state');
                }
            }
        });
    }

    function checkWriteSyncState() {
        log.debug({url: self._primaryUrl, queryString: queryString},
                  'querying sync state from pg');

        querySyncState();
    }

    vasync.pipeline({
        funcs: [
            function stopInterval(_, _cb) {
                if (self._intervalId) {
                    clearInterval(self._intervalId);
                    self._intervalId = null;
                }
                return _cb();
            },
            function dcOldPgClient(_, _cb) {
                if (self._pgClient) {
                    try {
                        self._pgClient.end();
                    } catch (e) {}
                    self._pgClient = null;
                }
                return _cb();
            },
            function createNewPgClient(_, _cb) {
                /*
                 * ignore any errors whilst creating pg client, since we always
                 * try to create a new one if this client doesn't exist.
                 */
                createPgClient(self._primaryUrl, function () {
                    return _cb();
                });
            },
            function startInterval(_, _cb) {
                self._intervalId =
                    setInterval(checkWriteSyncState, self._interval);
                // manually invoke the function the first time.
                checkWriteSyncState();
                return _cb();
            }
        ]
    }, function (err, results) {
        return cb(err);
    });
};

/**
 * Stop checking the sync state of my primary. We only want to delete the
 * cookie if we are becoming the primary, otherwise we would like to preserve
 * the cookie information.
 *
 * @param {bool} delCookie Whether to delete the cookie.
 * @param {SyncStateChecker-cb} cb
 */
SyncStateChecker.prototype.stop = function (delCookie, cb) {
    assert.func(cb, 'cb');

    var self = this;
    self._log.info({
        currPrimaryUrl: self._primaryUrl,
        delCookie: delCookie
    }, 'SyncStateChecker.stop: entering');

    assert.func(cb, 'cb');

    vasync.pipeline({
        funcs: [
            function stopInterval(_, _cb) {
                if (self._intervalId) {
                    clearInterval(self._intervalId);
                    self._intervalId = null;
                }
                return _cb();
            },
            function dcOldPgClient(_, _cb) {
                if (self._pgClient) {
                    try {
                        self._pgClient.end();
                    } catch (e) {}
                    self._pgClient = null;
                }
                return _cb();
            },
            function deleteCookie(_, _cb) {
                if (delCookie) {
                    fs.unlink(self._cookieLocation, function (err) {
                        self._log.warn({err: err},
                            'SyncStateChecker.stop: unable to del cookie');
                        return _cb();
                    });
                } else {
                    return _cb();
                }
            }
        ]
    }, function (err, results) {
        self._log.info({err: err}, 'SyncStateChecker.stop: exiting');
        return cb(err);
    });
};

/**
 * @callback SyncStateChecker-lastRoleCb
 * @param {Error} error
 * @param {string} state
 */

/**
 * Get the last role of this instance.
 *
 * @param {SyncStateChecker-lastRoleCb} cb
 */
SyncStateChecker.prototype.getLastRole = function (cb) {
    var self = this;
    self._log.info({
        cookieLocaltion: self._cookieLocation
    }, 'SyncStateChecker.getLastRole: entering');

    assert.func(cb, 'cb');
    fs.readFile(self._cookieLocation, 'utf8', function (err, data) {
        if (err) {
            // do nothing if we can't get the cookie
            self._log.warn({err: err}, 'unable to get last role');
            return cb();
        }
        var state;
        if (data) {
            state = JSON.parse(data);
        }

        self._log.info({
            role: state
        }, 'SyncStateChecker.getLastRole: exiting');
        return cb(null, state);
    });
};

/**
 * Write the primary cookie.
 *
 * @param {SyncStateChecker-cb} cb
 */
SyncStateChecker.prototype.writePrimaryCookie = function writePrimaryCookie(cb)
{
    var self = this;
    self._log.info({myUrl: self._url},
        'SyncStateChecker.writePrimaryCookie: entering');

    var state = {
        role: 'primary'
    };

    fs.writeFile(self._cookieLocation, JSON.stringify(state), function (err) {
        if (err) {
            err = new verror.VError(err);
        }

        self._log.info({err: err, cookie: state},
            'SyncStateChecker.writePrimaryCookie: exiting');
        return cb(err);
    });
};

/**
 * @callback SyncStateChecker-masterCb
 * @param {boolean} canStart
 */

/**
 * Check whether this instance can start as a master.
 *
 * @param {SyncStateChecker-masterCb} cb
 */
SyncStateChecker.prototype.canStartAsMaster = function (cb) {
    var self = this;
    self._log.info({
        cookieLocaltion: self._cookieLocation
    }, 'SyncStateChecker.canStartAsMaster: entering');

    assert.func(cb, 'cb');

    fs.readFile(self._cookieLocation, 'utf8', function (err, data) {
        var can = true;
        var state;
        if (data) {
            state = JSON.parse(data);
        }
        if (state && state.role === self.ASYNC) {
            can = false;
        }

        self._log.info({
            cookieLocaltion: self._cookieLocation,
            data: data,
            canStart: can
        }, 'SyncStateChecker.canStartAsMaster: exiting');
        return cb(can);
    });
};
<|endoftext|>


<commit_before>"use strict";

var assemble = require('assemble');
var runSequence = require('run-sequence').use(assemble);
var extname = require('gulp-extname');
var controller = require('../assemble/plugins/controller.js');
var injection = require('../assemble/plugins/injection.js');
var errorHandler = require('../assemble/plugins/error');
var livereload = require('gulp-livereload');
var taskGenerator = require('../taskGenerator');

module.exports = function(name, config, server) {
    return taskGenerator(name, config, function(taskName, task) {
        assemble.task(taskName, function() {
            assemble.layouts(config.layouts);
            assemble.partials(config.partials);
            return assemble.src(task.files.src, getData(task.layout, server))
                .on('error', errorHandler)
                .pipe(extname())
                .pipe(assemble.dest(task.files.dest))
                .pipe(injection(server))
                .pipe(assemble.dest(task.files.dest))
                .pipe(livereload())
                .pipe(controller.collect())
        });
    }, function(config, tasks, cb) {
        controller.reset();
        runSequence.call(null, tasks, function() {
            controller.createRegistry();
            cb();
        });
    });
};

function getData(layout, server) {
    return {
        layout: layout,
        env: {
            js: {
                main: server.dest + '/js/app.js',
                embed: server.dest + '/js/embed.js'
            },
            css: {
                main: server.dest + '/css/style.css',
                critical: server.dest + '/css/critical.css'
            },
            webpack: server.webpack
        }
    }
}<commit_msg>update tmpl options object<commit_after>"use strict";

var assemble = require('assemble');
var runSequence = require('run-sequence').use(assemble);
var extname = require('gulp-extname');
var controller = require('../assemble/plugins/controller.js');
var injection = require('../assemble/plugins/injection.js');
var errorHandler = require('../assemble/plugins/error');
var livereload = require('gulp-livereload');
var taskGenerator = require('../taskGenerator');

module.exports = function(name, config, server) {
    return taskGenerator(name, config, function(taskName, task) {
        assemble.task(taskName, function() {
            assemble.layouts(config.layouts);
            assemble.partials(config.partials);
            return assemble.src(task.files.src, getData(task.layout, server))
                .on('error', errorHandler)
                .pipe(extname())
                .pipe(assemble.dest(task.files.dest))
                .pipe(livereload())
                .pipe(controller.collect())
        });
    }, function(config, tasks, cb) {
        controller.reset();
        runSequence.call(null, tasks, function() {
            controller.createRegistry();
            cb();
        });
    });
};

function getData(layout, server) {
    return {
        layout: layout,
        env: {
            js: {
                main: server.dest + '/js/app.js',
                embed: server.dest + '/js/embed.js'
            },
            css: {
                main: server.dest + '/css/style.css',
                critical: server.dest + '/css/critical.css'
            },
            server: server
        }
    }
}<|endoftext|>


<commit_before>DS.DjangoTastypieAdapter = DS.RESTAdapter.extend({
  /*
   * Set this parameter if you are planning to do cross-site
   * requests to the destination domain. Remember trailing slash
   */

  serverDomain: "",

  /*
   * This is the default Tastypie url found in the documentation.
   * You may change it if necessary when creating the adapter
   */
  tastypieApiUrl: "api/v1/",

  /*
   * Bulk commits are not supported at this time by the adapter.
   * Changing this setting will not work
   */
  bulkCommit: false,

  /**
   * Transforms the association fields to Resource URI django-tastypie format
   */
  _urlifyData: function(type, model, raw){
    var self = this;
    var value;

    var jsonData = model.toJSON({ associations: true });

    var associations = Em.get(type, 'associationsByName');

    associations.forEach(function(key, meta){

      if (meta.kind === "belongsTo") {
        key = meta.options.key || Em.get(model, 'namingConvention').foreignKey(key);
        value = jsonData[key];
        if (!!value) {
          jsonData[key] = self.getItemUrl(type, meta, value);
        }

      } else if (meta.kind === "hasMany") {
        key = meta.options.key || Em.get(model, 'namingConvention').keyToJSONKey(key);
        value = jsonData[key] || [];
        $.each(value, function(i, item) {
          value[i] = self.getItemUrl(type, meta, item);
        });
        }
    });

    return (raw) ? jsonData : JSON.stringify(jsonData);
  },

  /**
   * Transforms the association fields from Resource URI django-tastypie format to IDs
   */
  _deurlifyData: function(type, jsonObject) {
    var meta,
      value;

    var deurlify = function(value) {
      if (!!value) {
        return value.split('/').reverse()[1];
      }
    };

    var associations = Em.get(type, 'associationsByName'),
        self = this;

    associations.forEach(function(key, meta) {
      meta = type.metaForProperty(key);

      if (meta.kind === "belongsTo") {
        key = meta.options.key || type.prototype.get('namingConvention').foreignKey(key);
        value = jsonObject[key];
        if (!!value) {
          jsonObject[key] = (meta.options.embedded) ? self._deurlifyData( meta.type, value ) : deurlify(value);
        }
      } else if (meta.kind === "hasMany") {
        key = meta.options.key || type.prototype.get('namingConvention').keyToJSONKey(key);
        if (!!jsonObject[key]) {
          jsonObject[key].forEach(function(item, i, collection) {
            collection[i] = (meta.options.embedded) ? item : deurlify(item);
          });
        }
      }
    });
    return jsonObject;
  },

  /*
   * Create a record in the Django server. POST actions must
   * be enabled in the Resource
   */
  createRecord: function(store, type, model) {
    var self = this;
    var root = this.rootForType(type);

    var data = this._urlifyData(type, model);

    this.ajax(root, "POST", {
      data: data,
      success: function(json) {
        json = self._deurlifyData(type, json);
        store.didCreateRecord(model, json);
      },
      error: function(error) {
      }
    });
  },

  /*
   * Edit a record in the Django server. PUT actions must
   * be enabled in the Resource
   */
  updateRecord: function(store, type, model) {
    var self = this;

    var id = Em.get(model, 'id');
    var root = this.rootForType(type);

    var data = this._urlifyData(type, model);

    var url = [root, id].join("/");

    this.ajax(url, "PUT", {
      data: data,
      success: function(json) {
        json = self._deurlifyData(type, json);
        store.didUpdateRecord(model, json);
      },
      error: function(error) {
      }
    });
  },

  /*
   * Delete a record in the Django server. DELETE actions
   * must be enabled in the Resource
   */
  deleteRecord: function(store, type, model) {
    var id = Em.get(model, 'id');
    var root = this.rootForType(type);

    var url = [root, id].join("/");

    this.ajax(url, "DELETE", {
      success: function(json) {
        store.didDeleteRecord(model);
      }
    });
  },

  find: function(store, type, id) {
    var self = this;

    // FindMany array through subset of resources
    if (id instanceof Array) {
      id = "set/" + id.join(";");
    }

    var root = this.rootForType(type);
    var url = [root, id].join("/");

    this.ajax(url, "GET", {
      success: function(json) {

        // Loads collection for findMany
        if (json.hasOwnProperty("objects")) {
          json["objects"].forEach(function(item, i, collection) {
            collection[i] = self._deurlifyData(type, item);
          });
          store.loadMany(type, json["objects"]);
        // Loads unique element with find by id
        } else {
          json = self._deurlifyData(type, json);
          store.load(type, json);
        }
      }
    });
  },

  findMany: function() {
    this.find.apply(this, arguments);
  },

  findAll: function(store, type) {
    var self = this;
    var root = this.rootForType(type);

    this.ajax(root, "GET", {
      success: function(json) {
        json["objects"].forEach(function(item, i, collection) {
          collection[i] = self._deurlifyData(type, item);
        });
        store.loadMany(type, json["objects"]);
      }
    });
  },

  findQuery: function(store, type, query, recordArray){
    var self = this;
    var root = this.rootForType(type);

    this.ajax(root, "GET", {
      data: query,
      success: function(json) {
        json["objects"].forEach(function(item, i, collection) {
          collection[i] = self._deurlifyData(type, item);
        });
        recordArray.load(json["objects"]);
      }
    });
  },

  getItemUrl: function(type, meta, id){
    var url;
    Em.assert("tastypieApiUrl parameters is mandatory.", !!this.tastypieApiUrl);
    url = this.rootForType(meta.type);
    return ["", this.tastypieApiUrl.slice(0,-1), url, id, ""].join('/');
  },

  getTastypieUrl: function(url){
    Em.assert("tastypieApiUrl parameters is mandatory.", !!this.tastypieApiUrl);
    return this.serverDomain + this.tastypieApiUrl + url + "/";

  },

  ajax: function(url, type, hash) {
    hash.url = this.getTastypieUrl(url);
    hash.type = type;
    hash.dataType = "json";
    hash.contentType = 'application/json';
    jQuery.ajax(hash);
  },

  /**
    * django-tastypie does not pluralize names for lists
    */
  pluralize: function(name) {
    return name;
  }
});
<commit_msg>Updated to use the new store.didSaveRecord.<commit_after>DS.DjangoTastypieAdapter = DS.RESTAdapter.extend({
  /*
   * Set this parameter if you are planning to do cross-site
   * requests to the destination domain. Remember trailing slash
   */

  serverDomain: "",

  /*
   * This is the default Tastypie url found in the documentation.
   * You may change it if necessary when creating the adapter
   */
  tastypieApiUrl: "api/v1/",

  /*
   * Bulk commits are not supported at this time by the adapter.
   * Changing this setting will not work
   */
  bulkCommit: false,

  /**
   * Transforms the association fields to Resource URI django-tastypie format
   */
  _urlifyData: function(type, model, raw){
    var self = this;
    var value;

    var jsonData = model.toJSON({ associations: true });

    var associations = Em.get(type, 'associationsByName');

    associations.forEach(function(key, meta){

      if (meta.kind === "belongsTo") {
        key = meta.options.key || Em.get(model, 'namingConvention').foreignKey(key);
        value = jsonData[key];
        if (!!value) {
          jsonData[key] = self.getItemUrl(type, meta, value);
        }

      } else if (meta.kind === "hasMany") {
        key = meta.options.key || Em.get(model, 'namingConvention').keyToJSONKey(key);
        value = jsonData[key] || [];
        $.each(value, function(i, item) {
          value[i] = self.getItemUrl(type, meta, item);
        });
        }
    });

    return (raw) ? jsonData : JSON.stringify(jsonData);
  },

  /**
   * Transforms the association fields from Resource URI django-tastypie format to IDs
   */
  _deurlifyData: function(type, jsonObject) {
    var meta,
      value;

    var deurlify = function(value) {
      if (!!value) {
        return value.split('/').reverse()[1];
      }
    };

    var associations = Em.get(type, 'associationsByName'),
        self = this;

    associations.forEach(function(key, meta) {
      meta = type.metaForProperty(key);

      if (meta.kind === "belongsTo") {
        key = meta.options.key || type.prototype.get('namingConvention').foreignKey(key);
        value = jsonObject[key];
        if (!!value) {
          jsonObject[key] = (meta.options.embedded) ? self._deurlifyData( meta.type, value ) : deurlify(value);
        }
      } else if (meta.kind === "hasMany") {
        key = meta.options.key || type.prototype.get('namingConvention').keyToJSONKey(key);
        if (!!jsonObject[key]) {
          jsonObject[key].forEach(function(item, i, collection) {
            collection[i] = (meta.options.embedded) ? item : deurlify(item);
          });
        }
      }
    });
    return jsonObject;
  },

  /*
   * Create a record in the Django server. POST actions must
   * be enabled in the Resource
   */
  createRecord: function(store, type, record) {
    var self = this;
    var root = this.rootForType(type);

    var data = this._urlifyData(type, record);

    this.ajax(root, "POST", {
      data: data,
      success: function(json) {
        json = self._deurlifyData(type, json);
        this.didCreateRecord(store, type, record, json);
      },
      error: function(error) {
      }
    });
  },

  /*
   * Edit a record in the Django server. PUT actions must
   * be enabled in the Resource
   */
  updateRecord: function(store, type, record) {
    var self = this;

    var id = Em.get(record, 'id');
    var root = this.rootForType(type);

    var data = this._urlifyData(type, record);

    var url = [root, id].join("/");

    this.ajax(url, "PUT", {
      data: data,
      success: function(json) {
        json = self._deurlifyData(type, json);
        this.didUpdateRecord(store, type, record, json);
      },
      error: function(error) {
      }
    });
  },

  /*
   * Delete a record in the Django server. DELETE actions
   * must be enabled in the Resource
   */
  deleteRecord: function(store, type, record) {
    var id = Em.get(record, 'id');
    var root = this.rootForType(type);

    var url = [root, id].join("/");

    this.ajax(url, "DELETE", {
      success: function(json) {
        this.didDeleteRecord(store, type, record, json);
      }
    });
  },

  /*
   * Mark record as saved in after creating
   */
  didCreateRecord: function(store, type, record, json) {
    store.didSaveRecord(record, json);
  },

  /*
   * Mark record as saved after updating
   */
  didUpdateRecord: function(store, type, record, json) {
    store.didSaveRecord(record, json);
  },

  /*
   * Mark record as saved after deleting
   */
  didDeleteRecord: function(store, type, record, json) {
    store.didSaveRecord(record, json);
  },

  find: function(store, type, id) {
    var self = this;

    // FindMany array through subset of resources
    if (id instanceof Array) {
      id = "set/" + id.join(";");
    }

    var root = this.rootForType(type);
    var url = [root, id].join("/");

    this.ajax(url, "GET", {
      success: function(json) {

        // Loads collection for findMany
        if (json.hasOwnProperty("objects")) {
          json["objects"].forEach(function(item, i, collection) {
            collection[i] = self._deurlifyData(type, item);
          });
          store.loadMany(type, json["objects"]);
        // Loads unique element with find by id
        } else {
          json = self._deurlifyData(type, json);
          store.load(type, json);
        }
      }
    });
  },

  findMany: function() {
    this.find.apply(this, arguments);
  },

  findAll: function(store, type) {
    var self = this;
    var root = this.rootForType(type);

    this.ajax(root, "GET", {
      success: function(json) {
        json["objects"].forEach(function(item, i, collection) {
          collection[i] = self._deurlifyData(type, item);
        });
        store.loadMany(type, json["objects"]);
      }
    });
  },

  findQuery: function(store, type, query, recordArray){
    var self = this;
    var root = this.rootForType(type);

    this.ajax(root, "GET", {
      data: query,
      success: function(json) {
        json["objects"].forEach(function(item, i, collection) {
          collection[i] = self._deurlifyData(type, item);
        });
        recordArray.load(json["objects"]);
      }
    });
  },

  getItemUrl: function(type, meta, id){
    var url;
    Em.assert("tastypieApiUrl parameters is mandatory.", !!this.tastypieApiUrl);
    url = this.rootForType(meta.type);
    return ["", this.tastypieApiUrl.slice(0,-1), url, id, ""].join('/');
  },

  getTastypieUrl: function(url){
    Em.assert("tastypieApiUrl parameters is mandatory.", !!this.tastypieApiUrl);
    return this.serverDomain + this.tastypieApiUrl + url + "/";

  },

  ajax: function(url, type, hash) {
    hash.url = this.getTastypieUrl(url);
    hash.type = type;
    hash.dataType = "json";
    hash.contentType = 'application/json';
    jQuery.ajax(hash);
  },

  /**
    * django-tastypie does not pluralize names for lists
    */
  pluralize: function(name) {
    return name;
  }
});
<|endoftext|>