1.8.2 • Published 9 years ago

join-wrap v1.8.2

Weekly downloads
2
License
WTFPL
Repository
github
Last release
9 years ago

It is still alpha!!! Major changes can be made without any warning.

Library for application state management. In a lot of ways repeats features of CortexJS, but with a few important distinctions. I tried to make API look as close to Cortex as I could, so switching from Cortex to joinWrap should not be painful.

Key differences from CortexJS

  • you can extend wrapper class
  • you can extend wrapper class only in certain positions of your data structure
  • you can create references between parts of data structure that doesn't have parent-child relationships which turns structure of your data from tree to arbitrary graph
  • you can manage parts of your state separately

####Setup

var joinWrap = require('joinWrap');

var wrapper = joinWrap();

var data = { users: ['John'] };
var model = wrapper.wrap(data).on('update', function () {
  // trigger your view to update
});

And now variable model acts a lot like in CortexJS.

You have .set(), .val(), .on(), .off(), .push(), remove(), etc.

Events

When wrapped data changes wrapper emits multiple events that "bubbles" over sequence of parents up to the root wrapper. All events provided with two arguments at least.

  • object that caused event
  • object to which handler is attached

####Add callbacks

function logUpdates(changeSourceWrapper) {
  console.log(
    changeSourceWrapper.getRef().join('.') + ' = ' + changeSourceWrapper.val()
  );
}
model.on('update', logUpdates);

//"users.0 = not a John" will be written to console
model.users[0].set('not a John');

####Remove callback

model.off('update', logUpdates);

####Specific events

  • update - something changes
  • set, beforeSet - someone called .set(). Emitted with additional argument (previous value for set, new value for beforeSet)
  • add, beforeAdd - someone called .add() or .push(). In array wrapper there is also method .slice() which can trigger these events. Additional argument: array with key and value.
  • remove, beforeRemove - for .remove(), .removeChild(), .splice(). Extra argument: index.
  • splice, beforeSplice - for .splice(). Extra argument: array with arguments to .slice().

###Descriptors

If you compared examples above with that of Cortex, you probably noticed that object Cortex is a wrapper itself which is not true for joinWrap. To wrap data in joinWrap you should create "wrapper" by calling joinWrap. And then pass your raw data to wrapper.wrap.

So why do we need intermediate step wrapper = joinWrap().

That is because joinWrap has two optional parameters.

  • options on this level of nesting
  • object with nested joinWraps

####Local extensions

Most important option is extension. It allows you to replace wrapper on this level of nesting by creating your own wrapper from scratch orby extending provided wrapper class.

var Robot = joinWrap({
  extension: function (Wrapper) {
    function RobotWrapper() {
      Wrapper.apply(this, arguments);
      this.add('noiseLog', []);
    }
    RobotWrapper.prototype = Object.create(Wrapper.prototype);
    RobotWrapper.prototype.beep = function () {
      this.noiseLog.push({noise: 'beep', time: Date.now()});
      return this;
    };
    RobotWrapper.prototype.boop = function () {
      this.noiseLog.push({noise: 'boop', time: Date.now()});
      return this;
    };

    return RobotWrapper;
  }
});

var robot = Robot.wrap({pos: {x: 63, y: -21}, favoriteNumber: 42});

robot.beep().boop();

console.log(robot.noiseLog.val());

####Nesting

So we can make our wrapper better for robots. Let's suppose we also created extension for zombies. But what if we want to have both robot and zombie in our app state?

That is where second argument of joinWrap comes in.

var Robot = joinWrap(...);
var Zombie = joinWrap(...);

var BestFriends = joinWrap(null, {
  robot: Robot,
  zombie: Zombie
});

var bestFriends = BestFriends.wrap({
  robot: {pos: {x: 63, y: -21}, favoriteNumber: 42},
  zombie: {pos: {x: 600, y: 100}, like: "to eat brains"}
});

bestFriends.robot.beep().boop();
bestFriends.zombie.whateverZombieCanDo();

####Regularity in nestings

Well... one robot and one zombie is good, but what if we want an army. And that we can also have:

var TwoArmies = joinWrap(null, {
  'robots[]': Robot,
  'zombies[]': Zombie
});

var twoArmies = TwoArmies.wrap({
  robots: [
    {pos: {x: 63, y: -21}, favoriteNumber: 42},
    {pos: {x: 221, y: 445}, favoriteNumber: 666}
  ],
  zombies: [... list of zombies]
});

twoArmies.robots[0].beep().boop();

And we want to have control over entire army not just robots one by one

var TwoArmies = joinWrap(null, {
  'robots': joinWrap({
    extension: function (Wrapper) {
      function RoboticArmyWrapper() {
        Wrapper.apply(this, arguments);
      }
      RoboticArmyWrapper.prototype = Object.create(Wrapper.prototype);
      RoboticArmyWrapper.prototype.massiveBeepBoop = function () {
        this.forEach(function (robot) {
          robot.beep().boop();
        });
        return this;
      }
    }
  }, {
    // actualy {'robots[]':...} - is a shorthand form of {robots: {'@number':...}}
    // which is also shorthand for {robots: joinWrap(null, {'@number':...})}
    '@number': Robot
  }),
  'zombies[]': Zombie
});

var twoArmies = TwoArmies.wrap(...);

twoArmies.robots.massiveBeepBoop();
twoArmies.robots[0].beep().beep();

References

OK. Forget about robots and zombies.

Let's say we have triangles and points.

Point - consists of some coordinates, and triangle made of three points.

And triangles can also share some points, so when such point changes it changes in all related triangles.

var Point = joinWrap('Point');// shorthand for {entityName: 'Point'}
var Triangle = joinWrap(null, {
  A: '@Point',
  B: '@Point',
  C: '@Point'
});

var World = joinWrap(null, {
  'triangles[]': Triangle,
  'points[]': Points
});

var world = World.wrap({
  points: [
    {x: 1, y: 2},
    {x: 2, y: 5},
    {x: 5, y: 1},
    {x: 3, y: 2}
  ],
  triangles: []
});

var p = world.points;

world.triangles.push(
  {A: p[0], B: p[1], C: p[2] },
  {A: p[3], B: p[2], C: p[0] }
);

var ts = world.triangles;

ts[0].A.deref().x.val()// -> 1
ts[1].C.deref().x.val()// -> 1
ts[0].A.deref().val() === ts[1].C.deref().val()// -> true
ts[0].A.deref().val() === p[0].val()// -> true

Now in variable world we have value that can be serialized, saved and restored with that relation preserved.

When you set value of reference X to object Y, Y start piping events not only to it's parent but also to X.

For example events flow from world.points[0] not only to object world.points but also to objects world.triangles[0].A and world.triangles[1].C.

1.8.2

9 years ago

1.8.1

9 years ago

1.8.0

9 years ago

1.7.2

9 years ago

1.7.1

9 years ago

1.7.0

9 years ago

1.6.9

9 years ago

1.6.8

9 years ago

1.6.7

9 years ago

1.6.6

9 years ago

1.6.5

9 years ago

1.6.4

9 years ago

1.6.3

9 years ago

1.6.2

9 years ago

1.6.1

9 years ago

1.6.0

9 years ago

1.5.0

9 years ago

1.4.9

9 years ago

1.4.8

9 years ago

1.4.7

9 years ago

1.4.6

9 years ago

1.4.5

9 years ago

1.4.4

9 years ago

1.4.3

9 years ago

1.4.2

9 years ago

1.4.1

9 years ago

1.4.0

9 years ago

1.3.0

9 years ago

1.2.0

9 years ago

1.1.2

9 years ago

1.1.1

9 years ago

1.1.0

9 years ago

1.0.1

9 years ago