Skip to content

Commit

Permalink
Added basePath argument to DataPackage, Resource (#26)
Browse files Browse the repository at this point in the history
* Add basepath

* Add basePath tests

* Export Datapackage class
  • Loading branch information
dumyan authored and roll committed Dec 22, 2016
1 parent 8f33faf commit b8cb28c
Show file tree
Hide file tree
Showing 6 changed files with 104 additions and 13 deletions.
4 changes: 4 additions & 0 deletions data/dp2-tabular/data2.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
year,title,director
1964,Dr. Strangelove or: How I Learned to Stop Worrying and Love the Bomb,Stanley Kubrick
2004,"Spring, Summer, Fall, Winter... and Spring",Kim Ki-duk
2006,Inland Empire,David Lynch
30 changes: 26 additions & 4 deletions src/datapackage.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import _ from 'lodash'
import path from 'path'

import Resource from './resource'
import Profiles from './profiles'
Expand All @@ -13,16 +14,19 @@ class DataPackage {
* @param {String} [profile='base'] - Profile to validate against
* @param {Boolean} [raiseInvalid=true] - Throw errors if validation fails
* @param {Boolean} [remoteProfiles=false] - Use remote profiles
* @param {String|path} [basePath=null] - Base path for the resources. If the provided
* descriptor is a local path to a file, the default value is the dirname of the path.
* @return {Promise} - Resolves in class instance or rejects with errors
*/
constructor(descriptor, profile = 'base', raiseInvalid = true
, remoteProfiles = false) {
, remoteProfiles = false, basePath) {
const self = this

return new Promise((resolve, reject) => {
self._profile = profile
self._raiseInvalid = raiseInvalid
self._remoteProfiles = remoteProfiles
self._basePath = basePath || self._getBasePath(descriptor)

new Profiles(self._remoteProfiles).then(profilesInstance => {
self._Profiles = profilesInstance
Expand Down Expand Up @@ -113,7 +117,7 @@ class DataPackage {
*/
addResource(descriptor) {
if (_.isObject(descriptor) && !_.isFunction(descriptor)) {
const newResource = new Resource(descriptor)
const newResource = new Resource(descriptor, this._basePath)
, resourceFound = _.find(this.resources
, resource => _.isEqual(resource, newResource))

Expand All @@ -123,7 +127,7 @@ class DataPackage {
const valid = this._validateDescriptor(newDescriptor, this._profile)
if (this._shouldRaise(valid)) throw new Array(this._errors)
this._descriptor = newDescriptor
this._resources.push(new Resource(descriptor))
this._resources.push(new Resource(descriptor, this._basePath))

return this.valid
}
Expand Down Expand Up @@ -203,7 +207,7 @@ class DataPackage {
const resources = []

_.forEach(descriptor.resources, resource => {
resources.push(new Resource(resource))
resources.push(new Resource(resource, this._basePath))
})

return resources
Expand All @@ -220,6 +224,24 @@ class DataPackage {
_shouldRaise(valid) {
return !valid && this._raiseInvalid
}

/**
* Returns the basepath from the path of the current descriptor if it is a
* local path.
*
* @param {String} descriptor
* @return {String|null}
* @private
*/
_getBasePath(descriptor) {
if (typeof descriptor === 'string') {
if (!Utils.isRemoteURL(descriptor)) {
return path.dirname(descriptor)
}
}

return null
}
}

export default DataPackage
Expand Down
1 change: 1 addition & 0 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import Datapackage from './datapackage'
import Resource from './resource'
import Profiles from './profiles'
import validate from './validate'
Expand Down
27 changes: 18 additions & 9 deletions src/resource.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@

import url from 'url'
import jts from 'jsontableschema'
import _ from 'lodash'
import path from 'path'

/**
* Base class for all Data Package's resource types.
*
* @returns {Resource}
*/
/* Base class for all Data Package's resource types. */
export default class Resource {

constructor(descriptor) {
/**
* Create a Resource instance.
*
* @param {Object} descriptor
* @param {String} [basePath=null]
*/
constructor(descriptor, basePath = null) {
this._descriptor = descriptor
this._basePath = basePath
this.REMOTE_PROTOCOLS = ['http:', 'https:', 'ftp:', 'ftps:']
}

Expand Down Expand Up @@ -56,11 +58,18 @@ export default class Resource {

/**
* Returns the path where data is located or
* if the data is inline it returns the actual data
* if the data is inline it returns the actual data.
* If the source is a path, the basepath is prepended
* if provided on Resource initialization.
*
* @returns {String|Array|Object}
*/
get source() {
if (this._sourceKey === 'path' && this._basePath) {
const resourcePath = this.descriptor[this._sourceKey]
return path.normalize(`${this._basePath}/${resourcePath}`)
}

return this.descriptor[this._sourceKey]
}

Expand Down
37 changes: 37 additions & 0 deletions test/datapackage.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'babel-polyfill'
import fs from 'fs'
import { assert } from 'chai'
import _ from 'lodash'
import parse from 'csv-parse/lib/sync'

import Datapackage from '../src/datapackage'

Expand Down Expand Up @@ -142,5 +143,41 @@ describe('Datapackage', () => {
assert(validation === false, 'Package not marked as invalid')
assert(datapackage.errors.length > 0, 'Validation errors not added')
})

it('provides the dirname of the descriptor as basePath to the Resource instances', async () => {
const datapackage = await new Datapackage('data/dp2-tabular/datapackage.json', 'tabular')
, newResource = {
"name": "books",
"format": "csv",
"path": "data2.csv",
"schema": {
"fields": [
{
"name": "year",
"type": "integer"
},
{
"name": "title",
"type": "string"
},
{
"name": "director",
"type": "string"
}
]
}
}
, expectedText = fs.readFileSync('data/dp2-tabular/data2.csv', 'utf8')
, expectedData = parse(expectedText, { auto_parse: true })

expectedData.shift()
const validation = datapackage.addResource(newResource)
, table = await datapackage.resources[1].table
, data = await table.read()

assert(validation === true, 'Added resource failed to validate')
assert(datapackage.resources.length === 2, 'New resource not added to datapackge')
assert(_.isEqual(data, expectedData), 'Wrong data loaded')
})
})
})
18 changes: 18 additions & 0 deletions test/resource.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { assert } from 'chai'
import jts from 'jsontableschema'
import Resource from '../src/resource'
import _ from 'lodash'
import path from 'path'

import dp1 from '../data/dp1/datapackage.json'

Expand Down Expand Up @@ -91,6 +92,23 @@ describe('Resource', () => {
assert(table === null, 'Returned value not null')
})

describe('_basePath', () => {
it('accepts a basePath', () => {
const basePath = 'data/dp1'
, resource = new Resource(dp1.resources[0], basePath)
, resourceBasePath = resource.source

assert(path.dirname(resourceBasePath) === path.normalize(basePath), 'Incorrect base path')
})

it('_basePath is `null` if basePath argument is not provided', () => {
const resource = new Resource({})
, source = resource._basePath

assert(source === null, 'basePath not `null`')
})
})

describe('Tests with dp1 from data', () => {
let dpResources = []

Expand Down

0 comments on commit b8cb28c

Please sign in to comment.