Unable to add custom operator to the Observable class - javascript

I am writing a custom operator to load a csv file and emit each line as data. This operator is supposed to work like the of operator, which is a static function to create observable. I follow the instruction of operator creation and add the operator function directly to Observable prototype.
All following code is written in JavaScript ES6.
My source code is this
import { Observable } from 'rxjs';
import { createInterface } from 'readline';
import { createReadStream } from 'fs';
function fromCSV(path, options) {
return Observable.create((subscriber) => {
let readStream = createReadStream(path);
let reader = createInterface({ input: readStream });
let isHeader = true;
let columns;
reader.on('line', (line) => {
if (isHeader) {
columns = line.split(',');
isHeader = false;
} else {
let values = line.split(',');
let valueObject = {};
for (let i = 0, n = columns.length; i < n; i++) {
valueObject[columns[i]] = values[i] || undefined;
}
subscriber.next(valueObject);
}
});
reader.on('close', () => subscriber.complete());
readStream.on('error', (error) => subscriber.error(error));
});
}
Observable.prototype.fromCSV = fromCSV;
The operator function looks totally correct, but when I try to use this operator like
import { Observable } from 'rxjs';
import './rx-from-csv';
Observable.fromCSV(testCSV)
.subscribe((row) => {
console.log(row);
});
It throws an error TypeError: _rxjs.Observable.fromCSV is not a function. So the function binding fails and I have no idea why it happens :-( Any help is appreciated.
This particularly confuses me because I have successfully done a similar operator binding for another custom csv operator.

The problem is that TypeScript doesn't know about the operator because it couldn't find it in RxJS's *.d.ts.
Have a look at how it's done by the default RxJS operators: https://github.com/ReactiveX/rxjs/blob/master/src/add/operator/bufferCount.ts
In you case you'll need just the declare module ... part with a correct path to the Observable definition. For example:
function fromCSV(path, options) {
...
}
Observable.prototype.fromCSV = fromCSV;
declare module 'rxjs/Observable' {
interface Observable<T> {
fromCSV: typeof fromCSV;
}
}

It turns out that I used a wrong way to add static function. See this post for more information.
To add a static function to the Observable class, the code needs to be
Observable.fromCSV = fromCSV;
Adding the function to the class's prototype will make it only available after newing that class.

Related

Get the names of the parameters of the wrapped function

I want to use the methods of the Minio class without specifying all their parameters, but substituting some of the parameters automatically. How do I do it...
I get all the class methods from the prototype of the Minio class and dynamically create wrappers for them in my class.
For each wrapper method, I get the parameter names from the original method of the Test class.
If there is one in the list of parameters that I want to omit when calling my wrapper method, then I add it to the list of arguments and call originalMethod.apply(this.minioClient, args).
Everything was fine until there were methods that were already wrapped.
I need to get the parameter list of the bucketExists method from outside the Minio class. Any idea how to get parameter names from such a wrapped method?
// minio/dist/main/helpers.js
exports function promisify(fn){
return function(){
const args = [...arguments];
fn.apply(this, args);
}
}
// minio/dist/main/minio.js
class Minio{
bucketExists(bucketName){
return bucketName;
}
methodThatNotWrappedByPromisifyAndWorksFine(bucketName){
return bucketName;
}
}
module.exports = Minio;
Minio.prototype.bucketExists = (0,helpers.promisify)(Minio.prototype.bucketExists)
I want to give an instance of my class with methods wrapped from the original class link the ability to work with only one bucket, that was passed to the my class constructor, without the ability to specify some other one after initialize.
My wrapper
const proxyHandler = () => {
return {
apply: (target, thisArg, argumentsList) => {
const funcParams = getMethodParamNames(target.source ? target.source.functionForWrap : target);
const bucketNameIndex = funcParams.indexOf("bucketName");
const regionNameIndex = funcParams.indexOf("region");
if (bucketNameIndex >= 0) {
argumentsList.splice(bucketNameIndex, 0, this.bucket.name);
}
if (regionNameIndex >= 0) {
argumentsList.splice(regionNameIndex, 0, this.bucket.region);
}
try {
return target.apply(this.minioClient, argumentsList);
} catch (error) {
logger.engine.error(`S3 '${this.bucket.name}' ${target} error: ${error.message}`, error.stack);
}
},
}
}
getMethods(this.minioClient).forEach(func => {
this[func] = new Proxy(this.minioClient[func], proxyHandler());
})
Solved the problem by overriding the method wrapper like this.
const MinioHelpers = require('minio/dist/main/helpers');
const origMinioPromisify = MinioHelpers.promisify;
MinioHelpers.promisify = (functionForWrap) => {
console.log("PATCHED!", functionForWrap);
var fn = origMinioPromisify(functionForWrap);
//from this i'll get all need information about wrapped function
fn.source = {
functionForWrap,
promisify: origMinioPromisify,
}
return fn
}
var Minio = require('minio');

filtering an array issue

I'm importing an array into a module, and adding and removing items from that array. when I give a push, it adds the item to the array globally, so much so that if I use that same array in another module, it will include this item that I pushed. but when I try to filter, with that same array getting itself with the filter, it only removes in that specific module. How can I make it modify globally?
let { ignore } = require('../utils/handleIgnore');
const questions = require('./quesiton');
const AgendarCollector = async (client, message) => {
ignore.push(message.from);
let counter = 0;
const filter = (m) => m.from === message.from;
const collector = client.createMessageCollector(message.from, filter, {
max: 4,
time: 1000 * 60,
});
await client.sendText(message.from, questions[counter++]);
collector.on('start', () => {});
await collector.on('collect', async (m) => {
if (m) {
if (counter < questions.length) {
await client.sendText(message.from, questions[counter++]);
}
}
});
await collector.on('end', async (all) => {
ignore = ignore.filter((ignored) => ignored !== message.from);
console.log(ignore);
const finished = [];
if (all.size < questions) {
console.log('não terminado');
}
await all.forEach((one) => finished.push(` ${one.content}`));
await client.sendText(message.from, `${finished}.\nConfirma?`);
});
};
module.exports = AgendarCollector;
see, in this code, import the ignore array and i push an item to then when the code starts and remove when its end.
but the item continues when I check that same array in another module.
I tried to change this array ignore by using functions inside this module but still not working
let ignore = [];
const addIgnore = (message) => {
ignore.push(message.from);
};
const removeIgnore = (message) => {
ignore = ignore.filter((ignored) => ignored !== message.from);
console.log(ignore);
};
console.log(ignore);
module.exports = { ignore, addIgnore, removeIgnore };
You are using the variables for import and export and hence cought up with issues.
Instead, make use of getters.
Write a function which will return the array of ignore. something like this:
const getIgnoredList = () => {
return ignore;
};
and in your first code, import getIgnoredList and replace ignore with getIgnoredList()
Explanation :
Whenever we import the variables only the value at that particular time will be imported and there will not be any data binding. Hence there won't be any change in the data even though you think you are updating the actual data.
When you use require(...) statement it's executed only once. Hence when you try to access the property it gives the same value everytime.
Instead you should use getters
let data = {
ignore : [],
get getIgnore() {
return this.ignore
}
}
module.export = {
getIgnore: data.getIgnore,
}
Then wherever you want to access ignore do
var {getIgnore}= require('FILE_NAME')
Now: console.log(getIgnore) will invoke the getter and give you current value of ignore
Using getters will allow you to access particular variables from other modules but if you want to make changes in value of those variables from other module you have to use setter.
More about getters here
More about setters here

Observable value inside subscribe is undefined when chaning observables

I have a function from a certain library that returns an Observable that I want to call from another function. I have a need to propagate that Observable into multiple function calls. Here is how my code is structured:
extractSignature = (xml, signatureCount = 1) => {
const observ = this.generateDigest(signedContent, alg).pipe(map(digest => {
const sigContainer = {
alg: alg,
signature: signatureValue,
signedContent: signedContent,
digest: digest
};
console.log('sigContainer inside pipe: ');
console.log(sigContainer);
return sigContainer;
}));
return observ;
}
dissasemble(xml): Observable<SignatureContainerModel[]> {
const observables: Observable<any>[] = [];
for (let i = 1; i <= count; i++) {
const extractSigObservable = this.extractSignature(xml, i);
console.log('extractSigObs inside pipe: ');
console.log(extractSigObservable);
const observ = extractSigObservable.pipe(map(sigContainer => {
console.log('sigContainer inside pipe: ');
console.log(sigContainer);
const hashContainers: HashContainerModel[] = [];
const hashContainer: HashContainerModel = new HashContainerModel();
hashContainer.digestAlgorithm = sigContainer.alg;
hashContainer.bytes = sigContainer.digest;
hashContainers.push(hashContainer);
const signatureContainer: SignatureContainerModel = {
hashContainers: hashContainers,
signature: sigContainer.signature
};
console.log('observable inside pipe: ');
console.log(observ);
}));
observables.push(observ);
}
return forkJoin(observables);
}
verify() {
this.sigExec.dissasemble(this.fileContent).subscribe((signatureContainers: SignatureContainerModel[]) => {
// signatureContainers is [undefined] here
console.log('Sig Containers: ');
console.log(signatureContainers);
this.verifyHash(signatureContainers);
});
}
signatureContainers variable is [undefined] inside the subscribe. I'm not sure what the problem is since when I check all the logs that I wrote inside map functions they seem fine
RXJS Documentation on forkJoin:
Be aware that if any of the inner observables supplied to forkJoin error you will lose the value of any other observables that would or have already completed if you do not catch the error correctly on the inner observable. If you are only concerned with all inner observables completing successfully you can catch the error on the outside.
https://www.learnrxjs.io/learn-rxjs/operators/combination/forkjoin
There a possibility you are erroring out inside your pipe and those values are being lost.
Also, I noticed that you're not returning anything from your pipe. This could also be an issue.

How to add keyword to acorn or esprima parser

I am working on a language that transpiles to javascript and has a similar syntax. However I want to include some new type of block statements. For syntax purposes they are the same as an IfStatement. How can I get esprima or acorn to parse this program MyStatement {a=1;} without throwing an error? Its fine if it calls it an IfStatement. I would prefer not to fork esprima.
It turns out, that the plugin capabilities of acorn are not really documented. It seems like forking acorn would be the easiest route. In this case, it is as simple as searching for occurances of _if and following a similar pattern for _MyStatement.
However it is possible to write a plugin to accomplish what I was trying to do. It seems a bit of a hack, but here is the code. The basic steps are:
To exend Parse and add to the list of keywords that will be recognized by the first pass
Create a TokenType for the new keyword and add it to the Parser.acorn.keywordTypes, extend parseStatement so that it processes the new TokenType
Create a handler for the new TokenType which will add information to the Abstract Syntax Tree as required by the keyword functionality and also consume tokens using commands like this.expect(tt.parenR) to eat a '(' or this.parseExpression() to process an entire expression.
Here is the code:
var program =
`
MyStatement {
MyStatement(true) {
MyStatement() {
var a = 1;
}
}
if (1) {
var c = 0;
}
}
`;
const acorn = require("acorn");
const Parser = acorn.Parser;
const tt = acorn.tokTypes; //used to access standard token types like "("
const TokenType = acorn.TokenType; //used to create new types of Tokens.
//add a new keyword to Acorn.
Parser.acorn.keywordTypes["MyStatement"] = new TokenType("MyStatement",{keyword: "MyStatement"});
//const isIdentifierStart = acorn.isIdentifierStart;
function wordsRegexp(words) {
return new RegExp("^(?:" + words.replace(/ /g, "|") + ")$")
}
var bruceware = function(Parser) {
return class extends Parser {
parse(program) {
console.log("hooking parse.");
//it appears it is necessary to add keywords here also.
var newKeywords = "break case catch continue debugger default do else finally for function if return switch throw try var while with null true false instanceof typeof void delete new in this const class extends export import super";
newKeywords += " MyStatement";
this.keywords = wordsRegexp(newKeywords);
return(super.parse(program));
}
parseStatement(context, topLevel, exports) {
var starttype = this.type;
console.log("!!!hooking parseStatement", starttype);
if (starttype == Parser.acorn.keywordTypes["MyStatement"]) {
console.log("Parse MyStatement");
var node = this.startNode();
return this.parseMyStatement(node);
}
else {
return(super.parseStatement(context, topLevel, exports));
}
}
parseMyStatement(node) {
console.log("parse MyStatement");
this.next();
//In my language, MyStatement doesn't have to have a parameter. It could be called as `MyStatement { ... }`
if (this.type == tt.parenL) {
node.test = this.parseOptionalParenExpression();
}
else {
node.test = 0; //If there is no test, just make it 0 for now (note that this may break code generation later).
}
node.isMyStatement = true; //set a flag so we know that this if a "MyStatement" instead of an if statement.
//process the body of the block just like a normal if statement for now.
// allow function declarations in branches, but only in non-strict mode
node.consequent = this.parseStatement("if");
//node.alternate = this.eat(acornTypes["else"]) ? this.parseStatement("if") : null;
return this.finishNode(node, "IfStatement")
};
//In my language, MyStatement, optionally has a parameter. It can also by called as MyStatement() { ... }
parseOptionalParenExpression() {
this.expect(tt.parenL);
//see what type it is
console.log("Type: ", this.type);
//allow it to be blank.
var val = 0; //for now just make the condition 0. Note that this may break code generation later.
if (this.type == tt.parenR) {
this.expect(tt.parenR);
}
else {
val = this.parseExpression();
this.expect(tt.parenR);
}
return val
};
}
}
process.stdout.write('\033c'); //cls
var result2 = Parser.extend(bruceware).parse(program); //attempt to parse
console.log(JSON.stringify(result2,null,' ')); //show the results.

Javascript - Singleton - Race Condition

I have the following code to implement singleton
const singleton = Symbol();
const singletonEnforcer = Symbol()
class SingletonTest {
constructor(enforcer) {
if(enforcer != singletonEnforcer) throw "Cannot construct singleton";
}
static get instance() {
if(!this[singleton]) {
this[singleton] = new SingletonTest(singletonEnforcer);
}
return this[singleton];
}
}
export default SingletonTest
And
import SingletonTest from 'singleton-test';
const instance = SingletonTest.instance;
I have a race condition problem , when SingletonTest.instance is executed at same time twice , I have two instance of SingletonTest , How can I solve issue?

Categories