Skip to content

Instantly share code, notes, and snippets.

@toddbranch
Last active August 29, 2015 14:14
Show Gist options
  • Select an option

  • Save toddbranch/fde6f7a156daebf97963 to your computer and use it in GitHub Desktop.

Select an option

Save toddbranch/fde6f7a156daebf97963 to your computer and use it in GitHub Desktop.
Hiding Implementation Details in Javascript
// Code snippets from 8facd2c613eee0c81581e840c33b1d5a4770d935
// No need to understand how they work, just need to know that:
// removeArticle is the interface that client-side code uses
// removeArticleFromTemplate and getTemplateFromProduct are internal helper functions
/**
* Remove an article at the specified index
*
* @param index
* @returns {*}
*/
removeArticle: function (index) {
var metaData = this.get('metaData');
var article = metaData.articles[index];
if (article) {
this.removeArticleFromTemplate(article);
metaData.articles.splice(index, 1);
this.set({metaData: metaData});
}
return this;
},
removeArticleFromTemplate: function (article) {
var product = this.getTemplateFromProduct(article.product);
var templates = this.get('templates');
var articles = templates[product].articles;
var index = _.indexOf(articles, article);
if (index > -1) {
articles.splice(index, 1);
this.set({templates: templates});
}
},
getTemplateFromProduct: function (product) {
return product === 'honeymoons' ? 'honeymoon' : product;
},
// Code snippets from 8facd2c613eee0c81581e840c33b1d5a4770d935
// Since all of the functions (removeArticle, removeArticleFromTemplate, and
// getTemplateFromProduct) are attached to the view, they are all publicly accessible.
// This leads me toward attempting to test them. But testing the removeArticleFromTemplate
// and getTemplateFromProduct makes any refactor attempt on removeArticle onerous.
describe('getTemplateFromProduct', function() {
it('should return honeymoon for honeymoons', function() {
expect(model.getTemplateFromProduct('honeymoons')).toBe('honeymoon');
});
it('should return product for everything else', function() {
expect(model.getTemplateFromProduct('destination')).toBe('destination');
expect(model.getTemplateFromProduct('local')).toBe('local');
});
});
describe('removeArticle', function() {
var article;
beforeEach(function() {
article = {};
});
it('should remove local articles from metaData', function() {
article.product = 'local';
model.addArticle('local', article);
expect(model.get('metaData').articles.length).toBe(1);
model.removeArticle(0);
expect(model.get('metaData').articles.length).toBe(0);
});
it('should remove honeymoon articles from metaData and honeymoon', function() {
article.product = 'honeymoons';
model.addArticle('honeymoons', article);
expect(model.get('metaData').articles.length).toBe(1);
expect(model.get('templates').honeymoon.articles.length).toBe(1);
model.removeArticle(0);
expect(model.get('metaData').articles.length).toBe(0);
expect(model.get('templates').honeymoon.articles.length).toBe(0);
});
it('should remove destination articles from metaData and destination', function() {
article.product = 'destination';
model.addArticle('destination', article);
expect(model.get('metaData').articles.length).toBe(1);
expect(model.get('templates').destination.articles.length).toBe(1);
model.removeArticle(0);
expect(model.get('metaData').articles.length).toBe(0);
expect(model.get('templates').destination.articles.length).toBe(0);
});
})
// How can we fix this?
// The removeArticle tests are comprehensive, so we can eliminate testing the
// getTemplateFromProduct helper function. But we still have the problem of it
// being publicly accessible. How do we fix that?
// We can extract the helper functions out of the view and put them at the top
// of the define block. That way, they are accessible in the functions via closure
// but can't be accessed elsewhere.
// These changes implemented in 0a38520f1651fce67162c5e50e5c9397e336e64c
function removeArticleFromTemplate(model, article) {
var product = getTemplateFromProduct(article.product);
var templates = model.get('templates');
var articles = templates[product].articles;
var index = _.indexOf(articles, article);
if (index > -1) {
articles.splice(index, 1);
model.set({templates: templates});
}
}
function getTemplateFromProduct(product) {
return product === 'honeymoons' ? 'honeymoon' : product;
}
return Backbone.View.extend({
/**
* Remove an article at the specified index
*
* @param index
* @returns {*}
*/
removeArticle: function (index) {
var metaData = this.get('metaData');
var article = metaData.articles[index];
if (article) {
removeArticleFromTemplate(this, article);
metaData.articles.splice(index, 1);
this.set({metaData: metaData});
}
return this;
}
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment