As mentioned in the previous post, the string containing a complete module can be pretty big and could lead to run out of memory. Therefore the question:
How can a module be broken up into small(er) pieces and composed at runtime from such pieces.
In order to have the a piece of code working as a module, it is good enough to have a 'hock object' as the first or core component in the Modules cache to which more components can 'be attached' to. To attache components to a module core component, the module has to be required from the cache and then the components can 'be attached' to it. After all, the module - in this case the 'class'/prototype definition - is just a function object - one and the same whether in the cache or in the application code.
Since the constructor function is such a first and core component, let's try to attache a the .getTemp() as a component.
Intentionally, the module was retrieved into a temporary variable tempTemp - line 37 - to prove that the module is the very same object that is later retrieved into the original variable Temperature - line 57 - and includes now the complete Temperature 'class'/prototype definition.
The split up module code into core component - lines 4..34 - and other component(s) - lines 49/41..48 - and attaching the other component(s), such as a method method - line 40 - represents composing a complete module from components component by component. The code for it looks like this:
// TemperatureDevTest2.js
require("DS18B20"); // used by the dynamically loaded module
Modules.addCached("Temperature",`
// Temperature.js
// 'class' (Prototype definition)
var Temperature = function // module (class), for multiple instances
( name // name hinting room / location of sensor\
, oneWire // one-wire; for example: new OneWire(B8);
, addr // address on the one-wire (required for multiples)
, interval // interval in milliseconds of measurements
, preferred // optional, unit - "C" or "F" (default and not F is C)
) {
// set givens
this.name = name;
this.oneWire = oneWire;
this.addr = addr;
this.interval = interval;
this.preferred = (preferred==="F") ? "F" : "C";
this.enabled = (typeof enabled === "undefined") || enabled;
this.t = (this.preferred==="C") ? 0 : 32; // set current to 'frozen'
this.intervalId = null; // later also used as indicator for enabled
this.sensor = null; // laster also used as indicatore for connected
// get going
this.sensor = require("DS18B20").connect(this.oneWire);
this.intervalId = setInterval(function(_this) {
_this.sensor.getTemp(function(t) {
_this.t = t;
});
}, this.interval, this);
};
exports = Temperature;
`);
// require the Temperature module into a temporary tempTemp var:
var tempTemp = require("Temperature");
// attach the .getTemp() method
tempTemp.prototype.getTemp = function
( unit // optional, unit "F" or "C", default "C"
) {
var u = ((typeof unit !== "undefined") && (unit === "F")) ? "F" : "C";
var d = (u === "F")
? (this.t * 1.8) + " Fahrenheit"
: this.t + " Celsius";
return d;
};
// usage
// setup oneWire
var oneWire = new OneWire(B8);
// setup 1st temperature sensor ts1
var Temperature = require("Temperature");
var ts1 = new Temperature
( "office" // name / location of temperatre sensor 1
, oneWire
, null // addr on one-wire currently not implemented / used
, 5000 // every 5 secs make a read (to keep it not booring)
, "F" // preferred unit is Fahrenheit
);
// for sample's sake, do everhthing deferred for more
// than 5 seconds in order to have value(s) to display
setTimeout(function(){
// get temp in preferred unit
console.log(ts1.getTemp());
},6000);
There is one comment to make: if the to-attach module component is retrieved over the network or read from a storage it is retrieved as a string and not as an object... but that is no issue what so ever... we just 'ab-use' the Modules.addCached(...) and require(...) infrastructure as a stepping stone:
add the to-attach component to the Modules cache as a module with Modules.addCached(...) - lines 36..49.
retrieve the to-attach component as module from the Modules cache with require() - line 55.
attach it to the core module retrieved from the Modules cache as done before - line 55.
The code with all module components as strings looks then like:
// TemperatureDevTest2.js
require("DS18B20"); // used by the dynamically loaded module
Modules.addCached("Temperature",`
// Temperature.js
// 'class' (Prototype definition)
var Temperature = function // module (class), for multiple instances
( name // name hinting room / location of sensor\
, oneWire // one-wire; for example: new OneWire(B8);
, addr // address on the one-wire (required for multiples)
, interval // interval in milliseconds of measurements
, preferred // optional, unit - "C" or "F" (default and not F is C)
) {
// set givens
this.name = name;
this.oneWire = oneWire;
this.addr = addr;
this.interval = interval;
this.preferred = (preferred==="F") ? "F" : "C";
this.enabled = (typeof enabled === "undefined") || enabled;
this.t = (this.preferred==="C") ? 0 : 32; // set current to 'frozen'
this.intervalId = null; // later also used as indicator for enabled
this.sensor = null; // laster also used as indicatore for connected
// get going
this.sensor = require("DS18B20").connect(this.oneWire);
this.intervalId = setInterval(function(_this) {
_this.sensor.getTemp(function(t) {
_this.t = t;
});
}, this.interval, this);
};
exports = Temperature;
`);
// add .getTemp() method as moduleComponent to the Modules cache
Modules.addCached("moduleComponent",`
var moduleComponent = function
( unit // optional, unit "F" or "C", default "C"
) {
var u = ((typeof unit !== "undefined") && (unit === "F")) ? "F" : "C";
var d = (u === "F")
? (this.t * 1.8) + " Fahrenheit"
: this.t + " Celsius";
return d;
};
exports = moduleComponent;
`);
// require the Temperature module into a temporary tempTemp var:
var tempTemp = require("Temperature");
// require and attach the .getTemp() method as module component
tempTemp.prototype.getTemp = require("moduleComponent");
// usage
// setup oneWire
var oneWire = new OneWire(B8);
// setup 1st temperature sensor ts1
var Temperature = require("Temperature");
var ts1 = new Temperature
( "office" // name / location of temperatre sensor 1
, oneWire
, null // addr on one-wire currently not implemented / used
, 5000 // every 5 secs make a read (to keep it not booring)
, "F" // preferred unit is Fahrenheit
);
// for sample's sake, do everhthing deferred for more
// than 5 seconds in order to have value(s) to display
setTimeout(function(){
// get temp in preferred unit
console.log(ts1.getTemp());
},6000);
...and works - and we get two (2) warnings about module not found on upload as expected... of course it runs... no magic... just JavaScript brilliantly and to it's true nature implemented by @Gordon.
Espruino is a JavaScript interpreter for low-power Microcontrollers. This site is both a support community for Espruino and a place to share what you are working on.
As mentioned in the previous post, the string containing a complete module can be pretty big and could lead to run out of memory. Therefore the question:
How can a module be broken up into small(er) pieces and composed at runtime from such pieces.
In order to have the a piece of code working as a module, it is good enough to have a 'hock object' as the first or core component in the
Modules
cache to which more components can 'be attached' to. To attache components to a module core component, the module has to be required from the cache and then the components can 'be attached' to it. After all, the module - in this case the 'class'/prototype definition - is just a function object - one and the same whether in the cache or in the application code.Since the constructor function is such a first and core component, let's try to attache a the
.getTemp()
as a component.Intentionally, the module was retrieved into a temporary variable
tempTemp
- line 37 - to prove that the module is the very same object that is later retrieved into the original variableTemperature
- line 57 - and includes now the complete Temperature 'class'/prototype definition.The split up module code into core component - lines 4..34 - and other component(s) - lines 49/41..48 - and attaching the other component(s), such as a method method - line 40 - represents composing a complete module from components component by component. The code for it looks like this:
There is one comment to make: if the to-attach module component is retrieved over the network or read from a storage it is retrieved as a string and not as an object... but that is no issue what so ever... we just 'ab-use' the
Modules.addCached(...)
andrequire(...)
infrastructure as a stepping stone:Modules
cache as a module withModules.addCached(...)
- lines 36..49.Modules
cache withrequire()
- line 55.Modules
cache as done before - line 55.The code with all module components as strings looks then like:
...and works - and we get two (2) warnings about module not found on upload as expected... of course it runs... no magic... just JavaScript brilliantly and to it's true nature implemented by @Gordon.