Comparing version 0.0.6 to 1.0.0
220
lib/main.js
@@ -1,6 +0,8 @@ | ||
// Generated by CoffeeScript 1.10.0 | ||
// Generated by CoffeeScript 2.3.2 | ||
(function() { | ||
var CND, MULTIMIX, alert, badge, debug, echo, help, info, join, log, rpr, urge, warn, whisper, σ_finalize, σ_new_state, σ_reject, σ_unknown_type, | ||
slice = [].slice; | ||
'use strict'; | ||
var CND, Multimix, alert, badge, debug, help, info, module_keywords, rpr, urge, warn, whisper, | ||
indexOf = [].indexOf; | ||
//########################################################################################################### | ||
CND = require('cnd'); | ||
@@ -10,14 +12,10 @@ | ||
badge = 'MULTIMIX'; | ||
badge = 'MULTIMIX/main'; | ||
log = CND.get_logger('plain', badge); | ||
debug = CND.get_logger('debug', badge); | ||
info = CND.get_logger('info', badge); | ||
alert = CND.get_logger('alert', badge); | ||
whisper = CND.get_logger('whisper', badge); | ||
alert = CND.get_logger('alert', badge); | ||
debug = CND.get_logger('debug', badge); | ||
warn = CND.get_logger('warn', badge); | ||
@@ -29,134 +27,106 @@ | ||
echo = CND.echo.bind(CND); | ||
info = CND.get_logger('info', badge); | ||
join = require('path').join; | ||
//=========================================================================================================== | ||
// MODULE METACLASS provides static methods `@extend()`, `@include()` | ||
//----------------------------------------------------------------------------------------------------------- | ||
/* The little dance around the module_keywords variable is to ensure we have callback support when mixins | ||
extend a class. See https://arcturo.github.io/library/coffeescript/03_classes.html */ | ||
//----------------------------------------------------------------------------------------------------------- | ||
module_keywords = ['extended', 'included']; | ||
σ_new_state = Symbol["for"]('new_state'); | ||
σ_reject = Symbol["for"]('reject'); | ||
σ_finalize = Symbol["for"]('finalize'); | ||
σ_unknown_type = Symbol["for"]('unknown_type'); | ||
MULTIMIX = {}; | ||
MULTIMIX.TOOLS = require('./tools'); | ||
MULTIMIX.RECIPES = require('./recipes'); | ||
MULTIMIX.COPIERS = require('./copiers'); | ||
MULTIMIX._get_seed = function(S, seed) { | ||
var copy, description, has_fields, type; | ||
type = CND.type_of(seed); | ||
description = MULTIMIX.COPIERS.type_descriptions[type]; | ||
if (description == null) { | ||
description = MULTIMIX.COPIERS.type_descriptions[σ_unknown_type]; | ||
} | ||
has_fields = description.has_fields, copy = description.copy; | ||
return copy.call(MULTIMIX, S, seed); | ||
}; | ||
MULTIMIX.mix = function(mixins, recipe, root, selector) { | ||
var S, field_key, field_value, fields, hook, i, len, mixin, mixin_seed, mixin_tail, mx_key, mx_value, partial_mixins, ref, ref1, ref2, seed, type; | ||
if (root == null) { | ||
root = null; | ||
} | ||
if (selector == null) { | ||
selector = []; | ||
} | ||
if (mixins.length === 0) { | ||
return null; | ||
} | ||
mixin_seed = mixins[0], mixin_tail = 2 <= mixins.length ? slice.call(mixins, 1) : []; | ||
S = MULTIMIX.RECIPES[σ_new_state](recipe, mixins); | ||
seed = MULTIMIX._get_seed(S, mixin_seed); | ||
S.seed = seed; | ||
if (root == null) { | ||
root = seed; | ||
} | ||
/* TAINT this part needs to be rewritten */ | ||
/* Deal with nested recipe first: */ | ||
if ((fields = S.recipe['fields']) != null) { | ||
for (field_key in fields) { | ||
field_value = fields[field_key]; | ||
if (!CND.isa_pod(field_value)) { | ||
continue; | ||
} | ||
selector.push(field_key); | ||
partial_mixins = (function() { | ||
var i, len, results; | ||
results = []; | ||
for (i = 0, len = mixins.length; i < len; i++) { | ||
mixin = mixins[i]; | ||
if (mixin[field_key]) { | ||
results.push(mixin[field_key]); | ||
} | ||
Multimix = (function() { | ||
//=========================================================================================================== | ||
class Multimix { | ||
//--------------------------------------------------------------------------------------------------------- | ||
static extend(object) { | ||
var key, ref, value; | ||
for (key in object) { | ||
value = object[key]; | ||
if (indexOf.call(module_keywords, key) < 0) { | ||
this[key] = value; | ||
} | ||
return results; | ||
})(); | ||
if (partial_mixins.length > 0) { | ||
S.seed[field_key] = MULTIMIX.mix(partial_mixins, field_value, root, selector); | ||
} | ||
S.recipe[field_key] = 'skip'; | ||
selector.pop(field_key); | ||
if ((ref = object.extended) != null) { | ||
ref.apply(this); | ||
} | ||
return this; | ||
} | ||
} | ||
/* Process unnested recipe: */ | ||
for (i = 0, len = mixins.length; i < len; i++) { | ||
mixin = mixins[i]; | ||
for (mx_key in mixin) { | ||
mx_value = mixin[mx_key]; | ||
S.path = join.apply(null, slice.call(selector).concat([mx_key])); | ||
S.root = root; | ||
S.current = S.seed; | ||
S.recipe_name = (ref = (ref1 = S.recipe['fields']) != null ? ref1[mx_key] : void 0) != null ? ref : S.recipe_fallback; | ||
if (CND.isa_pod(S.recipe_name)) { | ||
continue; | ||
//--------------------------------------------------------------------------------------------------------- | ||
static include(object) { | ||
var key, ref, value; | ||
for (key in object) { | ||
value = object[key]; | ||
if (indexOf.call(module_keywords, key) < 0) { | ||
// Assign properties to the prototype | ||
this.prototype[key] = value; | ||
} | ||
} | ||
if (MULTIMIX.RECIPES[σ_reject](S, mx_key, mx_value)) { | ||
continue; | ||
if ((ref = object.included) != null) { | ||
ref.apply(this); | ||
} | ||
if ((recipe = MULTIMIX.RECIPES[S.recipe_name]) == null) { | ||
throw new Error("unknown recipe " + (rpr(S.recipe_name))); | ||
return this; | ||
} | ||
//--------------------------------------------------------------------------------------------------------- | ||
export_methods() { | ||
/* Return an object with methods, bound to the current instance. */ | ||
var R, k, ref, ref1, v; | ||
R = {}; | ||
ref = this; | ||
for (k in ref) { | ||
v = ref[k]; | ||
if ((v != null ? v.bind : void 0) == null) { | ||
continue; | ||
} | ||
if ((ref1 = v[Multimix.isa_keymethod_proxy]) != null ? ref1 : false) { | ||
R[k] = Multimix.get_keymethod_proxy(this, v); | ||
} else { | ||
R[k] = v.bind(this); | ||
} | ||
} | ||
recipe.call(MULTIMIX.RECIPES, S, mx_key, mx_value); | ||
return R; | ||
} | ||
} | ||
MULTIMIX.RECIPES[σ_finalize](S); | ||
if ((hook = (ref2 = S.recipe) != null ? ref2['after'] : void 0) != null) { | ||
if ((type = CND.type_of(hook)) !== 'function') { | ||
throw new Error("expected function for 'after' hook, got a " + type); | ||
//========================================================================================================= | ||
// KEYMETHOD FACTORY | ||
//--------------------------------------------------------------------------------------------------------- | ||
static get_keymethod_proxy(bind_target, f) { | ||
var R; | ||
R = new Proxy(f.bind(bind_target), { | ||
get: function(target, key) { | ||
if (key === 'bind') { // ... other properties ... | ||
return target[key]; | ||
} | ||
if ((Multimix.js_type_of(key)) === 'symbol') { | ||
return target[key]; | ||
} | ||
return function(...xP) { | ||
return target(key, ...xP); | ||
}; | ||
} | ||
}); | ||
R[Multimix.isa_keymethod_proxy] = true; | ||
return R; | ||
} | ||
hook(S); | ||
} | ||
return S.seed; | ||
}; | ||
MULTIMIX.use = function() { | ||
var R, recipe, recipes; | ||
recipes = 1 <= arguments.length ? slice.call(arguments, 0) : []; | ||
recipes.splice(0, 0, { | ||
'fallback': 'assign' | ||
}); | ||
recipe = MULTIMIX.mix(recipes); | ||
R = function() { | ||
var mixins; | ||
mixins = 1 <= arguments.length ? slice.call(arguments, 0) : []; | ||
return MULTIMIX.mix(mixins, recipe); | ||
//========================================================================================================= | ||
static js_type_of(x) { | ||
return ((Object.prototype.toString.call(x)).slice(8, -1)).toLowerCase(); | ||
} | ||
}; | ||
R.use = MULTIMIX.use; | ||
return R; | ||
}; | ||
module.exports = { | ||
mix: MULTIMIX.use() | ||
}; | ||
Multimix.isa_keymethod_proxy = Symbol('proxy'); | ||
return Multimix; | ||
}).call(this); | ||
//########################################################################################################### | ||
module.exports = Multimix; | ||
}).call(this); | ||
//# sourceMappingURL=main.js.map |
{ | ||
"name": "multimix", | ||
"version": "0.0.6", | ||
"version": "1.0.0", | ||
"description": "flexible object copying", | ||
@@ -30,7 +30,7 @@ "main": "lib/main.js", | ||
"devDependencies": { | ||
"guy-test": "^1.2.3" | ||
"guy-test": "^1.4.0" | ||
}, | ||
"dependencies": { | ||
"cnd": "^4.1.5" | ||
"cnd": "^4.5.0" | ||
} | ||
} |
134
README.md
@@ -1,33 +0,129 @@ | ||
<!-- START doctoc generated TOC please keep comment here to allow auto update --> | ||
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --> | ||
**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* | ||
- [MultiMix](#multimix) | ||
<!-- END doctoc generated TOC please keep comment here to allow auto update --> | ||
![](https://raw.githubusercontent.com/loveencounterflow/multimix/master/artwork/multimix.png) | ||
<!-- | ||
# MultiMix | ||
Conventional inheritance in its two main | ||
An ES6 `class` with some metaprogramming capabilities: | ||
—the 'classical' variant known from languages like Java, Python and others as well | ||
as the 'prototypal' variant known mainly from JavaScript—both assert | ||
* easy to mixin instance methods from an arbitrary number of objects; | ||
* easy to mixin static (class) methods from an arbitrary number of objects; | ||
* sample implementation for a kind of 'keymethod proxies' (essentially instance method with custum special | ||
behavior); | ||
* ability to 'export' an object with methods bound to a particular instance (great in conjunction with ES6 | ||
object destructuring). | ||
* that it makes sense to 'derive' one object (the 'derivative') from another (its 'antetype') | ||
Implementation was inspired by / copy-pasted from [Chapter 3 of *The Little Book on | ||
CoffeeScript*](https://arcturo.github.io/library/coffeescript/03_classes.html). | ||
* both to conserve memory and to organize functionalities; | ||
## Usage | ||
* that the only formally supported *intentional* relationship between a derivative and its antetype should | ||
be 'is-a' (to the exclusion of 'has-a', 'uses-a', 'partly-resembles-a' &c); | ||
Have a look at [the | ||
demo](https://github.com/loveencounterflow/multimix/blob/master/src/experiments/demo.coffee): | ||
* that the only formally defined *extensional* relationship between the attributes of a derived object and | ||
those of its antetype should be take-all-or-override-all: either your derivation has an attribute `x` or | ||
it doesn't; if it does, it will simply override (shadow) any attribute by the same name in the antetype. | ||
* `class X extends Multimix` gives you a class with the following static methods inherited through | ||
`Multimix`: | ||
Crucially, the only way to *formally* derive one object from another is to provide a list of overriding | ||
features, period. | ||
* `<class>.extend: ( object ) ->`—extends class with the properties of `object` (class-level mixin). | ||
* `<class>.include: ( object ) ->`—extends instances with the properties of `object` (instance-level | ||
mixin). | ||
* `<class>.get_keymethod_proxy = ( bind_target, f ) ->`—produces an instance method `f` which will | ||
translate calls from immediate attributes (as in, `f.some_text some_value`) to calls to `f` proper, | ||
using the attribute name as first argument: `f some_text, some_value`. I needed this for a specific | ||
purpose and included the code as a demo how to implement such a thing. | ||
* `export_methods: ->`—when called on an instance, returns an object with bound instance methods; this | ||
allows to 'export' instance methods into a namespace without fearing 'JavaScript method tear-off | ||
symptome': | ||
```coffee | ||
my_instance = new My_class | ||
{ method_a | ||
method_b } = my_instance.export_methods() | ||
# now you can use `method_a`, `method_b` without prefixing them with `my_instance`: | ||
method_a 42 | ||
``` | ||
Code: | ||
--> | ||
```coffee | ||
Multimix = require 'multimix' | ||
#========================================================================================================= | ||
# SAMPLE OBJECTS WITH INSTANCE METHODS, STATIC METHODS | ||
#--------------------------------------------------------------------------------------------------------- | ||
object_with_class_properties = | ||
find: ( id ) -> info "class method 'find()'", ( k for k of @ ) | ||
create: ( attrs ) -> info "class method 'create()'", ( k for k of @ ) | ||
#--------------------------------------------------------------------------------------------------------- | ||
object_with_instance_properties = | ||
save: -> info "instance method 'save()'", ( k for k of @ ) | ||
#========================================================================================================= | ||
# CLASS DECLARATION | ||
#--------------------------------------------------------------------------------------------------------- | ||
isa = ( type, xP... ) -> | ||
### NOTE realistic method should throw error when `type` not in `specs` ### | ||
urge "µ1129 object #{rpr @instance_name} isa #{rpr type} called with #{rpr xP}" | ||
urge "µ1129 my @specs: #{rpr @specs}" | ||
urge "µ1129 spec for type #{rpr type}: #{rpr @specs[ type ]}" | ||
#--------------------------------------------------------------------------------------------------------- | ||
class Intertype extends Multimix | ||
@extend object_with_class_properties | ||
@include object_with_instance_properties | ||
#------------------------------------------------------------------------------------------------------- | ||
constructor: ( @instance_name ) -> | ||
super() | ||
@specs = {} | ||
@declare type, value for type, value of @constructor.base_types | ||
@isa = Multimix.get_keymethod_proxy @, isa | ||
#------------------------------------------------------------------------------------------------------- | ||
declare: ( type, value ) -> | ||
whisper 'µ7474', 'declare', type, rpr value | ||
@specs[ type ] = value | ||
#------------------------------------------------------------------------------------------------------- | ||
@base_types = | ||
foo: 'spec for type foo' | ||
bar: 'spec for type bar' | ||
########################################################################################################## | ||
intertype_1 = new Intertype | ||
intertype_2 = new Intertype | ||
info 'µ002-1', Intertype.base_types | ||
info 'µ002-2', intertype_1.declare 'new_on_it1', 'a new hope' | ||
info 'µ002-3', 'intertype_1.specs', intertype_1.specs | ||
info 'µ002-4', 'intertype_2.specs', intertype_2.specs | ||
info 'µ002-5', intertype_1.isa 'new_on_it1', 1, 2, 3 | ||
info 'µ002-6', intertype_1.isa.new_on_it1 1, 2, 3 | ||
info 'µ002-7', intertype_2.isa 'new_on_it1', 1, 2, 3 | ||
info 'µ002-8', intertype_2.isa.new_on_it1 1, 2, 3 | ||
{ isa, declare, } = intertype_1.export_methods() | ||
info 'µ002-9', isa 'new_on_it1', 1, 2, 3 | ||
info 'µ002-10', isa.new_on_it1 1, 2, 3 | ||
``` | ||
## Links | ||
* [jeremyckahn/inherit-by-proxy.js](https://gist.github.com/jeremyckahn/5552373) | ||
* [JS Objects: Distractions](https://davidwalsh.name/javascript-objects-distractions) | ||
* [JS Objects: De"construct"ion](https://davidwalsh.name/javascript-objects-deconstruction) | ||
## Motivation | ||
"JavaScript's prototypal inheritance is vastly simpler than class-based, 'classical' OOP". <sup>*[citation | ||
needed]*</sup> | ||
[Is it](https://davidwalsh.name/javascript-objects-deconstruction)? | ||
![](https://raw.githubusercontent.com/loveencounterflow/multimix/master/artwork/JavaScriptObjects--Full.png) | ||
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
0
130
1585211
12
412
Updatedcnd@^4.5.0