Skip to content

Commit

Permalink
refactor(proxy): cleanup proxy handler and base relation
Browse files Browse the repository at this point in the history
  • Loading branch information
thetutlage committed Jun 26, 2017
1 parent acb0b66 commit 78251ad
Show file tree
Hide file tree
Showing 12 changed files with 415 additions and 170 deletions.
72 changes: 72 additions & 0 deletions lib/proxyGet.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
* adonis-lucid
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

module.exports = function proxyGet (proxyOn, bindFn = false, customCallback = null) {
return function (target, name) {
/**
* If value exists on the target object, return it
*/
if (typeof (target[name]) !== 'undefined') {
return target[name]
}

/**
* If there is a custom callback to resolve values, call
* the callback and return the value if it's not
* undefined
*/
if (typeof (customCallback) === 'function') {
const result = customCallback.bind(this)(target, name)
if (typeof (result) !== 'undefined') {
return result
}
}

/**
* If there is no fallback then return undefined
*/
if (!proxyOn) {
return undefined
}

/**
* Get access to source by looking up value on target. Also if
* value is a function, the fn is executed and return value
* is used as source.
*/
const source = typeof (target[proxyOn]) === 'function' ? target[proxyOn]() : target[proxyOn]

if (typeof (source[name]) === 'function') {
/**
* If bindFn is set to true, we disconnect the
* proxy trap and instead return the instance
* of source[name]
*/
if (bindFn) {
return source[name].bind(source)
}

/**
* Otherwise wrap it inside a closure and return the value.
* The reason we need to wrap it, so that the `proxyOn`
* method should have correct reference to `this`.
*/
return function (...args) {
source[name](...args)
return this
}
}

/**
* Finally fallback to `proxyOn` and return the value of property
* and if value does not exists, `undefined` is returned
*/
return source[name]
}
}
19 changes: 4 additions & 15 deletions src/Database/Manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,7 @@ require('./MonkeyPatch')

const Database = require('.')
const CE = require('../Exceptions')

const proxyHandler = {
get (target, name) {
if (typeof (target[name]) !== 'undefined') {
return target[name]
}

const db = target.connection()
if (typeof (db[name]) === 'function') {
return db[name].bind(db)
}
return db[name]
}
}
const proxyGet = require('../../lib/proxyGet')

/**
* DatabaseManager is a layer on top of @ref('Database') class. It
Expand All @@ -43,7 +30,9 @@ class DatabaseManager {
constructor (Config) {
this.Config = Config
this._connectionPools = {}
return new Proxy(this, proxyHandler)
return new Proxy(this, {
get: proxyGet('connection', true)
})
}

/**
Expand Down
2 changes: 0 additions & 2 deletions src/Database/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
'use strict'

/*
* adonis-lucid
*
Expand Down
18 changes: 9 additions & 9 deletions src/Lucid/EagerLoad/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,16 +32,16 @@ class EagerLoad {
*
* @method _applyRuntimeConstraints
*
* @param {Object} options.query
* @param {Object} options.relatedQuery
* @param {Function} callback
*
* @return {void}
*
* @private
*/
_applyRuntimeConstraints ({ query }, callback) {
_applyRuntimeConstraints ({ relatedQuery }, callback) {
if (typeof (callback) === 'function') {
callback(query)
callback(relatedQuery)
}
}

Expand All @@ -52,17 +52,17 @@ class EagerLoad {
*
* @method _chainNested
*
* @param {Object} options.query
* @param {Object} options.relatedQuery
* @param {Object} nested
*
* @return {void}
*
* @private
*/
_chainNested ({ query }, nested) {
_chainNested ({ relatedQuery }, nested) {
if (nested) {
const name = _.first(_.keys(nested))
query.with(name, nested[name])
relatedQuery.with(name, nested[name])
}
}

Expand All @@ -83,7 +83,7 @@ class EagerLoad {
*/
async _eagerLoad (rows, relationInstance) {
const relatedInstances = await relationInstance
.query
.relatedQuery
.whereIn(relationInstance.foreignKey, relationInstance.mapValues(rows))
.fetch()

Expand All @@ -100,11 +100,11 @@ class EagerLoad {
* The return value is a key/value pair of relations and
* their resolved values.
*
* @method loadOne
* @method loadForOne
*
* @return {Object}
*/
async loadOne (modelInstance) {
async loadForOne (modelInstance) {
const relationsKeys = _.keys(this._relations)

/**
Expand Down
9 changes: 7 additions & 2 deletions src/Lucid/Model/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const Hooks = require('../Hooks')
const QueryBuilder = require('../QueryBuilder')
const CollectionSerializer = require('../Serializers/Collection')
const HasOne = require('../Relations/HasOne')
const HasMany = require('../Relations/HasMany')
const EagerLoad = require('../EagerLoad')

const CE = require('../../Exceptions')
Expand Down Expand Up @@ -950,7 +951,7 @@ class Model {
*/
async load (relation, callback) {
const eagerLoad = new EagerLoad({ [relation]: callback })
const result = await eagerLoad.loadOne(this)
const result = await eagerLoad.loadForOne(this)
_.each(result, (values, name) => this.setRelated(name, values))
}

Expand All @@ -966,7 +967,7 @@ class Model {
*/
async loadMany (eagerLoadMap) {
const eagerLoad = new EagerLoad(eagerLoadMap)
const result = await eagerLoad.loadOne(this)
const result = await eagerLoad.loadForOne(this)
_.each(result, (values, name) => this.setRelated(name, values))
}

Expand All @@ -984,6 +985,10 @@ class Model {
hasOne (relatedModel, primaryKey = this.constructor.primaryKey, foreignKey = this.constructor.foreignKey) {
return new HasOne(this, relatedModel, primaryKey, foreignKey)
}

hasMany (relatedModel, primaryKey = this.constructor.primaryKey, foreignKey = this.constructor.foreignKey) {
return new HasMany(this, relatedModel, primaryKey, foreignKey)
}
}

module.exports = Model
30 changes: 22 additions & 8 deletions src/Lucid/Model/proxyHandler.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,36 @@
* file that was distributed with this source code.
*/

const proxyGet = require('../../../lib/proxyGet')

const proxyHandler = exports = module.exports = {}

/**
* Setter for proxy object
*
* @method set
*
* @param {Object} target
* @param {String} name
* @param {Mixed} value
*/
proxyHandler.set = function (target, name, value) {
if (target.__setters__.indexOf(name) > -1) {
return target[name] = value
}
return target.set(name, value)
}

proxyHandler.get = function (target, name) {
if (typeof (target[name]) !== 'undefined') {
return target[name]
}

/**
* Getter for proxy handler
*
* @method
*
* @param {Object} target
* @param {String} name
*/
proxyHandler.get = proxyGet('$attributes', false, function (target, name) {
if (typeof (target.$sideLoaded[name]) !== 'undefined') {
return target.$sideLoaded[name]
}

return target.$attributes[name]
}
})
69 changes: 58 additions & 11 deletions src/Lucid/QueryBuilder/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,38 +15,63 @@ const util = require('../../../lib/util')
const EagerLoad = require('../EagerLoad')
const RelationsParser = require('../Relations/Parser')
const CE = require('../../Exceptions')
const proxyGet = require('../../../lib/proxyGet')

const proxyHandler = {
get (target, name) {
if (typeof (target[name]) !== 'undefined') {
return target[name]
}
get: proxyGet('query', false, function (target, name) {
const queryScope = util.makeScopeName(name)

/**
* If property accessed is a local query scope
* then call it
* if value is a local query scope and a function, please
* execute it
*/
const queryScope = util.makeScopeName(name)
if (typeof (target.model[queryScope]) === 'function') {
return function (...args) {
target.model[queryScope](this, ...args)
return this
}
}

return target.query[name]
}
})
}

class QueryBuilder {
constructor (model, connection) {
this.model = model
this.db = ioc.use('Adonis/Src/Database').connection(connection)
const table = this.model.prefix ? `${this.model.prefix}${this.model.table}` : this.model.table

/**
* Reference to database provider
*/
this.db = ioc.use('Adonis/Src/Database').connection(connection)

/**
* Reference to query builder with pre selected table
*/
this.query = this.db.table(table).on('query', this.model._executeListeners.bind(this.model))

/**
* Scopes to be ignored at runtime
*
* @type {Array}
*
* @private
*/
this._ignoreScopes = []

/**
* Relations to be eagerloaded
*
* @type {Object}
*/
this._eagerLoads = {}

/**
* The sideloaded data for this query
*
* @type {Array}
*/
this._sideLoaded = []

return new Proxy(this, proxyHandler)
}

Expand Down Expand Up @@ -642,6 +667,28 @@ class QueryBuilder {

return this
}

/**
* Returns the sql representation of query
*
* @method toSQL
*
* @return {Object}
*/
toSQL () {
return this.query.toSQL()
}

/**
* Returns string representation of query
*
* @method toString
*
* @return {String}
*/
toString () {
return this.query.toString()
}
}

module.exports = QueryBuilder
Loading

0 comments on commit 78251ad

Please sign in to comment.