Skip to content

Commit

Permalink
feat(namespace): auto resolve namespace (#33)
Browse files Browse the repository at this point in the history
  • Loading branch information
rhwilr authored Feb 25, 2019
1 parent 8a6ff04 commit 570eac6
Show file tree
Hide file tree
Showing 10 changed files with 156 additions and 54 deletions.
15 changes: 11 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,19 +168,26 @@ as covered in the next section, only work when an object is returned.

#### Using the Transformer

Once the Transformer class is defined, it can be passed to the resource as the
Once the transformer class is defined, it can be passed to the resource as the
second argument.

```js
const users = await User.all()

return transform.collection(users, 'App/Transformers/UserTransformer')
return transform.collection(users, 'UserTransformer')
```

*Note:* Passing the Transformer as the second argument will terminate the fluent
If the transformer was placed in the default location `App/Transformers`, you
can reference it by just passing the name of the transformer. If you placed the
transformer class somewhere else or use a different path for your transformers,
you may have to pass the full namespace or change the default namespace in the
config file. Lastly, you can also pass a reference to the transformer class
directly.

*Note:* Passing the transformer as the second argument will terminate the fluent
interface. If you want to chain more methods after the call to `collection` or
`item` you should only pass the first argument and then use the `transformWith`
method to define the transformer. See [Fluent Interface](#fluent-interface).
method to define the transformer. See [Fluent Interface](#fluent-interface)


### Including Data
Expand Down
16 changes: 11 additions & 5 deletions examples/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,21 @@ module.exports = {
parseRequest: false,

/*
* Nested includes will be resolved up to this limit
* any further nested resources are going to be ignored
* Nested includes will be resolved up to this limit any further nested
* resources are going to be ignored
*/
includeRecursionLimit: 10,

/*
* The serializer will be used to transform the data
* into it's final representation.
* The serializer will be used to transform the data into its final
* representation.
* Currently supported: 'plain', 'data'
*/
serializer: 'plain'
serializer: 'plain',

/*
* When a transformer is reffered to by its name only, Bumblebee will try to
* resolve the transformer using this namespace as prefix.
*/
namespace: 'App/Transformers'
}
19 changes: 12 additions & 7 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion providers/BumblebeeProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class BumblebeeProvider extends ServiceProvider {
}

/**
* Bind ally to the http context
* Bind transform to the http context
*
* @method boot
*
Expand Down
24 changes: 23 additions & 1 deletion src/Bumblebee/Scope.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'use strict'

const { ioc } = require('@adonisjs/fold')
const { trimEnd: _trimEnd } = require('lodash')

const TransformerAbstract = require('./TransformerAbstract')
const Resources = require('./Resources')
Expand Down Expand Up @@ -178,7 +179,7 @@ class Scope {
_getTransformerInstance (Transformer) {
// if the transformer is a string, use the IoC to fetch the instance.
if (typeof Transformer === 'string') {
Transformer = ioc.use(Transformer)
Transformer = this._resolveTransformer(Transformer)
}

// if the transformer is a class, create a new instance
Expand All @@ -200,6 +201,27 @@ class Scope {
throw new Error('A transformer must be a function or a class extending TransformerAbstract')
}

/**
* Resolves a transformer from the ioc container
*
* @param {*} Transformer
*/
_resolveTransformer (transformer) {
let prefix = ''

// if the provided transformer name does not start with the App namespace
// we assume we need to add the prefix to the name
if (!transformer.startsWith('App')) {
const Config = ioc.use('Adonis/Src/Config')
const namespace = _trimEnd(Config.get('bumblebee.namespace', 'App/Transformers'), '/')

prefix = `${namespace}/`
}

// try to load the transformer using the ioc container
return ioc.use(`${prefix}${transformer}`)
}

/**
* Checks if any variants are defined and calls the corresponding transform method
*
Expand Down
4 changes: 2 additions & 2 deletions src/Commands/MakeTransformer.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
* file that was distributed with this source code.
*/

const { upperFirst: _upperFirst, camelCase: _camelCase } = require('lodash')
const { Command } = require('@adonisjs/ace')
const { join } = require('path')
const _ = require('lodash')

class MakeTransformer extends Command {
/**
Expand Down Expand Up @@ -62,7 +62,7 @@ class MakeTransformer extends Command {
let directories = name.split('/')
let filename = directories[directories.length - 1]

return (_.upperFirst(_.camelCase(filename.replace('Transformer', ''))) + 'Transformer')
return (_upperFirst(_camelCase(filename.replace('Transformer', ''))) + 'Transformer')
}

/**
Expand Down
2 changes: 1 addition & 1 deletion test/context.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class IDTransformer extends TransformerAbstract {
}
}

test.group('Context', (group) => {
test.group('Context', group => {
group.before(async () => {
await setup()
})
Expand Down
91 changes: 91 additions & 0 deletions test/ioc.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
'use strict'

/**
* adonis-bumblebee
*
* (c) Ralph Huwiler <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

const test = require('japa')
const { ioc } = require('@adonisjs/fold')

const Bumblebee = require('../src/Bumblebee')
const TransformerAbstract = require('../src/Bumblebee/TransformerAbstract')
const setup = require('./setup')

class IDTransformer extends TransformerAbstract {
transform (model) {
return {
id: model.item_id
}
}
}

test.group('IoC', group => {
group.before(async () => {
await setup()

ioc.fake('App/Transformers/IDTransformer', () => IDTransformer)
ioc.fake('App/APITransformers/IDTransformer', () => IDTransformer)
})

test('a transformer can be resolved using its namespace', async (assert) => {
let data = { item_id: 3 }

let transformed = await Bumblebee.create()
.item(data)
.transformWith('App/Transformers/IDTransformer')
.toJSON()

assert.equal(transformed.id, 3)
})

test('a transformer can use a custom namespace if the fully qualified namespace is used', async (assert) => {
let data = { item_id: 3 }

let transformed = await Bumblebee.create()
.item(data)
.transformWith('App/APITransformers/IDTransformer')
.toJSON()

assert.equal(transformed.id, 3)
})

test('the default namespace is used if only the transformer name is defined', async (assert) => {
let data = { item_id: 3 }

let transformed = await Bumblebee.create()
.item(data)
.transformWith('IDTransformer')
.toJSON()

assert.equal(transformed.id, 3)
})

test('an exception is thrown when namespace doesn\'t exists', async (assert) => {
assert.plan(2)

let data = { item_id: 3 }

try {
await Bumblebee.create()
.item(data)
.transformWith('IDontExists')
.toJSON()
} catch (e) {
assert.equal(e.message, 'Cannot find module \'App/Transformers/IDontExists\'')
}

try {
await Bumblebee.create()
.item(data)
.transformWith('App/Transformers/IDontExists')
.toJSON()
} catch (e) {
assert.equal(e.message, 'Cannot find module \'App/Transformers/IDontExists\'')
}
})
})
8 changes: 4 additions & 4 deletions test/transformer-variants.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ test.group('Transformer Variants', () => {
})

test('a transformer can be defined using dot-notation', async (assert) => {
ioc.bind('App/Transformers/IDTransformer', () => new IDTransformer())
ioc.fake('App/Transformers/IDTransformer', () => IDTransformer)

let data = { item_id: 3 }

Expand All @@ -103,7 +103,7 @@ test.group('Transformer Variants', () => {
})

test('variants can be used in shorthand form', async (assert) => {
ioc.bind('App/Transformers/IDTransformer', () => new IDTransformer())
ioc.fake('App/Transformers/IDTransformer', () => IDTransformer)

let data = { item_id: 3 }

Expand All @@ -119,7 +119,7 @@ test.group('Transformer Variants', () => {
})

test('includes can use a variant', async (assert) => {
ioc.bind('App/Transformers/IDIncludeTransformer', () => new IDIncludeTransformer())
ioc.fake('App/Transformers/IDIncludeTransformer', () => IDIncludeTransformer)

let data = { item_id: 3 }

Expand All @@ -134,7 +134,7 @@ test.group('Transformer Variants', () => {
})

test('a transformer variant can reference the default transformer', async (assert) => {
ioc.bind('App/Transformers/IDRefTransformer', () => new IDRefTransformer())
ioc.fake('App/Transformers/IDRefTransformer', () => IDRefTransformer)

let data = { item_id: 3, name: 'Leta' }

Expand Down
29 changes: 0 additions & 29 deletions test/transformer.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
*/

const test = require('japa')
const { ioc } = require('@adonisjs/fold')

const Bumblebee = require('../src/Bumblebee')
const TransformerAbstract = require('../src/Bumblebee/TransformerAbstract')
Expand All @@ -30,34 +29,6 @@ class PrimitiveTransformer extends TransformerAbstract {
}

test.group('Transformer', () => {
test('a transformer can be resolved using its namespace', async (assert) => {
ioc.bind('App/Transformers/IDTransformer', () => IDTransformer)

let data = { item_id: 3 }

let transformed = await Bumblebee.create()
.item(data)
.transformWith('App/Transformers/IDTransformer')
.toJSON()

assert.equal(transformed.id, 3)
})

test('an exception is thrown when namespace doesn\'t exists', async (assert) => {
assert.plan(1)

let data = { item_id: 3 }

try {
await Bumblebee.create()
.item(data)
.transformWith('IDontExists')
.toJSON()
} catch (e) {
assert.equal(e.message, 'Cannot find module \'IDontExists\'')
}
})

test('a transformer maps item properties', async (assert) => {
let data = { item_id: 3 }

Expand Down

0 comments on commit 570eac6

Please sign in to comment.