Skip to content

Commit

Permalink
Allow date and term calculation in liquid (#6)
Browse files Browse the repository at this point in the history
* added moment library

* allow date and term calculation in liquid

* refactor addDuration and subtractDates

* return values directly

* Fix duration in days calculation

* add tests

* Maintain consistency in naming decorators

* use existing decorators to calculate date term calculation

* spell check

* remove functions that are not needed

* WIP: tests updated

* Co-authored-by: Madhav Bhagat <madhav89@gmail.com> tests for add duration and subrtract date

* return duration instead of no of days

* add default argument for getObjectValues function

* handle from_date greater than to_date case

* update tests for negative value of durationInDays

* support for adding durartion and tests for the same

* added tests for assign

* throw error if either value or type is incorrcect in duration calculation.

Co-authored-by: Sumanth Subbarao <sumanth@Sumanths-MacBook-Pro.local>
  • Loading branch information
sumanth-spotdraft and Sumanth Subbarao authored Mar 31, 2020
1 parent dc59ece commit bf20a09
Show file tree
Hide file tree
Showing 3 changed files with 128 additions and 9 deletions.
92 changes: 84 additions & 8 deletions filters.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const strftime = require("./src/util/strftime.js");
const _ = require("./src/util/underscore.js");
const isTruthy = require("./src/syntax.js").isTruthy;
const moment = require("moment");

var escapeMap = {
"&": "&amp;",
Expand All @@ -9,6 +10,7 @@ var escapeMap = {
'"': "&#34;",
"'": "&#39;"
};

var unescapeMap = {
"&amp;": "&",
"&lt;": "<",
Expand Down Expand Up @@ -39,7 +41,6 @@ var filters = {
divided_by: (v, arg) => divide(v, arg),
downcase: v => v.toLowerCase(),
escape: escape,

escape_once: str => escape(unescape(str)),
first: v => v[0],
floor: v => Math.floor(v),
Expand Down Expand Up @@ -146,15 +147,41 @@ function filterNumericKeysFromObject(obj) {
return Object.keys(obj).filter(key => !Number.isNaN(parseInt(obj[key])));
}

function getObjectValues(obj) {
function getObjectValues(obj = {}) {
let resultObj = {};
keys = Object.keys(obj);
keys.forEach(key => {
resultObj[key] = obj[key];
});
return resultObj;
}

function calculateDurationInDays(fromDate, toDate) {
const durationInMilliSeconds = toDate.getTime() - fromDate.getTime();
const durationInDays = durationInMilliSeconds/(1000*3600*24);
if(durationInDays < 0) {
throw new Error("toDate should be greater than fromDate");
}
return {
type: "days",
value: durationInDays
}
}
function addDuration(v, arg) {
const v_days = computeDaysFromUnit(v.value, v.type);
const arg_days = computeDaysFromUnit(arg.value, arg.type);
const total_days = v_days + arg_days;
return {
type: "days",
value: total_days
}
}
function checkIfDurationObjects(v, arg) {
const units = ['days', 'weeks', 'months', 'years'];
if(units.indexOf(v.type) != -1 && units.indexOf(arg.type) != -1) {
return true;
}
return false;
}
function subtract(v, arg) {
return performOperations(v, arg, "SUBTRACT");
}
Expand All @@ -167,8 +194,26 @@ function add(v, arg) {
return performOperations(v, arg, "ADD");
}


function performOperations(v, arg, operation) {
if(Object.prototype.toString.call(v) === '[object Date]' && Object.prototype.toString.call(arg) === '[object Date]') {
return operationOnDate(v, arg, operation);
}
if (Object.prototype.toString.call(v) === '[object Date]' && typeof arg === "object") {
const addType = ['days', 'weeks', 'months', 'years'];
const {value, type} = arg;
if (value && addType.indexOf(type) != -1) {
return operationOnDate(v, arg, operation);
}else {
throw new Error("value or type is incorrect.")
}
}
if (typeof v === "object" && typeof arg === "object") {
const isDurationObjects = checkIfDurationObjects(v, arg)
if(isDurationObjects) {
const result = addDuration(v, arg);
return result;
}
result = Object.assign(getObjectValues(arg), getObjectValues(v));
const numberKeysOfArg = filterNumericKeysFromObject(arg);
const numberKeysOfV = filterNumericKeysFromObject(v);
Expand All @@ -182,24 +227,28 @@ function performOperations(v, arg, operation) {
return result;
} else {
console.warn("The objects don't have any common numeric attributes");
return;
}
} else if (typeof v === "number" && typeof arg === "object") {
}
if (typeof v === "number" && typeof arg === "object") {
result = getObjectValues(arg);
const numberKeys = filterNumericKeysFromObject(arg);
numberKeys.forEach(key => {
result[key] = operationOnItem(v, arg[key], operation);
});
return result;
} else if (typeof v === "object" && typeof arg === "number") {
}
if (typeof v === "object" && typeof arg === "number") {
result = getObjectValues(v);
const numberKeys = filterNumericKeysFromObject(v);
numberKeys.forEach(key => {
result[key] = operationOnItem(v[key], arg, operation);
});
return result;
} else {
return operationOnItem(v, arg, operation);
}
}

return operationOnItem(v, arg, operation);

}

function operationOnItem(v, arg, operation) {
Expand All @@ -215,5 +264,32 @@ function operationOnItem(v, arg, operation) {
}
}

function operationOnDate(v, arg, operation) {
switch(operation) {
case "ADD":
return new Date(moment(v).add(arg.value, arg.type));
case "SUBTRACT":
return calculateDurationInDays(v, arg);
default:
throw new Error(`${operation}, not supported`)
}
}

function computeDaysFromUnit(value, type) {
switch(type) {
case "days":
return value;
case "weeks":
return value * 7;
case "months":
return value * 30;
case "years":
return value * 365;
default:
return 0;
}
}


registerAll.filters = filters;
module.exports = registerAll;
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"homepage": "https://github.com/harttle/liquidjs#readme",
"dependencies": {
"any-promise": "^1.3.0",
"moment": "^2.24.0",
"resolve-url": "^0.2.1"
},
"browser": {
Expand Down
44 changes: 43 additions & 1 deletion test/filters.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ const chaiAsPromised = require('chai-as-promised')
const expect = chai.expect
var liquid = require('..')()
chai.use(chaiAsPromised)
const moment = require("moment");


var ctx = {
date: new Date(),
Expand All @@ -16,7 +18,15 @@ var ctx = {
category: 'foo'
}, {
category: 'bar'
}]
}],

duration_10_weeks: {value: 10, type: "weeks"},
duration_20_days: {value: 20, type: "days"},
duration_2_months: {value: 2, type: "months"},
duration_3_years: {value: 3, type: "years"},

from_date: new Date("March 17, 2020"),
to_date: new Date("March 17, 2022")
}

function test (src, dst) {
Expand Down Expand Up @@ -184,6 +194,14 @@ describe('filters', function () {
() => test('{{ 183.357 | minus: 12 }}', '171.357'))
it('should convert first arg as number', () => test('{{ "4" | minus: 1 }}', '3'))
it('should convert both args as number', () => test('{{ "4" | minus: "1" }}', '3'))
it('should return {"type":"days","value":730}', () => {
try {
const dst = {type: "days", value: 730};
return test('{% assign duration = from_date | minus: to_date %}{{duration}}', JSON.stringify(dst))
} catch(e) {
console.error(e.message)
}
})
})

describe('modulo', function () {
Expand Down Expand Up @@ -213,6 +231,30 @@ describe('filters', function () {
() => test('{{ 183.357 | plus: 12 }}', '195.357'))
it('should convert first arg as number', () => test('{{ "4" | plus: 2 }}', '6'))
it('should convert both args as number', () => test('{{ "4" | plus: "2" }}', '6'))
it('should add 10 weeks to current date', () => {
const dst = new Date(moment(ctx.date).add(10, "weeks")).toDateString()
return test('{% assign term = date | plus: duration_10_weeks | date: "%a %b %d %Y" %}{{ term }}', dst)
})
it('should add 20 days to current date', () => {
const dst = new Date(moment(new Date()).add(20, "days")).toDateString()
return test('{% assign term = date | plus: duration_20_days | date: "%a %b %d %Y"%}{{ term }}', dst)
})
it('should add 2 months to current date', () => {
const dst = new Date(moment(new Date()).add(2, "months")).toDateString()
return test('{% assign term = date | plus: duration_2_months | date: "%a %b %d %Y"%}{{ term }}', dst)
})
it('should add 3 years to current date', () => {
const dst = new Date(moment(new Date()).add(3, "years")).toDateString()
return test('{{ date | plus: duration_3_years | date: "%a %b %d %Y"}}', dst)
})
it('should return {type: "days", value: 80};', () => {
const dst = {type: "days", value: 80};
return test('{{ duration_20_days | plus: duration_2_months }}', JSON.stringify(dst))
})
it('should return {type: "days", value: 130};', () => {
const dst = {type: "days", value: 130};
return test('{{ duration_10_weeks | plus: duration_2_months }}', JSON.stringify(dst))
})
})

it('should support prepend', function () {
Expand Down

0 comments on commit bf20a09

Please sign in to comment.