From d3104756af781bc4e612900f745e02f5ac3f0d40 Mon Sep 17 00:00:00 2001 From: Mike Morris Date: Tue, 4 Aug 2015 14:50:04 -0400 Subject: [PATCH] make 'cancel' function in FileSource optional update README --- README.md | 103 +++++++++++++++++++-------------------- examples/load-from-fs.js | 18 +++---- package.json | 2 +- src/node_map.cpp | 8 +-- src/node_map.hpp | 5 +- test/js/gzip.test.js | 46 ++++++++--------- test/js/map.test.js | 18 +++---- 7 files changed, 93 insertions(+), 107 deletions(-) diff --git a/README.md b/README.md index 568e6f8cc..1b7d4b2d6 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ Other platforms will fall back to a source compile with `npm run build`. To comp ## Rendering a map tile ```js -var map = new mbgl.Map(fileSource); +var map = new mbgl.Map({ request: function() {} }); map.load(require('./test/fixtures/style.json')); map.render({}, function(err, image) { if (err) throw err; @@ -61,22 +61,20 @@ npm test ## Implementing a file source -When creating a `Map` object, you need to pass a `FileSource` object as the first parameter +When creating a `Map`, you must pass an options object (with a required `request` and optional `cancel` method) as the first parameter. ```js -var request = require('request'); - -var fileSource = new mbgl.FileSource(); -fileSource.request = function(req) { - // TODO -}; - -fileSource.cancel = function(req) { - // TODO -}; +var map = new mbgl.Map({ + request: function(req) { + // TODO + }, + cancel: function(req) { + // TODO + } +}); ``` -The `request()` method starts a new request to a file, while `cancel()` tells your FileSource implementation to cancel the request (if possible). The `req` parameter has two properties: +The `request()` method starts a new request to a file, while `cancel()` tells the FileSource to cancel the request (if possible). The `req` parameter has two properties: ```json { @@ -104,12 +102,13 @@ It has no significance for anything but serves as a hint to your implemention as A sample implementation that reads files from disk would look like the following: ```js -fileSource.request = function(req) { - fs.readFile(path.join('base/path', req.url), function(err, data) { - req.respond(err, { data: data }); - }); -}; -fileSource.cancel = function() {}; +var map = new mbgl.Map({ + request: function(req) { + fs.readFile(path.join('base/path', req.url), function(err, data) { + req.respond(err, { data: data }); + }); + } +}); ``` This is a very barebones implementation and you'll probably want a better implementation. E.g. it passes the url verbatim to the file system, but you'd want add some logic that normalizes `http` URLs. You'll notice that once your implementation has obtained the requested file, you have to deliver it to the requestee by calling `req.respond()`, which takes either an error object or `null` and an object with several settings: @@ -129,51 +128,51 @@ A sample implementation that uses [`request`](https://github.com/request/request var mbgl = require('mapbox-gl-native'); var request = require('request'); -var fileSource = new mbgl.FileSource(); -fileSource.request = function(req) { - request({ - url: req.url, - encoding: null, - gzip: true - }, function (err, res, body) { - if (req.canceled) { - return; - } +var map = new mbgl.Map({ + request: function(req) { + request({ + url: req.url, + encoding: null, + gzip: true + }, function (err, res, body) { + if (req.canceled) { + return; + } - if (err) { - req.respond(err); - } else if (res.statusCode == 200) { - var response = {}; + if (err) { + req.respond(err); + } else if (res.statusCode == 200) { + var response = {}; - if (res.headers.modified) { response.modified = new Date(res.headers.modified); } - if (res.headers.expires) { response.expires = new Date(res.headers.expires); } - if (res.headers.etag) { response.etag = res.headers.etag; } - response.data = body; - req.respond(null, response); - } else { - req.respond(new Error(JSON.parse(body).message)); - } - }); -}; - -fileSource.cancel = function(req) { - req.canceled = true; -}; + if (res.headers.modified) { response.modified = new Date(res.headers.modified); } + if (res.headers.expires) { response.expires = new Date(res.headers.expires); } + if (res.headers.etag) { response.etag = res.headers.etag; } + response.data = body; + req.respond(null, response); + } else { + req.respond(new Error(JSON.parse(body).message)); + } + }); + }, + cancel: function(req) { + req.canceled = true; + } +}); ``` -Note that in reality, Mapbox GL uses two types of protocols: `asset://` for files that should be loaded from some local static system, and `http://` (and `https://`), which should be loaded from the internet. However, stylesheets are free to use other protocols too, if your implementation of FileSource supports these; e.g. you could use `s3://` to indicate that files are supposed to be loaded from S3. +Note that in reality, Mapbox GL uses two types of protocols: `asset://` for files that should be loaded from some local static system, and `http://` (and `https://`), which should be loaded from the internet. However, stylesheets are free to use other protocols too, if your implementation of `request` supports these; e.g. you could use `s3://` to indicate that files are supposed to be loaded from S3. ## Mapbox API Access tokens -To use styles that rely on Mapbox vector tiles, you must pass an [API access token](https://www.mapbox.com/developers/api/#access-tokens) in your `FileSource` implementation with requests to `mapbox://` protocols. +To use styles that rely on Mapbox vector tiles, you must pass an [API access token](https://www.mapbox.com/developers/api/#access-tokens) in your `request` implementation with requests to `mapbox://` protocols. ```js var mbgl = require('mapbox-gl-native'); var request = require('request'); var url = require('url'); -var fileSource = new mbgl.FileSource(); -fileSource.request = function(req) { +var options = {}; +options.request = function(req) { var opts = { url: req.url, encoding: null, @@ -201,7 +200,7 @@ fileSource.request = function(req) { }); } -var map = mbgl.Map(fileSource); +var map = new mbgl.Map(options); var style = mapboxStyle; // includes a datasource with a reference to something like `mapbox://mapbox.mapbox-streets-v6` map.load(style); diff --git a/examples/load-from-fs.js b/examples/load-from-fs.js index 9337ed854..352b01557 100755 --- a/examples/load-from-fs.js +++ b/examples/load-from-fs.js @@ -9,18 +9,16 @@ var PNG = require('pngjs').PNG; var base = path.join(path.dirname(process.mainModule.filename), '../test'); -var fileSource = new mbgl.FileSource(); -fileSource.request = function(req) { - fs.readFile(path.join(base, req.url), function(err, data) { - req.respond(err, { data: data }); - }); -}; +var map = new mbgl.Map({ + request: function(req) { + fs.readFile(path.join(base, req.url), function(err, data) { + req.respond(err, { data: data }); + }); + } +}); -fileSource.cancel = function(req) { -}; - -var map = new mbgl.Map(fileSource); map.load(require('../test/fixtures/style.json')); + map.render({}, function(err, data) { if (err) throw err; diff --git a/package.json b/package.json index 0e7daa555..bded247c7 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ } ], "dependencies": { - "nan": "^1.8.4", + "nan": "^1.9.0", "node-pre-gyp": "^0.6.7" }, "bundledDependencies": [ diff --git a/src/node_map.cpp b/src/node_map.cpp index 8a9245419..bca557f81 100644 --- a/src/node_map.cpp +++ b/src/node_map.cpp @@ -61,17 +61,17 @@ NAN_METHOD(NodeMap::New) { } if (args.Length() < 1 || !args[0]->IsObject()) { - return NanThrowTypeError("Requires FileSource options as first argument"); + return NanThrowTypeError("Requires an options object as first argument"); } auto options = args[0]->ToObject(); // Check that request() and cancel() are defined. if (!options->Has(NanNew("request")) || !options->Get(NanNew("request"))->IsFunction()) { - return NanThrowError("FileSource options must have a request member function"); + return NanThrowError("Options object must have a 'request' method"); } - if (!options->Has(NanNew("cancel")) || !options->Get(NanNew("cancel"))->IsFunction()) { - return NanThrowError("FileSource options must have a cancel member function"); + if (options->Has(NanNew("cancel")) && !options->Get(NanNew("cancel"))->IsFunction()) { + return NanThrowError("Options object 'cancel' property must be a function"); } try { diff --git a/src/node_map.hpp b/src/node_map.hpp index 0e2879800..2c87900d4 100644 --- a/src/node_map.hpp +++ b/src/node_map.hpp @@ -44,13 +44,10 @@ public: //////////////////////////////////////////////////////////////////////////////////////////////// // Instance private: - NodeMap(v8::Handle source); + NodeMap(v8::Handle); ~NodeMap(); private: - // For retaining the FileSource object. - v8::Persistent source; - mbgl::HeadlessView view; NodeFileSource fs; std::unique_ptr map; diff --git a/test/js/gzip.test.js b/test/js/gzip.test.js index b3c8724b5..537a00581 100644 --- a/test/js/gzip.test.js +++ b/test/js/gzip.test.js @@ -31,31 +31,27 @@ function setup(options, callback) { callback(new mbgl.Map(options)); } -function getFileSourceOptions(gzip, t) { - var options = {}; +function getOptions(gzip, t) { + return { + request: function(req) { + var parts = req.url.split('.'); + var filetype = parts[parts.length - 1]; - options.request = function(req) { - var parts = req.url.split('.'); - var filetype = parts[parts.length - 1]; - - request({ - url: 'http://localhost:' + server.address().port + path.join('/', req.url), - encoding: null, - gzip: filetype === 'pbf' ? gzip : true, - headers: { - 'Accept-Encoding': 'gzip' - } - }, function (err, res, body) { - t.error(err); - var response = {}; - response.data = res.body; - req.respond(null, response); - }); + request({ + url: 'http://localhost:' + server.address().port + path.join('/', req.url), + encoding: null, + gzip: filetype === 'pbf' ? gzip : true, + headers: { + 'Accept-Encoding': 'gzip' + } + }, function (err, res, body) { + t.error(err); + var response = {}; + response.data = res.body; + req.respond(null, response); + }); + } }; - - options.cancel = function(req) {}; - - return options; } test('gzip', function(t) { @@ -64,7 +60,7 @@ test('gzip', function(t) { if (msg.severity == 'ERROR') t.error(msg); }); - setup(getFileSourceOptions(true, t), function(map) { + setup(getOptions(true, t), function(map) { map.load(style); map.render({}, function(err, data) { mbgl.removeAllListeners('message'); @@ -110,7 +106,7 @@ test('gzip', function(t) { } }); - setup(getFileSourceOptions(false, t), function(map) { + setup(getOptions(false, t), function(map) { map.load(style); map.render({}, function(err, data) { map.release(); diff --git a/test/js/map.test.js b/test/js/map.test.js index 509eaa17d..21369fd38 100644 --- a/test/js/map.test.js +++ b/test/js/map.test.js @@ -34,39 +34,35 @@ test('Map', function(t) { t.end(); }); - t.test('should require a FileSource options object as first parameter', function(t) { + t.test('should require an options object as first parameter', function(t) { t.throws(function() { new mbgl.Map(); - }, /Requires FileSource options as first argument/); + }, /Requires an options object as first argument/); t.throws(function() { new mbgl.Map('options'); - }, /Requires FileSource options as first argument/); + }, /Requires an options object as first argument/); t.end(); }); - t.test('should require the FileSource options object to have request and cancel methods', function(t) { + t.test('should require then options object to have request and cancel methods', function(t) { var options = {}; t.throws(function() { new mbgl.Map(options); - }, /FileSource options must have a request member function/); + }, /Options object must have a 'request' method/); options.request = 'test'; t.throws(function() { new mbgl.Map(options); - }, /FileSource options must have a request member function/); + }, /Options object must have a 'request' method/); options.request = function() {}; - t.throws(function() { - new mbgl.Map(options); - }, /FileSource options must have a cancel member function/); - options.cancel = 'test'; t.throws(function() { new mbgl.Map(options); - }, /FileSource options must have a cancel member function/); + }, /Options object 'cancel' property must be a function/); options.cancel = function() {}; t.doesNotThrow(function() {