Add methods to create snapshot of List and Results

The Results class was updated to match the style of List and include a flag (m_live) that determines if it should sync updates. If an object in the static Results is deleted, then it will return null.
This commit is contained in:
Scott Kyle 2015-11-20 14:05:18 -08:00
parent 602613b992
commit 632f9d737e
10 changed files with 126 additions and 10 deletions

View File

@ -13,6 +13,12 @@ module.exports = {
class List {}
// Non-mutating methods:
util.createMethods(List.prototype, constants.propTypes.LIST, [
'snapshot',
]);
// Mutating methods:
util.createMethods(List.prototype, constants.propTypes.LIST, [
'pop',
'shift',

View File

@ -14,6 +14,7 @@ module.exports = {
class Results {}
util.createMethods(Results.prototype, constants.objectTypes.RESULTS, [
'snapshot',
'sortByProperty',
]);

View File

@ -4,6 +4,7 @@
#include "js_list.hpp"
#include "js_object.hpp"
#include "js_results.hpp"
#include "js_util.hpp"
#include "object_accessor.hpp"
@ -184,6 +185,22 @@ JSValueRef ListSplice(JSContextRef ctx, JSObjectRef function, JSObjectRef thisOb
return NULL;
}
JSValueRef ListStaticResults(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException) {
try {
List *list = RJSGetInternal<List *>(thisObject);
RJSValidateArgumentCount(argumentCount, 0);
Query query = list->get_query();
return RJSResultsCreate(ctx, list->realm(), list->object_schema, query, false);
}
catch (std::exception &exp) {
if (jsException) {
*jsException = RJSMakeError(ctx, exp);
}
}
return NULL;
}
JSObjectRef RJSListCreate(JSContextRef ctx, realm::List &list) {
return RJSWrapObject<List *>(ctx, RJSListClass(), new List(list));
}
@ -194,6 +211,7 @@ static const JSStaticFunction RJSListFuncs[] = {
{"shift", ListShift, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete},
{"unshift", ListUnshift, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete},
{"splice", ListSplice, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete},
{"snapshot", ListStaticResults, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete},
{NULL, NULL},
};

View File

@ -22,9 +22,12 @@ JSValueRef ResultsGetProperty(JSContextRef ctx, JSObjectRef object, JSStringRef
return JSValueMakeNumber(ctx, size);
}
return RJSObjectCreate(ctx, Object(results->get_realm(),
results->object_schema,
results->get(RJSValidatedPositiveIndex(indexStr))));
auto row = results->get(RJSValidatedPositiveIndex(indexStr));
if (!row) {
return JSValueMakeNull(ctx);
}
return RJSObjectCreate(ctx, Object(results->get_realm(), results->object_schema, row));
}
catch (std::out_of_range &exp) {
// getters for nonexistent properties in JS should always return undefined
@ -62,19 +65,40 @@ bool ResultsSetProperty(JSContextRef ctx, JSObjectRef object, JSStringRef proper
void ResultsPropertyNames(JSContextRef ctx, JSObjectRef object, JSPropertyNameAccumulatorRef propertyNames) {
Results *results = RJSGetInternal<Results *>(object);
size_t size = results->size();
char str[32];
for (int i = 0; i < results->size(); i++) {
sprintf(str, "%i", i);
for (size_t i = 0; i < size; i++) {
sprintf(str, "%zu", i);
JSStringRef name = JSStringCreateWithUTF8CString(str);
JSPropertyNameAccumulatorAddName(propertyNames, name);
JSStringRelease(name);
}
}
JSValueRef SortByProperty(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException) {
JSValueRef ResultsStaticCopy(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException) {
try {
Results *results = RJSGetInternal<Results *>(thisObject);
RJSValidateArgumentCount(argumentCount, 0);
Results *copy = new Results(*results);
copy->set_live(false);
return RJSWrapObject<Results *>(ctx, RJSResultsClass(), copy);
}
catch (std::exception &exp) {
if (jsException) {
*jsException = RJSMakeError(ctx, exp);
}
}
return NULL;
}
JSValueRef ResultsSortByProperty(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException) {
try {
Results *results = RJSGetInternal<Results *>(thisObject);
RJSValidateArgumentRange(argumentCount, 1, 2);
std::string propName = RJSValidatedStringForValue(ctx, arguments[0]);
const Property *prop = results->object_schema.property_for_name(propName);
if (!prop) {
@ -105,7 +129,6 @@ JSObjectRef RJSResultsCreate(JSContextRef ctx, SharedRealm realm, std::string cl
return RJSWrapObject<Results *>(ctx, RJSResultsClass(), new Results(realm, *object_schema, *table));
}
JSObjectRef RJSResultsCreate(JSContextRef ctx, SharedRealm realm, std::string className, std::string queryString, std::vector<JSValueRef> args) {
TableRef table = ObjectStore::table_for_object_type(realm->read_group(), className);
Query query = table->where();
@ -121,8 +144,16 @@ JSObjectRef RJSResultsCreate(JSContextRef ctx, SharedRealm realm, std::string cl
return RJSWrapObject<Results *>(ctx, RJSResultsClass(), new Results(realm, *object_schema, std::move(query)));
}
JSObjectRef RJSResultsCreate(JSContextRef ctx, SharedRealm realm, const ObjectSchema &objectSchema, const Query &query, bool live) {
Results *results = new Results(realm, objectSchema, query);
results->set_live(live);
return RJSWrapObject<Results *>(ctx, RJSResultsClass(), results);
}
static const JSStaticFunction RJSResultsFuncs[] = {
{"sortByProperty", SortByProperty, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete},
{"snapshot", ResultsStaticCopy, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete},
{"sortByProperty", ResultsSortByProperty, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete},
{NULL, NULL},
};

View File

@ -6,9 +6,11 @@
namespace realm {
class Realm;
class Query;
typedef std::shared_ptr<Realm> SharedRealm;
}
JSClassRef RJSResultsClass();
JSObjectRef RJSResultsCreate(JSContextRef ctx, realm::SharedRealm realm, std::string className);
JSObjectRef RJSResultsCreate(JSContextRef ctx, realm::SharedRealm realm, std::string className, std::string query, std::vector<JSValueRef> args);
JSObjectRef RJSResultsCreate(JSContextRef ctx, realm::SharedRealm realm, const realm::ObjectSchema &objectSchema, const realm::Query &query, bool live = true);

View File

@ -59,6 +59,11 @@ void List::remove(size_t row_ndx) {
m_link_view->remove(row_ndx);
}
Query List::get_query() {
verify_attached();
return m_link_view->get_target_table().where(m_link_view);
}
void List::verify_valid_row(size_t row_ndx, bool insertion) {
size_t size = m_link_view->size();
if (row_ndx > size || (!insertion && row_ndx == size)) {

View File

@ -47,6 +47,8 @@ namespace realm {
template<typename ValueType, typename ContextType>
void set(ContextType ctx, ValueType value, size_t list_ndx);
Query get_query();
void verify_valid_row(size_t row_ndx, bool insertion = false);
void verify_attached();
void verify_in_tranaction();

View File

@ -64,6 +64,17 @@ void Results::validate_write() const
throw InvalidTransactionException("Must be in a write transaction");
}
void Results::set_live(bool live)
{
if (!live && m_mode == Mode::Table) {
m_query = m_table->where();
m_mode = Mode::Query;
}
update_tableview();
m_live = live;
}
size_t Results::size()
{
validate_read();
@ -91,7 +102,7 @@ RowExpr Results::get(size_t row_ndx)
case Mode::TableView:
update_tableview();
if (row_ndx < m_table_view.size())
return m_table_view.get(row_ndx);
return (!m_live && !m_table_view.is_row_attached(row_ndx)) ? RowExpr() : m_table_view.get(row_ndx);
break;
}
@ -147,7 +158,9 @@ void Results::update_tableview()
m_mode = Mode::TableView;
break;
case Mode::TableView:
if (m_live) {
m_table_view.sync_if_needed();
}
break;
}
}

View File

@ -60,6 +60,9 @@ public:
// Get the object type which will be returned by get()
StringData get_object_type() const noexcept { return object_schema.name; }
// Set whether the TableView should sync if needed before accessing results
void set_live(bool live);
// Get the size of this results
// Can be either O(1) or O(N) depending on the state of things
size_t size();
@ -153,6 +156,7 @@ private:
TableView m_table_view;
Table* m_table = nullptr;
SortOrder m_sort;
bool m_live = true;
Mode m_mode = Mode::Empty;

View File

@ -341,4 +341,38 @@ module.exports = BaseTest.extend({
TestCase.assertEqual(array.length, 2);
TestCase.assertEqual(objects.length, 4);
},
testStaticResults: function() {
var realm = new Realm({schema: [schemas.LinkTypes, schemas.TestObject]});
var objects = realm.objects('TestObject');
var array;
realm.write(function() {
var obj = realm.create('LinkTypesObject', [[1], [2], [[3], [4]]]);
array = obj.arrayCol;
});
var objectsCopy = objects.snapshot();
var arrayCopy = array.snapshot();
TestCase.assertEqual(objectsCopy.length, 4);
TestCase.assertEqual(arrayCopy.length, 2);
realm.write(function() {
array.push([5]);
TestCase.assertEqual(objectsCopy.length, 4);
TestCase.assertEqual(arrayCopy.length, 2);
TestCase.assertEqual(objectsCopy.snapshot().length, 4);
TestCase.assertEqual(arrayCopy.snapshot().length, 2);
TestCase.assertEqual(objects.snapshot().length, 5);
TestCase.assertEqual(array.snapshot().length, 3);
realm.delete(array[0]);
TestCase.assertEqual(objectsCopy.length, 4);
TestCase.assertEqual(arrayCopy.length, 2);
TestCase.assertEqual(arrayCopy[0], null);
});
},
});