Skip to content

Commit

Permalink
feat(cache): support for in-memory cache
Browse files Browse the repository at this point in the history
  • Loading branch information
thetutlage committed Mar 21, 2017
1 parent f1bf6e0 commit 7e7b61b
Show file tree
Hide file tree
Showing 7 changed files with 145 additions and 122 deletions.
58 changes: 58 additions & 0 deletions src/Cache/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
'use strict'

/*
* edge
*
* (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.
*/

class Cache {
constructor () {
this._items = {}
}

/**
* Add key value pair to the memory cache
*
* @method add
*
* @param {String} location
* @param {String} output
*
* @return {void}
*/
add (location, output) {
this._items[location] = output
}

/**
* Get value back from the cache
*
* @method get
*
* @param {String} location
*
* @return {String}
*/
get (location) {
return this._items[location]
}

/**
* Remove item from the cache
*
* @method remove
*
* @param {String} location
*
* @return {void}
*/
remove (location) {
delete this._items[location]
}
}

module.exports = new Cache()
13 changes: 0 additions & 13 deletions src/Edge/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,19 +57,6 @@ class Edge {
return new Template(this._tags, this._globals, this._loader)
}

/**
* The directory to be used for reading compiled templates
*
* @method compiledDir
*
* @param {String} compiledDir
*
* @return {String}
*/
compiledDir (compiledDir) {
this._loader.compiledDir = compiledDir
}

/**
* Registers a new tag. A tag must have following
* attributes.
Expand Down
57 changes: 3 additions & 54 deletions src/Loader/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,9 @@ const CE = require('../Exceptions')
* @class Loader
*/
class Loader {
constructor (viewsPath, presentersPath, compiledDir) {
constructor (viewsPath, presentersPath) {
this._viewsPath = viewsPath
this._presentersPath = presentersPath
this._compiledDir = compiledDir
}

/**
Expand Down Expand Up @@ -67,30 +66,6 @@ class Loader {
this._presentersPath = presentersPath
}

/**
* The path from where the load the compiled
* views.
*
* @attribute compiledDir
*
* @return {String}
*/
get compiledDir () {
return this._compiledDir
}

/**
* Set the path to the compiled directory
* for reading pre-compiled views
*
* @param {String} compiledDir
*
* @return {void}
*/
set compiledDir (compiledDir) {
this._compiledDir = compiledDir
}

/**
* Normalizes the view name
*
Expand All @@ -110,8 +85,8 @@ class Loader {
*
* @private
*/
_normalizeViewName (view, extension = 'edge') {
return `${view.replace(/\.edge$/, '').replace(/\.(\w+|\d+)/, '/$1')}.${extension}`
normalizeViewName (view, extension = 'edge') {
return `${view.replace(/\.edge$/, '').replace(/\.(\w+|\d+)/g, '/$1').replace(/\/{3}/, '.')}.${extension}`
}

/**
Expand Down Expand Up @@ -149,8 +124,6 @@ class Loader {
* @throws {RunTimeException} If unable to load the view
*/
load (view) {
view = this._normalizeViewName(view)

try {
return fs.readFileSync(this.getViewPath(view), 'utf-8')
} catch (error) {
Expand Down Expand Up @@ -188,30 +161,6 @@ class Loader {
throw error
}
}

/**
* Load and return pre compiled template. It will
* return null when unable to load the precompiled
* view.
*
* @method loadPreCompiled
*
* @param {String} view
*
* @return {Function|Null}
*/
loadPreCompiled (view) {
view = this._normalizeViewName(view, 'js')
if (!this.compiledDir) {
return null
}

try {
return require(path.join(this.compiledDir, view))
} catch (error) {
return null
}
}
}

module.exports = Loader
23 changes: 20 additions & 3 deletions src/Template/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@
*/

const _ = require('lodash')
const debug = require('debug')('edge:template')
const TemplateCompiler = require('./Compiler')
const TemplateRunner = require('./Runner')
const Context = require('../Context')
const BasePresenter = require('../Presenter')
const cache = require('../Cache')

/**
* Template class is used to compile and render the
Expand Down Expand Up @@ -161,9 +163,24 @@ class Template {
* @return {String}
*/
compile (view, asFunction = false) {
const normalizedView = this._loader.normalizeViewName(view)
const preCompiledView = cache.get(normalizedView)

/**
* Return the precompiled view from the cache if
* it exists.
*/
if (preCompiledView) {
debug('resolving view %s from cache', normalizedView)
return preCompiledView
}

const compiler = new TemplateCompiler(this._tags, this._loader, asFunction)
try {
return compiler.compile(view)
const compiledView = compiler.compile(normalizedView)
cache.add(normalizedView, compiledView)
debug('adding view %s to cache', normalizedView)
return compiledView
} catch (error) {
throw (this._prepareStack(error))
}
Expand Down Expand Up @@ -200,7 +217,7 @@ class Template {
*/
render (view, data = {}) {
this.sourceView(view)
const compiledTemplate = this._loader.loadPreCompiled(view) || this.compile(view, true)
const compiledTemplate = this.compile(view, true)
this._makeContext(data)
return new TemplateRunner(compiledTemplate, this).run()
}
Expand Down Expand Up @@ -232,7 +249,7 @@ class Template {
* @return {String}
*/
runTimeRender (view) {
const compiledTemplate = this._loader.loadPreCompiled(view) || this.compile(view, true)
const compiledTemplate = this.compile(view, true)
const template = new TemplateRunner(compiledTemplate, this).run()
return template
}
Expand Down
22 changes: 0 additions & 22 deletions test/unit/edge.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -253,28 +253,6 @@ test.group('Edge', (group) => {
assert.equal(output.trim(), '<p> Hello virk </p>')
})

test('define compiled dir', (assert) => {
const edge = new Edge()
const compiledDir = path.join(__dirname, '../../test-helpers/views/compiled')
edge.compiledDir(compiledDir)
assert.equal(compiledDir, edge._loader.compiledDir)
})

test('load a file from the precompiled template over compiling it from source', (assert) => {
const edge = new Edge()
const compiledDir = path.join(__dirname, '../../test-helpers/views/compiled')
edge.compiledDir(compiledDir)
edge._loader.load = function () {
throw new Error('Not expecting a call to load')
}
const output = edge.render('loopUsers', {
users: [{username: 'virk'}, {username: 'nikk'}]
})

assert.equal(output.trim(), `virk
nikk`)
})

test('should have access to inbuilt globals', (assert) => {
const edge = new Edge()
const statement = `{{ size('foo') }}`
Expand Down
71 changes: 42 additions & 29 deletions test/unit/loader.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,48 +50,83 @@ test.group('Loader', () => {
test('throw exception when views path has not been registered', (assert) => {
const loader = new Loader()
const output = () => loader.load('foo')
assert.throw(output, 'E_MISSING_VIEW: Cannot render foo.edge. Make sure to register the views path')
assert.throw(output, 'E_MISSING_VIEW: Cannot render foo. Make sure to register the views path')
})

test('throw exception if unable to load view', (assert) => {
const viewsPath = path.join(__dirname, './')
const loader = new Loader(viewsPath)
const output = () => loader.load('foo')
assert.throw(output, `E_MISSING_VIEW: Cannot render foo.edge. Make sure the file exists at ${viewsPath} location`)
assert.throw(output, `E_MISSING_VIEW: Cannot render foo. Make sure the file exists at ${viewsPath} location`)
})

test('load and return the view', (assert) => {
const viewsPath = path.join(__dirname, '../../test-helpers/views')
const loader = new Loader(viewsPath)
const output = loader.load('welcome')
const output = loader.load(loader.normalizeViewName('welcome'))
assert.equal(output.trim(), '{{ username }}')
})

test('normalize view name', (assert) => {
const viewsPath = path.join(__dirname, '../../test-helpers/views')
const loader = new Loader(viewsPath)
const output = loader.normalizeViewName('welcome')
assert.equal(output, 'welcome.edge')
})

test('normalize view name when .edge extension is defined', (assert) => {
const viewsPath = path.join(__dirname, '../../test-helpers/views')
const loader = new Loader(viewsPath)
const output = loader.normalizeViewName('welcome.edge')
assert.equal(output, 'welcome.edge')
})

test('normalize nested view name', (assert) => {
const viewsPath = path.join(__dirname, '../../test-helpers/views')
const loader = new Loader(viewsPath)
const output = loader.normalizeViewName('layouts.users.list.edge')
assert.equal(output, 'layouts/users/list.edge')
})

test('normalize pre formatted view name', (assert) => {
const viewsPath = path.join(__dirname, '../../test-helpers/views')
const loader = new Loader(viewsPath)
const output = loader.normalizeViewName('layouts/users/list.edge')
assert.equal(output, 'layouts/users/list.edge')
})

test('escape .', (assert) => {
const viewsPath = path.join(__dirname, '../../test-helpers/views')
const loader = new Loader(viewsPath)
const output = loader.normalizeViewName('layouts.users//.list.edge')
assert.equal(output, 'layouts/users.list.edge')
})

test('make absolute path to view', (assert) => {
const viewsPath = path.join(__dirname, '../../test-helpers/views')
const loader = new Loader(viewsPath)
const output = loader.getViewPath(loader._normalizeViewName('welcome'))
const output = loader.getViewPath(loader.normalizeViewName('welcome'))
assert.equal(output, path.join(viewsPath, 'welcome.edge'))
})

test('make absolute path to view when name has .edge extension', (assert) => {
const viewsPath = path.join(__dirname, '../test-helpers/views')
const loader = new Loader(viewsPath)
const output = loader.getViewPath(loader._normalizeViewName('welcome.edge'))
const output = loader.getViewPath(loader.normalizeViewName('welcome.edge'))
assert.equal(output, path.join(viewsPath, 'welcome.edge'))
})

test('make absolute path to view when name is seperated with (.)', (assert) => {
const viewsPath = path.join(__dirname, '../test-helpers/views')
const loader = new Loader(viewsPath)
const output = loader.getViewPath(loader._normalizeViewName('partials.users'))
const output = loader.getViewPath(loader.normalizeViewName('partials.users'))
assert.equal(output, path.join(viewsPath, 'partials/users.edge'))
})

test('make absolute path to view when name is seperated with (.) and has edge extension', (assert) => {
const viewsPath = path.join(__dirname, '../test-helpers/views')
const loader = new Loader(viewsPath)
const output = loader.getViewPath(loader._normalizeViewName('partials.users.edge'))
const output = loader.getViewPath(loader.normalizeViewName('partials.users.edge'))
assert.equal(output, path.join(viewsPath, 'partials/users.edge'))
})

Expand All @@ -114,26 +149,4 @@ test.group('Loader', () => {
const output = () => loader.loadPresenter('Foo')
assert.throw(output, `E_MISSING_PRESENTER: Cannot load Foo Presenter. Make sure the file exists at ${presentersPath} location`)
})

test('load a pre-compiled template', (assert) => {
const compiledDir = path.join(__dirname, '../../test-helpers/views/compiled')
const loader = new Loader()
loader.compiledDir = compiledDir
const template = loader.loadPreCompiled('loopUsers')
assert.isFunction(template)
})

test('return null when unable to load a pre-compiled template', (assert) => {
const compiledDir = path.join(__dirname, '../../test-helpers/views/compiled')
const loader = new Loader()
loader.compiledDir = compiledDir
const template = loader.loadPreCompiled('foo')
assert.isNull(template)
})

test('return null when compiled dir is not registered', (assert) => {
const loader = new Loader()
const template = loader.loadPreCompiled('foo')
assert.isNull(template)
})
})
Loading

0 comments on commit 7e7b61b

Please sign in to comment.