From bdd6610208e9fe5971e6aed4378598092f2b9b5a Mon Sep 17 00:00:00 2001 From: Alan Pearce Date: Sat, 24 Jun 2017 16:34:47 +0200 Subject: refactor: separate app, responders --- src/app.js | 65 ++++++++++++++++++++++++++++ src/index.js | 124 ++--------------------------------------------------- src/responders.js | 76 ++++++++++++++++++++++++++++++++ test/app.test.js | 34 +++++++++++++++ test/index.test.js | 34 --------------- 5 files changed, 179 insertions(+), 154 deletions(-) create mode 100644 src/app.js create mode 100644 src/responders.js create mode 100644 test/app.test.js delete mode 100644 test/index.test.js diff --git a/src/app.js b/src/app.js new file mode 100644 index 0000000..84722fc --- /dev/null +++ b/src/app.js @@ -0,0 +1,65 @@ +'use strict' + +const Koa = require('koa') +const app = new Koa() + +const streamify = require('stream-array') + +const send = require('koa-send') +const responders = require('./responders.js') + +const config = require('./modules/config.js') + +const Router = require('koa-router') +const router = new Router() + +app.context.getURL = router.url.bind(router) + +const Posts = require('./modules/posts.js') +const posts = Posts.getFolder(config.posts.folder) + +function toArrayStream (iterator) { + return streamify(Array.from(iterator.entries())) +} + +const postsStream = toArrayStream(posts) +router.get('home', '/', async function (ctx, next) { + responders.home(ctx, config, postsStream) +}) + +router.get('post', '/post/:filename', async function (ctx, next) { + ctx.assert(posts.has(ctx.params.filename), 404, 'Post not found') + const post = posts.get(ctx.params.filename) + post.body = Posts.render(post) + + responders.post(ctx, config, post) +}) + +const taxonomies = Posts.taxonomise(config.taxonomies, posts) +for (let [term, items] of taxonomies) { + router.get(`taxon-${term}`, `/${term}/:value`, async function (ctx, next) { + const value = ctx.params.value + ctx.assert( + items.has(ctx.params.value), + 404, + `Could not find ${term} ${value}` + ) + + const taxonItems = toArrayStream(items.get(value)) + + responders.taxon(ctx, config, taxonItems) + }) +} + +app.use(router.routes()).use(router.allowedMethods()) + +const prefix = /^\/static\// +app.use(async function (ctx) { + if (prefix.test(ctx.path)) { + await send(ctx, ctx.path.replace(prefix, ''), { + root: './static' + }) + } +}) + +module.exports = app diff --git a/src/index.js b/src/index.js index a98a32d..a3e8dea 100644 --- a/src/index.js +++ b/src/index.js @@ -1,129 +1,13 @@ 'use strict' -const Koa = require('koa') -const app = new Koa() - -const fs = require('fs') -const streamify = require('stream-array') - -const send = require('koa-send') - const config = require('./modules/config.js') const PORT = process.env.PORT || config.server.port -const Router = require('koa-router') -const router = new Router() - -const rheo = require('rheo') - -const Posts = require('./modules/posts.js') -const posts = Posts.getFolder(config.posts.folder) - -const templateReader = template => () => - fs.createReadStream(`${__dirname}/templates/${template}.html`) -const templates = { - layout: templateReader('layout'), - home: templateReader('home'), - post: templateReader('post'), - taxon: templateReader('taxon') -} - -function setTitle (pageTitle) { - return rheo.template(function (s) { - return s - .inner('title', rheo((pageTitle ? ' · ' : '') + config.site.title)) - .inner('body header h1', rheo(config.site.title)) - }) -} - -function renderPostListItem (template, [, post]) { - return template - .attribute('a', 'href', () => router.url('post', post.basename)) - .inner('a', () => rheo(post.data.get('title'))) -} - -function toArrayStream (iterator) { - return streamify(Array.from(iterator.entries())) -} - -function showPage (name) { - return function (els) { - return rheo(templates[name]()) - } -} - -const postsStream = toArrayStream(posts) -router.get('home', '/', async function (ctx, next) { - ctx.set('Content-Type', 'text/html') - ctx.body = templates - .layout() - .pipe(rheo()) - .outer('main', showPage('home')) - .inner('.posts', function (postsTemplate) { - return postsStream.pipe(postsTemplate.map(renderPostListItem)) - }) - .pipe(setTitle()) - .render() -}) - -router.get('post', '/post/:filename', async function (ctx, next) { - ctx.assert(posts.has(ctx.params.filename), 404, 'Post not found') - const post = posts.get(ctx.params.filename) - post.body = Posts.render(post) - - ctx.set('Content-Type', 'text/html') - ctx.body = templates - .layout() - .pipe(rheo()) - .inner('main', showPage('post')) - .inner('article h1', rheo(post.data.get('title'))) - .inner('article main', rheo(post.body)) - .pipe(setTitle()) - .render() -}) - -const taxonomies = Posts.taxonomise(config.taxonomies, posts) -for (let [term, items] of taxonomies) { - router.get(`taxon-${term}`, `/${term}/:value`, async function (ctx, next) { - const value = ctx.params.value - ctx.assert( - items.has(ctx.params.value), - 404, - `Could not find ${term} ${value}` - ) - - ctx.set('Content-Type', 'text/html') - ctx.body = templates - .layout() - .pipe(rheo()) - .inner('main', showPage('taxon')) - .inner('h1', rheo(config.site.title)) - .inner('.posts', function (postsTemplate) { - return toArrayStream(items.get(value)).pipe( - postsTemplate.map(renderPostListItem) - ) - }) - .pipe(setTitle()) - .render() - }) -} - -app.use(router.routes()).use(router.allowedMethods()) - -const prefix = /^\/static\// -app.use(async function (ctx) { - if (prefix.test(ctx.path)) { - await send(ctx, ctx.path.replace(prefix, ''), { - root: './static' - }) - } -}) +const app = require('./app.js') module.exports = app -if (require.main === module) { - app.listen(PORT, () => { - console.log(`App listening on port ${PORT}`) - }) -} +app.listen(PORT, () => { + console.log(`App listening on port ${PORT}`) +}) diff --git a/src/responders.js b/src/responders.js new file mode 100644 index 0000000..1a9e8ce --- /dev/null +++ b/src/responders.js @@ -0,0 +1,76 @@ +'use strict' + +const fs = require('fs') +const rheo = require('rheo') + +const templateReader = template => () => + fs.createReadStream(`${__dirname}/templates/${template}.html`) +const templates = { + layout: templateReader('layout'), + home: templateReader('home'), + post: templateReader('post'), + taxon: templateReader('taxon') +} + +function setTitle (siteTitle, pageTitle) { + return rheo.template(function (s) { + return s + .inner('title', rheo((pageTitle ? ' · ' : '') + siteTitle)) + .inner('body header h1', rheo(siteTitle)) + }) +} + +function renderPostListItem (ctx) { + return function (template, [, post]) { + return template + .attribute('a', 'href', () => ctx.getURL('post', post.basename)) + .inner('a', () => rheo(post.data.get('title'))) + } +} + +function showPage (name) { + return function (els) { + return rheo(templates[name]()) + } +} + +module.exports = { + home (ctx, config, postsStream) { + ctx.set('Content-Type', 'text/html') + ctx.body = templates + .layout() + .pipe(rheo()) + .outer('main', showPage('home')) + .inner('.posts', function (postsTemplate) { + return postsStream.pipe(postsTemplate.map(renderPostListItem(ctx))) + }) + .pipe(setTitle(config.site.title)) + .render() + }, + + post (ctx, config, post) { + ctx.set('Content-Type', 'text/html') + ctx.body = templates + .layout() + .pipe(rheo()) + .inner('main', showPage('post')) + .inner('article h1', rheo(post.data.get('title'))) + .inner('article main', rheo(post.body)) + .pipe(setTitle(config.site.title, post.data.get('title'))) + .render() + }, + + taxon (ctx, config, taxonItems) { + ctx.set('Content-Type', 'text/html') + ctx.body = templates + .layout() + .pipe(rheo()) + .inner('main', showPage('taxon')) + .inner('h1', rheo(config.site.title)) + .inner('.posts', function (postsTemplate) { + return taxonItems.pipe(postsTemplate.map(renderPostListItem(ctx))) + }) + .pipe(setTitle(config.site.title)) + .render() + } +} diff --git a/test/app.test.js b/test/app.test.js new file mode 100644 index 0000000..04596fa --- /dev/null +++ b/test/app.test.js @@ -0,0 +1,34 @@ +const test = require('ava') +const path = require('path') +const request = require('supertest') + +const config = require('../src/modules/config.js') +config.posts.folder = path.resolve(__dirname, './data/') + +const app = require('../src/app.js') + +test('homepage', t => { + return request(app.listen()) + .get('/') + .expect(200) + .expect(/Test Site<\/title>/) + .expect(/<h1>Test Site<\/h1>/) + .expect(/This is a test/) + .then(() => t.pass()) +}) + +test('post', t => { + return request(app.listen()) + .get('/post/testfile') + .expect(200) + .expect(/<h1>Lorem ipsum<\/h1>/) + .then(() => t.pass()) +}) + +test('tags', t => { + return request(app.listen()) + .get('/tag/a') + .expect(200) + .expect(/This is a test/) + .then(() => t.pass()) +}) diff --git a/test/index.test.js b/test/index.test.js deleted file mode 100644 index 769097f..0000000 --- a/test/index.test.js +++ /dev/null @@ -1,34 +0,0 @@ -const test = require('ava') -const path = require('path') -const request = require('supertest') - -const config = require('../src/modules/config.js') -config.posts.folder = path.resolve(__dirname, './data/') - -const app = require('../src/index.js') - -test('homepage', t => { - return request(app.listen()) - .get('/') - .expect(200) - .expect(/<title>Test Site<\/title>/) - .expect(/<h1>Test Site<\/h1>/) - .expect(/This is a test/) - .then(() => t.pass()) -}) - -test('post', t => { - return request(app.listen()) - .get('/post/testfile') - .expect(200) - .expect(/<h1>Lorem ipsum<\/h1>/) - .then(() => t.pass()) -}) - -test('tags', t => { - return request(app.listen()) - .get('/tag/a') - .expect(200) - .expect(/This is a test/) - .then(() => t.pass()) -}) -- cgit 1.4.1