Skip to content

Commit

Permalink
Merge pull request #69 from LinusU/sync-api
Browse files Browse the repository at this point in the history
added sync api
  • Loading branch information
pksunkara committed May 22, 2015
2 parents 775b9b2 + e57ccc7 commit 84957c8
Show file tree
Hide file tree
Showing 10 changed files with 193 additions and 60 deletions.
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,16 @@ protagonist.parse('# My API', function(error, result) {
});
```

### Synchronous usage

If you for some reason need to use the api synchronous, that is also possible.
Please note that the recommended way is to use the asynchronous api as to not
block the event loop.

```js
var result = protagonist.parseSync('# My API');
```

### Parsing Result

Parsing this blueprint:
Expand Down
4 changes: 3 additions & 1 deletion binding.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@
],
"sources": [
"src/annotation.cc",
"src/parse.cc",
"src/options_parser.cc",
"src/parse_async.cc",
"src/parse_sync.cc",
"src/protagonist.cc",
"src/protagonist.h",
"src/result.cc",
Expand Down
50 changes: 50 additions & 0 deletions src/options_parser.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#include "protagonist.h"
#include "snowcrash.h"

using namespace v8;
using namespace protagonist;

//static const std::string RenderDescriptionsOptionKey = "renderDescriptions";
static const std::string RequireBlueprintNameOptionKey = "requireBlueprintName";
static const std::string ExportSourcemapOptionKey = "exportSourcemap";

OptionsResult* protagonist::ParseOptionsObject(Handle<Object> optionsObject) {
OptionsResult *optionsResult = (OptionsResult *) malloc(sizeof(OptionsResult));

optionsResult->options = 0;
optionsResult->error = NULL;

const Local<Array> properties = optionsObject->GetPropertyNames();
const uint32_t length = properties->Length();

for (uint32_t i = 0 ; i < length ; ++i) {
const Local<Value> key = properties->Get(i);
const Local<Value> value = optionsObject->Get(key);

if (RequireBlueprintNameOptionKey == *String::Utf8Value(key)) {
// RequireBlueprintNameOption
if (value->IsTrue())
optionsResult->options |= snowcrash::RequireBlueprintNameOption;
else
optionsResult->options &= snowcrash::RequireBlueprintNameOption;
}
else if (ExportSourcemapOptionKey == *String::Utf8Value(key)) {
// ExportSourcemapOption
if (value->IsTrue())
optionsResult->options |= snowcrash::ExportSourcemapOption;
else
optionsResult->options &= snowcrash::ExportSourcemapOption;
}
else {
// Unrecognized option
std::stringstream ss;
ss << "unrecognized option '" << *String::Utf8Value(key) << "', expected: ";
ss << "'" << RequireBlueprintNameOptionKey << "' or '" << ExportSourcemapOptionKey << "'";

optionsResult->error = ss.str().c_str();
return optionsResult;
}
}

return optionsResult;
}
55 changes: 14 additions & 41 deletions src/parse.cc → src/parse_async.cc
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,6 @@ using std::string;
using namespace v8;
using namespace protagonist;

//static const std::string RenderDescriptionsOptionKey = "renderDescriptions";
static const std::string RequireBlueprintNameOptionKey = "requireBlueprintName";
static const std::string ExportSourcemapOptionKey = "exportSourcemap";

// Async Parse
void AsyncParse(uv_work_t* request);

Expand Down Expand Up @@ -58,47 +54,24 @@ NAN_METHOD(protagonist::Parse) {
if (args.Length() == 3 && !args[1]->IsObject()) {
NanThrowTypeError("wrong argument - object expected, `parse(string, options, callback)`");
NanReturnUndefined();
}
}

// Get source data
String::Utf8Value sourceData(args[0]->ToString());

// Prepare options
snowcrash::BlueprintParserOptions options = 0;

if (args.Length() == 3) {
Handle<Object> optionsObject = Handle<Object>::Cast(args[1]);
const Local<Array> properties = optionsObject->GetPropertyNames();
const uint32_t length = properties->Length();

for (uint32_t i = 0 ; i < length ; ++i) {
const Local<Value> key = properties->Get(i);
const Local<Value> value = optionsObject->Get(key);

if (RequireBlueprintNameOptionKey == *String::Utf8Value(key)) {
// RequireBlueprintNameOption
if (value->IsTrue())
options |= snowcrash::RequireBlueprintNameOption;
else
options &= snowcrash::RequireBlueprintNameOption;
}
else if (ExportSourcemapOptionKey == *String::Utf8Value(key)) {
// ExportSourcemapOption
if (value->IsTrue())
options |= snowcrash::ExportSourcemapOption;
else
options &= snowcrash::ExportSourcemapOption;
}
else {
// Unrecognized option
std::stringstream ss;
ss << "unrecognized option '" << *String::Utf8Value(key) << "', expected: ";
ss << "'" << RequireBlueprintNameOptionKey << "' or '" << ExportSourcemapOptionKey << "'";

NanThrowTypeError(ss.str().c_str());
NanReturnUndefined();
}
}
OptionsResult *optionsResult = ParseOptionsObject(Handle<Object>::Cast(args[1]));

if (optionsResult->error != NULL) {
NanThrowTypeError(optionsResult->error);
NanReturnUndefined();
}

options = optionsResult->options;
free(optionsResult);
}

// Get Callback
Expand All @@ -115,14 +88,14 @@ NAN_METHOD(protagonist::Parse) {
request->data = baton;

// Schedule the work request
int status = uv_queue_work(uv_default_loop(),
request,
int status = uv_queue_work(uv_default_loop(),
request,
AsyncParse,
(uv_after_work_cb)AsyncParseAfter);

assert(status == 0);
NanReturnUndefined();
}
}

void AsyncParse(uv_work_t* request) {
Baton* baton = static_cast<Baton*>(request->data);
Expand Down
58 changes: 58 additions & 0 deletions src/parse_sync.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#include <string>
#include <sstream>
#include "protagonist.h"
#include "snowcrash.h"
#include "drafter.h"

using std::string;
using namespace v8;
using namespace protagonist;

NAN_METHOD(protagonist::ParseSync) {
NanScope();

// Check arguments
if (args.Length() != 1 && args.Length() != 2) {
NanThrowTypeError("wrong number of arguments, `parseSync(string, options)` expected");
NanReturnUndefined();
}

if (!args[0]->IsString()) {
NanThrowTypeError("wrong argument - string expected, `parseSync(string, options)`");
NanReturnUndefined();
}

if (args.Length() == 2 && !args[1]->IsObject()) {
NanThrowTypeError("wrong argument - object expected, `parseSync(string, options)`");
NanReturnUndefined();
}

// Get source data
String::Utf8Value sourceData(args[0]->ToString());

// Prepare options
snowcrash::BlueprintParserOptions options = 0;

if (args.Length() == 2) {
OptionsResult *optionsResult = ParseOptionsObject(Handle<Object>::Cast(args[1]));

if (optionsResult->error != NULL) {
NanThrowTypeError(optionsResult->error);
NanReturnUndefined();
}

options = optionsResult->options;
free(optionsResult);
}

// Parse the source data
snowcrash::ParseResult<snowcrash::Blueprint> parseResult;
drafter::ParseBlueprint(*sourceData, options, parseResult);

if (parseResult.report.error.code != snowcrash::Error::OK) {
NanThrowError(SourceAnnotation::WrapSourceAnnotation(parseResult.report.error));
NanReturnUndefined();
}

NanReturnValue(Result::WrapResult(parseResult.report, parseResult.node, parseResult.sourceMap, options));
}
1 change: 1 addition & 0 deletions src/protagonist.cc
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ void Init(Handle<Object> exports) {

// Parse function
exports->Set(NanNew<String>("parse"), NanNew<FunctionTemplate>(Parse)->GetFunction());
exports->Set(NanNew<String>("parseSync"), NanNew<FunctionTemplate>(ParseSync)->GetFunction());
}

NODE_MODULE(protagonist, Init)
11 changes: 11 additions & 0 deletions src/protagonist.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,16 @@

namespace protagonist {

//
// Options parsing
///
struct OptionsResult {
snowcrash::BlueprintParserOptions options;
const char *error;
};

OptionsResult* ParseOptionsObject(v8::Handle<v8::Object>);

//
// SourceAnnotation
//
Expand Down Expand Up @@ -55,6 +65,7 @@ namespace protagonist {
// Parse function
//
extern NAN_METHOD(Parse);
extern NAN_METHOD(ParseSync);
}

#endif
4 changes: 2 additions & 2 deletions test/ast-test.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ path = require 'path'
{assert} = require 'chai'
protagonist = require '../build/Release/protagonist'

describe "Parser AST", ->
describe "Parser AST - Async", ->

ast_fixture = require './fixtures/sample-api-ast.json'
ast_parsed = null
Expand All @@ -24,4 +24,4 @@ describe "Parser AST", ->

# Parser AST should conform to AST serialization JSON media type
it '`ast` field conforms to `vnd.apiblueprint.ast.raw+json; version=3.0`', ->
assert.deepEqual ast_parsed, ast_parsed
assert.deepEqual ast_parsed, ast_fixture
36 changes: 20 additions & 16 deletions test/fixtures/sample-api-ast.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@
"description": "",
"method": "GET",
"parameters": [],
"attributes": {
"relation": "",
"uriTemplate": ""
},
"content": [],
"examples": [
{
Expand All @@ -68,10 +72,7 @@
"content": [
{
"element": "dataStructure",
"name": {
"literal": "",
"variable": false
},
"name": null,
"typeDefinition": {
"typeSpecification": {
"name": {
Expand All @@ -95,6 +96,10 @@
"description": "",
"method": "DELETE",
"parameters": [],
"attributes": {
"relation": "",
"uriTemplate": ""
},
"content": [],
"examples": [
{
Expand Down Expand Up @@ -124,10 +129,7 @@
},
"typeDefinition": {
"typeSpecification": {
"name": {
"literal": "",
"variable": false
},
"name": null,
"nestedTypes": []
},
"attributes": []
Expand Down Expand Up @@ -230,6 +232,10 @@
"description": "",
"method": "GET",
"parameters": [],
"attributes": {
"relation": "",
"uriTemplate": ""
},
"content": [],
"examples": [
{
Expand All @@ -251,10 +257,7 @@
"content": [
{
"element": "dataStructure",
"name": {
"literal": "",
"variable": false
},
"name": null,
"typeDefinition": {
"typeSpecification": {
"name": {
Expand All @@ -278,6 +281,10 @@
"description": "",
"method": "DELETE",
"parameters": [],
"attributes": {
"relation": "",
"uriTemplate": ""
},
"content": [],
"examples": [
{
Expand Down Expand Up @@ -307,10 +314,7 @@
},
"typeDefinition": {
"typeSpecification": {
"name": {
"literal": "",
"variable": false
},
"name": null,
"nestedTypes": []
},
"attributes": []
Expand Down
24 changes: 24 additions & 0 deletions test/sync-test.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
fs = require 'fs'
path = require 'path'
{assert} = require 'chai'
protagonist = require '../build/Release/protagonist'

describe "Parser AST - Sync", ->

ast_fixture = require './fixtures/sample-api-ast.json'
ast_parsed = null

# Read & parse blueprint fixture
before (done) ->

fixture_path = path.join __dirname, './fixtures/sample-api.apib'

fs.readFile fixture_path, 'utf8', (err, data) ->
return done err if err

ast_parsed = protagonist.parseSync(data).ast
done()

# Parser AST should conform to AST serialization JSON media type
it '`ast` field conforms to `vnd.apiblueprint.ast.raw+json; version=3.0`', ->
assert.deepEqual ast_parsed, ast_fixture

0 comments on commit 84957c8

Please sign in to comment.