function found (article, next) { if (!article) { res.status(404).json({ messages: ['Article not found'] }); return; } contra.concurrent([removal, unlinkLeft, unlinkRight], next); function removal (next) { article.remove(next); } function unlinkLeft (next) { if (!article.prev) { next(); return; } article.prev.next = article.next; article.prev.save(function saved (err) { next(err); }); } function unlinkRight (next) { if (!article.next) { next(); return; } article.next.prev = article.prev; article.next.save(function saved (err) { next(err); }); } }
], function(err, fb){ if(err) return t.end(err); λ.concurrent({ one_row: λ.curry(q, fb, [["a", "->", "?va"]]), no_join: λ.curry(q, fb, [["a", "->", "?va"], ["b", "->", "?vb"]]), da_join: λ.curry(q, fb, [["a", "->", "?va"], ["b", "->", "?vb"], ["?va", "->", "?vb"]]), da_join_reverse: λ.curry(q, fb, [["?va", "->", "?vb"], ["a", "->", "?va"], ["b", "->", "?vb"]]), da_join_mix: λ.curry(q, fb, [["a", "->", "?va"], ["?va", "->", "?vb"], ["b", "->", "?vb"]]) }, function(err, r){ if(err) return t.end(err); t.equal(r.one_row.length, 3, "should return everthing a points to"); t.equal(r.no_join.length, 9, "should return every combination of a and b pointers"); t.deepEqual(r.da_join, [{'?va': 'd', '?vb': 'g'}]); t.deepEqual(r.da_join_reverse, r.da_join, "q tuple order shouldn't matter"); t.deepEqual(r.da_join_mix, r.da_join, "q tuple order shouldn't matter"); t.end(); }); });
], function(err){ if(err) return t.end(err); var fb = transactor.connection.snap(); λ.concurrent({ should_be_a_var: λ.curry(q, fb, [["?id", "name", "?notavar"]]), bind_it: λ.curry(q, fb, [["?id", "name", "?name"]], [{"?name": "?notavar"}]), escape_it: λ.curry(q, fb, [["?id", "name", "\\?notavar"]]), bind_it2: λ.curry(q, fb, [["?id", "name", "?name"]], [{"?name": "\\?notavar"}]), not_actually_escaped: λ.curry(q, fb, [["?id", "name", "\\\\?notavar"]]), double_slash: λ.curry(q, fb, [["?id", "name", "\\\\\\"]]), double_slash_bind: λ.curry(q, fb, [["?id", "name", "?name"]], [{"?name": "\\\\"}]), not_a_throw_away: λ.curry(q, fb, [["?id", "name", "\\?_"]]), not_a_throw_away2: λ.curry(q, fb, [["?id", "name", "?name"]], [{"?name": "?_"}]), }, function(err, r){ t.deepEqual(_.sortBy(r.should_be_a_var, "?id"), [{"?id": "1", "?notavar": "?notavar"}, {"?id": "2", "?notavar": "notavar"}, {"?id": "3", "?notavar": "\\?notavar"}, {"?id": "4", "?notavar": "\\\\"}, {"?id": "5", "?notavar": "?_"}]); t.deepEqual(r.bind_it, [{"?id": "1", "?name": "?notavar"}]); t.deepEqual(r.escape_it, [{"?id": "1"}]); t.deepEqual(r.bind_it2, [{"?id": "3", "?name": "\\?notavar"}]); t.deepEqual(r.not_actually_escaped, [{"?id": "3"}]); t.deepEqual(r.double_slash, [{"?id": "4"}]); t.deepEqual(r.double_slash_bind, [{"?id": "4", "?name": "\\\\"}]); t.deepEqual(r.not_a_throw_away, [{"?id": "5"}]); t.deepEqual(r.not_a_throw_away2, [{"?id": "5", "?name": "?_"}]); t.end(err); }); });
function getModel (req, res, next) { contra.concurrent({ live: function (next) { settingService.getKey('PONYFOOWEEKLY_CRON', next); }, weeklies: function (next) { WeeklyIssue.find({}).sort([['publication', -1], ['updated', -1]]).exec(next); } }, respond); function respond (err, result) { if (err) { next(err); return; } var models = result.weeklies.map(weeklyService.toMetadata); var sorted = _.sortBy(models, sortByStatus); res.viewModel = { model: { title: 'Newsletter Review', meta: { canonical: '/author/weeklies' }, weeklies: sorted, live: result.live } }; next(); } }
next => contra.concurrent([ next => console.log(date + article.slug), next(), next => write('source/metadata.json', JSON.stringify({ id: article._id, title: article.titleMarkdown, slug: article.slug, tags: article.tags, publication: moment(article.publication).format('DD-MM-YYYY HH:mm'), status: article.status, social: { email: article.email, tweet: article.tweet, fb: article.fb, echojs: article.echojs, lobsters: article.lobsters, hn: article.hn } }, null, 2), next), next => dec('source/summary.markdown', article.summaryHtml, next), next => dec('source/teaser.markdown', article.teaserHtml, next), next => dec('source/introduction.markdown', article.introductionHtml, next), next => dec('source/body.markdown', article.bodyHtml, next), next => abs('summary.html', article.summaryHtml, next), next => abs('teaser.html', article.teaserHtml, next), next => abs('introduction.html', article.introductionHtml, next), next => abs('body.html', article.bodyHtml, next) ], next)
function updateArticle (dir, next) { const readFile = file => next => fs.readFile(path.join(dir, file), 'utf8', next); contra.concurrent({ metadata: readFile('metadata.json'), summary: readFile('summary.markdown'), teaser: readFile('teaser.markdown'), editorNote: readFile('editor-notes.markdown'), introduction: readFile('introduction.markdown'), body: readFile('body.markdown') }, mergeFiles); function mergeFiles (err, data) { if (err) { next(err); return; } const metadata = JSON.parse(data.metadata); const query = { _id: metadata.id, author: metadata.author }; Article.findOne(query).exec(found); function found (err, article) { if (err) { next(err); return; } if (!article) { next(new Error(`Article couldn't be found!`)); return; } const fromGit = { titleMarkdown: metadata.title, slug: metadata.slug, status: article.status, summary: data.summary, teaser: data.teaser, editorNote: data.editorNote, introduction: data.introduction, body: data.body, tags: metadata.tags, heroImage: metadata.heroImage || '' }; const sign = articleService.computeSignature(fromGit); if (sign === article.sign) { next(null); return; } article.titleMarkdown = fromGit.titleMarkdown; article.slug = fromGit.slug; article.summary = fromGit.summary; article.teaser = fromGit.teaser; article.editorNote = fromGit.editorNote; article.introduction = fromGit.introduction; article.body = fromGit.body; article.tags = fromGit.tags; article.heroImage = fromGit.heroImage; article.save(but(next)); } } }
var testFB = function(fb, callback){ λ.concurrent({ q: errPassingCurry(q, fb, [["?sue", "mother", "?mother"], ["?sibling", "mother", "?mother"]], [{"?sue": "sue"}]), qTuple: errPassingCurry(qTuple, fb, ["axl", "mother", "?mother"]), getEntity: errPassingCurry(getEntity, fb, "axl") }, callback); };
module.exports = function (req, res, next) { var max = 100; var page = parseInt(req.params.page, 10) || 1; var p = page - 1; var start = max * p; contra.concurrent({ subscriberGraph: pullData, subscribers: pullSubscribers }, ready); function pullSubscribers (next) { Subscriber .find({}) .sort('-created') .skip(p * max) .limit(max) .lean() .exec(found); function found (err, subscribers) { if (err) { next(err); return; } next(null, subscribers.map(toModel)); } } function toModel (subscriber) { return { created: datetimeService.field(subscriber.created), email: subscriber.email, avatar: userService.getAvatar(subscriber), topics: subscriber.topics, source: subscriber.source.split('+')[0], verified: subscriber.verified, hash: subscriberService.getHash(subscriber) }; } function ready (err, result) { if (err) { next(err); return; } res.viewModel = { model: { title: 'Subscribers \u2014 Pony Foo', subscriberGraph: result.subscriberGraph, subscribers: result.subscribers, allTopics: subscriberService.getTopics(), more: result.subscribers.length >= max, page: page } }; next(); } };
function campaign (article, done) { if (done === void 0) { done = noop; } contra.concurrent([ contra.curry(email, article), contra.curry(tweet, article) ], done); }
function pollCards (done) { if (!username || !password || !ads || (!articles && !newsletter)) { done(new Error('Method not implemented.')); return; } var tasks = []; if (articles) { tasks.push(makeTask('', articles)); } if (newsletter) { tasks.push(makeTask('+newsletter', newsletter)); } contra.concurrent(tasks, done); function makeTask (key, id) { return function pull (next) { Subscriber .findOne({ source: 'twitter' + key }) .sort('-created') .exec(found); function found (err, last) { if (err) { next(err); return; } var since = last ? last.created : new Date(0); var options = { username: username, password: password, ads: ads, card: id, since: since }; leads(options, pulled); } function pulled (err, leads) { if (err) { next(err); return; } if (leads.length) { winston.info('Found %s lead%s via Twitter Cards %s', leads.length, leads.length === 1 ? '' : 's', key || '+articles'); } else { winston.debug('No new leads via Twitter Cards %s', key || '+articles'); } contra.each(leads, 3, follow, next); } function follow (lead, next) { subscriberService.add({ created: lead.time, email: lead.email, name: lead.name, source: 'twitter' + key, verified: true }, next); } }; } }
function getModelData (done) { contra.concurrent({ engagements: function (next) { Engagement.find({}).lean().exec(next); }, presentations: function (next) { Presentation.find({}).sort('-presented').limit(5).lean().exec(next); } }, done); }
function notify (article) { contra.concurrent({ recipients: contra.curry(findRecipients, article), html: absolutizeHtml, gravatar: fetchGravatar }, function prepare (err, data) { data.article = article; send(err, data); }); }
function getFeed (done) { contra.concurrent({ weekly: contra.curry(weeklyFeedService.getFeed), articles: contra.curry(articleFeedService.getFeed) }, merge); function merge (err, result) { if (err) { done(err); return; } done(null, result.weekly.concat(result.articles)); } }
function verify (t, req) { var expected = path.join(__dirname, 'expected.png'); var actual = tmp.tmpNameSync({ postfix: '.png' }); var diff = tmp.tmpNameSync({ postfix: '.png' }); var tunnel; contra.concurrent({ psi: insights, screenshot: screenshot }, assessment); function insights (next) { console.log('\n' + exercise.__('tunnel', { port: req.port })); localtunnel(req.port, tunneled); function tunneled (err, tune) { if (err) { next(err); return; } tunnel = tune; console.log(exercise.__('get_feedback', { url: tune.url })); psi(tune.url, next); } } function screenshot (done) { contra.series({ take: function take (next) { webshot(req.url, actual, next); }, compare: function compare (next) { imageDiff({ actualImage: actual, expectedImage: expected, diffImage: diff, threshold: 0.1 }, next); } }, done); } function assessment (err, result) { if (err) { t.error(err); return; } var amount = result.psi.pageStats.numberResources; tunnel.close(); t.fgroup('overall'); t.fpass('psi_score', { amount: amount }, amount < 5); t.fpass('visualcomp', result.screenshot.compare); t.end(); } }
setupMiddleDataset(function(err, fb){ if(err) return t.end(err); λ.concurrent({ axl_mother: λ.curry(qTuple, fb, ["axl", "mother", "?mother"]), axl_relation_to_mike: λ.curry(qTuple, fb, ["axl", "?relation", "mike"], {}), mikes_children: λ.curry(qTuple, fb, ["?children", "father", "?father"], {"?father": "mike"}), axl_has_no_children: λ.curry(qTuple, fb, ["?children", "father", "axl"]) }, function(err, r){ t.deepEqual(_.pluck(r.axl_mother, "?mother"), ["frankie"]); t.deepEqual(_.pluck(r.axl_relation_to_mike, "?relation"), ["father"]); t.deepEqual(_.pluck(r.mikes_children, "?children").sort(), ["axl", "brick", "sue"]); t.equal(r.axl_has_no_children.length, 0); t.end(err); }); });
module.exports = function (done) { contra.concurrent({ subscribers: contra.curry(pullSubscribers), pageviews: contra.curry(pullPageViews) }, render); function render (err, result) { if (err) { done(err); return; } done(null, { subscribers: result.subscribers.list, verified: result.subscribers.verified, pageviews: result.pageviews }); } }
function jsonx () { var strategy = 'callback' in req.query ? 'jsonp' : 'json'; var hijacked = 'hijacker' in req.query; var response = { version: data.version }; if (hijacked === false) { response.model = data.model; } if (action !== route.action && !hijacked) { respond(); // only add view/controller for the default route action or hijackers } else { contra.concurrent([ add('template', 'views'), add('controller', 'client_controllers') ], respond); } function add (demand, base) { return function addModule (done) { var needsModule = demand in req.query; if (needsModule === false) { done(); return; } var a = req.query.hijacker || action; var file = path.join(rc[base], a + '.js'); bro(file, include); function include (err, data) { if (!err) { response[demand] = data; } done(err); } }; } function respond (err) { if (err) { next(err); return; } end(strategy, response); } }
setupMiddleDataset(function(err, fb){ if(err) return t.end(err); λ.concurrent({ all_entities: λ.curry(q, fb, [["?entity"]]), all_fathers: λ.curry(q, fb, [["?_", "father", "?father"]]), sue_siblings: λ.curry(q, fb, [[ "?sue", "mother", "?_"], ["?sibling", "mother", "?_"]], [{"?sue": "sue"}]) }, function(err, r){ t.deepEqual(_.pluck(r.all_entities, "?entity").sort(), ['01', '02', '_txid1', '_txid2', 'axl', 'brick', 'frankie', 'janet', 'mike', 'rusty', 'sue']); t.deepEqual(_.sortBy(r.all_fathers, "?father"), [{"?father": 'big mike'}, {"?father": 'mike'}, {"?father": 'tag'}], "should not have ?_ bound to anything"); t.deepEqual(_.sortBy(r.sue_siblings, "?sibling"), [{'?sibling': 'axl', '?sue': 'sue'}, {'?sibling': 'brick', '?sue': 'sue'}, {'?sibling': 'frankie', '?sue': 'sue'}, {'?sibling': 'janet', '?sue': 'sue'}, {'?sibling': 'sue', '?sue': 'sue'}], "should be everyone with a mother b/c ?_ shouldn't join"); t.end(err); }); });
], function(err, fb_versions){ if(err) return t.end(err); var fb = transactor.connection.snap(); λ.concurrent({ my_emails: λ.curry(q, fb, [["me", "emails", "?emails"]]), the_first_me: λ.curry(getEntity, fb_versions[1], "me"), the_last_me: λ.curry(getEntity, fb, "me") }, function(err, r){ if(err) return t.end(err); t.deepEqual(_.pluck(r.my_emails, "?emails"), ["1@email", "2@email", "3@email"]); t.deepEqual(r.the_first_me, {emails: ["1@email"]}); t.deepEqual(r.the_last_me, {emails: ["1@email", "2@email", "3@email"]}); t.end(); }); });
setupProphetDataset(function(err, fb_versions){ if(err) return t.end(err); var fb = _.last(fb_versions); λ.concurrent({ first: λ.curry(q, fb, [["prophet", "is", "?name", 2]]), third: λ.curry(q, fb, [["prophet", "is", "?name", 4]]), when_was_young: λ.curry(q, fb, [["prophet", "is", "young", "?txn"]]), who_is_current: λ.curry(q, fb, [["prophet", "is", "?name" ]]), names_in_order: λ.curry(q, fb, [["prophet", "is", "?name", "?txn"]]) }, function(err, r){ t.deepEqual(_.pluck(r.first, "?name"), ["smith"]); t.deepEqual(_.pluck(r.third, "?name"), ["taylor"]); t.deepEqual(_.pluck(r.when_was_young, "?txn"), [3]); t.deepEqual(_.pluck(r.who_is_current, "?name"), ["monson"]); t.deepEqual(_.pluck(_.sortBy(r.names_in_order, "?txn"), "?name"), prophets); t.end(err); }); });
function operational () { winston.info('Connected to target database!'); models(); contra.concurrent([ function (next) { Article.remove({}, next); }, function (next) { Subscriber.remove({}, next); }, function (next) { User.remove({}, next); }, function (next) { UnverifiedUser.remove({}, next); }, function (next) { UserVerificationToken.remove({}, next); }, function (next) { PasswordResetToken.remove({}, next); } ], prod); function prod () { production = mongoose.createConnection(prd, options); production.once('connected', connected); production.on('error', error); } }
function getDefaultViewModel (done) { contra.concurrent({ bioHtml: bioService.getHtml.bind(null, authorEmail), popularArticles: popularityService.getArticles, javascriptLoader: read('.bin/inline/javascript.js'), styleLoader: read('.bin/inline/styles.js'), fontLoader: read('.bin/inline/fonts.js') }, forward); function forward (err, data) { if (err) { done(err); return; } done(null, { rss: 'https://feeds.feedburner.com/ponyfoo', author: { contact: 'Nicolás Bevacqua <*****@*****.**>', twitter: '@nzgb' }, description: '', model: { authority: authority, title: 'Pony Foo \u2014 JavaScript consulting, modularity, front-end architecture, performance, and more. Authored by Nicolás Bevacqua', pkg: { version: pkg.version }, meta: { description: 'Pony Foo is a technical blog maintained by Nicolás Bevacqua, where he shares his thoughts on JavaScript and the web. Nico likes writing, public speaking, and open-source.', images: [authority + staticService.unroll('/img/thumbnail.png')], keywords: [] }, bioHtml: data.bioHtml, popularArticles: data.popularArticles }, javascriptLoader: data.javascriptLoader, styleLoader: data.styleLoader, fontLoader: data.fontLoader }); } }
setupMiddleDataset(function(err, fb){ if(err) return t.end(err); λ.concurrent({ husbands_and_wifes: λ.curry(q, fb, [["?child", "mother", "?wife"], ["?child", "father", "?husband"]]), sue_grandfathers: λ.curry(q, fb, [[ "sue", "father", "?father"], [ "sue", "mother", "?mother"], ["?mother", "father", "?grandpa1"], ["?father", "father", "?grandpa2"]], [{}]), sue_siblings: λ.curry(q, fb, [[ "?sue", "mother", "?mother"], ["?sibling", "mother", "?mother"]], [{"?sue": "sue"}]), }, function(err, r){ t.deepEqual(_.unique(_.map(r.husbands_and_wifes, function(result){ return result["?husband"] + " & " + result["?wife"] }).sort()), ["mike & frankie", "tag & pat"]); t.deepEqual(_.unique(_.pluck(r.sue_grandfathers, "?grandpa1").concat(_.pluck(r.sue_grandfathers, "?grandpa2"))).sort(), ["big mike", "tag"]); t.deepEqual(_.unique(_.pluck(r.sue_siblings, "?sibling")).sort(), ["axl", "brick", "sue"]); t.end(err); }); });
function share (article, done) { if (done === void 0) { done = noop; } contra.concurrent([ curried('email', email), curried('tweet', tweet), curried('fb', facebook), curried('echojs', echojs), curried('hn', hackernews), curried('lobsters', lobsters) ], done); function curried (key, fn) { return function shareVia (next) { if (article[key] === false) { next(); return; } fn(article, {}, next); }; } }
function share (article, done) { if (done === void 0) { done = noop; } contra.concurrent([ medium('email', 'email'), medium('tweet', 'twitter'), medium('fb', 'facebook'), medium('echojs', 'echojs'), medium('hn', 'hackernews') ], done); function medium (key, method) { return function shareVia (next) { if (article[key] === false) { winston.info('Sharing turned off via "%s" channel for article %s.', method, article.title); next(); return; } winston.info('Sharing article %s via "%s" channel.', article.title, method); articleSharingService[method](article, {}, next); }; } }
function rebuild () { contra.concurrent({ articles: fetchArticles, weeklies: fetchWeeklies }, models); function models (err, result) { if (err) { done(err); return; } var modified = toLastMod(result.articles[0] ? result.articles[0].updated : Date.now()); var urls = getArticleUrls(result.articles) .concat(getWeeklyUrls(result.weeklies)) .concat(getOtherUrls()); var map = sitemap.createSitemap({ hostname: authority, cacheTime: 15 * 60 * 1000, // 15 minutes urls: urls }); var xml = map.toXML(); persist(xml, done); } }
function getModel (req, res, next) { contra.concurrent({ live: function (next) { settingService.getKey('PONYFOOWEEKLY_CRON', next); }, level: function (next) { settingService.getKey('PONYFOOWEEKLY_CRON_LEVEL', next); }, weeklies: function (next) { WeeklyIssue .find({}) .sort([['publication', -1], ['created', -1]]) .populate('author', 'slug email avatar') .exec(next); } }, respond); function respond (err, result) { if (err) { next(err); return; } var sorted = _.sortBy(result.weeklies, sortByStatus); var models = sorted.map(weeklyService.toMetadata); res.viewModel = { model: { title: 'Newsletter Review', meta: { canonical: '/weekly/review' }, weeklies: models, live: result.live, level: result.level } }; next(); } }
], function(err, fb_versions){ if(err) return t.end(err); var fb = transactor.connection.snap(); t.ok(fb.schema.time["_db/is-multi-valued"] === true, "must also decode db default schema values"); λ.concurrent({ time1: λ.curry(q, fb, [["1", "time", "?val"]]), integer1: λ.curry(q, fb, [["2", "int", "?val"]]), number1: λ.curry(q, fb, [["3", "float", "?val"]]), //query with variable attribute name time2: λ.curry(q, fb, [["1", "?a", "?val"]]), integer2: λ.curry(q, fb, [["2", "?a", "?val"]]), number2: λ.curry(q, fb, [["3", "?a", "?val"]]), //query with unknown attribute name time3: λ.curry(q, fb, [["1", "?_", "?val"]]), integer3: λ.curry(q, fb, [["2", "?_", "?val"]]), number3: λ.curry(q, fb, [["3", "?_", "?val"]]), //encode values at query with known attribute name time4: λ.curry(q, fb, [["?e", "time", new Date(2010, 11, 25)]]), integer4: λ.curry(q, fb, [["?e", "int", 123]]), number4: λ.curry(q, fb, [["?e", "float", 123.45]]), //encode values at query with variable attribute name time5: λ.curry(q, fb, [["?e", "?a", new Date(2010, 11, 25)]]), integer5: λ.curry(q, fb, [["?e", "?a", 123]]), number5: λ.curry(q, fb, [["?e", "?a", 123.45]]), //encode values at query with unknown attribute name time6: λ.curry(q, fb, [["?e", "?_", new Date(2010, 11, 25)]]), integer6: λ.curry(q, fb, [["?e", "?_", 123]]), number6: λ.curry(q, fb, [["?e", "?_", 123.45]]) }, function(err, r){ if(err) return t.end(err); _.each(r, function(results, key){ t.equal(results.length, 1, "all these type encode/decode queries should return 1 result"); }); t.ok(_.isDate(r.time1[0]['?val'])); t.ok(_.isDate(r.time2[0]['?val'])); t.ok(_.isDate(r.time3[0]['?val'])); t.ok(_.isNumber(r.integer1[0]['?val'])); t.ok(_.isNumber(r.integer2[0]['?val'])); t.ok(_.isNumber(r.integer3[0]['?val'])); t.equal(r.integer1[0]['?val'], 123); t.equal(r.integer2[0]['?val'], 123); t.equal(r.integer3[0]['?val'], 123); t.ok(_.isNumber(r.number1[0]['?val'])); t.ok(_.isNumber(r.number2[0]['?val'])); t.ok(_.isNumber(r.number3[0]['?val'])); t.equal(r.number1[0]['?val'], 123.45); t.equal(r.number2[0]['?val'], 123.45); t.equal(r.number3[0]['?val'], 123.45); t.equal(r.time4[0]['?e'], "1"); t.equal(r.time5[0]['?e'], "1"); t.equal(r.time6[0]['?e'], "1"); t.equal(r.integer4[0]['?e'], "2"); t.equal(r.integer5[0]['?e'], "2"); t.equal(r.integer6[0]['?e'], "2"); t.equal(r.number4[0]['?e'], "3"); t.equal(r.number5[0]['?e'], "3"); t.equal(r.number6[0]['?e'], "3"); t.end(); }); });
function send (model, done) { var provider = model.provider || {}; var providerTags = provider.tags || []; var merge = provider.merge || {}; var domain = addrs.parseOneAddress(model.from).domain; var authority = model.authority || options.authority var client = mailgunjs({ apiKey: options.apiKey, domain: domain }); contra.concurrent({ html: inlineHtml, images: getImages, attachments: getAttachments, }, ready); function inlineHtml (next) { var config = { url: authority }; inlineCss(model.html, config) .then(function inlined (html) { next(null, html); }) .catch(function failed (err) { next(err); }); } function getImages (next) { var images = model.images ? model.images : []; if (model._header) { images.unshift({ name: '_header', data: model._header.data, mime: model._header.mime }); } next(null, images.map(transform)); function transform (image) { return new client.Attachment({ data: new Buffer(image.data, 'base64'), filename: image.name, contentType: image.mime }); } } function getAttachments (next) { var attachments = model.attachments ? model.attachments : []; next(null, attachments.map(transform)); function transform (attachment) { return new client.Attachment({ data: attachment.file, filename: attachment.name }); } } function ready (err, result) { if (err) { done(err); return; } post(result.html, result.images, result.attachments); } function post (html, images, attachments) { var inferConfig = { wordwrap: 130, linkHrefBaseUrl: authority, hideLinkHrefIfSameAsText: true }; var inferredText = htmlToText.fromString(html, inferConfig); var tags = [model._template].concat(providerTags); var batches = getRecipientBatches(); expandWildcard(model.to, model.cc, model.bcc); contra.each(batches, 4, postBatch, responses); function postBatch (batch, next) { var req = { from: model.from, to: batch, cc: model.cc, bcc: model.bcc, subject: model.subject, html: html, text: inferredText, inline: images.slice(), attachment: attachments.slice(), 'o:tag': tags.slice(), 'o:tracking': true, 'o:tracking-clicks': true, 'o:tracking-opens': true, 'recipient-variables': parseMergeVariables(batch, model.cc, model.bcc) }; client.messages().send(req, next); } function responses (err, results) { done(err, results); } } function getRecipientBatches () { var size = 250; // "Note: The maximum number of recipients allowed for Batch Sending is 1,000." var batches = []; for (var i = 0; i < model.to.length; i += size) { batches.push(model.to.slice(i, i + size)); } return batches; } function parseMergeVariables (to, cc, bcc) { var variables = {}; to .concat(cc) .concat(bcc) .forEach(addVariables); return variables; function addVariables (recipient) { if (merge[recipient]) { variables[recipient] = merge[recipient]; } } } function expandWildcard (to, cc, bcc) { if ('*' in merge) { wildcarding(); } function wildcarding () { var wildcard = merge['*']; to .concat(cc) .concat(bcc) .forEach(addWildcardToRecipient); function addWildcardToRecipient (recipient) { Object.keys(wildcard).forEach(addWildcardKeyToRecipient); function addWildcardKeyToRecipient (key) { // don't override: wildcard has default values if (!merge[recipient]) { merge[recipient] = {}; } if (!merge[recipient].hasOwnProperty(key)) { merge[recipient][key] = wildcard[key]; } } } } } }
next => contra.concurrent(filenames.map(filename => next => write(filename, files[filename], next) ), next)