From 76865d0da4fc0fd15953c2c8e81bebdd80f50f2e Mon Sep 17 00:00:00 2001 From: Scott Kyle Date: Wed, 13 Jan 2016 02:40:26 -0800 Subject: [PATCH] Allow sortedBy to accept array of properties It now optionally accepts an array of properties for both parameters and the tests have been updated to reflect new capabilities. --- src/js_results.cpp | 45 ++++++++++++++++++++---- src/js_util.hpp | 16 +++++---- tests/lib/results-tests.js | 70 +++++++++++++++++++++++++++----------- 3 files changed, 99 insertions(+), 32 deletions(-) diff --git a/src/js_results.cpp b/src/js_results.cpp index 2f6e2c25..55a857bd 100644 --- a/src/js_results.cpp +++ b/src/js_results.cpp @@ -95,19 +95,50 @@ JSValueRef ResultsSorted(JSContextRef ctx, JSObjectRef function, JSObjectRef thi try { Results *results = RJSGetInternal(thisObject); RJSValidateArgumentRange(argumentCount, 1, 2); + size_t prop_count; + std::vector prop_names; - std::string propName = RJSValidatedStringForValue(ctx, arguments[0]); - const Property *prop = results->get_object_schema().property_for_name(propName); - if (!prop) { - throw std::runtime_error("Property '" + propName + "' does not exist on object type '" + results->get_object_schema().name + "'"); + if (RJSIsValueArray(ctx, arguments[0])) { + JSObjectRef js_prop_names = RJSValidatedValueToObject(ctx, arguments[0]); + prop_count = RJSValidatedListLength(ctx, js_prop_names); + prop_names.resize(prop_count); + + for (unsigned int i = 0; i < prop_count; i++) { + prop_names[i] = RJSValidatedStringForValue(ctx, RJSValidatedPropertyAtIndex(ctx, js_prop_names, i)); + } + } + else { + prop_count = 1; + prop_names.push_back(RJSValidatedStringForValue(ctx, arguments[0])); + } + + std::vector columns(prop_count); + std::vector ascending(prop_count, true); + size_t index = 0; + + for (std::string prop_name : prop_names) { + const Property *prop = results->get_object_schema().property_for_name(prop_name); + if (!prop) { + throw std::runtime_error("Property '" + prop_name + "' does not exist on object type '" + results->get_object_schema().name + "'"); + } + columns[index++] = prop->table_column; } - bool ascending = true; if (argumentCount == 2) { - ascending = !JSValueToBoolean(ctx, arguments[1]); + if (RJSIsValueArray(ctx, arguments[1])) { + JSObjectRef js_reverse = RJSValidatedValueToObject(ctx, arguments[1]); + size_t js_reverse_count = std::min(RJSValidatedListLength(ctx, js_reverse), prop_count); + + for (unsigned int i = 0; i < js_reverse_count; i++) { + ascending[i] = !JSValueToBoolean(ctx, RJSValidatedPropertyAtIndex(ctx, js_reverse, i)); + } + } + else if (JSValueToBoolean(ctx, arguments[1])) { + ascending.assign(prop_count, false); + } } - results = new Results(results->sort({{prop->table_column}, {ascending}})); + results = new Results(results->sort({std::move(columns), std::move(ascending)})); return RJSWrapObject(ctx, RJSResultsClass(), results); } catch (std::exception &exp) { diff --git a/src/js_util.hpp b/src/js_util.hpp index f74f9991..95886940 100644 --- a/src/js_util.hpp +++ b/src/js_util.hpp @@ -143,6 +143,15 @@ static inline JSValueRef RJSValidatedPropertyValue(JSContextRef ctx, JSObjectRef return propertyValue; } +static inline JSValueRef RJSValidatedPropertyAtIndex(JSContextRef ctx, JSObjectRef object, unsigned int index) { + JSValueRef exception = NULL; + JSValueRef propertyValue = JSObjectGetPropertyAtIndex(ctx, object, index, &exception); + if (exception) { + throw RJSException(ctx, exception); + } + return propertyValue; +} + static inline JSObjectRef RJSValidatedObjectProperty(JSContextRef ctx, JSObjectRef object, JSStringRef property, const char *err = NULL) { JSValueRef propertyValue = RJSValidatedPropertyValue(ctx, object, property); if (JSValueIsUndefined(ctx, propertyValue)) { @@ -152,12 +161,7 @@ static inline JSObjectRef RJSValidatedObjectProperty(JSContextRef ctx, JSObjectR } static inline JSObjectRef RJSValidatedObjectAtIndex(JSContextRef ctx, JSObjectRef object, unsigned int index) { - JSValueRef exception = NULL; - JSValueRef objectValue = JSObjectGetPropertyAtIndex(ctx, object, index, &exception); - if (exception) { - throw RJSException(ctx, exception); - } - return RJSValidatedValueToObject(ctx, objectValue); + return RJSValidatedValueToObject(ctx, RJSValidatedPropertyAtIndex(ctx, object, index)); } static inline std::string RJSValidatedStringProperty(JSContextRef ctx, JSObjectRef object, JSStringRef property) { diff --git a/tests/lib/results-tests.js b/tests/lib/results-tests.js index 85eb626f..76af9e0f 100644 --- a/tests/lib/results-tests.js +++ b/tests/lib/results-tests.js @@ -136,29 +136,61 @@ module.exports = BaseTest.extend({ }); }, testSort: function() { - var realm = new Realm({schema: [schemas.TestObject]}); - var objects = realm.objects('TestObject'); + var realm = new Realm({schema: [schemas.IntPrimary]}); + var objects = realm.objects('IntPrimaryObject'); realm.write(function() { - realm.create('TestObject', {doubleCol: 2}); - realm.create('TestObject', {doubleCol: 3}); - realm.create('TestObject', {doubleCol: 1}); - realm.create('TestObject', {doubleCol: 4}); - realm.create('TestObject', {doubleCol: 0}); + realm.create('IntPrimaryObject', {primaryCol: 2, valueCol: 'a'}); + realm.create('IntPrimaryObject', {primaryCol: 3, valueCol: 'a'}); + realm.create('IntPrimaryObject', {primaryCol: 1, valueCol: 'b'}); + realm.create('IntPrimaryObject', {primaryCol: 4, valueCol: 'c'}); + realm.create('IntPrimaryObject', {primaryCol: 0, valueCol: 'c'}); }); - objects = objects.sortedBy('doubleCol'); - TestCase.assertEqual(objects[0].doubleCol, 0); - TestCase.assertEqual(objects[1].doubleCol, 1); - TestCase.assertEqual(objects[2].doubleCol, 2); - TestCase.assertEqual(objects[3].doubleCol, 3); - TestCase.assertEqual(objects[4].doubleCol, 4); + var primaries = function(results, prop) { + return Array.prototype.map.call(results, function(object) { + return object.primaryCol; + }); + }; - objects = objects.sortedBy('doubleCol', true); - TestCase.assertEqual(objects[0].doubleCol, 4); - TestCase.assertEqual(objects[1].doubleCol, 3); - TestCase.assertEqual(objects[2].doubleCol, 2); - TestCase.assertEqual(objects[3].doubleCol, 1); - TestCase.assertEqual(objects[4].doubleCol, 0); + objects = objects.sortedBy('primaryCol'); + TestCase.assertArraysEqual(primaries(objects), [0, 1, 2, 3, 4]); + + objects = objects.sortedBy('primaryCol', true); + TestCase.assertArraysEqual(primaries(objects), [4, 3, 2, 1, 0]); + + objects = objects.sortedBy(['primaryCol', 'valueCol']); + TestCase.assertArraysEqual(primaries(objects), [0, 1, 2, 3, 4]); + + objects = objects.sortedBy(['primaryCol', 'valueCol'], true); + TestCase.assertArraysEqual(primaries(objects), [4, 3, 2, 1, 0]); + + objects = objects.sortedBy(['primaryCol', 'valueCol'], [false]); + TestCase.assertArraysEqual(primaries(objects), [0, 1, 2, 3, 4]); + + objects = objects.sortedBy(['valueCol', 'primaryCol']); + TestCase.assertArraysEqual(primaries(objects), [2, 3, 1, 0, 4]); + + objects = objects.sortedBy(['valueCol', 'primaryCol'], [false, true]); + TestCase.assertArraysEqual(primaries(objects), [3, 2, 1, 4, 0]); + + objects = objects.sortedBy(['valueCol', 'primaryCol'], [true, false]); + TestCase.assertArraysEqual(primaries(objects), [0, 4, 1, 2, 3]); + + objects = objects.sortedBy(['valueCol', 'primaryCol'], true); + TestCase.assertArraysEqual(primaries(objects), [4, 0, 1, 3, 2]); + + TestCase.assertThrows(function() { + objects.sortedBy(1); + }); + TestCase.assertThrows(function() { + objects.sortedBy([1]); + }); + TestCase.assertThrows(function() { + objects.sortedBy('fish'); + }); + TestCase.assertThrows(function() { + objects.sortedBy(['valueCol', 'fish']); + }); }, });