Yeoman Prompts with Rx Interface - yeoman

I'm trying to write a Yeoman generator, and I really don't enjoy the documented interface for writing prompts. The Reactive Interface seems like it would be much easier to write branching and looping interfaces. However when I write mine like so:
prompting: function () {
var prompts = [{ type: 'input',
name: 'howdy',
message:'howdy'
}];
prompts = Rx.Observable.from(prompts);
this.prompt(prompts, function(answers) { this.log(answers); }.bind(this));
},
I get this error:
events.js:85
throw er; // Unhandled 'error' event
^
Error: You must provide a `message` parameter
at Prompt.throwParamError (/Users/user/.nvm/versions/node/v0.12.0/lib/node_modules/yo/node_modules/inquirer/lib/prompts/base.js:88:9)
at Prompt (/Users/user/.nvm/versions/node/v0.12.0/lib/node_modules/yo/node_modules/inquirer/lib/prompts/base.js:44:10)
at new Prompt (/Users/user/.nvm/versions/node/v0.12.0/lib/node_modules/yo/node_modules/inquirer/lib/prompts/input.js:25:15)
at PromptUI.fetchAnswer (/Users/user/.nvm/versions/node/v0.12.0/lib/node_modules/yo/node_modules/inquirer/lib/ui/prompt.js:92:16)
at MapObserver.selector (/Users/user/.nvm/versions/node/v0.12.0/lib/node_modules/yo/node_modules/inquirer/node_modules/rx/dist/rx.js:4215:20)
at MapObserver.tryCatcher (/Users/user/.nvm/versions/node/v0.12.0/lib/node_modules/yo/node_modules/inquirer/node_modules/rx/dist/rx.js:568:29)
at MapObserver.onNext (/Users/user/.nvm/versions/node/v0.12.0/lib/node_modules/yo/node_modules/inquirer/node_modules/rx/dist/rx.js:4423:42)
at MapObserver.tryCatcher (/Users/user/.nvm/versions/node/v0.12.0/lib/node_modules/yo/node_modules/inquirer/node_modules/rx/dist/rx.js:568:29)
at AutoDetachObserverPrototype.next (/Users/user/.nvm/versions/node/v0.12.0/lib/node_modules/yo/node_modules/inquirer/node_modules/rx/dist/rx.js:4856:51)
at AutoDetachObserver.Rx.internals.AbstractObserver.AbstractObserver.onNext (/Users/user/.nvm/versions/node/v0.12.0/lib/node_modules/yo/node_modules/inquirer/node_modules/rx/dist/rx.js:1856:35)

Instead of using the generator's built in instance of Inquirer via this.prompt(), I installed Inquirer and followed their example. It works perfectly; except it duplicates the first prompt.
prompting: function () {
var done = this.async();
var log = function(answers) { this.log(answers); }.bind(this);
var complete = function() {
this.log('complete');
done();
}.bind(this);
var prompts = Rx.Observable.create(function(obs) {
this.log(obs);
obs.onNext({ type: 'input',
name: 'howdy',
message:'howdy'
});
obs.onNext({ type: 'input',
name: 'okee',
message:'okee'
});
obs.onCompleted();
}.bind(this));
inquirer.prompt(prompts).process.subscribe(log, log, complete);
}

Related

Trying to Script a Search Result into a Netsuite Entity Field

Having two issues with this. One is that I keep getting an error when trying to upload my script. The other is that one version that I did get to upload, didn't load any value into the field (ie. field blank after script ran)
The error I keep getting on upload is "Fail to evaluate script: All SuiteScript API Modules are unavailable while executing your define callback." And although I've made drastic changes to the script, it still won't allow me to upload.
/**
*#NApiVersion 2.x
*#NScriptType ScheduledScript
*/
define(['N/search', "N/record"],
function(search, record) {
function loadAndRunSearch(scriptContext) {
var mySearch = search.load({
id: 'customsearch1088'
});
mySearch.run().each(function (result) {
var countt = result.getValue({
name: 'number'
});
var entity = result.getValue({
name: 'internalid'
});
var objRecord = record.load({
type: record.Type.CUSTOMER,
id: entity,
isDynamic: true,
});
var vfield = objRecord.getField({
fieldId: 'custentity_orders_12m'
});
objRecord.setValue({fieldId: 'custentity_orders_12m', value: countt});
objRecord.save();
});
}
return {
execute: loadAndRunSearch
};
});
That's the script stripped down to the bare bones (FYI still doesn't upload), and the script that uploaded was basically a more complicated version of the same script, except it didn't set the field value. Can anyone see where I've gone wrong?
You haven't returned the entry function.
/**
*#NApiVersion 2.x
*#NScriptType ScheduledScript
*/
define(['N/search', 'N/record'],
function(search, record) {
function loadAndRunSearch(scriptContext) {
var mySearch = search.load({
id: 'customsearch1088'
});
mySearch.run().each(function (result) {
var countt = result.getValue({
name: 'number'
});
var entity = result.getValue({
name: 'internalid'
});
record.submitField({
type: record.Type.CUSTOMER,
id: entity,
values: {
'custentity_orders_12m' :countt
}
});
});
}
return {
execute : loadAndRunSearch
}
});

Calling a javascript function, which is passed as a parameter from a javascript library

I'm currently trying to implement the fullcalendar javascript library into an angular 2 dart webapp.
I'm having problems porting this javascript code to dart though:
$('#fullCalendar').fullCalendar(
{
events: function(start, end, timezone, callback) {
var generated_events=[
{
title : 'test',
start : '2016-08-08'
}];
callback(generated_events);
},
allDaySlot: false
//More options can go here
});
I've gotten as far as being able to pass a dart function to the events parameter with this code:
context.callMethod(r'$',['#fullCalendar'])
.callMethod('fullCalendar',[new JsObject.jsify({
'events': (start, end, timezone, callback){
print("called!");
List<FullCalendarEvent> generated_events= [
new FullCalendarEvent(title: "test", start: "2016-08-08")
];
try{
callback(generated_events);
}catch(exception,stackTrace){
print("Caught exception!");
print(exception);
print(stackTrace);
}
},
'allDaySlot': false
//more options can go here
})]);
Where the FullCalendarEvent is a simple anoymous class structure:
#JS()
#anonymous
class FullCalendarEvent{
external String get title;
external set title(String v);
external String get start;
external set start(String v);
external factory FullCalendarEvent({
String title,
String start
});
}
However the callback(generated_events); throws this exception:
NoSuchMethodError: method not found: 'call$1' (callback.call$1 is not a function)
Edit:
With the help of Günter's replies I managed to fix the problem. Instead of doing callback(generated_events); I instead use callback.apply([generated_events]); Additionally instead of using
List<FullCalendarEvent> generated_events= [
new FullCalendarEvent(title: "test", start: "2016-08-08")
];
I instead use:
var generated_events = new JsObject.jsify([{'title':'test','start':'2016-08-08'}]);
My working code looks like this:
context.callMethod(r'$',['#fullCalendar'])
.callMethod('fullCalendar',[new JsObject.jsify({
'events': (start, end, timezone, callback){
print("called!");
var generated_events = new JsObject.jsify([{'title':'test','start':'2016-08-08'}]);
try{
callback.apply([generated_events]);
}catch(exception,stackTrace){
print("Caught exception!");
print(exception);
print(stackTrace);
}
},
'allDaySlot': false
//more options can go here
})]);
A JS function should be callable with
callback.apply([gen‌​erated_events])

Splitting Yeoman prompts into multiple separate groups

how would one split a yeoman prompt into parts?
I have a rather extended prompt that i'd like to split into parts with a title for each part.
CSS
- prompt1
HTML
-prompt 2
Something like this:
prompt1: function(){
var done = this.async();
condole.log('title 1');
var prompts = [{
name: 'prompt1',
message: 'Prompt 1:',
}]
},
prompt2: function(){
var done = this.async();
condole.log('title 2');
var prompts = [{
name: 'prompt2',
message: 'Prompt 2:',
}]
},
Thanks!
Update as #Deimyts notes in the comments, the original code stopped working. This is due to API changes in Inquirer.JS documented here.
The base API interface is now inquirer.prompt(questions).then(). There's no more callback function.
Any async question functions is taking a promise as return value instead of requiring this.async().
In a nutshell, instead of using the old var done = this.async() API and resolving the prompt inside the callback with done() just return a promise from the prompting functions (see docs).
prompt1: function() {
this.log("HTML")
return this.prompt([
// configure prompts as before
]).then(function (answers) {
// callback body as before, but no more calling done()
}.bind(this));
},
For more details see #Deimyts answer below.
Yeoman uses a run loop with certain predefined priorities that you can use to put your actions into. As mentioned in the ☞ docs you can group several methods at one priority. Here is a snippet to illustrate a generator with prompts split into two groups HTML and CSS:
'use strict';
var generators = require('yeoman-generator');
module.exports = generators.Base.extend({
constructor: function () {
generators.Base.apply(this, arguments);
},
prompting: {
prompt1: function() {
this.log("HTML")
var done = this.async();
this.prompt([{
type : 'input',
name : 'foo',
message : 'Foo',
}, {
type : 'input',
name : 'bar',
message : 'Bar'
}], function (answers) {
this.foo = answers.foo;
this.bar = answers.bar;
done();
}.bind(this));
},
prompt2: function() {
this.log("CSS")
var done = this.async();
this.prompt([{
type : 'input',
name : 'bam',
message : 'Bam',
}], function (answers) {
this.bam = answers.bam;
done();
}.bind(this));
}
},
configuring: function () {
console.log(this.foo, this.bar, this.bam);
}
});
Using this feature of Yeoman you could modularize your code even further, e.g. by putting your different prompts in separate code files and require / import them into your generator file. But basically the above snippet should do the trick.
Let me know if that helps.
The previous answer wasn't working for me until I made several modifications to the example code.
I can't be 100% certain, but I believe that the difference might be due to differing versions of the yeoman-generator module. So, I'm recording this here in case anyone else runs into the same issue.
For reference, I'm using yeoman-generator v0.23.4, yo v1.8.4, node v6.2.2, & npm v3.9.5.
Modifications:
Remove all instances of var done = this.async(); and done().
The async() function was causing the generator to exit after prompt1, and never run prompt2 or the configuring function.
Add return before calling this.prompt();
Removing async() causes the generator to rush through the prompts without waiting for an answer. Adding return fixes this.
Replace the callback function inside this.prompt() with .then().
Before making this change, the generator would run through the prompts correctly, but would not save the answers, and configuring would simply log undefined undefined undefined.
Original: this.prompt(prompts, callback(answers).bind(this))
Revised: this.prompt(prompts).then(callback(answers).bind(this));
Full Example:
'use strict';
var generators = require('yeoman-generator');
module.exports = generators.Base.extend({
constructor: function () {
generators.Base.apply(this, arguments);
},
prompting: {
prompt1: function() {
this.log("HTML")
return this.prompt([{
type : 'input',
name : 'foo',
message : 'Foo',
}, {
type : 'input',
name : 'bar',
message : 'Bar'
}]).then(function (answers) {
this.foo = answers.foo;
this.bar = answers.bar;
}.bind(this));
},
prompt2: function() {
this.log("CSS")
return this.prompt([{
type : 'input',
name : 'bam',
message : 'Bam',
}])
.then(function(answers) {
this.bam = answers.bam;
}.bind(this));
}
},
configuring: function () {
console.log(this.foo, this.bar, this.bam);
console.log('Config: ', this.config);
},
});

Yeoman generator: how do I access user supplied options?

I'm building a yeoman generator. I prompt the user to name the project like this:
SimplesiteGenerator.prototype.askFor = function askFor() {
var cb = this.async();
console.log(this.yeoman);
var prompts = [{
name: 'siteName',
message: 'What do you want to call your site?'
}];
this.prompt(prompts, function (props) {
this.siteName = props.siteName;
cb();
}.bind(this));
};
Further on, I build the file system:
SimplesiteGenerator.prototype.app = function app() {
this.mkdir( 'app');
this.mkdir('app/templates');
this.mkdir( 'img');
I'd like to build the filesystem within a directory that is given the same name as the project. How do I get the user-supplied option and pass it into app ?
You will need to make an empty global variable, then assign it the value of the users answer like so
var projectFolderName = '';
this.prompt(prompts, function (props) {
this.siteName = props.siteName;
projectFolderName = props.siteName;
cb();
}.bind(this));
Then inject this variable into your build path string via concatenation like so
SimplesiteGenerator.prototype.app = function app() {
this.mkdir( 'app/' + projectFolderName);
this.mkdir('app/templates');
this.mkdir( 'img');
}

How to parse a XML string in a Firefox addon using Add-on SDK

I am trying to create a FF AddOn that brings some XML data from a website. But I can't find a way to parse my RESPONSE. First I used DOMParser but I get this error:
ReferenceError: DOMParser is not defined.
Someone suggested to use XMLHttpRequest, because the parsing is done automatically but then I get this other error:
Error: An exception occurred. Traceback (most recent call last):
File
"resource://jid0-a23vmnhgidl8wlymvolsst4ca98-at-jetpack/api-utils/lib/cuddlefish.js",
line 208, in require
let module, manifest = this.manifest[base], requirer = this.modules[base]; TypeError: this.manifest is undefined
I really don't know what else to do. I must note that I am using the AddOn Builder to achieve this.
Below the code that doesn't seem to work.
Option 1:
exports.main = function() {
require("widget").Widget({
id: "widgetID1",
label: "My Mozilla Widget",
contentURL: "http://www.mozilla.org/favicon.ico",
onClick: function(event) {
var Request = require("request").Request;
var goblecontent = Request({
url: "http://www.myexperiment.org/search.xml?query=goble",
onComplete: function (response) {
var parser = new DOMParser();
var xml = parser.parseFromString(response.text, "application/xml");
var packs = xml.getElementsByTagName("packs");
console.log(packs);
}
});
goblecontent.get();
}
});
};
Option 2:
exports.main = function() {
require("widget").Widget({
id: "widgetID1",
label: "My Mozilla Widget",
contentURL: "http://www.mozilla.org/favicon.ico",
onClick: function(event) {
var request = new require("xhr").XMLHttpRequest();
request.open("GET", "http://www.myexperiment.org/search.xml?query=goble", false);
request.send(null);
if (request.status === 200) {
console.log(request.responseText);
}
}
});
};
DOMParser constructor isn't defined in the context of SDK modules. You can still get it using chrome authority however:
var {Cc, Ci} = require("chrome");
var parser = Cc["#mozilla.org/xmlextras/domparser;1"].createInstance(Ci.nsIDOMParser);
nsIDOMParser documentation.
That said, your approach with XMLHttpRequest should work as well. You used the new operator incorrectly however, the way you wrote it a new "require object" is being created. This way it should work however:
var {XMLHttpRequest} = require("xhr");
var request = new XMLHttpRequest();
Please consider using an asynchronous XMLHttpRequest object however, use request.onreadystatechange to attach your listener (the xhr module currently doesn't support other types of listeners or addEventListener).
If you use XMLHttpRequest (available via the xhr module) you can easily avoid the use of DOMParser. Bellow I provide an example supposing request is an XMLHttpRequest object which request is successfully completed:
Instead of:
var parser = new DOMParser();
var xmlDoc = parser.parseFromString(request.responseText, "application/xml");
Use:
var xmlDoc = request.responseXML;
An then you can:
var packs = xmlDoc.getElementsByTagName("packs");
console.log(packs);
Or whatever.

Resources