-
Notifications
You must be signed in to change notification settings - Fork 10
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
adding an async function called "shout" #3
Merged
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
18d6c48
adding an async function called "shout"
mapsam 0524c12
shout louder
mapsam 4e8bfd1
simplify louder check
mapsam e6b659a
convert Baton to a class using C++ style (rather than C Struct)
4118273
try/catch error tests
mapsam defbf80
Merge branch 'async' of github.com:mapbox/node-cpp-skel into async
mapsam d59c1b8
Merge branch 'master' into async
mapsam fb18d0b
set baton result
mapsam f17066b
generate API.md
mapsam 7c1ae28
clean up docs
mapsam c3bab72
Merge pull request #8 from mapbox/docs
mapsam File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
# HelloWorld | ||
|
||
Main class, called HelloWorld | ||
|
||
**Examples** | ||
|
||
```javascript | ||
var HelloWorld = require('index.js'); | ||
var HW = new HelloWorld(); | ||
``` | ||
|
||
## wave | ||
|
||
Say howdy to the world | ||
|
||
**Examples** | ||
|
||
```javascript | ||
var wave = HW.wave(); | ||
console.log(wave); // => 'howdy world!' | ||
``` | ||
|
||
Returns **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** a happy-go-lucky string saying hi | ||
|
||
## shout | ||
|
||
Shout a phrase really loudly by adding an exclamation to the end, asynchronously | ||
|
||
**Parameters** | ||
|
||
- `phrase` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** to shout | ||
- `different` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** ways to shout | ||
- `callback` **[Function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function)** from whence the shout comes, returns a string | ||
|
||
**Examples** | ||
|
||
```javascript | ||
var HW = new HelloWorld(); | ||
HW.shout('rawr', {}, function(err, shout) { | ||
if (err) throw err; | ||
console.log(shout); // => 'rawr!' | ||
}); | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
default: | ||
npm install --build-from-source | ||
|
||
docs: | ||
npm run docs |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,6 +3,13 @@ | |
#include <exception> | ||
#include <iostream> | ||
|
||
/** | ||
* Main class, called HelloWorld | ||
* @class HelloWorld | ||
* @example | ||
* var HelloWorld = require('index.js'); | ||
* var HW = new HelloWorld(); | ||
*/ | ||
NAN_METHOD(HelloWorld::New) | ||
{ | ||
if (info.IsConstructCall()) | ||
|
@@ -26,17 +33,166 @@ NAN_METHOD(HelloWorld::New) | |
} | ||
} | ||
|
||
Nan::Persistent<v8::Function> &HelloWorld::constructor() | ||
{ | ||
static Nan::Persistent<v8::Function> init; | ||
return init; | ||
} | ||
|
||
/** | ||
* Say howdy to the world | ||
* | ||
* @name wave | ||
* @memberof HelloWorld | ||
* @returns {String} a happy-go-lucky string saying hi | ||
* @example | ||
* var wave = HW.wave(); | ||
* console.log(wave); // => 'howdy world!' | ||
*/ | ||
NAN_METHOD(HelloWorld::wave) | ||
{ | ||
// info comes from the NAN_METHOD macro, which returns differently | ||
// according to the version of node | ||
info.GetReturnValue().Set(Nan::New<v8::String>("howdy world!").ToLocalChecked()); | ||
info.GetReturnValue().Set(Nan::New<v8::String>("howdy world").ToLocalChecked()); | ||
} | ||
|
||
Nan::Persistent<v8::Function> &HelloWorld::constructor() | ||
/** | ||
* Shout a phrase really loudly by adding an exclamation to the end, asynchronously | ||
* | ||
* @name shout | ||
* @memberof HelloWorld | ||
* @param {String} phrase to shout | ||
* @param {Object} different ways to shout | ||
* @param {Function} callback - from whence the shout comes, returns a string | ||
* @example | ||
* var HW = new HelloWorld(); | ||
* HW.shout('rawr', {}, function(err, shout) { | ||
* if (err) throw err; | ||
* console.log(shout); // => 'rawr!' | ||
* }); | ||
* | ||
*/ | ||
|
||
// this is the cpp object that will be passed around in 'shout' and callbacks | ||
// referred to as a "baton" | ||
class AsyncBaton | ||
{ | ||
static Nan::Persistent<v8::Function> init; | ||
return init; | ||
public: | ||
uv_work_t request; // required | ||
Nan::Persistent<v8::Function> cb; // callback function type | ||
std::string phrase; | ||
bool louder; | ||
std::string error_name; | ||
std::string result; | ||
}; | ||
|
||
NAN_METHOD(HelloWorld::shout) | ||
{ | ||
std::string phrase = ""; | ||
bool louder = false; | ||
|
||
// check first argument, should be a 'phrase' string | ||
if (!info[0]->IsString()) | ||
{ | ||
Nan::ThrowTypeError("first arg 'phrase' must be a string"); | ||
return; | ||
} | ||
phrase = *v8::String::Utf8Value((info[0])->ToString()); | ||
|
||
// check second argument, should be an 'options' object | ||
if (!info[1]->IsObject()) | ||
{ | ||
Nan::ThrowTypeError("second arg 'options' must be an object"); | ||
return; | ||
} | ||
|
||
v8::Local<v8::Object> options = info[1].As<v8::Object>(); | ||
if (options->Has(Nan::New("louder").ToLocalChecked())) | ||
{ | ||
v8::Local<v8::Value> louder_val = options->Get(Nan::New("louder").ToLocalChecked()); | ||
if (!louder_val->IsBoolean()) | ||
{ | ||
Nan::ThrowError("option 'louder' must be a boolean"); | ||
return; | ||
} | ||
louder = louder_val->BooleanValue(); | ||
} | ||
|
||
// check third argument, should be a 'callback' function | ||
if (!info[2]->IsFunction()) | ||
{ | ||
Nan::ThrowTypeError("third arg 'callback' must be a function"); | ||
return; | ||
} | ||
v8::Local<v8::Value> callback = info[2]; | ||
|
||
// set up the baton to pass into our threadpool | ||
AsyncBaton *baton = new AsyncBaton(); | ||
baton->request.data = baton; | ||
baton->phrase = phrase; | ||
baton->louder = louder; | ||
baton->cb.Reset(callback.As<v8::Function>()); | ||
|
||
/* | ||
`uv_queue_work` is the all-important way to pass info into the threadpool. | ||
It cannot take v8 objects, so we need to do some manipulation above to convert into cpp objects | ||
otherwise things get janky. It takes four arguments: | ||
|
||
1) which loop to use, node only has one so we pass in a pointer to the default | ||
2) the baton defined above, we use this to access information important for the method | ||
3) operations to be executed within the threadpool | ||
4) operations to be executed after #3 is complete to pass into the callback | ||
*/ | ||
uv_queue_work(uv_default_loop(), &baton->request, AsyncShout, (uv_after_work_cb)AfterShout); | ||
return; | ||
} | ||
|
||
// this is where we actually exclaim our shout phrase | ||
void HelloWorld::AsyncShout(uv_work_t* req) | ||
{ | ||
AsyncBaton *baton = static_cast<AsyncBaton *>(req->data); | ||
|
||
/***************** custom code here ******************/ | ||
try | ||
{ | ||
std::string return_string = baton->phrase + "!"; | ||
|
||
if (baton->louder) | ||
{ | ||
return_string += "!!!!"; | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ah, I see what the issue was @springmeyer - we lost the |
||
|
||
baton->result = return_string; | ||
} | ||
catch (std::exception const& ex) | ||
{ | ||
baton->error_name = ex.what(); | ||
} | ||
/***************** end custom code *******************/ | ||
|
||
} | ||
|
||
// handle results from AsyncShout - if there are errors return those | ||
// otherwise return the type & info to our callback | ||
void HelloWorld::AfterShout(uv_work_t* req) | ||
{ | ||
Nan::HandleScope scope; | ||
|
||
AsyncBaton *baton = static_cast<AsyncBaton *>(req->data); | ||
|
||
if (!baton->error_name.empty()) | ||
{ | ||
v8::Local<v8::Value> argv[1] = { Nan::Error(baton->error_name.c_str()) }; | ||
Nan::MakeCallback(Nan::GetCurrentContext()->Global(), Nan::New(baton->cb), 1, argv); | ||
} | ||
else | ||
{ | ||
v8::Local<v8::Value> argv[2] = { Nan::Null(), Nan::New<v8::String>(baton->result.data()).ToLocalChecked() }; | ||
Nan::MakeCallback(Nan::GetCurrentContext()->Global(), Nan::New(baton->cb), 2, argv); | ||
} | ||
|
||
baton->cb.Reset(); | ||
delete baton; | ||
} | ||
|
||
NAN_MODULE_INIT(HelloWorld::Init) | ||
|
@@ -47,16 +203,13 @@ NAN_MODULE_INIT(HelloWorld::Init) | |
fnTp->InstanceTemplate()->SetInternalFieldCount(1); | ||
fnTp->SetClassName(whoami); | ||
|
||
// custom methods added here | ||
SetPrototypeMethod(fnTp, "wave", wave); | ||
SetPrototypeMethod(fnTp, "shout", shout); | ||
|
||
const auto fn = Nan::GetFunction(fnTp).ToLocalChecked(); | ||
|
||
constructor().Reset(fn); | ||
|
||
Nan::Set(target, whoami, fn); | ||
} | ||
|
||
NODE_MODULE(HelloWorld, HelloWorld::Init); | ||
|
||
|
||
|
||
NODE_MODULE(HelloWorld, HelloWorld::Init); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,67 @@ | ||
var test = require('tape'); | ||
var HelloWorld = require('../lib/index.js'); | ||
var HW = new HelloWorld(); | ||
|
||
test('wave hi', function(t) { | ||
var HW = new HelloWorld(); | ||
test('wave success', function(t) { | ||
var hello = HW.wave(); | ||
t.equal(hello, 'howdy world!', 'output of HelloWorld.wave'); | ||
t.equal(hello, 'howdy world', 'output of HelloWorld.wave'); | ||
t.end(); | ||
}) | ||
}); | ||
|
||
test('shout success', function(t) { | ||
HW.shout('rawr', {}, function(err, shout) { | ||
if (err) throw err; | ||
t.equal(shout, 'rawr!'); | ||
t.end(); | ||
}); | ||
}); | ||
|
||
test('shout success - options.louder', function(t) { | ||
HW.shout('rawr', { louder: true }, function(err, shout) { | ||
if (err) throw err; | ||
t.equal(shout, 'rawr!!!!!'); | ||
t.end(); | ||
}); | ||
}); | ||
|
||
// we have to wrap these in try/catch statements right now | ||
// https://github.com/mapbox/node-cpp-skel/issues/4 | ||
test('shout error - non string phrase', function(t) { | ||
try { | ||
HW.shout(4, {}, function(err, shout) {}); | ||
} catch (err) { | ||
t.ok(err, 'expected error'); | ||
t.ok(err.message.indexOf('phrase') > -1, 'proper error message'); | ||
t.end(); | ||
} | ||
}); | ||
|
||
test('shout error - no options object', function(t) { | ||
try { | ||
HW.shout('rawr', true, function(err, shout) {}); | ||
} catch (err) { | ||
t.ok(err, 'expected error'); | ||
t.ok(err.message.indexOf('options') > -1, 'proper error message'); | ||
t.end(); | ||
} | ||
}); | ||
|
||
test('shout error - options.louder non boolean', function(t) { | ||
try { | ||
HW.shout('rawr', { louder: 3 }, function(err, shout) {}); | ||
} catch (err) { | ||
t.ok(err, 'expected error'); | ||
t.ok(err.message.indexOf('louder') > -1, 'proper error message'); | ||
t.end(); | ||
} | ||
}); | ||
|
||
test('shout error - no callback', function(t) { | ||
try { | ||
HW.shout('rawr', {}); | ||
} catch (err) { | ||
t.ok(err, 'expected error'); | ||
t.ok(err.message.indexOf('callback') > -1, 'proper error message'); | ||
t.end(); | ||
} | ||
}); |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@springmeyer seeing that the
return_string
now comes back empty for this method after wrapping it all within thetry/catch
. Seems odd, but if I pull it out:things work fine