Skip to content

Commit

Permalink
Implements a more modularized and testable code
Browse files Browse the repository at this point in the history
  • Loading branch information
djGrill committed Nov 2, 2013
1 parent 5914eae commit b4f0c79
Show file tree
Hide file tree
Showing 11 changed files with 263 additions and 177 deletions.
2 changes: 1 addition & 1 deletion config.ru
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use Rack::Static,
urls: ["/img", "/js", "/css"],
urls: ["/img", "/javascripts", "/css"],
root: "public"

map "/" do
Expand Down
4 changes: 1 addition & 3 deletions public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@
<head>
<title>BBC - British Binary Coins</title>
<link href="/css/main.css" media="all" rel="stylesheet" type="text/css">
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script src="/js/underscore-min.js" type="text/javascript"></script>
<script src="/js/functions.js" type="text/javascript"></script>
<script data-main="javascripts/main" src="/javascripts/require.js" type="text/javascript"></script>
</head>

<body>
Expand Down
8 changes: 8 additions & 0 deletions public/javascripts/Constants.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
define([], function() {
var Constants = {
"DECIMAL_MULTIPLIER": 100,
"VALID_COINS": [2, 1, 0.5, 0.2, 0.02, 0.01]
};

return Constants;
});
81 changes: 81 additions & 0 deletions public/javascripts/FormHelper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
define(["jquery", "underscore", "SterlingValidator", "FormatHelper", "PenniesCalculator", "Constants"],
function($, _, SterlingValidator, FormatHelper, PenniesCalculator, Constants) {

function FormHelper() {}


/**
* Handles the validation of the user input
* If the input is valid, run the calculator. If not, show error message
* @param {string} input Input text form the user
*/
FormHelper.prototype.processInput = function(input) {
var validator = new SterlingValidator(input);

if (validator.validate()) {
var formatHelper = new FormatHelper();
var numericInput = formatHelper.roundToDecimalPlace(formatHelper.convertToValidFloat(input));
var calculator = new PenniesCalculator(numericInput);
this.showResult(calculator.calculate());
} else {
$("#result").html("Error: wrong format");
}
};


/**
* Loads result in a friendly format into the page
* @param {object} result Data to show
*/
FormHelper.prototype.showResult = function(result) {

/**
* Returns the name of the coin in a human-friendly format. For example:
* 1 => £1
* 0.20 => 20p
* @param {string} coin Numeric name of the coin
*/
function getHumanizedCoinName(coin) {
if (/\./.test(coin)) {
coin *= Constants.DECIMAL_MULTIPLIER;
coin += "p";
} else {
coin = "\xA3" + coin;
}

return coin;
}

function isPence(coinName) {
return (/p$/.test(coinName));
}

// reverse the keys order to show result sorted by coin value
var resultKeys = Object.keys(result).sort().reverse();

var $ul = $("<ul>");
$ul.addClass("coins");

_.each(resultKeys, function(key) {
var $li = $("<li>");
var coinName = getHumanizedCoinName(key);

var $newCoin = $("<span>");
$newCoin.html(coinName);
$newCoin.addClass("coin");

if (isPence(coinName)) {
$newCoin.addClass("pence");
}

$li.html(result[key] + " x");
$li.append($newCoin);
$ul.append($li);
});

$("#result").html($ul);
};


return FormHelper;
});
41 changes: 41 additions & 0 deletions public/javascripts/FormatHelper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
define(["Constants"], function(Constants) {

function FormatHelper() {}


/**
* Converts str and returns a float number with 2 decimal values. For example:
* £1.25 => 1.25
* 125 => 1.25
* 25p => 0.25
* @param {string} str String to convert
*/
FormatHelper.prototype.convertToValidFloat = function(str) {
var number = /\d+(\.\d+)?/.exec(str)[0];

// if str has the format "£NUMBER" (pounds only), convert number to "NUMBER.00"
if (/^\xA3\d+$/.test(str)) {
number += ".00";
}

// if str has the format "NUMBER" or "NUMBERp" (pences only), convert number to
// -- the POUND.pence version with decimal numbers (determined by DECIMAL_MULTIPLIER)
if (/^\d+p?$/.test(str)) {
number /= Constants.DECIMAL_MULTIPLIER;
}

return parseFloat(number);
};


/**
* Returns the number rounded to de decimal place specified by DECIMAL_MULTIPLIER
* @param {number} number Number to convert
*/
FormatHelper.prototype.roundToDecimalPlace = function(number) {
return Math.round(number * Constants.DECIMAL_MULTIPLIER) / Constants.DECIMAL_MULTIPLIER;
};


return FormatHelper;
});
43 changes: 43 additions & 0 deletions public/javascripts/PenniesCalculator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
define(["underscore", "Constants", "FormatHelper"],

function(_, Constants, FormatHelper) {

/**
* Calculates the minimum amount of VALID_COINS needed to make the specified amount
* @constructor
* @param {float} numericInput Valid amount specified by the user
*/
function PenniesCalculator(numericInput) {
this.numericInput = numericInput;
}


PenniesCalculator.prototype.calculate = function() {
// here we'll store the amount of each type of coin needed
var resultCoins = {};
var formatHelper = new FormatHelper();

function updateResultCoins(coin) {
if (!resultCoins[coin]) {
resultCoins[coin] = 1;
} else {
resultCoins[coin] += 1;
}
}

_.each(Constants.VALID_COINS, function(coin, index) {
while (this.numericInput >= coin) {
updateResultCoins(coin);
this.numericInput -= coin;

// make sure that numericInput is properly rounded
this.numericInput = formatHelper.roundToDecimalPlace(this.numericInput);
}
}, this);

return resultCoins;
};


return PenniesCalculator;
});
21 changes: 21 additions & 0 deletions public/javascripts/SterlingValidator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
define([], function() {

/**
* Used to validate an input that represents an Sterling value
* @constructor
* @param {string} input Input text from the user
*/
function SterlingValidator(input) {
this.input = input;
this.pattern = /^\xA3?\d+(\.\d*)?p?$|^\d+p?$/;
}


// returns true if this.input matches this.pattern
SterlingValidator.prototype.validate = function() {
return this.pattern.test(this.input);
};


return SterlingValidator;
});
31 changes: 31 additions & 0 deletions public/javascripts/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
requirejs.config({
paths: {
jquery: "//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min",
underscore: "underscore-min"
},

shim: {
underscore: {
exports: '_'
},
jquery: {
exports: "$"
}
}
});

require(["jquery", "FormHelper"], function($, FormHelper) {
$(function() {
$("#input-amount").focus();

$("#form-calculate").submit(function(e) {
e.preventDefault();
var inputVal = $.trim($("#input-amount").val());

if (inputVal) {
var formHelper = new FormHelper();
formHelper.processInput(inputVal);
}
});
});
});
Loading

0 comments on commit b4f0c79

Please sign in to comment.