strain

Created: 2014-05-18 19:55
Updated: 2015-08-24 16:41
License: mit

readme.md

strain

Build Status

defines callable, method-chained js components, inspired by d3's pretty api.

var strain = require('strain');


var thing = strain()
  .prop('foo')
    .default(22)
  .prop('bar')
    .default(3)

  .meth('foobar', function() {
    console.log(this.foo() + this.bar());
  });


var subthing = strain(thing)
  .prop('bar')
    .default(23)
    .get(function(v) {
      return v * 2;
    })
    .set(function(v) {
      return v + 1;
    })

  .init(function(arg) {
    console.log('init! ' + arg);
  })

  .invoke(function() {
    console.log('invoke!');
  });


var t = subthing('arg!')  // init! arg!
  .foobar()  // 70
  .bar(42)
  .foobar();  // 108


t();  // invoke!

install

node:

$ npm install strain

browser:

$ bower install strain
<script src="/bower_components/strain/strain.js"></script>

api

strain([parent])

creates a new type.

var eventable = strain(EventEmitter);

eventable()
  .on('foo', function() {
    console.log('bar');
  })
  .emit('foo');  // bar

if parent is specified, properties on the parent are attached to the type, prototype properties on the parent are accessible via the type's prototype, and the parent is attached to the new type as _super_.

.extend()

creates a child type from the calling type. shorthand for strain(<type>).

var thing = strain.extend();
var subthing = thing.extend();

.new()

creates a new instance of the type.

strain()
  .init(function(arg) {
    console.log(arg);
  })
  .new('foo');  // foo

.static(name, value)

defines a new property directly on the calling type.

var thing = strain()
  .static('foo', 23)
  .static('bar', function() {
    console.log('bar!');
  })
  .bar()  // bar!
  .bar();  // bar!

console.log(thing.foo);  // 23

if value is a function that does not return a value or returns undefined, the type is returned instead to allow for further method chaining.

.static(fn)

defines a new method directly on the calling type from a named function.

var thing = strain()
  .static(function bar() {
    console.log('bar!');
  })
  .bar()  // bar!
  .bar();  // bar!

if fn does not return a value or returns undefined, the type is returned instead to allow for further method chaining.

.prop(name)

defines or re-references a new gettable and settable property on a type.

var t = strain().prop('foo')();
console.log(t.foo());  // 23

re-referencing a property allows .default, .set and .get to be applied to a property after it has been defined (possibly by a parent type), without breaking method chaining for the type currently being defined.

.default(v)

sets the default value of the most recently referenced property.

var thing = strain()
  .prop('foo')
  .prop('bar').default(23)
  .prop('foo').default(42);

console.log(thing().foo());  // 42
console.log(thing().bar());  // 23

note that setting mutable values using .default means that all instances of the type will be affected by modifications to the value. .init or .defaults could be used if you need to create mutables per-instance.

.get(fn)

sets the coercion function to use when getting the most recently referenced property.

var thing = strain()
  .prop('foo')
  .prop('bar').get(function(v) {
    return v + 1;
  })
  .prop('foo').get(function(v) {
    return v * 2;
  });

console.log(thing().bar(23).bar());  // 24
console.log(thing().foo(42).foo());  // 84

.set(fn)

sets the coercion function to use when setting the most recently referenced property.

var thing = strain()
  .prop('foo')
  .prop('bar').set(function(v) {
    return v + 1;
  })
  .prop('foo').set(function(v) {
    return v * 2;
  });

console.log(thing().bar(23).bar());  // 24
console.log(thing().foo(42).foo());  // 84

.meth(name, fn)

defines a new method on a type.

var thing = strain()
  .meth('foo', function() {
    return 23;
  })
  .meth('bar', function() {
    console.log(this.foo());
  })();


thing()
  .bar()  // 23
  .bar()  // 23
  .bar();  // 23

if fn does not return a value or returns undefined, the type's instance is returned instead to allow for further method chaining.

.meth(fn)

defines a new method on a type from a named function.

var thing = strain()
  .meth(function foo() {
    console.log('bar');
  })();


thing().foo();  // bar

.defaults(obj)

sets the default values of properties for each new instance using a data object.

var thing = strain()
  .prop('foo')
  .defaults({foo: 23});

console.log(thing().foo());  // 23

.defaults(fn)

sets the default values of properties for each new instance using a function that returns a data object.

var thing = strain()
  .prop('foo')
  .defaults(function() {
    return {foo: 23};
  });

console.log(thing().foo());  // 23

the given function is invoked at initialisation time for each new instance.

.init(fn)

defines a function to be called on initialisation. shorthand for .meth('_init_', fn).

var thing = strain().init(function() {
  this.foo = 'bar'
});


console.log(thing().foo);  // bar

.invoke(fn)

defines the function that is called when the instance is called. shorthand for .meth('_invoke_', fn).

var t = strain().invoke(function() {
  return 23;
})();


console.log(t());  // 23

.instanceof(instance, type)

determines whether the instance is an instance of type, regardless of whether instance and type are strain-based or not.

var foo = strain();
var bar = function() {};
console.log(strain.instanceof(foo(), foo));  // true
console.log(strain.instanceof(new bar(), bar));  // true

<instance>.prop(name[, value])

gets or sets a property by its name.

var thing = strain().prop('foo')
console.log(thing().prop('foo', 'bar').prop('foo'));  // bar

<instance>.props()

returns a shallow copy of the strain instances's properties as a data object.

var thing = strain()
  .prop('foo')
  .prop('bar');

var t = thing()
  .foo(2)
  .bar(3);

console.log(t.props());  // { foo: 2, bar: 3 }

<instance>.toJSON()

an alias to .props for stringifying the strain instance with JSON.stringify().

var thing = strain()
  .prop('foo')
  .prop('bar');

var t = thing()
  .foo(2)
  .bar(3);

console.log(JSON.stringify(t));  // {"foo":2,"bar":3}

<instance>.invoke([arg1[, arg2[, ...]]])

calls the instance with the given args.

strain()
  .invoke(function(arg) {
    console.log(arg);
  })
  .new()
  .invoke('foo');  // foo

<instance>.instanceof(type)

determines whether calling instance is an instance of the given type.

function thing() {}
var subthing = strain(thing);
var t = subthing();
console.log(t.instanceof(thing));  // true
console.log(t.instanceof(t));  // false

this is a workaround, since there is no easy, portable way to construct a callable with a properly set up prototype chain in js.

Cookies help us deliver our services. By using our services, you agree to our use of cookies Learn more