Skip to content

Instantly share code, notes, and snippets.

@ChetHarrison
Last active August 29, 2015 14:10
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save ChetHarrison/c610d4fe4a5e9f9280a2 to your computer and use it in GitHub Desktop.
Save ChetHarrison/c610d4fe4a5e9f9280a2 to your computer and use it in GitHub Desktop.
'use strict';
var EventEmitter = require('events').EventEmitter,
globalChannel = new EventEmitter(),
Rx = require('rx'),
nutrients = require('./food-objects/nutrients'),
nutrients,
defaultHandlers = {
onError: function (err) {
console.log('Error: ' + err);
},
onComplete: function () {
console.log('Completed');
}
},
globalChannelDestroyEvent = Rx.Observable.fromEvent( globalChannel, 'destroy').take(1),
// declaration
//-----------------------------------------
frenchFryNutrition = Object.create( nutrients ); // using delegation pattern
// lazy instantiation
//-----------------------------------------
frenchFryNutrition.initNutrition({
state: {
salt: 100,
sugar: 95,
calories: 50
},
init: function() {
console.log('init was called on this stateContainer');
}
});
// events
//-----------------------------------------
// create observables
Rx.Observable.fromEvent( globalChannel, 'nutrientRequest',
function( eventArgs ) {
return frenchFryNutrition.getState()[ eventArgs[0] ];
}).
takeUntil(globalChannelDestroyEvent).
subscribe(
function( response ) { globalChannel.emit( 'nutrientResponse', response ); },
defaultHandlers.onError,
defaultHandlers.onComplete
);
Rx.Observable.fromEvent( globalChannel, 'nutrientResponse' ).
takeUntil(globalChannelDestroyEvent).
subscribe(
function( response ) { console.log( response ); },
defaultHandlers.onError,
defaultHandlers.onComplete
);
//-----------------------------------------
// main
//-----------------------------------------
console.log(frenchFryNutrition.getNutrients()); // { salt: 100, sugar: 95, calories: 50 }
// test that we got a value not reference
nutrients = frenchFryNutrition.getNutrients();
nutrients.salt = 25; // this does not work because getNutrients() returns a shallow copy not a reference
console.log(frenchFryNutrition.getNutrients()); // { salt: 100, sugar: 95, calories: 50 }
// test modification of ingredient
frenchFryNutrition.setNutrients({ salt: 5 });
console.log(frenchFryNutrition.getNutrients()); // { salt: 5, sugar: 95, calories: 50 }
// test events
globalChannel.emit('nutrientRequest', 'salt'); // 5
globalChannel.emit('nutrientRequest', 'sugar'); // 95
globalChannel.emit('nutrientRequest', 'calories'); // 50
globalChannel.emit('destroy'); // Completed\nCompleted
// output
//-----------------------------------------
// $ node index.js
// init was called on this stateContainer
// { salt: 100, sugar: 95, calories: 50 }
// { salt: 100, sugar: 95, calories: 50 }
// { salt: 5, sugar: 95, calories: 50 }
// 5
// 95
// 50
// Completed
// Completed
'use strict';
var nutrient = Object.create( require('../base-objects/state-container') );
nutrient.initNutrition = function( options ) {
this.initStateContainer( options );
};
nutrient.getNutrients = function() {
return this.getState();
};
nutrient.setNutrients = function( nutrients ) {
// only change the ones in the arg
var keys = Object.keys( nutrients ),
state = this.state;
keys.forEach(function(key) {
state[key] = nutrients[key];
});
};
module.exports = nutrient;
'use strict';
var util = require('./utilities');
module.exports = {
initStateContainer: function( options ) {
// can send events
options = options || {};
this.setState( options.state );
this.observables = options.observables || [];
this.init = options.init || util.noop;
this.init();
},
setState: function( state ) {
this.state = state || {};
},
getState: function() {
return util.shallowCopy( this.state );
}
};
'use strict';
module.exports = {
noArg: function fatal(name) {
throw new Error('Missing required parameter: ' + name);
},
noop: function(){},
shallowCopy: function( obj ) {
var keys = Object.keys( obj ),
copy = {};
keys.forEach(function(key) {
copy[key] = obj[key];
});
return copy;
}
};
@ChetHarrison
Copy link
Author

This was an experiment to see if I could replicate Radio's functionality in RxJs and also play with Kyle Simpson's delegate pattern.

Radio

https://gist.github.com/ChetHarrison/c610d4fe4a5e9f9280a2#file-index-js-L43-L52 sets up an observable that takes a Node.js EventEmitter as a "channel" and listens for a "nutrientRequest" and takes an arg. the takeUntil() call sets the terms of disposal and RxJs takes 3 handlers: onNext, onError, and onComplete, onNext === success.

https://gist.github.com/ChetHarrison/c610d4fe4a5e9f9280a2#file-index-js-L54-L60 sets up an observable on the same "channel" to wait for responses from our request. So it achieves bidirectional eventing with payloads.

Delegation

Look mom, no constructors!. Kyle's argument is that JS is not a classical OO language. Trying to dress it up as one (as we all do) just adds to your brain damage. It is a much simpler paradigm to think in terms of just linking objects. All instance state hangs off this.

Because context is determined by the "call site" Object.create() is great way to establish the links. The context is right. I'm not thinking about binding.

In this example var nutrient = Object.create( require('../base-objects/state-container') ); links a nutrient object to a base object called stateContainer and frenchFryNutrition = Object.create( nutrients ); links frenchFryNutrition to nutrient. So I can call getState() in stateContainer from frenchFryNutrition with out having to figure out if it is hanging off a prototype because all of these objects are just objects, no constructor.

So how do you add instance attributes to your instance without a constructor? Add an init function to your object https://gist.github.com/ChetHarrison/c610d4fe4a5e9f9280a2#file-state-container-js-L6-L13

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment