I'm writing a JS terminal, and, for executing commands I use the following method:
function cmd(command){
var args = command.split(" ");
var size = args.filter(function(value) { return value !== undefined }).length;
if (args[0] === "command-name") {
...stuff to do
return;
}
if (args[0] === "another command") {
...stuff to do
return;
}
}
Is there some more efficient/neat way to do that?
You can use an object to store the command-name to function mapping, something akin to
var commands = {
"command-name": function doCommandName (args) {
alert("My arguments: " + args);
},
"another-command": function doAnotherCommand (args) {
}
};
function cmd(command) {
var args = command.split(" ");
var cmd = args[0];
if (commands.hasOwnProperty(cmd)) {
commands[cmd](args.slice(1));
}
else {
alert("Unknown command " + cmd);
}
};
I believe this can hardly be done in a more elegant way. However, you could use an array of objects, each of which would have a string name and some function execute as members, and the implementation would select the array member based on args[0].
Related
i want to make a for loop with a pattern-array and one object to check if the object is matched by one pattern. The patterns-array should processed parallel and if one of the patterns matches the object the onMatch()-callback-function should be called and cancel the other operations else the onNoMatch()-function should be called.
At the Moment it looks like this. I dont know where to put the onNoMatch()-function and sometimes there are multiple callbacks:
module.exports = function matchingPattern(patterns, object, onMatch, onNoMatch) {
for (key in patterns) {
if(patterns[key].param1 != object.param1) continue;
else if(patterns[key].param2 != object.param2) continue;
else if(patterns[key].param3 != object.param3) continue;
else {
onMatch(patterns[key].id);
}
}
}
//EDIT working solution
var arr = [1,2,3,4,5]
var async = require('async');
function matchPattern(pat, object, cb){
console.log(pat +' % '+ object);
if(pat % object == 0) cb(pat);
else cb();
}
function matchingPattern(patterns, object, onMatch, onNoMatch) {
async.each(patterns, function(pat, callback){
matchPattern(pat, object, function(match){
return callback(match);
});
}, function (res){
if(res) return onMatch(res);
return onNoMatch()
});
}
matchingPattern(arr, {2=2matches, 6=no matches}, function onMath(a){
console.log('onMatch('+a+')');
}, function onNoMatch(){
console.log('onNoMatch()');
});
I would personally prefer using async library as these sort of workflows can be easily handled using async.
var FOUND = {
code: 'Custom'
item: null,
pattern: null
};
function matchingPattern(patterns, object, onMatch, onNoMatch) {
async.each(patterns, function(pattern, callback){
// check pattern as per your business logic.
// assuming matchPattern is async
matchPattern(pattern, object, function(match){
if(match){
FOUND.item = object;
FOUND.pattern = pattern;
return callback(FOUND);
}else{
return callback();
}
});
},
function (error, result){
if(error){
if(error.code == 'Custom'){
// its not an error. We have used it as an error only.
return onMatch();
}else{
// handle error here.
}
}
// all items done and we have not found any pattern matching.
return onNoMatch();
});// end of async.each();
}// end of matchingPattern()
This is probably a noob JavaScript question, but I'm looking to know if my solution to a problem I am having is 'correct'
I have created the following sample application that recreates my error:
Firstly in index.js
var processor = require('./fileProcessor/processor.js');
var container = {
source: "source.txt",
destination: "destination.txt"
};
new processor().process(container);
I create my container object which has the name of the source file and the name of the destination file. This is passed into the process function of the processor:
var fileProcessor = require('./fileProcessor.js');
module.exports = function Processor() {
this.process = function(container) {
var file = new fileProcessor();
if(container.finished === undefined) {
if(container.body === undefined) {
file.read(container, this.process);
} else {
file.write(container, this.process);
}
}
};
};
As you can see this calls the read and write functions passing in the container and the process function as the callback, the fileProcessor looks like this:
var fs = require('fs');
module.exports = function() {
this.read = function(container, callback) {
fs.readFile(container.source, function (err, data) {
if(err) throw err;
container.body = data;
callback(container);
});
};
this.write = function(container, callback) {
fs.writeFile(container.destination, container.body, function(err) {
if(err) {
return console.log(err);
}
container.finished = true;
callback(container);
});
};
};
In simple terms the processor calls file.read, which reads the file and calls back into the process function, which then calls the write function. However at the end of the write function an error is thrown:
callback(container);
^
TypeError: object is not a function
Obviously when passing in this.process to file.write(container, this.process); the this isn't the this I intend it to be!
If I update my processor by adding a processFunction variable:
var fileProcessor = require('./fileProcessor.js');
module.exports = function Processor() {
var processFunction = function(container) {
var file = new fileProcessor();
if(container.finished === undefined) {
if(container.body === undefined) {
file.read(container, processFunction);
} else {
file.write(container, processFunction);
}
}
};
this.process = function(container) {
processFunction(container);
};
};
Everything works fine. Is this a good way to do this or is there a better solution?
I think this is a fine way to do it. There is one possible modification that you might make. Since you are creating a new name in your scope just for the purpose of recursing, you could just name your function and refer to it by its name inside of the function.
module.exports = function Processor() {
this.process = function processFunction(container) {
var file = new fileProcessor();
if(container.finished === undefined) {
if(container.body === undefined) {
file.read(container, processFunction);
} else {
file.write(container, processFunction);
}
}
};
};
Then you can avoid creating a name (processFunction) that will be visible outside the function.
Take a look here for reference:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/function#Named_function_expression
I have an application communicating with a device via serial port. Every sent command is answered by a data event containing the status/answer. Basically there are commands changing the device and a command which just returns the status. Every time the last command has been answered (so upon receiving data) the app should send the next command or as a default query the status. I'm trying to model this with rxjs.
My idea here is that there is a command observable and a data observable derived from the data event. These two should be combined in such a way, that the resulting observable only emits values, when there is data and combine it with a command or the default command (request status), if no command came down the command stream.
data: ---------d---d-----d---------d------d-------
command: --c1---c2----------------------c3-----------
______________________________________________________
combined ---------c1--c2----dc--------dc-----c3
dc is the default command. Also no commands should be lost.
Currently I have an implementation with a anonymous subject, implementing the observable and observer myself. Collecting commands from the command stream in an array, subscribing to the data event, publish the data by hand with onNext and sending the next command from the array or the default. This works, but I have the feeling this could be expressed more elegantly with rxjs.
One approach was to have a separate default_command stream, repeating the default command every 100ms. This was merged with the command stream and then zipped with the data stream. The problem here was the merged command stream, because it piled up default commands, but the default command should only apply, if there is no other command.
Only thing I can think of is to:
subscribe to the command stream and queue the results (in an array)
Apply a mapping operation to the data stream which will pull from the queue (or use default if the queue is empty).
We can wrap this up into a generic observable operator. I'm bad with names, so I'll call it zipWithDefault:
Rx.Observable.prototype.zipWithDefault = function(bs, defaultB, selector) {
var source = this;
return Rx.Observable.create(function(observer) {
var sourceSubscription = new Rx.SingleAssignmentDisposable(),
bSubscription = new Rx.SingleAssignmentDisposable(),
subscriptions = new Rx.CompositeDisposable(sourceSubscription, bSubscription),
bQueue = [],
mappedSource = source.map(function(value) {
return selector(value, bQueue.length ? bQueue.shift() : defaultB);
});
bSubscription.setDisposable(bs.subscribe(
function(b) {
bQueue.push(b);
},
observer.onError.bind(observer)));
sourceSubscription.setDisposable(mappedSource.subscribe(observer));
return subscriptions;
});
};
And use it like so:
combined = dataStream
.zipWithDefault(commandStream, defaultCommand, function (data, command) {
return command;
});
I think the sample operator would be your best bet. Unfortunately, it does not come with a built in default value, so you would have to roll your own from the existing operator:
Rx.Observable.prototype.sampleWithDefault = function(sampler, defaultValue){
var source = this;
return new Rx.AnonymousObservable(function (observer) {
var atEnd, value, hasValue;
function sampleSubscribe() {
observer.onNext(hasValue ? value : defaultValue);
hasValue = false;
}
function sampleComplete() {
atEnd && observer.onCompleted();
}
return new Rx.CompositeDisposable(
source.subscribe(function (newValue) {
hasValue = true;
value = newValue;
}, observer.onError.bind(observer), function () {
atEnd = true;
}),
sampler.subscribe(sampleSubscribe, observer.onError.bind(observer), sampleComplete)
);
}, source);
}
You can achieve the queuing behavior using the controlled operator. Thus your final data chain would look like so:
var commands = getCommandSource().controlled();
var pipeline = commands
.sampleWithDefault(data, defaultCommand)
.tap(function() { commands.request(1); });
Here is a full example:
Rx.Observable.prototype.sampleWithDefault = function(sampler, defaultValue) {
var source = this;
return new Rx.AnonymousObservable(function(observer) {
var atEnd, value, hasValue;
function sampleSubscribe() {
observer.onNext(hasValue ? value : defaultValue);
hasValue = false;
}
function sampleComplete() {
atEnd && observer.onCompleted();
}
return new Rx.CompositeDisposable(
source.subscribe(function(newValue) {
hasValue = true;
value = newValue;
}, observer.onError.bind(observer), function() {
atEnd = true;
}),
sampler.subscribe(sampleSubscribe, observer.onError.bind(observer), sampleComplete)
);
}, source);
}
var scheduler = new Rx.TestScheduler();
var onNext = Rx.ReactiveTest.onNext;
var onCompleted = Rx.ReactiveTest.onCompleted;
var data = scheduler.createHotObservable(onNext(210, 18),
onNext(220, 17),
onNext(230, 16),
onNext(250, 15),
onCompleted(1000));
var commands = scheduler.createHotObservable(onNext(205, 'a'),
onNext(210, 'b'),
onNext(240, 'c'),
onNext(400, 'd'),
onCompleted(800))
.controlled(true, scheduler);
var pipeline = commands
.sampleWithDefault(data, 'default')
.tap(function() {
commands.request(1);
});
var output = document.getElementById("output");
pipeline.subscribe(function(x) {
var li = document.createElement("li");
var text = document.createTextNode(x);
li.appendChild(text);
output.appendChild(li);
});
commands.request(1);
scheduler.start();
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/2.5.2/rx.all.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/2.5.2/rx.testing.js"></script>
<div>
<ul id="output" />
</div>
This can be solved by using the scan function. In the accumulated value the commands are stored for which no data response has been received yet.
var result = Rx.Observable
.merge(data, command)
.scan(function (acc, x) {
if (x === 'd') {
acc.result = acc.commands.length > 0 ? acc.commands.shift() : 'dc';
} else {
acc.result = '';
acc.commands.push(x);
}
return acc;
}, {result: '', commands: []})
.map(function (x) {
return x.result;
})
.filter(function (x) {
return x !== '';
});
Please find a complete more detail here: http://jsbin.com/tubade/edit?html,js,console
I am facing following issue:
I am calling in foreach cycle following browse function. When the rb.wsc.browse(symbol) is called the program do some WebSocket request and when the message is returned the event is emmited. The problem is that I always get the same browseData even when I know that the event is emited with different one. I think that this is closure issue, but I don't know how to solve it.
function browse(rb, symbol, callback) {
var result = function(wsc, browseData) {
wsc.off('browse', result);
wsc.off('failed', result);
var err = null;
if (wsc.errno < 0) {
err = new Error("Browsing symbol " + symbol + " failed!");
err.status = wsc.errno;
} else {
saveBrowseData(rb, browseData);
}
callback(err, symbol);
};
// Register temporary listeners
rb.wsc.on('browse', result);
rb.wsc.on('failed', result);
// Browse symbol
rb.wsc.browse(symbol);
}
RexBrowser.prototype.refresh = function() {
var that = this;
var browseRequestNumber = 1;
var browseResult = function(err, symbol) {
browseRequestNumber--;
var item = that.getSymbol(symbol);
_.each(item.children, function(child) {
if (child.browse) {
browseRequestNumber++;
debug("Browsing: " + child.cstring);
browse(that,child.cstring, browseResult);
}
});
if (browseRequestNumber === 0) {
that.emit('refresh', that);
}
};
// Start recursive browsing
browse(this,'$', browseResult);
};-
You could try using a IIFE:
} else {
function(rbInner, browseDataInner){
saveBrowseData(rbInner, browseDataInner);
}(rb, browseData);
}
This makes sure the variables used by / in saveBrowseData have the values they have when the function is called.
I have some filters:
var jade = require('jade');
jade.filters.Posts = function(block) {
return '{block:Posts}'+jade.render(block)+'{/block:Posts}';
};
jade.filters.Audio = function(block) {
return '{block:Audio}'+jade.render(block)+'{/block:Audio}';
};
jade.filters.Video = function(block) {
return '{block:Video}'+jade.render(block)+'{/block:Video}';
};
And have some input
:Posts
Posts
:Audio
| Audio
:Video
| Video
So I have an error:
>> unknown filter ":Audio"
Can I handle or fix this problem?
PS You can look at the code in this repository — I'm using grunt and grunt-contrib-jade plugin, but to force grunt-contrib-jade work with filters you should edit ./node_modules/grunt-contrib-jade/tasks/jade.js to reflect changes from this pull request.
PS2: I found the stumbling block. When I use render() method inside filter, I invoke it from local jade instance, which is don't know anything about filters, but global jade instance (from Gruntfile.js) have all information about that filters. That's why the main question is: how can I throw global Jade-instance to file with filters?
PS3: I don't know how create fiddle for such case. But you can clone my Hampi repo, implement changes to grunt-contrib-jade from my PR to them, then at start run npm i. To compile templates run grunt jade. Pay attention to these line in body.jade and commented section in filters.
PS4. I find the reason and it in different scope. I describe it with details here. Can you solve this issue?
I'm open to additional answers and I will accept fixes in jade core (if it would be required).
We just should bind global jade instance to filters like this:
var jade = require('jade');
if (options.filters) {
// have custom filters
Object.keys(options.filters).forEach(function(filter) {
if (_.isFunction(data)) {
// have custom options
jade.filters[filter] = options.filters[filter].bind({jade: jade, locals: data()});
} else {
// have no custom options
jade.filters[filter] = options.filters[filter].bind({jade: jade });
}
});
}
See implementation here — in this commit
I think you are right at problem place, the problem is in the filter.js file
location jade/lib/filters.js
var transformers = require('transformers');
module.exports = filter;
function filter(name, str, options) {
if (typeof filter[name] === 'function') {
var res = filter[name](str, options);
} else if (transformers[name]) {
var res = transformers[name].renderSync(str, options);
if (transformers[name].outputFormat === 'js') {
res = '<script type="text/javascript">\n' + res + '</script>';
} else if (transformers[name].outputFormat === 'css') {
res = '<style type="text/css">' + res + '</style>';
} else if (transformers[name].outputFormat === 'xml') {
res = res.replace(/'/g, ''');
}
} else {
throw new Error('unknown filter ":' + name + '"');
}
return res; // returns RES that is not defined in scope of function.
}
filter.exists = function (name, str, options) {
return typeof filter[name] === 'function' || transformers[name];
};
Here I have identified one flaw that you can correct like this,
var transformers = require('transformers');
module.exports = filter;
function filter(name, str, options) {
var res;//defined a variable which is global to the scope of function.
if (typeof filter[name] === 'function') {
res = filter[name](str, options);
} else if (transformers[name]) {
res = transformers[name].renderSync(str, options);
if (transformers[name].outputFormat === 'js') {
res = '<script type="text/javascript">\n' + res + '</script>';
} else if (transformers[name].outputFormat === 'css') {
res = '<style type="text/css">' + res + '</style>';
} else if (transformers[name].outputFormat === 'xml') {
res = res.replace(/'/g, ''');
}
} else {
throw new Error('unknown filter ":' + name + '"');
}
return res;
}
filter.exists = function (name, str, options) {
return typeof filter[name] === 'function' || transformers[name];
};
It may be possible that nesting under some function makes audio function out of scope. Does audio function works alone!?
although there may be other things if the problem not solved, please create a fiddle for your for better understanding.