mirror of
https://github.com/status-im/realm-js.git
synced 2025-01-27 06:44:56 +00:00
Add support for lists of things other than objects
And add a shorthand syntax for schema definitions.
This commit is contained in:
parent
54bbc708e9
commit
9a31febc4c
@ -54,7 +54,7 @@ struct Arguments {
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
using ArgumentsMethodType = void(typename T::Context, typename T::Function, typename T::Object, Arguments<T>, ReturnValue<T> &);
|
||||
using ArgumentsMethodType = void(typename T::Context, typename T::Object, Arguments<T>, ReturnValue<T> &);
|
||||
|
||||
template<typename T>
|
||||
struct PropertyType {
|
||||
|
224
src/js_list.hpp
224
src/js_list.hpp
@ -54,30 +54,33 @@ struct ListClass : ClassDefinition<T, realm::js::List<T>, CollectionClass<T>> {
|
||||
using Object = js::Object<T>;
|
||||
using Value = js::Value<T>;
|
||||
using ReturnValue = js::ReturnValue<T>;
|
||||
using Arguments = js::Arguments<T>;
|
||||
|
||||
static ObjectType create_instance(ContextType, realm::List);
|
||||
|
||||
// properties
|
||||
static void get_length(ContextType, ObjectType, ReturnValue &);
|
||||
static void get_type(ContextType, ObjectType, ReturnValue &);
|
||||
static void get_optional(ContextType, ObjectType, ReturnValue &);
|
||||
static void get_index(ContextType, ObjectType, uint32_t, ReturnValue &);
|
||||
static bool set_index(ContextType, ObjectType, uint32_t, ValueType);
|
||||
|
||||
// methods
|
||||
static void push(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
||||
static void pop(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
||||
static void unshift(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
||||
static void shift(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
||||
static void splice(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
||||
static void snapshot(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
||||
static void filtered(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
||||
static void sorted(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
||||
static void is_valid(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
||||
static void index_of(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
||||
static void push(ContextType, ObjectType, Arguments, ReturnValue &);
|
||||
static void pop(ContextType, ObjectType, Arguments, ReturnValue &);
|
||||
static void unshift(ContextType, ObjectType, Arguments, ReturnValue &);
|
||||
static void shift(ContextType, ObjectType, Arguments, ReturnValue &);
|
||||
static void splice(ContextType, ObjectType, Arguments, ReturnValue &);
|
||||
static void snapshot(ContextType, ObjectType, Arguments, ReturnValue &);
|
||||
static void filtered(ContextType, ObjectType, Arguments, ReturnValue &);
|
||||
static void sorted(ContextType, ObjectType, Arguments, ReturnValue &);
|
||||
static void is_valid(ContextType, ObjectType, Arguments, ReturnValue &);
|
||||
static void index_of(ContextType, ObjectType, Arguments, ReturnValue &);
|
||||
|
||||
// observable
|
||||
static void add_listener(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
||||
static void remove_listener(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
||||
static void remove_all_listeners(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
||||
static void add_listener(ContextType, ObjectType, Arguments, ReturnValue &);
|
||||
static void remove_listener(ContextType, ObjectType, Arguments, ReturnValue &);
|
||||
static void remove_all_listeners(ContextType, ObjectType, Arguments, ReturnValue &);
|
||||
|
||||
std::string const name = "List";
|
||||
|
||||
@ -99,9 +102,14 @@ struct ListClass : ClassDefinition<T, realm::js::List<T>, CollectionClass<T>> {
|
||||
|
||||
PropertyMap<T> const properties = {
|
||||
{"length", {wrap<get_length>, nullptr}},
|
||||
{"type", {wrap<get_type>, nullptr}},
|
||||
{"optional", {wrap<get_optional>, nullptr}},
|
||||
};
|
||||
|
||||
IndexPropertyType<T> const index_accessor = {wrap<get_index>, wrap<set_index>};
|
||||
|
||||
private:
|
||||
static void validate_value(ContextType, realm::List&, ValueType);
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
@ -115,70 +123,83 @@ void ListClass<T>::get_length(ContextType, ObjectType object, ReturnValue &retur
|
||||
return_value.set((uint32_t)list->size());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void ListClass<T>::get_type(ContextType, ObjectType object, ReturnValue &return_value) {
|
||||
auto list = get_internal<T, ListClass<T>>(object);
|
||||
return_value.set(string_for_property_type(list->get_type() & ~realm::PropertyType::Flags));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void ListClass<T>::get_optional(ContextType, ObjectType object, ReturnValue &return_value) {
|
||||
auto list = get_internal<T, ListClass<T>>(object);
|
||||
return_value.set(is_nullable(list->get_type()));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void ListClass<T>::get_index(ContextType ctx, ObjectType object, uint32_t index, ReturnValue &return_value) {
|
||||
auto list = get_internal<T, ListClass<T>>(object);
|
||||
auto realm_object = realm::Object(list->get_realm(), list->get_object_schema(), list->get(index));
|
||||
|
||||
return_value.set(RealmObjectClass<T>::create_instance(ctx, std::move(realm_object)));
|
||||
NativeAccessor<T> accessor(ctx, *list);
|
||||
return_value.set(list->get(accessor, index));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool ListClass<T>::set_index(ContextType ctx, ObjectType object, uint32_t index, ValueType value) {
|
||||
auto list = get_internal<T, ListClass<T>>(object);
|
||||
NativeAccessor<T> accessor(ctx, list->get_realm(), list->get_object_schema());
|
||||
validate_value(ctx, *list, value);
|
||||
NativeAccessor<T> accessor(ctx, *list);
|
||||
list->set(accessor, index, value);
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void ListClass<T>::push(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
|
||||
validate_argument_count_at_least(argc, 1);
|
||||
|
||||
void ListClass<T>::push(ContextType ctx, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||
auto list = get_internal<T, ListClass<T>>(this_object);
|
||||
NativeAccessor<T> accessor(ctx, list->get_realm(), list->get_object_schema());
|
||||
for (size_t i = 0; i < argc; i++) {
|
||||
list->add(accessor, arguments[i]);
|
||||
for (size_t i = 0; i < args.count; i++) {
|
||||
validate_value(ctx, *list, args[i]);
|
||||
}
|
||||
|
||||
NativeAccessor<T> accessor(ctx, *list);
|
||||
for (size_t i = 0; i < args.count; i++) {
|
||||
list->add(accessor, args[i]);
|
||||
}
|
||||
|
||||
return_value.set((uint32_t)list->size());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void ListClass<T>::pop(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
|
||||
validate_argument_count(argc, 0);
|
||||
void ListClass<T>::pop(ContextType ctx, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||
args.validate_maximum(0);
|
||||
|
||||
auto list = get_internal<T, ListClass<T>>(this_object);
|
||||
size_t size = list->size();
|
||||
auto size = static_cast<unsigned int>(list->size());
|
||||
if (size == 0) {
|
||||
list->verify_in_transaction();
|
||||
return_value.set_undefined();
|
||||
}
|
||||
else {
|
||||
size_t index = size - 1;
|
||||
auto realm_object = realm::Object(list->get_realm(), list->get_object_schema(), list->get(index));
|
||||
|
||||
return_value.set(RealmObjectClass<T>::create_instance(ctx, std::move(realm_object)));
|
||||
list->remove(index);
|
||||
get_index(ctx, this_object, size - 1, return_value);
|
||||
list->remove(size - 1);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void ListClass<T>::unshift(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
|
||||
validate_argument_count_at_least(argc, 1);
|
||||
|
||||
void ListClass<T>::unshift(ContextType ctx, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||
auto list = get_internal<T, ListClass<T>>(this_object);
|
||||
NativeAccessor<T> accessor(ctx, list->get_realm(), list->get_object_schema());
|
||||
for (size_t i = 0; i < argc; i++) {
|
||||
list->insert(accessor, i, arguments[i]);
|
||||
for (size_t i = 0; i < args.count; i++) {
|
||||
validate_value(ctx, *list, args[i]);
|
||||
}
|
||||
|
||||
NativeAccessor<T> accessor(ctx, *list);
|
||||
for (size_t i = 0; i < args.count; i++) {
|
||||
list->insert(accessor, i, args[i]);
|
||||
}
|
||||
|
||||
return_value.set((uint32_t)list->size());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void ListClass<T>::shift(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
|
||||
validate_argument_count(argc, 0);
|
||||
void ListClass<T>::shift(ContextType ctx, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||
args.validate_maximum(0);
|
||||
|
||||
auto list = get_internal<T, ListClass<T>>(this_object);
|
||||
if (list->size() == 0) {
|
||||
@ -186,151 +207,108 @@ void ListClass<T>::shift(ContextType ctx, FunctionType, ObjectType this_object,
|
||||
return_value.set_undefined();
|
||||
}
|
||||
else {
|
||||
auto realm_object = realm::Object(list->get_realm(), list->get_object_schema(), list->get(0));
|
||||
|
||||
return_value.set(RealmObjectClass<T>::create_instance(ctx, std::move(realm_object)));
|
||||
get_index(ctx, this_object, 0, return_value);
|
||||
list->remove(0);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void ListClass<T>::splice(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
|
||||
validate_argument_count_at_least(argc, 1);
|
||||
|
||||
void ListClass<T>::splice(ContextType ctx, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||
auto list = get_internal<T, ListClass<T>>(this_object);
|
||||
size_t size = list->size();
|
||||
long index = std::min<long>(Value::to_number(ctx, arguments[0]), size);
|
||||
long index = std::min<long>(Value::to_number(ctx, args[0]), size);
|
||||
if (index < 0) {
|
||||
index = std::max<long>(size + index, 0);
|
||||
}
|
||||
|
||||
size_t remove;
|
||||
if (argc < 2) {
|
||||
if (args.count < 2) {
|
||||
remove = size - index;
|
||||
}
|
||||
else {
|
||||
remove = std::max<long>(Value::to_number(ctx, arguments[1]), 0);
|
||||
remove = std::max<long>(Value::to_number(ctx, args[1]), 0);
|
||||
remove = std::min<long>(remove, size - index);
|
||||
}
|
||||
|
||||
std::vector<ValueType> removed_objects;
|
||||
removed_objects.reserve(remove);
|
||||
|
||||
NativeAccessor<T> accessor(ctx, list->get_realm(), list->get_object_schema());
|
||||
NativeAccessor<T> accessor(ctx, *list);
|
||||
for (size_t i = 0; i < remove; i++) {
|
||||
auto realm_object = realm::Object(list->get_realm(), list->get_object_schema(), list->get(index));
|
||||
|
||||
removed_objects.push_back(RealmObjectClass<T>::create_instance(ctx, std::move(realm_object)));
|
||||
removed_objects.push_back(list->get(accessor, index));
|
||||
list->remove(index);
|
||||
}
|
||||
for (size_t i = 2; i < argc; i++) {
|
||||
list->insert(accessor, index + i - 2, arguments[i]);
|
||||
for (size_t i = 2; i < args.count; i++) {
|
||||
list->insert(accessor, index + i - 2, args[i]);
|
||||
}
|
||||
|
||||
return_value.set(Object::create_array(ctx, removed_objects));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void ListClass<T>::snapshot(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
|
||||
validate_argument_count(argc, 0);
|
||||
|
||||
void ListClass<T>::snapshot(ContextType ctx, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||
args.validate_maximum(0);
|
||||
auto list = get_internal<T, ListClass<T>>(this_object);
|
||||
return_value.set(ResultsClass<T>::create_instance(ctx, list->snapshot()));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void ListClass<T>::filtered(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
|
||||
validate_argument_count_at_least(argc, 1);
|
||||
|
||||
void ListClass<T>::filtered(ContextType ctx, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||
auto list = get_internal<T, ListClass<T>>(this_object);
|
||||
return_value.set(ResultsClass<T>::create_filtered(ctx, *list, argc, arguments));
|
||||
return_value.set(ResultsClass<T>::create_filtered(ctx, *list, args));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void ListClass<T>::sorted(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
|
||||
void ListClass<T>::sorted(ContextType ctx, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||
auto list = get_internal<T, ListClass<T>>(this_object);
|
||||
return_value.set(ResultsClass<T>::create_instance(ctx, list->sort(ResultsClass<T>::get_keypaths(ctx, argc, arguments))));
|
||||
return_value.set(ResultsClass<T>::create_instance(ctx, list->sort(ResultsClass<T>::get_keypaths(ctx, args))));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void ListClass<T>::is_valid(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
|
||||
void ListClass<T>::is_valid(ContextType ctx, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||
return_value.set(get_internal<T, ListClass<T>>(this_object)->is_valid());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void ListClass<T>::index_of(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
|
||||
validate_argument_count(argc, 1);
|
||||
|
||||
ObjectType arg = Value::validated_to_object(ctx, arguments[0]);
|
||||
if (Object::template is_instance<RealmObjectClass<T>>(ctx, arg)) {
|
||||
auto object = get_internal<T, RealmObjectClass<T>>(arg);
|
||||
if (!object->is_valid()) {
|
||||
throw std::runtime_error("Object is invalid. Either it has been previously deleted or the Realm it belongs to has been closed.");
|
||||
}
|
||||
|
||||
void ListClass<T>::index_of(ContextType ctx, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||
auto fn = [&](auto&& row) {
|
||||
auto list = get_internal<T, ListClass<T>>(this_object);
|
||||
size_t ndx = list->find(object->row());
|
||||
|
||||
if (ndx == realm::not_found) {
|
||||
return_value.set(-1);
|
||||
}
|
||||
else {
|
||||
return_value.set((uint32_t)ndx);
|
||||
}
|
||||
}
|
||||
else {
|
||||
return_value.set(-1);
|
||||
}
|
||||
NativeAccessor<T> accessor(ctx, *list);
|
||||
return list->find(accessor, row);
|
||||
};
|
||||
ResultsClass<T>::index_of(ctx, fn, args, return_value);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void ListClass<T>::add_listener(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
|
||||
validate_argument_count(argc, 1);
|
||||
|
||||
void ListClass<T>::add_listener(ContextType ctx, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||
auto list = get_internal<T, ListClass<T>>(this_object);
|
||||
auto callback = Value::validated_to_function(ctx, arguments[0]);
|
||||
Protected<FunctionType> protected_callback(ctx, callback);
|
||||
Protected<ObjectType> protected_this(ctx, this_object);
|
||||
Protected<typename T::GlobalContext> protected_ctx(Context<T>::get_global_context(ctx));
|
||||
|
||||
auto token = list->add_notification_callback([=](CollectionChangeSet change_set, std::exception_ptr exception) {
|
||||
HANDLESCOPE
|
||||
|
||||
ValueType arguments[2];
|
||||
arguments[0] = static_cast<ObjectType>(protected_this);
|
||||
arguments[1] = CollectionClass<T>::create_collection_change_set(protected_ctx, change_set);
|
||||
Function<T>::callback(protected_ctx, protected_callback, protected_this, 2, arguments);
|
||||
});
|
||||
list->m_notification_tokens.emplace_back(protected_callback, std::move(token));
|
||||
ResultsClass<T>::add_listener(ctx, *list, this_object, args);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void ListClass<T>::remove_listener(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
|
||||
validate_argument_count(argc, 1);
|
||||
|
||||
void ListClass<T>::remove_listener(ContextType ctx, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||
auto list = get_internal<T, ListClass<T>>(this_object);
|
||||
auto callback = Value::validated_to_function(ctx, arguments[0]);
|
||||
auto protected_function = Protected<FunctionType>(ctx, callback);
|
||||
|
||||
auto iter = list->m_notification_tokens.begin();
|
||||
typename Protected<FunctionType>::Comparator compare;
|
||||
while (iter != list->m_notification_tokens.end()) {
|
||||
if(compare(iter->first, protected_function)) {
|
||||
iter = list->m_notification_tokens.erase(iter);
|
||||
}
|
||||
else {
|
||||
iter++;
|
||||
}
|
||||
}
|
||||
ResultsClass<T>::remove_listener(ctx, *list, this_object, args);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void ListClass<T>::remove_all_listeners(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
|
||||
validate_argument_count(argc, 0);
|
||||
|
||||
template<typename T>
|
||||
void ListClass<T>::remove_all_listeners(ContextType ctx, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||
args.validate_maximum(0);
|
||||
auto list = get_internal<T, ListClass<T>>(this_object);
|
||||
list->m_notification_tokens.clear();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void ListClass<T>::validate_value(ContextType ctx, realm::List& list, ValueType value) {
|
||||
auto type = list.get_type();
|
||||
StringData object_type;
|
||||
if (type == realm::PropertyType::Object) {
|
||||
object_type = list.get_object_schema().name;
|
||||
}
|
||||
if (!Value::is_valid_for_property_type(ctx, value, type, object_type)) {
|
||||
throw TypeErrorException("Property", object_type ? object_type : string_for_property_type(type), Value::to_string(ctx, value));
|
||||
}
|
||||
}
|
||||
|
||||
} // js
|
||||
} // realm
|
||||
|
@ -47,12 +47,19 @@ public:
|
||||
using OptionalValue = util::Optional<ValueType>;
|
||||
|
||||
NativeAccessor(ContextType ctx, std::shared_ptr<Realm> realm, const ObjectSchema& object_schema)
|
||||
: m_ctx(ctx), m_realm(std::move(realm)), m_object_schema(object_schema) { }
|
||||
: m_ctx(ctx), m_realm(std::move(realm)), m_object_schema(&object_schema) { }
|
||||
|
||||
template<typename Collection>
|
||||
NativeAccessor(ContextType ctx, Collection const& collection)
|
||||
: m_ctx(ctx)
|
||||
, m_realm(collection.get_realm())
|
||||
, m_object_schema(collection.get_type() == realm::PropertyType::Object ? &collection.get_object_schema() : nullptr)
|
||||
{ }
|
||||
|
||||
NativeAccessor(NativeAccessor& parent, const Property& prop)
|
||||
: m_ctx(parent.m_ctx)
|
||||
, m_realm(parent.m_realm)
|
||||
, m_object_schema(*m_realm->schema().find(prop.object_type))
|
||||
, m_object_schema(&*m_realm->schema().find(prop.object_type))
|
||||
{ }
|
||||
|
||||
OptionalValue value_for_property(ValueType dict, std::string const& prop_name, size_t prop_index) {
|
||||
@ -61,11 +68,9 @@ public:
|
||||
return util::none;
|
||||
}
|
||||
ValueType value = Object::get_property(m_ctx, object, prop_name);
|
||||
const auto& prop = m_object_schema.persisted_properties[prop_index];
|
||||
const auto& prop = m_object_schema->persisted_properties[prop_index];
|
||||
if (!Value::is_valid_for_property(m_ctx, value, prop)) {
|
||||
throw TypeErrorException(m_object_schema.name, prop.name,
|
||||
js_type_name_for_property_type(prop.type),
|
||||
print(value));
|
||||
throw TypeErrorException(*this, m_object_schema->name, prop, value);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
@ -79,6 +84,14 @@ public:
|
||||
template<typename T>
|
||||
T unbox(ValueType value, bool create = false, bool update = false);
|
||||
|
||||
template<typename T>
|
||||
util::Optional<T> unbox_optional(ValueType value) {
|
||||
return is_null(value) ? util::none : util::make_optional(unbox<T>(value));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
ValueType box(util::Optional<T> v) { return v ? box(*v) : null_value(); }
|
||||
|
||||
ValueType box(bool boolean) { return Value::from_boolean(m_ctx, boolean); }
|
||||
ValueType box(int64_t number) { return Value::from_number(m_ctx, number); }
|
||||
ValueType box(float number) { return Value::from_number(m_ctx, number); }
|
||||
@ -88,11 +101,20 @@ public:
|
||||
ValueType box(Mixed) { throw std::runtime_error("'Any' type is unsupported"); }
|
||||
|
||||
ValueType box(Timestamp ts) {
|
||||
if (ts.is_null()) {
|
||||
return null_value();
|
||||
}
|
||||
return Object::create_date(m_ctx, ts.get_seconds() * 1000 + ts.get_nanoseconds() / 1000000);
|
||||
}
|
||||
ValueType box(realm::Object realm_object) {
|
||||
return RealmObjectClass<JSEngine>::create_instance(m_ctx, std::move(realm_object));
|
||||
}
|
||||
ValueType box(RowExpr row) {
|
||||
if (!row.is_attached()) {
|
||||
return Value::from_null(m_ctx);
|
||||
}
|
||||
return RealmObjectClass<JSEngine>::create_instance(m_ctx, realm::Object(m_realm, *m_object_schema, row));
|
||||
}
|
||||
ValueType box(realm::List list) {
|
||||
return ListClass<JSEngine>::create_instance(m_ctx, std::move(list));
|
||||
}
|
||||
@ -112,7 +134,7 @@ public:
|
||||
auto obj = Value::validated_to_object(m_ctx, value);
|
||||
uint32_t size = Object::validated_get_length(m_ctx, obj);
|
||||
for (uint32_t i = 0; i < size; ++i) {
|
||||
func(Object::validated_get_object(m_ctx, obj, i));
|
||||
func(Object::get_property(m_ctx, obj, i));
|
||||
}
|
||||
}
|
||||
|
||||
@ -128,12 +150,14 @@ public:
|
||||
void will_change(realm::Object&, realm::Property const&) { }
|
||||
void did_change() { }
|
||||
|
||||
std::string print(ValueType const& v) { return Value::to_string(m_ctx, v); }
|
||||
std::string print(ValueType const&);
|
||||
void print(std::string&, ValueType const&);
|
||||
const char *typeof(ValueType const& v) { return Value::typeof(m_ctx, v); }
|
||||
|
||||
private:
|
||||
ContextType m_ctx;
|
||||
std::shared_ptr<Realm> m_realm;
|
||||
const ObjectSchema& m_object_schema;
|
||||
const ObjectSchema* m_object_schema;
|
||||
std::string m_string_buffer;
|
||||
OwnedBinaryData m_owned_binary_data;
|
||||
|
||||
@ -173,34 +197,37 @@ struct Unbox<JSEngine, double> {
|
||||
template<typename JSEngine>
|
||||
struct Unbox<JSEngine, util::Optional<bool>> {
|
||||
static util::Optional<bool> call(NativeAccessor<JSEngine> *ctx, typename JSEngine::Value const& value, bool, bool) {
|
||||
return js::Value<JSEngine>::validated_to_boolean(ctx->m_ctx, value, "Property");
|
||||
return ctx->template unbox_optional<bool>(value);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename JSEngine>
|
||||
struct Unbox<JSEngine, util::Optional<int64_t>> {
|
||||
static util::Optional<int64_t> call(NativeAccessor<JSEngine> *ctx, typename JSEngine::Value const& value, bool, bool) {
|
||||
return js::Value<JSEngine>::validated_to_number(ctx->m_ctx, value, "Property");
|
||||
return ctx->template unbox_optional<int64_t>(value);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename JSEngine>
|
||||
struct Unbox<JSEngine, util::Optional<float>> {
|
||||
static util::Optional<float> call(NativeAccessor<JSEngine> *ctx, typename JSEngine::Value const& value, bool, bool) {
|
||||
return js::Value<JSEngine>::validated_to_number(ctx->m_ctx, value, "Property");
|
||||
return ctx->template unbox_optional<float>(value);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename JSEngine>
|
||||
struct Unbox<JSEngine, util::Optional<double>> {
|
||||
static util::Optional<double> call(NativeAccessor<JSEngine> *ctx, typename JSEngine::Value const& value, bool, bool) {
|
||||
return js::Value<JSEngine>::validated_to_number(ctx->m_ctx, value, "Property");
|
||||
return ctx->template unbox_optional<double>(value);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename JSEngine>
|
||||
struct Unbox<JSEngine, StringData> {
|
||||
static StringData call(NativeAccessor<JSEngine> *ctx, typename JSEngine::Value const& value, bool, bool) {
|
||||
if (ctx->is_null(value)) {
|
||||
return StringData();
|
||||
}
|
||||
ctx->m_string_buffer = js::Value<JSEngine>::validated_to_string(ctx->m_ctx, value, "Property");
|
||||
return ctx->m_string_buffer;
|
||||
}
|
||||
@ -209,6 +236,9 @@ struct Unbox<JSEngine, StringData> {
|
||||
template<typename JSEngine>
|
||||
struct Unbox<JSEngine, BinaryData> {
|
||||
static BinaryData call(NativeAccessor<JSEngine> *ctx, typename JSEngine::Value value, bool, bool) {
|
||||
if (ctx->is_null(value)) {
|
||||
return BinaryData();
|
||||
}
|
||||
ctx->m_owned_binary_data = js::Value<JSEngine>::validated_to_binary(ctx->m_ctx, value, "Property");
|
||||
return ctx->m_owned_binary_data.get();
|
||||
}
|
||||
@ -224,6 +254,9 @@ struct Unbox<JSEngine, Mixed> {
|
||||
template<typename JSEngine>
|
||||
struct Unbox<JSEngine, Timestamp> {
|
||||
static Timestamp call(NativeAccessor<JSEngine> *ctx, typename JSEngine::Value const& value, bool, bool) {
|
||||
if (ctx->is_null(value)) {
|
||||
return Timestamp();
|
||||
}
|
||||
auto date = js::Value<JSEngine>::validated_to_date(ctx->m_ctx, value, "Property");
|
||||
double milliseconds = js::Value<JSEngine>::to_number(ctx->m_ctx, date);
|
||||
int64_t seconds = milliseconds / 1000;
|
||||
@ -248,15 +281,15 @@ struct Unbox<JSEngine, RowExpr> {
|
||||
throw std::runtime_error("Realm object is from another Realm");
|
||||
}
|
||||
}
|
||||
else if (!create) {
|
||||
throw std::runtime_error("object is not a Realm Object");
|
||||
if (!create) {
|
||||
throw NonRealmObjectException();
|
||||
}
|
||||
|
||||
if (Value::is_array(ctx->m_ctx, object)) {
|
||||
object = Schema<JSEngine>::dict_for_property_array(ctx->m_ctx, ctx->m_object_schema, object);
|
||||
object = Schema<JSEngine>::dict_for_property_array(ctx->m_ctx, *ctx->m_object_schema, object);
|
||||
}
|
||||
|
||||
auto child = realm::Object::create<ValueType>(*ctx, ctx->m_realm, ctx->m_object_schema,
|
||||
auto child = realm::Object::create<ValueType>(*ctx, ctx->m_realm, *ctx->m_object_schema,
|
||||
static_cast<ValueType>(object), try_update);
|
||||
return child.row();
|
||||
}
|
||||
@ -269,6 +302,62 @@ U NativeAccessor<T>::unbox(ValueType value, bool create, bool update) {
|
||||
return _impl::Unbox<T, U>::call(this, std::move(value), create, update);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::string NativeAccessor<T>::print(ValueType const& value) {
|
||||
std::string ret;
|
||||
print(ret, value);
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void NativeAccessor<T>::print(std::string& str, ValueType const& value) {
|
||||
if (Value::is_null(m_ctx, value)) {
|
||||
str += "null";
|
||||
}
|
||||
else if (Value::is_undefined(m_ctx, value)) {
|
||||
str += "undefined";
|
||||
}
|
||||
else if (Value::is_array(m_ctx, value)) {
|
||||
auto array = Value::to_array(m_ctx, value);
|
||||
auto length = Object::validated_get_length(m_ctx, array);
|
||||
|
||||
str += "[";
|
||||
for (uint32_t i = 0; i < length; i++) {
|
||||
print(str, Object::get_property(m_ctx, array, i));
|
||||
if (i + 1 < length) {
|
||||
str += ", ";
|
||||
}
|
||||
}
|
||||
str += "]";
|
||||
}
|
||||
else if (Value::is_object(m_ctx, value)) {
|
||||
auto object = Value::to_object(m_ctx, value);
|
||||
if (Object::template is_instance<RealmObjectClass<T>>(m_ctx, object)) {
|
||||
auto realm_object = get_internal<T, RealmObjectClass<T>>(object);
|
||||
auto& object_schema = realm_object->get_object_schema();
|
||||
str += object_schema.name;
|
||||
str += "{";
|
||||
for (size_t i = 0, count = object_schema.persisted_properties.size(); i < count; ++i) {
|
||||
print(str, realm_object->template get_property_value<ValueType>(*this, object_schema.persisted_properties[i].name));
|
||||
if (i + 1 < count) {
|
||||
str += ", ";
|
||||
}
|
||||
}
|
||||
str += "}";
|
||||
}
|
||||
else {
|
||||
str += Value::to_string(m_ctx, value);
|
||||
}
|
||||
}
|
||||
else if (Value::is_string(m_ctx, value)) {
|
||||
str += "'";
|
||||
str += Value::to_string(m_ctx, value);
|
||||
str += "'";
|
||||
}
|
||||
else {
|
||||
str += Value::to_string(m_ctx, value);
|
||||
}
|
||||
}
|
||||
|
||||
} // js
|
||||
} // realm
|
||||
|
@ -18,6 +18,7 @@
|
||||
|
||||
#include "platform.hpp"
|
||||
#include "realm_coordinator.hpp"
|
||||
#include "js_types.hpp"
|
||||
|
||||
#if REALM_ENABLE_SYNC
|
||||
#include "sync/sync_manager.hpp"
|
||||
@ -61,6 +62,49 @@ void clear_test_state() {
|
||||
SyncManager::shared().configure_file_system(default_realm_file_directory(), SyncManager::MetadataMode::NoEncryption);
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string TypeErrorException::type_string(Property const& prop)
|
||||
{
|
||||
using realm::PropertyType;
|
||||
std::string ret;
|
||||
|
||||
switch (prop.type & ~PropertyType::Flags) {
|
||||
case PropertyType::Int:
|
||||
case PropertyType::Float:
|
||||
case PropertyType::Double:
|
||||
ret = "number";
|
||||
break;
|
||||
case PropertyType::Bool:
|
||||
ret = "boolean";
|
||||
break;
|
||||
case PropertyType::String:
|
||||
ret = "string";
|
||||
break;
|
||||
case PropertyType::Date:
|
||||
ret = "date";
|
||||
break;
|
||||
case PropertyType::Data:
|
||||
ret = "binary";
|
||||
break;
|
||||
case PropertyType::LinkingObjects:
|
||||
case PropertyType::Object:
|
||||
ret = prop.object_type;
|
||||
break;
|
||||
case PropertyType::Any:
|
||||
throw std::runtime_error("'Any' type is not supported");
|
||||
default:
|
||||
REALM_UNREACHABLE();
|
||||
}
|
||||
|
||||
if (realm::is_nullable(prop.type)) {
|
||||
ret += "?";
|
||||
}
|
||||
if (realm::is_array(prop.type)) {
|
||||
ret += "[]";
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
} // js
|
||||
} // realm
|
||||
|
@ -164,22 +164,22 @@ public:
|
||||
static FunctionType create_constructor(ContextType);
|
||||
|
||||
// methods
|
||||
static void objects(ContextType, FunctionType, ObjectType, Arguments, ReturnValue &);
|
||||
static void object_for_primary_key(ContextType, FunctionType, ObjectType, Arguments, ReturnValue &);
|
||||
static void create(ContextType, FunctionType, ObjectType, Arguments, ReturnValue &);
|
||||
static void delete_one(ContextType, FunctionType, ObjectType, Arguments, ReturnValue &);
|
||||
static void delete_all(ContextType, FunctionType, ObjectType, Arguments, ReturnValue &);
|
||||
static void write(ContextType, FunctionType, ObjectType, Arguments, ReturnValue &);
|
||||
static void begin_transaction(ContextType, FunctionType, ObjectType, Arguments, ReturnValue&);
|
||||
static void commit_transaction(ContextType, FunctionType, ObjectType, Arguments, ReturnValue&);
|
||||
static void cancel_transaction(ContextType, FunctionType, ObjectType, Arguments, ReturnValue&);
|
||||
static void add_listener(ContextType, FunctionType, ObjectType, Arguments, ReturnValue &);
|
||||
static void wait_for_download_completion(ContextType, FunctionType, ObjectType, Arguments, ReturnValue &);
|
||||
static void remove_listener(ContextType, FunctionType, ObjectType, Arguments, ReturnValue &);
|
||||
static void remove_all_listeners(ContextType, FunctionType, ObjectType, Arguments, ReturnValue &);
|
||||
static void close(ContextType, FunctionType, ObjectType, Arguments, ReturnValue &);
|
||||
static void compact(ContextType, FunctionType, ObjectType, Arguments, ReturnValue &);
|
||||
static void delete_model(ContextType, FunctionType, ObjectType, Arguments, ReturnValue &);
|
||||
static void objects(ContextType, ObjectType, Arguments, ReturnValue &);
|
||||
static void object_for_primary_key(ContextType, ObjectType, Arguments, ReturnValue &);
|
||||
static void create(ContextType, ObjectType, Arguments, ReturnValue &);
|
||||
static void delete_one(ContextType, ObjectType, Arguments, ReturnValue &);
|
||||
static void delete_all(ContextType, ObjectType, Arguments, ReturnValue &);
|
||||
static void write(ContextType, ObjectType, Arguments, ReturnValue &);
|
||||
static void begin_transaction(ContextType, ObjectType, Arguments, ReturnValue&);
|
||||
static void commit_transaction(ContextType, ObjectType, Arguments, ReturnValue&);
|
||||
static void cancel_transaction(ContextType, ObjectType, Arguments, ReturnValue&);
|
||||
static void add_listener(ContextType, ObjectType, Arguments, ReturnValue &);
|
||||
static void wait_for_download_completion(ContextType, ObjectType, Arguments, ReturnValue &);
|
||||
static void remove_listener(ContextType, ObjectType, Arguments, ReturnValue &);
|
||||
static void remove_all_listeners(ContextType, ObjectType, Arguments, ReturnValue &);
|
||||
static void close(ContextType, ObjectType, Arguments, ReturnValue &);
|
||||
static void compact(ContextType, ObjectType, Arguments, ReturnValue &);
|
||||
static void delete_model(ContextType, ObjectType, Arguments, ReturnValue &);
|
||||
|
||||
// properties
|
||||
static void get_empty(ContextType, ObjectType, ReturnValue &);
|
||||
@ -197,10 +197,10 @@ public:
|
||||
static void constructor(ContextType, ObjectType, size_t, const ValueType[]);
|
||||
static SharedRealm create_shared_realm(ContextType, realm::Realm::Config, bool, ObjectDefaultsMap &&, ConstructorMap &&);
|
||||
|
||||
static void schema_version(ContextType, FunctionType, ObjectType, Arguments, ReturnValue &);
|
||||
static void clear_test_state(ContextType, FunctionType, ObjectType, Arguments, ReturnValue &);
|
||||
static void copy_bundled_realm_files(ContextType, FunctionType, ObjectType, Arguments, ReturnValue &);
|
||||
static void delete_file(ContextType, FunctionType, ObjectType, Arguments, ReturnValue &);
|
||||
static void schema_version(ContextType, ObjectType, Arguments, ReturnValue &);
|
||||
static void clear_test_state(ContextType, ObjectType, Arguments, ReturnValue &);
|
||||
static void copy_bundled_realm_files(ContextType, ObjectType, Arguments, ReturnValue &);
|
||||
static void delete_file(ContextType, ObjectType, Arguments, ReturnValue &);
|
||||
|
||||
// static properties
|
||||
static void get_default_path(ContextType, ObjectType, ReturnValue &);
|
||||
@ -503,7 +503,7 @@ SharedRealm RealmClass<T>::create_shared_realm(ContextType ctx, realm::Realm::Co
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void RealmClass<T>::schema_version(ContextType ctx, FunctionType, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||
void RealmClass<T>::schema_version(ContextType ctx, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||
args.validate_maximum(2);
|
||||
|
||||
realm::Realm::Config config;
|
||||
@ -524,19 +524,19 @@ void RealmClass<T>::schema_version(ContextType ctx, FunctionType, ObjectType thi
|
||||
|
||||
|
||||
template<typename T>
|
||||
void RealmClass<T>::clear_test_state(ContextType ctx, FunctionType, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||
void RealmClass<T>::clear_test_state(ContextType ctx, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||
args.validate_maximum(0);
|
||||
js::clear_test_state();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void RealmClass<T>::copy_bundled_realm_files(ContextType ctx, FunctionType, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||
void RealmClass<T>::copy_bundled_realm_files(ContextType ctx, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||
args.validate_maximum(0);
|
||||
realm::copy_bundled_realm_files();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void RealmClass<T>::delete_file(ContextType ctx, FunctionType, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||
void RealmClass<T>::delete_file(ContextType ctx, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||
args.validate_maximum(1);
|
||||
|
||||
ValueType value = args[0];
|
||||
@ -567,7 +567,7 @@ void RealmClass<T>::delete_file(ContextType ctx, FunctionType, ObjectType this_o
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void RealmClass<T>::delete_model(ContextType ctx, FunctionType, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||
void RealmClass<T>::delete_model(ContextType ctx, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||
args.validate_maximum(1);
|
||||
ValueType value = args[0];
|
||||
|
||||
@ -641,7 +641,7 @@ void RealmClass<T>::get_sync_session(ContextType ctx, ObjectType object, ReturnV
|
||||
#endif
|
||||
|
||||
template<typename T>
|
||||
void RealmClass<T>::wait_for_download_completion(ContextType ctx, FunctionType, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||
void RealmClass<T>::wait_for_download_completion(ContextType ctx, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||
args.validate_maximum(3);
|
||||
auto config_object = Value::validated_to_object(ctx, args[0]);
|
||||
auto callback_function = Value::validated_to_function(ctx, args[1 + (args.count == 3)]);
|
||||
@ -743,7 +743,8 @@ void RealmClass<T>::wait_for_download_completion(ContextType ctx, FunctionType,
|
||||
}
|
||||
|
||||
ObjectType object = Object::create_empty(protected_ctx);
|
||||
Object::set_property(protected_ctx, object, "message", Value::from_string(protected_ctx, "Cannot asynchronously open synced Realm, because the associated session previously experienced a fatal error"));
|
||||
Object::set_property(protected_ctx, object, "message",
|
||||
Value::from_string(protected_ctx, "Cannot asynchronously open synced Realm because the associated session previously experienced a fatal error"));
|
||||
Object::set_property(protected_ctx, object, "errorCode", Value::from_number(protected_ctx, 1));
|
||||
|
||||
ValueType callback_arguments[1];
|
||||
@ -760,7 +761,7 @@ void RealmClass<T>::wait_for_download_completion(ContextType ctx, FunctionType,
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void RealmClass<T>::objects(ContextType ctx, FunctionType, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||
void RealmClass<T>::objects(ContextType ctx, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||
args.validate_maximum(1);
|
||||
|
||||
SharedRealm realm = *get_internal<T, RealmClass<T>>(this_object);
|
||||
@ -771,7 +772,7 @@ void RealmClass<T>::objects(ContextType ctx, FunctionType, ObjectType this_objec
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void RealmClass<T>::object_for_primary_key(ContextType ctx, FunctionType, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||
void RealmClass<T>::object_for_primary_key(ContextType ctx, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||
args.validate_maximum(2);
|
||||
|
||||
SharedRealm realm = *get_internal<T, RealmClass<T>>(this_object);
|
||||
@ -789,7 +790,7 @@ void RealmClass<T>::object_for_primary_key(ContextType ctx, FunctionType, Object
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void RealmClass<T>::create(ContextType ctx, FunctionType, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||
void RealmClass<T>::create(ContextType ctx, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||
args.validate_maximum(3);
|
||||
|
||||
SharedRealm realm = *get_internal<T, RealmClass<T>>(this_object);
|
||||
@ -813,7 +814,7 @@ void RealmClass<T>::create(ContextType ctx, FunctionType, ObjectType this_object
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void RealmClass<T>::delete_one(ContextType ctx, FunctionType, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||
void RealmClass<T>::delete_one(ContextType ctx, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||
args.validate_maximum(1);
|
||||
|
||||
SharedRealm realm = *get_internal<T, RealmClass<T>>(this_object);
|
||||
@ -861,7 +862,7 @@ void RealmClass<T>::delete_one(ContextType ctx, FunctionType, ObjectType this_ob
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void RealmClass<T>::delete_all(ContextType ctx, FunctionType, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||
void RealmClass<T>::delete_all(ContextType ctx, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||
args.validate_maximum(0);
|
||||
|
||||
SharedRealm realm = *get_internal<T, RealmClass<T>>(this_object);
|
||||
@ -877,7 +878,7 @@ void RealmClass<T>::delete_all(ContextType ctx, FunctionType, ObjectType this_ob
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void RealmClass<T>::write(ContextType ctx, FunctionType, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||
void RealmClass<T>::write(ContextType ctx, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||
args.validate_maximum(1);
|
||||
|
||||
SharedRealm realm = *get_internal<T, RealmClass<T>>(this_object);
|
||||
@ -897,7 +898,7 @@ void RealmClass<T>::write(ContextType ctx, FunctionType, ObjectType this_object,
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void RealmClass<T>::begin_transaction(ContextType ctx, FunctionType, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||
void RealmClass<T>::begin_transaction(ContextType ctx, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||
args.validate_maximum(0);
|
||||
|
||||
SharedRealm realm = *get_internal<T, RealmClass<T>>(this_object);
|
||||
@ -905,7 +906,7 @@ void RealmClass<T>::begin_transaction(ContextType ctx, FunctionType, ObjectType
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void RealmClass<T>::commit_transaction(ContextType ctx, FunctionType, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||
void RealmClass<T>::commit_transaction(ContextType ctx, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||
args.validate_maximum(0);
|
||||
|
||||
SharedRealm realm = *get_internal<T, RealmClass<T>>(this_object);
|
||||
@ -913,7 +914,7 @@ void RealmClass<T>::commit_transaction(ContextType ctx, FunctionType, ObjectType
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void RealmClass<T>::cancel_transaction(ContextType ctx, FunctionType, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||
void RealmClass<T>::cancel_transaction(ContextType ctx, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||
args.validate_maximum(0);
|
||||
|
||||
SharedRealm realm = *get_internal<T, RealmClass<T>>(this_object);
|
||||
@ -921,7 +922,7 @@ void RealmClass<T>::cancel_transaction(ContextType ctx, FunctionType, ObjectType
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void RealmClass<T>::add_listener(ContextType ctx, FunctionType, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||
void RealmClass<T>::add_listener(ContextType ctx, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||
args.validate_maximum(2);
|
||||
|
||||
validated_notification_name(ctx, args[0]);
|
||||
@ -933,7 +934,7 @@ void RealmClass<T>::add_listener(ContextType ctx, FunctionType, ObjectType this_
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void RealmClass<T>::remove_listener(ContextType ctx, FunctionType, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||
void RealmClass<T>::remove_listener(ContextType ctx, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||
args.validate_maximum(2);
|
||||
|
||||
validated_notification_name(ctx, args[0]);
|
||||
@ -945,7 +946,7 @@ void RealmClass<T>::remove_listener(ContextType ctx, FunctionType, ObjectType th
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void RealmClass<T>::remove_all_listeners(ContextType ctx, FunctionType, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||
void RealmClass<T>::remove_all_listeners(ContextType ctx, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||
args.validate_maximum(1);
|
||||
if (args.count) {
|
||||
validated_notification_name(ctx, args[0]);
|
||||
@ -957,7 +958,7 @@ void RealmClass<T>::remove_all_listeners(ContextType ctx, FunctionType, ObjectTy
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void RealmClass<T>::close(ContextType ctx, FunctionType, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||
void RealmClass<T>::close(ContextType ctx, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||
args.validate_maximum(0);
|
||||
|
||||
SharedRealm realm = *get_internal<T, RealmClass<T>>(this_object);
|
||||
@ -965,7 +966,7 @@ void RealmClass<T>::close(ContextType ctx, FunctionType, ObjectType this_object,
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void RealmClass<T>::compact(ContextType ctx, FunctionType, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||
void RealmClass<T>::compact(ContextType ctx, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||
args.validate_maximum(0);
|
||||
|
||||
SharedRealm realm = *get_internal<T, RealmClass<T>>(this_object);
|
||||
|
@ -127,9 +127,7 @@ bool RealmObjectClass<T>::set_property(ContextType ctx, ObjectType object, const
|
||||
|
||||
NativeAccessor<T> accessor(ctx, realm_object->realm(), realm_object->get_object_schema());
|
||||
if (!Value::is_valid_for_property(ctx, value, *prop)) {
|
||||
throw TypeErrorException(realm_object->get_object_schema().name, property_name,
|
||||
js_type_name_for_property_type(prop->type),
|
||||
accessor.print(value));
|
||||
throw TypeErrorException(accessor, realm_object->get_object_schema().name, *prop, value);
|
||||
}
|
||||
|
||||
realm_object->set_property_value(accessor, property_name, value, true);
|
||||
|
@ -33,6 +33,10 @@ namespace js {
|
||||
template<typename>
|
||||
class NativeAccessor;
|
||||
|
||||
struct NonRealmObjectException : public std::logic_error {
|
||||
NonRealmObjectException() : std::logic_error("Object is not a Realm object") { }
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class Results : public realm::Results {
|
||||
public:
|
||||
@ -56,29 +60,40 @@ struct ResultsClass : ClassDefinition<T, realm::js::Results<T>, CollectionClass<
|
||||
using Object = js::Object<T>;
|
||||
using Value = js::Value<T>;
|
||||
using ReturnValue = js::ReturnValue<T>;
|
||||
using Arguments = js::Arguments<T>;
|
||||
|
||||
static ObjectType create_instance(ContextType, realm::Results);
|
||||
static ObjectType create_instance(ContextType, SharedRealm, const std::string &object_type);
|
||||
|
||||
template<typename U>
|
||||
static ObjectType create_filtered(ContextType, const U &, size_t, const ValueType[]);
|
||||
static ObjectType create_filtered(ContextType, const U &, Arguments);
|
||||
|
||||
static std::vector<std::pair<std::string, bool>> get_keypaths(ContextType, size_t, const ValueType[]);
|
||||
static std::vector<std::pair<std::string, bool>> get_keypaths(ContextType, Arguments);
|
||||
|
||||
static void get_length(ContextType, ObjectType, ReturnValue &);
|
||||
static void get_type(ContextType, ObjectType, ReturnValue &);
|
||||
static void get_optional(ContextType, ObjectType, ReturnValue &);
|
||||
static void get_index(ContextType, ObjectType, uint32_t, ReturnValue &);
|
||||
|
||||
static void snapshot(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
||||
static void filtered(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
||||
static void sorted(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
||||
static void is_valid(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
||||
static void snapshot(ContextType, ObjectType, Arguments, ReturnValue &);
|
||||
static void filtered(ContextType, ObjectType, Arguments, ReturnValue &);
|
||||
static void sorted(ContextType, ObjectType, Arguments, ReturnValue &);
|
||||
static void is_valid(ContextType, ObjectType, Arguments, ReturnValue &);
|
||||
|
||||
static void index_of(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
||||
static void index_of(ContextType, ObjectType, Arguments, ReturnValue &);
|
||||
|
||||
template<typename Fn>
|
||||
static void index_of(ContextType, Fn&, Arguments, ReturnValue &);
|
||||
|
||||
// observable
|
||||
static void add_listener(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
||||
static void remove_listener(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
||||
static void remove_all_listeners(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
||||
static void add_listener(ContextType, ObjectType, Arguments, ReturnValue &);
|
||||
static void remove_listener(ContextType, ObjectType, Arguments, ReturnValue &);
|
||||
static void remove_all_listeners(ContextType, ObjectType, Arguments, ReturnValue &);
|
||||
|
||||
template<typename U>
|
||||
static void add_listener(ContextType, U&, ObjectType, Arguments);
|
||||
template<typename U>
|
||||
static void remove_listener(ContextType, U&, ObjectType, Arguments);
|
||||
|
||||
std::string const name = "Results";
|
||||
|
||||
@ -95,6 +110,8 @@ struct ResultsClass : ClassDefinition<T, realm::js::Results<T>, CollectionClass<
|
||||
|
||||
PropertyMap<T> const properties = {
|
||||
{"length", {wrap<get_length>, nullptr}},
|
||||
{"type", {wrap<get_type>, nullptr}},
|
||||
{"optional", {wrap<get_optional>, nullptr}},
|
||||
};
|
||||
|
||||
IndexPropertyType<T> const index_accessor = {wrap<get_index>, nullptr};
|
||||
@ -116,15 +133,19 @@ typename T::Object ResultsClass<T>::create_instance(ContextType ctx, SharedRealm
|
||||
|
||||
template<typename T>
|
||||
template<typename U>
|
||||
typename T::Object ResultsClass<T>::create_filtered(ContextType ctx, const U &collection, size_t argc, const ValueType arguments[]) {
|
||||
auto query_string = Value::validated_to_string(ctx, arguments[0], "predicate");
|
||||
typename T::Object ResultsClass<T>::create_filtered(ContextType ctx, const U &collection, Arguments args) {
|
||||
if (collection.get_type() != realm::PropertyType::Object) {
|
||||
throw std::runtime_error("Filtering non-object Lists and Results is not yet implemented.");
|
||||
}
|
||||
|
||||
auto query_string = Value::validated_to_string(ctx, args[0], "predicate");
|
||||
auto query = collection.get_query();
|
||||
auto const &realm = collection.get_realm();
|
||||
auto const &object_schema = collection.get_object_schema();
|
||||
|
||||
parser::Predicate predicate = parser::parse(query_string);
|
||||
NativeAccessor<T> accessor(ctx, realm, object_schema);
|
||||
query_builder::ArgumentConverter<ValueType, NativeAccessor<T>> converter(accessor, &arguments[1], argc - 1);
|
||||
query_builder::ArgumentConverter<ValueType, NativeAccessor<T>> converter(accessor, &args.value[1], args.count - 1);
|
||||
query_builder::apply_predicate(query, predicate, converter, realm->schema(), object_schema.name);
|
||||
|
||||
return create_instance(ctx, collection.filter(std::move(query)));
|
||||
@ -132,15 +153,18 @@ typename T::Object ResultsClass<T>::create_filtered(ContextType ctx, const U &co
|
||||
|
||||
template<typename T>
|
||||
std::vector<std::pair<std::string, bool>>
|
||||
ResultsClass<T>::get_keypaths(ContextType ctx, size_t argc, const ValueType arguments[]) {
|
||||
validate_argument_count(argc, 1, 2);
|
||||
ResultsClass<T>::get_keypaths(ContextType ctx, Arguments args) {
|
||||
args.validate_maximum(2);
|
||||
|
||||
std::vector<std::pair<std::string, bool>> sort_order;
|
||||
if (args.count == 0) {
|
||||
sort_order.emplace_back("self", true);
|
||||
return sort_order;
|
||||
}
|
||||
else if (Value::is_array(ctx, args[0])) {
|
||||
validate_argument_count(args.count, 1, "Second argument is not allowed if passed an array of sort descriptors");
|
||||
|
||||
if (argc > 0 && Value::is_array(ctx, arguments[0])) {
|
||||
validate_argument_count(argc, 1, "Second argument is not allowed if passed an array of sort descriptors");
|
||||
|
||||
ObjectType js_prop_names = Value::validated_to_object(ctx, arguments[0]);
|
||||
ObjectType js_prop_names = Value::validated_to_object(ctx, args[0]);
|
||||
size_t prop_count = Object::validated_get_length(ctx, js_prop_names);
|
||||
sort_order.reserve(prop_count);
|
||||
|
||||
@ -158,8 +182,13 @@ ResultsClass<T>::get_keypaths(ContextType ctx, size_t argc, const ValueType argu
|
||||
}
|
||||
}
|
||||
else {
|
||||
sort_order.emplace_back(Value::validated_to_string(ctx, arguments[0]),
|
||||
argc == 1 || !Value::to_boolean(ctx, arguments[1]));
|
||||
if (Value::is_boolean(ctx, args[0])) {
|
||||
sort_order.emplace_back("self", !Value::to_boolean(ctx, args[0]));
|
||||
}
|
||||
else {
|
||||
sort_order.emplace_back(Value::validated_to_string(ctx, args[0]),
|
||||
args.count == 1 || !Value::to_boolean(ctx, args[1]));
|
||||
}
|
||||
}
|
||||
return sort_order;
|
||||
}
|
||||
@ -171,124 +200,136 @@ void ResultsClass<T>::get_length(ContextType ctx, ObjectType object, ReturnValue
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void ResultsClass<T>::get_index(ContextType ctx, ObjectType object, uint32_t index, ReturnValue &return_value) {
|
||||
void ResultsClass<T>::get_type(ContextType, ObjectType object, ReturnValue &return_value) {
|
||||
auto results = get_internal<T, ResultsClass<T>>(object);
|
||||
auto row = results->get(index);
|
||||
|
||||
// Return null for deleted objects in a snapshot.
|
||||
if (!row.is_attached()) {
|
||||
return_value.set_null();
|
||||
return;
|
||||
}
|
||||
|
||||
auto realm_object = realm::Object(results->get_realm(), results->get_object_schema(), results->get(index));
|
||||
return_value.set(RealmObjectClass<T>::create_instance(ctx, std::move(realm_object)));
|
||||
return_value.set(string_for_property_type(results->get_type() & ~realm::PropertyType::Flags));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void ResultsClass<T>::snapshot(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
|
||||
validate_argument_count(argc, 0);
|
||||
void ResultsClass<T>::get_optional(ContextType, ObjectType object, ReturnValue &return_value) {
|
||||
auto results = get_internal<T, ResultsClass<T>>(object);
|
||||
return_value.set(is_nullable(results->get_type()));
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
void ResultsClass<T>::get_index(ContextType ctx, ObjectType object, uint32_t index, ReturnValue &return_value) {
|
||||
auto results = get_internal<T, ResultsClass<T>>(object);
|
||||
NativeAccessor<T> accessor(ctx, *results);
|
||||
return_value.set(results->get(accessor, index));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void ResultsClass<T>::snapshot(ContextType ctx, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||
args.validate_maximum(0);
|
||||
auto results = get_internal<T, ResultsClass<T>>(this_object);
|
||||
return_value.set(ResultsClass<T>::create_instance(ctx, results->snapshot()));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void ResultsClass<T>::filtered(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
|
||||
validate_argument_count_at_least(argc, 1);
|
||||
|
||||
void ResultsClass<T>::filtered(ContextType ctx, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||
auto results = get_internal<T, ResultsClass<T>>(this_object);
|
||||
return_value.set(create_filtered(ctx, *results, argc, arguments));
|
||||
return_value.set(create_filtered(ctx, *results, args));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void ResultsClass<T>::sorted(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
|
||||
void ResultsClass<T>::sorted(ContextType ctx, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||
auto results = get_internal<T, ResultsClass<T>>(this_object);
|
||||
return_value.set(ResultsClass<T>::create_instance(ctx, results->sort(ResultsClass<T>::get_keypaths(ctx, argc, arguments))));
|
||||
return_value.set(ResultsClass<T>::create_instance(ctx, results->sort(ResultsClass<T>::get_keypaths(ctx, args))));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void ResultsClass<T>::is_valid(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
|
||||
void ResultsClass<T>::is_valid(ContextType ctx, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||
return_value.set(get_internal<T, ResultsClass<T>>(this_object)->is_valid());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void ResultsClass<T>::index_of(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
|
||||
validate_argument_count(argc, 1);
|
||||
|
||||
ObjectType arg = Value::validated_to_object(ctx, arguments[0]);
|
||||
if (Object::template is_instance<RealmObjectClass<T>>(ctx, arg)) {
|
||||
auto object = get_internal<T, RealmObjectClass<T>>(arg);
|
||||
if (!object->is_valid()) {
|
||||
throw std::runtime_error("Object is invalid. Either it has been previously deleted or the Realm it belongs to has been closed.");
|
||||
}
|
||||
|
||||
size_t ndx;
|
||||
try {
|
||||
auto results = get_internal<T, ResultsClass<T>>(this_object);
|
||||
ndx = results->index_of(object->row());
|
||||
}
|
||||
catch (realm::Results::IncorrectTableException &) {
|
||||
throw std::runtime_error("Object type does not match the type contained in result");
|
||||
}
|
||||
|
||||
if (ndx == realm::not_found) {
|
||||
return_value.set(-1);
|
||||
}
|
||||
else {
|
||||
return_value.set((uint32_t)ndx);
|
||||
}
|
||||
template<typename Fn>
|
||||
void ResultsClass<T>::index_of(ContextType ctx, Fn& fn, Arguments args, ReturnValue &return_value) {
|
||||
args.validate_maximum(1);
|
||||
|
||||
size_t ndx;
|
||||
try {
|
||||
ndx = fn(args[0]);
|
||||
}
|
||||
else {
|
||||
catch (realm::Results::IncorrectTableException &) {
|
||||
throw std::runtime_error("Object type does not match the type contained in result");
|
||||
}
|
||||
catch (NonRealmObjectException&) {
|
||||
ndx = realm::not_found;
|
||||
}
|
||||
|
||||
if (ndx == realm::not_found) {
|
||||
return_value.set(-1);
|
||||
}
|
||||
else {
|
||||
return_value.set((uint32_t)ndx);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
void ResultsClass<T>::add_listener(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
|
||||
validate_argument_count(argc, 1);
|
||||
|
||||
auto results = get_internal<T, ResultsClass<T>>(this_object);
|
||||
auto callback = Value::validated_to_function(ctx, arguments[0]);
|
||||
void ResultsClass<T>::index_of(ContextType ctx, ObjectType this_object,
|
||||
Arguments args, ReturnValue &return_value) {
|
||||
auto fn = [&](auto&& row) {
|
||||
auto results = get_internal<T, ResultsClass<T>>(this_object);
|
||||
NativeAccessor<T> accessor(ctx, *results);
|
||||
return results->index_of(accessor, row);
|
||||
};
|
||||
index_of(ctx, fn, args, return_value);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
template<typename U>
|
||||
void ResultsClass<T>::add_listener(ContextType ctx, U& collection, ObjectType this_object, Arguments args) {
|
||||
args.validate_maximum(1);
|
||||
|
||||
auto callback = Value::validated_to_function(ctx, args[0]);
|
||||
Protected<FunctionType> protected_callback(ctx, callback);
|
||||
Protected<ObjectType> protected_this(ctx, this_object);
|
||||
Protected<typename T::GlobalContext> protected_ctx(Context<T>::get_global_context(ctx));
|
||||
|
||||
auto token = results->add_notification_callback([=](CollectionChangeSet change_set, std::exception_ptr exception) {
|
||||
auto token = collection.add_notification_callback([=](CollectionChangeSet const& change_set, std::exception_ptr exception) {
|
||||
HANDLESCOPE
|
||||
|
||||
ValueType arguments[2];
|
||||
arguments[0] = static_cast<ObjectType>(protected_this);
|
||||
arguments[1] = CollectionClass<T>::create_collection_change_set(protected_ctx, change_set);
|
||||
ValueType arguments[] {
|
||||
static_cast<ObjectType>(protected_this),
|
||||
CollectionClass<T>::create_collection_change_set(protected_ctx, change_set)
|
||||
};
|
||||
Function<T>::callback(protected_ctx, protected_callback, protected_this, 2, arguments);
|
||||
});
|
||||
results->m_notification_tokens.emplace_back(protected_callback, std::move(token));
|
||||
collection.m_notification_tokens.emplace_back(protected_callback, std::move(token));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void ResultsClass<T>::remove_listener(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
|
||||
validate_argument_count(argc, 1);
|
||||
|
||||
void ResultsClass<T>::add_listener(ContextType ctx, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||
auto results = get_internal<T, ResultsClass<T>>(this_object);
|
||||
auto callback = Value::validated_to_function(ctx, arguments[0]);
|
||||
auto protected_function = Protected<FunctionType>(ctx, callback);
|
||||
|
||||
auto iter = results->m_notification_tokens.begin();
|
||||
typename Protected<FunctionType>::Comparator compare;
|
||||
while (iter != results->m_notification_tokens.end()) {
|
||||
if(compare(iter->first, protected_function)) {
|
||||
iter = results->m_notification_tokens.erase(iter);
|
||||
}
|
||||
else {
|
||||
iter++;
|
||||
}
|
||||
}
|
||||
add_listener(ctx, *results, this_object, args);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void ResultsClass<T>::remove_all_listeners(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
|
||||
validate_argument_count(argc, 0);
|
||||
|
||||
template<typename U>
|
||||
void ResultsClass<T>::remove_listener(ContextType ctx, U& collection, ObjectType this_object, Arguments args) {
|
||||
args.validate_maximum(1);
|
||||
|
||||
auto callback = Value::validated_to_function(ctx, args[0]);
|
||||
auto protected_function = Protected<FunctionType>(ctx, callback);
|
||||
|
||||
auto& tokens = collection.m_notification_tokens;
|
||||
auto compare = [&](auto&& token) {
|
||||
return typename Protected<FunctionType>::Comparator()(token.first, protected_function);
|
||||
};
|
||||
tokens.erase(std::remove_if(tokens.begin(), tokens.end(), compare), tokens.end());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void ResultsClass<T>::remove_listener(ContextType ctx, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||
auto results = get_internal<T, ResultsClass<T>>(this_object);
|
||||
remove_listener(ctx, *results, this_object, args);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void ResultsClass<T>::remove_all_listeners(ContextType ctx, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||
args.validate_maximum(0);
|
||||
|
||||
auto results = get_internal<T, ResultsClass<T>>(this_object);
|
||||
results->m_notification_tokens.clear();
|
||||
}
|
||||
|
@ -23,6 +23,8 @@
|
||||
#include "js_types.hpp"
|
||||
#include "schema.hpp"
|
||||
|
||||
#include "util/format.hpp"
|
||||
|
||||
namespace realm {
|
||||
namespace js {
|
||||
|
||||
@ -41,7 +43,7 @@ struct Schema {
|
||||
using ConstructorMap = std::map<std::string, Protected<FunctionType>>;
|
||||
|
||||
static ObjectType dict_for_property_array(ContextType, const ObjectSchema &, ObjectType);
|
||||
static Property parse_property(ContextType, ValueType, std::string, ObjectDefaults &);
|
||||
static Property parse_property(ContextType, ValueType, StringData, std::string, ObjectDefaults &);
|
||||
static ObjectSchema parse_object_schema(ContextType, ObjectType, ObjectDefaultsMap &, ConstructorMap &);
|
||||
static realm::Schema parse_schema(ContextType, ObjectType, ObjectDefaultsMap &, ConstructorMap &);
|
||||
|
||||
@ -68,8 +70,67 @@ typename T::Object Schema<T>::dict_for_property_array(ContextType ctx, const Obj
|
||||
return dict;
|
||||
}
|
||||
|
||||
static inline void parse_property_type(StringData object_name, Property& prop, StringData type)
|
||||
{
|
||||
using realm::PropertyType;
|
||||
if (!type) {
|
||||
throw std::logic_error(util::format("Property %1.%2 must have a non-empty type", object_name, prop.name));
|
||||
}
|
||||
if (type.ends_with("[]")) {
|
||||
prop.type |= PropertyType::Array;
|
||||
type = type.substr(0, type.size() - 2);
|
||||
}
|
||||
if (type.ends_with("?")) {
|
||||
prop.type |= PropertyType::Nullable;
|
||||
type = type.substr(0, type.size() - 1);
|
||||
}
|
||||
|
||||
if (type == "bool") {
|
||||
prop.type |= PropertyType::Bool;
|
||||
}
|
||||
else if (type == "int") {
|
||||
prop.type |= PropertyType::Int;
|
||||
}
|
||||
else if (type == "float") {
|
||||
prop.type |= PropertyType::Float;
|
||||
}
|
||||
else if (type == "double") {
|
||||
prop.type |= PropertyType::Double;
|
||||
}
|
||||
else if (type == "string") {
|
||||
prop.type |= PropertyType::String;
|
||||
}
|
||||
else if (type == "date") {
|
||||
prop.type |= PropertyType::Date;
|
||||
}
|
||||
else if (type == "data") {
|
||||
prop.type |= PropertyType::Data;
|
||||
}
|
||||
else if (type == "list") {
|
||||
prop.type |= PropertyType::Object | PropertyType::Array;
|
||||
}
|
||||
else if (type == "linkingObjects") {
|
||||
prop.type |= PropertyType::LinkingObjects | PropertyType::Array;
|
||||
}
|
||||
else if (type == "object") {
|
||||
prop.type |= PropertyType::Object;
|
||||
}
|
||||
else {
|
||||
// The type could be the name of another object type in the same schema.
|
||||
prop.type |= PropertyType::Object;
|
||||
prop.object_type = type;
|
||||
}
|
||||
|
||||
// Object properties are implicitly optional
|
||||
if (prop.type == PropertyType::Object && !is_array(prop.type)) {
|
||||
prop.type |= PropertyType::Nullable;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
Property Schema<T>::parse_property(ContextType ctx, ValueType attributes, std::string property_name, ObjectDefaults &object_defaults) {
|
||||
Property Schema<T>::parse_property(ContextType ctx, ValueType attributes, StringData object_name,
|
||||
std::string property_name, ObjectDefaults &object_defaults) {
|
||||
static const String default_string = "default";
|
||||
static const String indexed_string = "indexed";
|
||||
static const String type_string = "type";
|
||||
@ -78,79 +139,23 @@ Property Schema<T>::parse_property(ContextType ctx, ValueType attributes, std::s
|
||||
static const String property_string = "property";
|
||||
|
||||
Property prop;
|
||||
prop.name = property_name;
|
||||
prop.name = std::move(property_name);
|
||||
|
||||
ObjectType property_object = {};
|
||||
std::string type;
|
||||
|
||||
using realm::PropertyType;
|
||||
PropertyType is_optional = PropertyType::Required;
|
||||
|
||||
if (Value::is_object(ctx, attributes)) {
|
||||
property_object = Value::validated_to_object(ctx, attributes);
|
||||
type = Object::validated_get_string(ctx, property_object, type_string);
|
||||
std::string property_type = Object::validated_get_string(ctx, property_object, type_string);
|
||||
parse_property_type(object_name, prop, property_type);
|
||||
|
||||
ValueType optional_value = Object::get_property(ctx, property_object, optional_string);
|
||||
if (!Value::is_undefined(ctx, optional_value) && Value::validated_to_boolean(ctx, optional_value, "optional")) {
|
||||
is_optional = PropertyType::Nullable;
|
||||
prop.type |= PropertyType::Nullable;
|
||||
}
|
||||
}
|
||||
else {
|
||||
type = Value::validated_to_string(ctx, attributes);
|
||||
}
|
||||
|
||||
if (type == "bool") {
|
||||
prop.type = PropertyType::Bool | is_optional;
|
||||
}
|
||||
else if (type == "int") {
|
||||
prop.type = PropertyType::Int | is_optional;
|
||||
}
|
||||
else if (type == "float") {
|
||||
prop.type = PropertyType::Float | is_optional;
|
||||
}
|
||||
else if (type == "double") {
|
||||
prop.type = PropertyType::Double | is_optional;
|
||||
}
|
||||
else if (type == "string") {
|
||||
prop.type = PropertyType::String | is_optional;
|
||||
}
|
||||
else if (type == "date") {
|
||||
prop.type = PropertyType::Date | is_optional;
|
||||
}
|
||||
else if (type == "data") {
|
||||
prop.type = PropertyType::Data | is_optional;
|
||||
}
|
||||
else if (type == "list") {
|
||||
if (!Value::is_valid(property_object)) {
|
||||
throw std::runtime_error("List property must specify 'objectType'");
|
||||
}
|
||||
prop.type = PropertyType::Object | PropertyType::Array;
|
||||
prop.object_type = Object::validated_get_string(ctx, property_object, object_type_string);
|
||||
}
|
||||
else if (type == "linkingObjects") {
|
||||
prop.type = PropertyType::LinkingObjects | PropertyType::Array;
|
||||
|
||||
if (!Value::is_valid(property_object)) {
|
||||
throw std::runtime_error("Object property must specify 'objectType'");
|
||||
}
|
||||
prop.object_type = Object::validated_get_string(ctx, property_object, object_type_string);
|
||||
prop.link_origin_property_name = Object::validated_get_string(ctx, property_object, property_string);
|
||||
}
|
||||
else if (type == "object") {
|
||||
prop.type = PropertyType::Object | PropertyType::Nullable;
|
||||
|
||||
if (!Value::is_valid(property_object)) {
|
||||
throw std::runtime_error("Object property must specify 'objectType'");
|
||||
}
|
||||
prop.object_type = Object::validated_get_string(ctx, property_object, object_type_string);
|
||||
}
|
||||
else {
|
||||
// The type could be the name of another object type in the same schema.
|
||||
prop.type = PropertyType::Object | PropertyType::Nullable;
|
||||
prop.object_type = type;
|
||||
}
|
||||
|
||||
if (Value::is_valid(property_object)) {
|
||||
ValueType default_value = Object::get_property(ctx, property_object, default_string);
|
||||
if (!Value::is_undefined(ctx, default_value)) {
|
||||
object_defaults.emplace(prop.name, Protected<ValueType>(ctx, default_value));
|
||||
@ -161,6 +166,27 @@ Property Schema<T>::parse_property(ContextType ctx, ValueType attributes, std::s
|
||||
prop.is_indexed = Value::validated_to_boolean(ctx, indexed_value);
|
||||
}
|
||||
}
|
||||
else {
|
||||
std::string property_type = Value::validated_to_string(ctx, attributes);
|
||||
parse_property_type(object_name, prop, property_type);
|
||||
}
|
||||
|
||||
if (prop.type == PropertyType::Object && prop.object_type.empty()) {
|
||||
if (!Value::is_valid(property_object)) {
|
||||
throw std::logic_error(util::format("%1 property %2.%3 must specify 'objectType'",
|
||||
is_array(prop.type) ? "List" : "Object", object_name, prop.name));
|
||||
}
|
||||
prop.object_type = Object::validated_get_string(ctx, property_object, object_type_string);
|
||||
}
|
||||
|
||||
if (prop.type == PropertyType::LinkingObjects) {
|
||||
if (!Value::is_valid(property_object)) {
|
||||
throw std::logic_error(util::format("Linking objects property %1.%2 must specify 'objectType'",
|
||||
object_name, prop.name));
|
||||
}
|
||||
prop.object_type = Object::validated_get_string(ctx, property_object, object_type_string);
|
||||
prop.link_origin_property_name = Object::validated_get_string(ctx, property_object, property_string);
|
||||
}
|
||||
|
||||
return prop;
|
||||
}
|
||||
@ -188,7 +214,7 @@ ObjectSchema Schema<T>::parse_object_schema(ContextType ctx, ObjectType object_s
|
||||
for (uint32_t i = 0; i < length; i++) {
|
||||
ObjectType property_object = Object::validated_get_object(ctx, properties_object, i);
|
||||
std::string property_name = Object::validated_get_string(ctx, property_object, name_string);
|
||||
Property property = parse_property(ctx, property_object, property_name, object_defaults);
|
||||
Property property = parse_property(ctx, property_object, object_schema.name, std::move(property_name), object_defaults);
|
||||
if (property.type == realm::PropertyType::LinkingObjects) {
|
||||
object_schema.computed_properties.emplace_back(std::move(property));
|
||||
}
|
||||
@ -200,9 +226,9 @@ ObjectSchema Schema<T>::parse_object_schema(ContextType ctx, ObjectType object_s
|
||||
}
|
||||
else {
|
||||
auto property_names = Object::get_property_names(ctx, properties_object);
|
||||
for (auto &property_name : property_names) {
|
||||
for (auto& property_name : property_names) {
|
||||
ValueType property_value = Object::get_property(ctx, properties_object, property_name);
|
||||
Property property = parse_property(ctx, property_value, property_name, object_defaults);
|
||||
Property property = parse_property(ctx, property_value, object_schema.name, property_name, object_defaults);
|
||||
if (property.type == realm::PropertyType::LinkingObjects) {
|
||||
object_schema.computed_properties.emplace_back(std::move(property));
|
||||
}
|
||||
@ -291,13 +317,25 @@ typename T::Object Schema<T>::object_for_property(ContextType ctx, const Propert
|
||||
Object::set_property(ctx, object, name_string, Value::from_string(ctx, property.name));
|
||||
|
||||
static const String type_string = "type";
|
||||
const std::string type = is_array(property.type) ? "list" : string_for_property_type(property.type);
|
||||
Object::set_property(ctx, object, type_string, Value::from_string(ctx, type));
|
||||
if (is_array(property.type)) {
|
||||
if (property.type == realm::PropertyType::LinkingObjects) {
|
||||
Object::set_property(ctx, object, type_string, Value::from_string(ctx, "linkingObjects"));
|
||||
}
|
||||
else {
|
||||
Object::set_property(ctx, object, type_string, Value::from_string(ctx, "list"));
|
||||
}
|
||||
}
|
||||
else {
|
||||
Object::set_property(ctx, object, type_string, Value::from_string(ctx, string_for_property_type(property.type)));
|
||||
}
|
||||
|
||||
static const String object_type_string = "objectType";
|
||||
if (property.object_type.size()) {
|
||||
Object::set_property(ctx, object, object_type_string, Value::from_string(ctx, property.object_type));
|
||||
}
|
||||
else if (is_array(property.type)) {
|
||||
Object::set_property(ctx, object, object_type_string, Value::from_string(ctx, string_for_property_type(property.type & ~realm::PropertyType::Flags)));
|
||||
}
|
||||
|
||||
static const String property_string = "property";
|
||||
if (property.type == realm::PropertyType::LinkingObjects) {
|
||||
@ -305,14 +343,10 @@ typename T::Object Schema<T>::object_for_property(ContextType ctx, const Propert
|
||||
}
|
||||
|
||||
static const String indexed_string = "indexed";
|
||||
if (property.is_indexed) {
|
||||
Object::set_property(ctx, object, indexed_string, Value::from_boolean(ctx, true));
|
||||
}
|
||||
Object::set_property(ctx, object, indexed_string, Value::from_boolean(ctx, property.is_indexed));
|
||||
|
||||
static const String optional_string = "optional";
|
||||
if (is_nullable(property.type)) {
|
||||
Object::set_property(ctx, object, optional_string, Value::from_boolean(ctx, true));
|
||||
}
|
||||
Object::set_property(ctx, object, optional_string, Value::from_boolean(ctx, is_nullable(property.type)));
|
||||
|
||||
return object;
|
||||
}
|
||||
|
147
src/js_types.hpp
147
src/js_types.hpp
@ -65,7 +65,7 @@ struct String {
|
||||
String(const char *);
|
||||
String(const StringType &);
|
||||
String(StringType &&);
|
||||
String(const std::string &);
|
||||
String(StringData);
|
||||
|
||||
operator StringType() const;
|
||||
operator std::string() const;
|
||||
@ -82,16 +82,21 @@ struct Context {
|
||||
|
||||
class TypeErrorException : public std::invalid_argument {
|
||||
public:
|
||||
TypeErrorException(StringData object_type, StringData property,
|
||||
std::string const& type, std::string const& value)
|
||||
: std::invalid_argument(util::format("%1.%2 must be of type '%3', got (%4)",
|
||||
object_type, property, type, value))
|
||||
template<typename NativeAccessor, typename ValueType>
|
||||
TypeErrorException(NativeAccessor& accessor, StringData object_type,
|
||||
Property const& prop, ValueType value)
|
||||
: std::invalid_argument(util::format("%1.%2 must be of type '%3', got '%4' (%5)",
|
||||
object_type, prop.name, type_string(prop),
|
||||
accessor.typeof(value),
|
||||
accessor.print(value)))
|
||||
{}
|
||||
|
||||
TypeErrorException(const char *name, std::string const& type, std::string const& value)
|
||||
: std::invalid_argument(util::format("%1 must be of type '%2', got (%3)",
|
||||
name ? name : "JS value", type, value))
|
||||
{}
|
||||
|
||||
static std::string type_string(Property const& prop);
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
@ -101,6 +106,8 @@ struct Value {
|
||||
using ObjectType = typename T::Object;
|
||||
using ValueType = typename T::Value;
|
||||
|
||||
static const char *typeof(ContextType, const ValueType &);
|
||||
|
||||
static bool is_array(ContextType, const ValueType &);
|
||||
static bool is_array_buffer(ContextType, const ValueType &);
|
||||
static bool is_array_buffer_view(ContextType, const ValueType &);
|
||||
@ -117,12 +124,17 @@ struct Value {
|
||||
static bool is_valid(const ValueType &);
|
||||
|
||||
static bool is_valid_for_property(ContextType, const ValueType&, const Property&);
|
||||
static bool is_valid_for_property_type(ContextType, const ValueType&, realm::PropertyType type, StringData object_type);
|
||||
|
||||
static ValueType from_boolean(ContextType, bool);
|
||||
static ValueType from_null(ContextType);
|
||||
static ValueType from_number(ContextType, double);
|
||||
static ValueType from_string(ContextType, const String<T> &);
|
||||
static ValueType from_binary(ContextType, BinaryData);
|
||||
static ValueType from_string(ContextType ctx, const char *s) { return s ? from_nonnull_string(ctx, s) : from_null(ctx); }
|
||||
static ValueType from_string(ContextType ctx, StringData s) { return s ? from_nonnull_string(ctx, s) : from_null(ctx); }
|
||||
static ValueType from_string(ContextType ctx, const std::string& s) { return from_nonnull_string(ctx, s.c_str()); }
|
||||
static ValueType from_binary(ContextType ctx, BinaryData b) { return b ? from_nonnull_binary(ctx, b) : from_null(ctx); }
|
||||
static ValueType from_nonnull_string(ContextType, const String<T>&);
|
||||
static ValueType from_nonnull_binary(ContextType, BinaryData);
|
||||
static ValueType from_undefined(ContextType);
|
||||
|
||||
static ObjectType to_array(ContextType, const ValueType &);
|
||||
@ -135,6 +147,7 @@ struct Value {
|
||||
static String<T> to_string(ContextType, const ValueType &);
|
||||
static OwnedBinaryData to_binary(ContextType, ValueType);
|
||||
|
||||
|
||||
#define VALIDATED(return_t, type) \
|
||||
static return_t validated_to_##type(ContextType ctx, const ValueType &value, const char *name = nullptr) { \
|
||||
if (!is_##type(ctx, value)) { \
|
||||
@ -351,82 +364,76 @@ REALM_JS_INLINE void set_internal(const typename T::Object &object, typename Cla
|
||||
template<typename T>
|
||||
inline bool Value<T>::is_valid_for_property(ContextType context, const ValueType &value, const Property& prop)
|
||||
{
|
||||
if (is_nullable(prop.type) && (is_null(context, value) || is_undefined(context, value))) {
|
||||
return true;
|
||||
}
|
||||
return is_valid_for_property_type(context, value, prop.type, prop.object_type);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline bool Value<T>::is_valid_for_property_type(ContextType context, const ValueType &value, realm::PropertyType type, StringData object_type) {
|
||||
using realm::PropertyType;
|
||||
if (realm::is_array(prop.type)) {
|
||||
if (prop.type != PropertyType::Object) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// FIXME: Do we need to validate the types of the contained objects?
|
||||
if (is_array(context, value)) {
|
||||
auto check_value = [&](auto&& value) {
|
||||
if (is_nullable(type) && (is_null(context, value) || is_undefined(context, value))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (is_object(context, value)) {
|
||||
auto object = to_object(context, value);
|
||||
return Object<T>::template is_instance<ResultsClass<T>>(context, object)
|
||||
|| Object<T>::template is_instance<ListClass<T>>(context, object);
|
||||
switch (type & ~PropertyType::Flags) {
|
||||
case PropertyType::Int:
|
||||
case PropertyType::Float:
|
||||
case PropertyType::Double:
|
||||
return is_number(context, value);
|
||||
case PropertyType::Bool:
|
||||
return is_boolean(context, value);
|
||||
case PropertyType::String:
|
||||
return is_string(context, value);
|
||||
case PropertyType::Data:
|
||||
return is_binary(context, value);
|
||||
case PropertyType::Date:
|
||||
return is_date(context, value);
|
||||
case PropertyType::Object:
|
||||
return true;
|
||||
case PropertyType::Any:
|
||||
return false;
|
||||
default:
|
||||
REALM_UNREACHABLE();
|
||||
}
|
||||
};
|
||||
auto check_collection_type = [&](auto&& list) {
|
||||
auto list_type = list->get_type();
|
||||
return list_type == type
|
||||
&& is_nullable(list_type) == is_nullable(type)
|
||||
&& (type != PropertyType::Object || list->get_object_schema().name == object_type);
|
||||
};
|
||||
|
||||
if (!realm::is_array(type)) {
|
||||
return check_value(value);
|
||||
}
|
||||
|
||||
if (is_object(context, value)) {
|
||||
auto object = to_object(context, value);
|
||||
if (Object<T>::template is_instance<ResultsClass<T>>(context, object)) {
|
||||
return check_collection_type(get_internal<T, ResultsClass<T>>(object));
|
||||
}
|
||||
if (Object<T>::template is_instance<ListClass<T>>(context, object)) {
|
||||
return check_collection_type(get_internal<T, ListClass<T>>(object));
|
||||
}
|
||||
}
|
||||
|
||||
if (type == PropertyType::Object) {
|
||||
// FIXME: Do we need to validate the types of the contained objects?
|
||||
return is_array(context, value);
|
||||
}
|
||||
|
||||
if (!is_array(context, value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (prop.type & ~PropertyType::Flags) {
|
||||
case PropertyType::Int:
|
||||
case PropertyType::Float:
|
||||
case PropertyType::Double:
|
||||
return is_number(context, value);
|
||||
case PropertyType::Bool:
|
||||
return is_boolean(context, value);
|
||||
case PropertyType::String:
|
||||
return is_string(context, value);
|
||||
case PropertyType::Data:
|
||||
return is_binary(context, value);
|
||||
case PropertyType::Date:
|
||||
return is_date(context, value);
|
||||
case PropertyType::Object:
|
||||
return true;
|
||||
case PropertyType::Any:
|
||||
auto array = to_array(context, value);
|
||||
uint32_t size = Object<T>::validated_get_length(context, array);
|
||||
for (uint32_t i = 0; i < size; ++i) {
|
||||
if (!check_value(Object<T>::get_property(context, array, i))) {
|
||||
return false;
|
||||
default:
|
||||
REALM_UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
inline std::string js_type_name_for_property_type(realm::PropertyType type)
|
||||
{
|
||||
using realm::PropertyType;
|
||||
if (realm::is_array(type)) {
|
||||
if (type == PropertyType::LinkingObjects) {
|
||||
throw std::runtime_error("LinkingObjects' type is not supported");
|
||||
}
|
||||
return "array";
|
||||
}
|
||||
|
||||
switch (type & ~PropertyType::Flags) {
|
||||
case PropertyType::Int:
|
||||
case PropertyType::Float:
|
||||
case PropertyType::Double:
|
||||
return "number";
|
||||
case PropertyType::Bool:
|
||||
return "boolean";
|
||||
case PropertyType::String:
|
||||
return "string";
|
||||
case PropertyType::Date:
|
||||
return "date";
|
||||
case PropertyType::Data:
|
||||
return "binary";
|
||||
case PropertyType::Object:
|
||||
return "object";
|
||||
case PropertyType::Any:
|
||||
throw std::runtime_error("'Any' type is not supported");
|
||||
default:
|
||||
REALM_UNREACHABLE();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // js
|
||||
|
@ -395,10 +395,10 @@ JSValueRef wrap(JSContextRef ctx, JSObjectRef function, JSObjectRef this_object,
|
||||
}
|
||||
|
||||
template<jsc::ArgumentsMethodType F>
|
||||
JSValueRef wrap(JSContextRef ctx, JSObjectRef function, JSObjectRef this_object, size_t argc, const JSValueRef arguments[], JSValueRef* exception) {
|
||||
JSValueRef wrap(JSContextRef ctx, JSObjectRef, JSObjectRef this_object, size_t argc, const JSValueRef arguments[], JSValueRef* exception) {
|
||||
jsc::ReturnValue return_value(ctx);
|
||||
try {
|
||||
F(ctx, function, this_object, jsc::Arguments{ctx, argc, arguments}, return_value);
|
||||
F(ctx, this_object, jsc::Arguments{ctx, argc, arguments}, return_value);
|
||||
return return_value;
|
||||
}
|
||||
catch (std::exception &e) {
|
||||
|
@ -38,6 +38,9 @@ class ReturnValue<jsc::Types> {
|
||||
void set(const std::string &string) {
|
||||
m_value = JSValueMakeString(m_context, jsc::String(string));
|
||||
}
|
||||
void set(const char *string) {
|
||||
m_value = JSValueMakeString(m_context, jsc::String(string));
|
||||
}
|
||||
void set(bool boolean) {
|
||||
m_value = JSValueMakeBoolean(m_context, boolean);
|
||||
}
|
||||
|
@ -32,7 +32,8 @@ class String<jsc::Types> {
|
||||
public:
|
||||
String(const char *s) : m_str(JSStringCreateWithUTF8CString(s)) {}
|
||||
String(const JSStringRef &s) : m_str(JSStringRetain(s)) {}
|
||||
String(const std::string &str) : String(str.c_str()) {}
|
||||
String(StringData str) : String(str.data()) {}
|
||||
String(const std::string& str) : String(str.c_str()) {}
|
||||
String(const StringType &o) : String(o.m_str) {}
|
||||
String(StringType &&o) : m_str(o.m_str) {
|
||||
o.m_str = nullptr;
|
||||
|
@ -47,7 +47,7 @@ bool jsc::Value::is_binary(JSContextRef ctx, const JSValueRef &value)
|
||||
}
|
||||
|
||||
template<>
|
||||
JSValueRef jsc::Value::from_binary(JSContextRef ctx, BinaryData data)
|
||||
JSValueRef jsc::Value::from_nonnull_binary(JSContextRef ctx, BinaryData data)
|
||||
{
|
||||
static jsc::String s_buffer = "buffer";
|
||||
static jsc::String s_uint8_array = "Uint8Array";
|
||||
|
@ -41,6 +41,18 @@ static inline bool is_object_of_type(JSContextRef ctx, JSValueRef value, jsc::St
|
||||
return result;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline const char *jsc::Value::typeof(JSContextRef ctx, const JSValueRef &value) {
|
||||
switch (JSValueGetType(ctx, value)) {
|
||||
case kJSTypeNull: return "null";
|
||||
case kJSTypeNumber: return "number";
|
||||
case kJSTypeObject: return "object";
|
||||
case kJSTypeString: return "string";
|
||||
case kJSTypeBoolean: return "boolean";
|
||||
case kJSTypeUndefined: return "undefined";
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
inline bool jsc::Value::is_array(JSContextRef ctx, const JSValueRef &value) {
|
||||
// JSValueIsArray() is not available until iOS 9.
|
||||
@ -124,7 +136,7 @@ inline JSValueRef jsc::Value::from_number(JSContextRef ctx, double number) {
|
||||
}
|
||||
|
||||
template<>
|
||||
inline JSValueRef jsc::Value::from_string(JSContextRef ctx, const jsc::String &string) {
|
||||
inline JSValueRef jsc::Value::from_nonnull_string(JSContextRef ctx, const jsc::String &string) {
|
||||
return JSValueMakeString(ctx, string);
|
||||
}
|
||||
|
||||
@ -134,26 +146,12 @@ inline JSValueRef jsc::Value::from_undefined(JSContextRef ctx) {
|
||||
}
|
||||
|
||||
template<>
|
||||
JSValueRef jsc::Value::from_binary(JSContextRef ctx, BinaryData data);
|
||||
JSValueRef jsc::Value::from_nonnull_binary(JSContextRef ctx, BinaryData data);
|
||||
|
||||
template<>
|
||||
inline bool jsc::Value::to_boolean(JSContextRef ctx, const JSValueRef &value) {
|
||||
return JSValueToBoolean(ctx, value);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline double jsc::Value::to_number(JSContextRef ctx, const JSValueRef &value) {
|
||||
JSValueRef exception = nullptr;
|
||||
double number = JSValueToNumber(ctx, value, &exception);
|
||||
if (exception) {
|
||||
throw jsc::Exception(ctx, exception);
|
||||
}
|
||||
if (isnan(number)) {
|
||||
throw std::invalid_argument("Value not convertible to a number.");
|
||||
}
|
||||
return number;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline jsc::String jsc::Value::to_string(JSContextRef ctx, const JSValueRef &value) {
|
||||
JSValueRef exception = nullptr;
|
||||
@ -168,6 +166,21 @@ inline jsc::String jsc::Value::to_string(JSContextRef ctx, const JSValueRef &val
|
||||
return string;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline double jsc::Value::to_number(JSContextRef ctx, const JSValueRef &value) {
|
||||
JSValueRef exception = nullptr;
|
||||
double number = JSValueToNumber(ctx, value, &exception);
|
||||
if (exception) {
|
||||
throw jsc::Exception(ctx, exception);
|
||||
}
|
||||
if (isnan(number)) {
|
||||
throw std::invalid_argument(util::format("Value '%1' not convertible to a number.",
|
||||
(std::string)to_string(ctx, value)));
|
||||
}
|
||||
return number;
|
||||
}
|
||||
|
||||
|
||||
template<>
|
||||
inline JSObjectRef jsc::Value::to_object(JSContextRef ctx, const JSValueRef &value) {
|
||||
JSValueRef exception = nullptr;
|
||||
|
@ -317,7 +317,7 @@ void wrap(const v8::FunctionCallbackInfo<v8::Value>& info) {
|
||||
auto arguments = node::get_arguments(info);
|
||||
|
||||
try {
|
||||
F(isolate, info.Callee(), info.This(), node::Arguments{isolate, arguments.size(), arguments.data()}, return_value);
|
||||
F(isolate, info.This(), node::Arguments{isolate, arguments.size(), arguments.data()}, return_value);
|
||||
}
|
||||
catch (std::exception &e) {
|
||||
Nan::ThrowError(node::Exception::value(isolate, e));
|
||||
|
@ -41,6 +41,17 @@ class ReturnValue<node::Types> {
|
||||
m_value.Set(Nan::New(string).ToLocalChecked());
|
||||
}
|
||||
}
|
||||
void set(const char *str) {
|
||||
if (!str) {
|
||||
m_value.SetNull();
|
||||
}
|
||||
else if (!*str) {
|
||||
m_value.SetEmptyString();
|
||||
}
|
||||
else {
|
||||
m_value.Set(Nan::New(str).ToLocalChecked());
|
||||
}
|
||||
}
|
||||
void set(bool boolean) {
|
||||
m_value.Set(boolean);
|
||||
}
|
||||
|
@ -23,6 +23,17 @@
|
||||
namespace realm {
|
||||
namespace js {
|
||||
|
||||
template<>
|
||||
inline const char *node::Value::typeof(v8::Isolate* isolate, const v8::Local<v8::Value> &value) {
|
||||
if (value->IsNull()) { return "null"; }
|
||||
if (value->IsNumber()) { return "number"; }
|
||||
if (value->IsString()) { return "string"; }
|
||||
if (value->IsBoolean()) { return "boolean"; }
|
||||
if (value->IsUndefined()) { return "undefined"; }
|
||||
if (value->IsObject()) { return "object"; }
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
template<>
|
||||
inline bool node::Value::is_array(v8::Isolate* isolate, const v8::Local<v8::Value> &value) {
|
||||
return value->IsArray();
|
||||
@ -110,12 +121,12 @@ inline v8::Local<v8::Value> node::Value::from_number(v8::Isolate* isolate, doubl
|
||||
}
|
||||
|
||||
template<>
|
||||
inline v8::Local<v8::Value> node::Value::from_string(v8::Isolate* isolate, const node::String &string) {
|
||||
inline v8::Local<v8::Value> node::Value::from_nonnull_string(v8::Isolate* isolate, const node::String &string) {
|
||||
return v8::Local<v8::String>(string);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline v8::Local<v8::Value> node::Value::from_binary(v8::Isolate* isolate, BinaryData data) {
|
||||
inline v8::Local<v8::Value> node::Value::from_nonnull_binary(v8::Isolate* isolate, BinaryData data) {
|
||||
v8::Local<v8::ArrayBuffer> buffer = v8::ArrayBuffer::New(isolate, data.size());
|
||||
v8::ArrayBuffer::Contents contents = buffer->GetContents();
|
||||
|
||||
@ -137,17 +148,18 @@ inline bool node::Value::to_boolean(v8::Isolate* isolate, const v8::Local<v8::Va
|
||||
}
|
||||
|
||||
template<>
|
||||
inline double node::Value::to_number(v8::Isolate* isolate, const v8::Local<v8::Value> &value) {
|
||||
double number = Nan::To<double>(value).FromMaybe(NAN);
|
||||
if (std::isnan(number)) {
|
||||
throw std::invalid_argument("Value not convertible to a number.");
|
||||
}
|
||||
return number;
|
||||
inline node::String node::Value::to_string(v8::Isolate* isolate, const v8::Local<v8::Value> &value) {
|
||||
return value->ToString();
|
||||
}
|
||||
|
||||
template<>
|
||||
inline node::String node::Value::to_string(v8::Isolate* isolate, const v8::Local<v8::Value> &value) {
|
||||
return value->ToString();
|
||||
inline double node::Value::to_number(v8::Isolate* isolate, const v8::Local<v8::Value> &value) {
|
||||
double number = Nan::To<double>(value).FromMaybe(NAN);
|
||||
if (std::isnan(number)) {
|
||||
throw std::invalid_argument(util::format("Value '%1' not convertible to a number.",
|
||||
(std::string)to_string(isolate, value)));
|
||||
}
|
||||
return number;
|
||||
}
|
||||
|
||||
template<>
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit 30b8a7853b17762719aa7671aac6ebea04473e33
|
||||
Subproject commit 49705c0fbdbff8536cfa134fc17b837db4385d31
|
@ -21,14 +21,27 @@
|
||||
module.exports = {
|
||||
assertSimilar: function(type, val1, val2, errorMessage, depth) {
|
||||
depth = depth || 0;
|
||||
if (type == 'float' || type == 'double') {
|
||||
this.assertEqualWithTolerance(val1, val2, errorMessage, depth + 1);
|
||||
this.assertDefined(type, depth + 1);
|
||||
type = type.replace('?', '');
|
||||
if (val2 === null) {
|
||||
this.assertNull(val1, errorMessage, depth + 1);
|
||||
}
|
||||
else if (type == 'data') {
|
||||
else if (type === 'float' || type === 'double') {
|
||||
this.assertEqualWithTolerance(val1, val2, 0.000001, errorMessage, depth + 1);
|
||||
}
|
||||
else if (type === 'data') {
|
||||
this.assertArraysEqual(new Uint8Array(val1), val2, errorMessage, depth + 1);
|
||||
}
|
||||
else if (type == 'date') {
|
||||
this.assertEqual(val1.getTime(), val2.getTime(), errorMessage, depth + 1);
|
||||
else if (type === 'date') {
|
||||
this.assertEqual(val1 && val1.getTime(), val2.getTime(), errorMessage, depth + 1);
|
||||
}
|
||||
else if (type === 'object') {
|
||||
for (const key of Object.keys(val1)) {
|
||||
this.assertEqual(val1[key], val2[key], errorMessage, depth + 1);
|
||||
}
|
||||
}
|
||||
else if (type === 'list') {
|
||||
this.assertArraysEqual(val1, val2, errorMessage, depth + 1);
|
||||
}
|
||||
else {
|
||||
this.assertEqual(val1, val2, errorMessage, depth + 1);
|
||||
@ -79,6 +92,8 @@ module.exports = {
|
||||
},
|
||||
|
||||
assertArraysEqual: function(val1, val2, errorMessage, depth) {
|
||||
this.assertDefined(val1, `val1 should be non-null but is ${val1}`, 1 + (depth || 0));
|
||||
this.assertDefined(val2, `val2 should be non-null but is ${val2}`, 1 + (depth || 0));
|
||||
const len1 = val1.length;
|
||||
const len2 = val2.length;
|
||||
|
||||
@ -90,8 +105,25 @@ module.exports = {
|
||||
throw new TestFailureError(message, depth);
|
||||
}
|
||||
|
||||
let compare;
|
||||
if (val1.type === "data") {
|
||||
compare = (i, a, b) => a === b || this.assertArraysEqual(new Uint8Array(a), b, `Data elements at index ${i}`, 1) || true;
|
||||
}
|
||||
else if (val1.type === "date") {
|
||||
compare = (i, a, b) => (a && a.getTime()) === (b && b.getTime());
|
||||
}
|
||||
else if (val1.type === "float" || val1.type === "double") {
|
||||
compare = (i, a, b) => a >= b - 0.000001 && a <= b + 0.000001;
|
||||
}
|
||||
else if (val1.type === 'object') {
|
||||
compare = (i, a, b) => Object.keys(a).every(key => a[key] === b[key]);
|
||||
}
|
||||
else {
|
||||
compare = (i, a, b) => a === b;
|
||||
}
|
||||
|
||||
for (let i = 0; i < len1; i++) {
|
||||
if (val1[i] !== val2[i]) {
|
||||
if (!compare(i, val1[i], val2[i])) {
|
||||
let message = `Array contents not equal at index ${i} (${val1[i]} != ${val2[i]})`;
|
||||
if (errorMessage) {
|
||||
message = `${errorMessage} - ${message}`;
|
||||
@ -136,7 +168,7 @@ module.exports = {
|
||||
},
|
||||
|
||||
assertThrowsContaining: function(func, expectedMessage, depth) {
|
||||
var caught = false;
|
||||
let caught = false;
|
||||
try {
|
||||
func();
|
||||
}
|
||||
@ -158,6 +190,12 @@ module.exports = {
|
||||
}
|
||||
},
|
||||
|
||||
assertFalse: function(condition, errorMessage, depth) {
|
||||
if (condition) {
|
||||
throw new TestFailureError(errorMessage || `Condition ${condition} expected to be false`, depth);
|
||||
}
|
||||
},
|
||||
|
||||
assertInstanceOf: function(object, type, errorMessage, depth) {
|
||||
if (!(object instanceof type)) {
|
||||
throw new TestFailureError(errorMessage || `Object ${object} expected to be of type ${type}`, depth);
|
||||
@ -168,6 +206,12 @@ module.exports = {
|
||||
this.assertEqual(typeof value, type, `Value ${value} expected to be of type ${type}`, 1 + depth || 0);
|
||||
},
|
||||
|
||||
assertDefined: function(value, errorMessage, depth) {
|
||||
if (value === undefined || value === null) {
|
||||
throw new TestFailureError(errorMessage || `Value ${value} expected to be non-null`, depth);
|
||||
}
|
||||
},
|
||||
|
||||
assertUndefined: function(value, errorMessage, depth) {
|
||||
if (value !== undefined) {
|
||||
throw new TestFailureError(errorMessage || `Value ${value} expected to be undefined`, depth);
|
||||
|
@ -18,34 +18,78 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
var Realm = require('realm');
|
||||
var TestCase = require('./asserts');
|
||||
var schemas = require('./schemas');
|
||||
const Realm = require('realm');
|
||||
let TestCase = require('./asserts');
|
||||
let schemas = require('./schemas');
|
||||
|
||||
const DATA1 = new Uint8Array([0x01]);
|
||||
const DATA2 = new Uint8Array([0x02]);
|
||||
const DATA3 = new Uint8Array([0x03]);
|
||||
const DATE1 = new Date(1);
|
||||
const DATE2 = new Date(2);
|
||||
const DATE3 = new Date(3);
|
||||
|
||||
module.exports = {
|
||||
testListConstructor: function() {
|
||||
var realm = new Realm({schema: [schemas.PersonObject, schemas.PersonList]});
|
||||
const realm = new Realm({schema: [schemas.PersonObject, schemas.PersonList]});
|
||||
|
||||
realm.write(function() {
|
||||
var obj = realm.create('PersonList', {list: []});
|
||||
TestCase.assertTrue(obj.list instanceof Realm.List);
|
||||
TestCase.assertTrue(obj.list instanceof Realm.Collection);
|
||||
realm.write(() => {
|
||||
let obj = realm.create('PersonList', {list: []});
|
||||
TestCase.assertInstanceOf(obj.list, Realm.List);
|
||||
TestCase.assertInstanceOf(obj.list, Realm.Collection);
|
||||
});
|
||||
|
||||
TestCase.assertThrows(function() {
|
||||
new Realm.List();
|
||||
TestCase.assertThrowsContaining(() => new Realm.List(), 'constructor');
|
||||
|
||||
TestCase.assertType(Realm.List, 'function');
|
||||
TestCase.assertInstanceOf(Realm.List, Function);
|
||||
},
|
||||
|
||||
testListType: function() {
|
||||
const realm = new Realm({schema: [schemas.LinkTypes, schemas.TestObject, schemas.PrimitiveArrays]});
|
||||
|
||||
let obj, prim;
|
||||
realm.write(() => {
|
||||
obj = realm.create('LinkTypesObject', {});
|
||||
prim = realm.create('PrimitiveArrays', {});
|
||||
});
|
||||
|
||||
TestCase.assertEqual(typeof Realm.List, 'function');
|
||||
TestCase.assertTrue(Realm.List instanceof Function);
|
||||
TestCase.assertEqual(obj.arrayCol.type, 'object');
|
||||
TestCase.assertEqual(obj.arrayCol1.type, 'object');
|
||||
|
||||
TestCase.assertEqual(prim.bool.type, 'bool');
|
||||
TestCase.assertEqual(prim.int.type, 'int');
|
||||
TestCase.assertEqual(prim.float.type, 'float');
|
||||
TestCase.assertEqual(prim.double.type, 'double');
|
||||
TestCase.assertEqual(prim.string.type, 'string');
|
||||
TestCase.assertEqual(prim.date.type, 'date');
|
||||
TestCase.assertEqual(prim.optBool.type, 'bool');
|
||||
TestCase.assertEqual(prim.optInt.type, 'int');
|
||||
TestCase.assertEqual(prim.optFloat.type, 'float');
|
||||
TestCase.assertEqual(prim.optDouble.type, 'double');
|
||||
TestCase.assertEqual(prim.optString.type, 'string');
|
||||
TestCase.assertEqual(prim.optDate.type, 'date');
|
||||
|
||||
TestCase.assertFalse(prim.bool.optional);
|
||||
TestCase.assertFalse(prim.int.optional);
|
||||
TestCase.assertFalse(prim.float.optional);
|
||||
TestCase.assertFalse(prim.double.optional);
|
||||
TestCase.assertFalse(prim.string.optional);
|
||||
TestCase.assertFalse(prim.date.optional);
|
||||
TestCase.assertTrue(prim.optBool.optional);
|
||||
TestCase.assertTrue(prim.optInt.optional);
|
||||
TestCase.assertTrue(prim.optFloat.optional);
|
||||
TestCase.assertTrue(prim.optDouble.optional);
|
||||
TestCase.assertTrue(prim.optString.optional);
|
||||
TestCase.assertTrue(prim.optDate.optional);
|
||||
},
|
||||
|
||||
testListLength: function() {
|
||||
var realm = new Realm({schema: [schemas.LinkTypes, schemas.TestObject]});
|
||||
var array;
|
||||
const realm = new Realm({schema: [schemas.LinkTypes, schemas.TestObject]});
|
||||
let array;
|
||||
|
||||
realm.write(function() {
|
||||
var obj = realm.create('LinkTypesObject', {
|
||||
realm.write(() => {
|
||||
let obj = realm.create('LinkTypesObject', {
|
||||
objectCol: {doubleCol: 1},
|
||||
objectCol1: {doubleCol: 2},
|
||||
arrayCol: [{doubleCol: 3}],
|
||||
@ -60,94 +104,286 @@ module.exports = {
|
||||
obj.arrayCol = [{doubleCol: 1}, {doubleCol: 2}];
|
||||
TestCase.assertEqual(array.length, 2);
|
||||
|
||||
TestCase.assertThrows(function() {
|
||||
array.length = 0;
|
||||
}, 'cannot set length property on lists');
|
||||
TestCase.assertThrowsContaining(() => array.length = 0,
|
||||
"Cannot assign to read only property 'length'");
|
||||
});
|
||||
|
||||
TestCase.assertEqual(array.length, 2);
|
||||
},
|
||||
|
||||
testListSubscriptGetters: function() {
|
||||
var realm = new Realm({schema: [schemas.LinkTypes, schemas.TestObject]});
|
||||
var array;
|
||||
const realm = new Realm({schema: [schemas.LinkTypes, schemas.TestObject, schemas.PrimitiveArrays]});
|
||||
let obj, prim;
|
||||
|
||||
realm.write(function() {
|
||||
var obj = realm.create('LinkTypesObject', {
|
||||
realm.write(() => {
|
||||
obj = realm.create('LinkTypesObject', {
|
||||
objectCol: {doubleCol: 1},
|
||||
objectCol1: {doubleCol: 2},
|
||||
arrayCol: [{doubleCol: 3}, {doubleCol: 4}],
|
||||
arrayCol1: [{doubleCol: 5}, {doubleCol: 6}],
|
||||
});
|
||||
prim = realm.create('PrimitiveArrays', {
|
||||
bool: [true, false],
|
||||
int: [1, 2],
|
||||
float: [1.1, 2.2],
|
||||
double: [1.11, 2.22],
|
||||
string: ['a', 'b'],
|
||||
date: [new Date(1), new Date(2)],
|
||||
data: [DATA1, DATA2],
|
||||
|
||||
array = obj.arrayCol;
|
||||
optBool: [true, null],
|
||||
optInt: [1, null],
|
||||
optFloat: [1.1, null],
|
||||
optDouble: [1.11, null],
|
||||
optString: ['a', null],
|
||||
optDate: [new Date(1), null],
|
||||
optData: [DATA1, null],
|
||||
});
|
||||
});
|
||||
|
||||
TestCase.assertEqual(array[0].doubleCol, 3);
|
||||
TestCase.assertEqual(array[1].doubleCol, 4);
|
||||
TestCase.assertEqual(array[2], undefined);
|
||||
TestCase.assertEqual(array[-1], undefined);
|
||||
TestCase.assertEqual(obj.arrayCol[0].doubleCol, 3);
|
||||
TestCase.assertEqual(obj.arrayCol[1].doubleCol, 4);
|
||||
TestCase.assertEqual(obj.arrayCol[2], undefined);
|
||||
TestCase.assertEqual(obj.arrayCol[-1], undefined);
|
||||
TestCase.assertEqual(obj.arrayCol['foo'], undefined);
|
||||
|
||||
TestCase.assertEqual(obj.arrayCol1[0].doubleCol, 5);
|
||||
TestCase.assertEqual(obj.arrayCol1[1].doubleCol, 6);
|
||||
TestCase.assertEqual(obj.arrayCol1[2], undefined);
|
||||
TestCase.assertEqual(obj.arrayCol1[-1], undefined);
|
||||
TestCase.assertEqual(obj.arrayCol1['foo'], undefined);
|
||||
|
||||
for (let field of Object.keys(prim)) {
|
||||
TestCase.assertEqual(prim[field][2], undefined);
|
||||
TestCase.assertEqual(prim[field][-1], undefined);
|
||||
TestCase.assertEqual(prim[field]['foo'], undefined);
|
||||
if (field.includes('opt')) {
|
||||
TestCase.assertEqual(prim[field][1], null);
|
||||
}
|
||||
}
|
||||
|
||||
TestCase.assertSimilar('bool', prim.bool[0], true);
|
||||
TestCase.assertSimilar('bool', prim.bool[1], false);
|
||||
TestCase.assertSimilar('int', prim.int[0], 1);
|
||||
TestCase.assertSimilar('int', prim.int[1], 2);
|
||||
TestCase.assertSimilar('float', prim.float[0], 1.1);
|
||||
TestCase.assertSimilar('float', prim.float[1], 2.2);
|
||||
TestCase.assertSimilar('double', prim.double[0], 1.11);
|
||||
TestCase.assertSimilar('double', prim.double[1], 2.22);
|
||||
TestCase.assertSimilar('string', prim.string[0], 'a');
|
||||
TestCase.assertSimilar('string', prim.string[1], 'b');
|
||||
TestCase.assertSimilar('data', prim.data[0], DATA1);
|
||||
TestCase.assertSimilar('data', prim.data[1], DATA2);
|
||||
TestCase.assertSimilar('date', prim.date[0], new Date(1));
|
||||
TestCase.assertSimilar('date', prim.date[1], new Date(2));
|
||||
|
||||
TestCase.assertSimilar('bool', prim.optBool[0], true);
|
||||
TestCase.assertSimilar('int', prim.optInt[0], 1);
|
||||
TestCase.assertSimilar('float', prim.optFloat[0], 1.1);
|
||||
TestCase.assertSimilar('double', prim.optDouble[0], 1.11);
|
||||
TestCase.assertSimilar('string', prim.optString[0], 'a');
|
||||
TestCase.assertSimilar('data', prim.optData[0], DATA1);
|
||||
TestCase.assertSimilar('date', prim.optDate[0], new Date(1));
|
||||
},
|
||||
|
||||
testListSubscriptSetters: function() {
|
||||
var realm = new Realm({schema: [schemas.LinkTypes, schemas.TestObject]});
|
||||
var array;
|
||||
const realm = new Realm({schema: [schemas.LinkTypes, schemas.TestObject,
|
||||
schemas.PrimitiveArrays]});
|
||||
let array;
|
||||
|
||||
realm.write(function() {
|
||||
var obj = realm.create('LinkTypesObject', {
|
||||
realm.write(() => {
|
||||
let obj = realm.create('LinkTypesObject', {
|
||||
objectCol: {doubleCol: 1},
|
||||
objectCol1: {doubleCol: 2},
|
||||
arrayCol: [{doubleCol: 3}, {doubleCol: 4}],
|
||||
});
|
||||
|
||||
let prim = realm.create('PrimitiveArrays', {});
|
||||
array = obj.arrayCol;
|
||||
|
||||
array[0] = {doubleCol: 5};
|
||||
array[1] = {doubleCol: 6};
|
||||
|
||||
TestCase.assertEqual(array[0].doubleCol, 5);
|
||||
TestCase.assertEqual(array[1].doubleCol, 6);
|
||||
|
||||
array[0] = obj.objectCol;
|
||||
array[1] = obj.objectCol1;
|
||||
|
||||
TestCase.assertEqual(array[0].doubleCol, 1);
|
||||
TestCase.assertEqual(array[1].doubleCol, 2);
|
||||
|
||||
TestCase.assertThrows(function() {
|
||||
array[2] = {doubleCol: 1};
|
||||
}, 'cannot set list item beyond its bounds');
|
||||
TestCase.assertThrowsContaining(() => array[0] = null,
|
||||
"JS value must be of type 'object', got (null)");
|
||||
TestCase.assertThrowsContaining(() => array[0] = {},
|
||||
"Missing value for property 'TestObject.doubleCol'");
|
||||
TestCase.assertThrowsContaining(() => array[0] = {foo: 'bar'},
|
||||
"Missing value for property 'TestObject.doubleCol'");
|
||||
TestCase.assertThrowsContaining(() => array[0] = prim,
|
||||
"Object of type (PrimitiveArrays) does not match List type (TestObject)");
|
||||
TestCase.assertThrowsContaining(() => array[0] = array,
|
||||
"Missing value for property 'TestObject.doubleCol'");
|
||||
TestCase.assertThrowsContaining(() => array[2] = {doubleCol: 1},
|
||||
"Requested index 2 greater than max 1");
|
||||
TestCase.assertThrowsContaining(() => array[-1] = {doubleCol: 1},
|
||||
"Index -1 cannot be less than zero.");
|
||||
|
||||
TestCase.assertThrows(function() {
|
||||
array[-1] = {doubleCol: 1};
|
||||
}, 'cannot set list item with negative index');
|
||||
array['foo'] = 'bar';
|
||||
TestCase.assertEqual(array.foo, 'bar');
|
||||
|
||||
function testAssign(name, v1, v2) {
|
||||
prim[name].push(v1);
|
||||
TestCase.assertSimilar(prim[name].type, prim[name][0], v1, undefined, 1);
|
||||
prim[name][0] = v2;
|
||||
TestCase.assertSimilar(prim[name].type, prim[name][0], v2, undefined, 1);
|
||||
}
|
||||
|
||||
testAssign('bool', true, false);
|
||||
testAssign('int', 1, 2);
|
||||
testAssign('float', 1.1, 2.2);
|
||||
testAssign('double', 1.1, 2.2);
|
||||
testAssign('string', 'a', 'b');
|
||||
testAssign('data', DATA1, DATA2);
|
||||
testAssign('date', DATE1, DATE2);
|
||||
|
||||
function testAssignNull(name, expected) {
|
||||
TestCase.assertThrowsContaining(() => prim[name][0] = null,
|
||||
`Property must be of type '${expected}', got (null)`,
|
||||
undefined, 1);
|
||||
}
|
||||
|
||||
testAssignNull('bool', 'bool');
|
||||
testAssignNull('int', 'int');
|
||||
testAssignNull('float', 'float');
|
||||
testAssignNull('double', 'double');
|
||||
testAssignNull('string', 'string');
|
||||
testAssignNull('data', 'data');
|
||||
testAssignNull('date', 'date');
|
||||
|
||||
testAssign('optBool', true, null);
|
||||
testAssign('optInt', 1, null);
|
||||
testAssign('optFloat', 1.1, null);
|
||||
testAssign('optDouble', 1.1, null);
|
||||
testAssign('optString', 'a', null);
|
||||
testAssign('optData', DATA1, null);
|
||||
testAssign('optDate', DATE1, null);
|
||||
});
|
||||
|
||||
TestCase.assertThrows(function() {
|
||||
array[0] = {doubleCol: 1};
|
||||
}, 'cannot set list item outside write transaction');
|
||||
TestCase.assertThrowsContaining(() => array[0] = {doubleCol: 1},
|
||||
"Cannot modify managed objects outside of a write transaction.");
|
||||
|
||||
array['foo'] = 'baz';
|
||||
TestCase.assertEqual(array.foo, 'baz');
|
||||
},
|
||||
|
||||
testListInvalidProperty: function() {
|
||||
var realm = new Realm({schema: [schemas.LinkTypes, schemas.TestObject]});
|
||||
var array;
|
||||
testListAssignment: function() {
|
||||
const realm = new Realm({schema: [schemas.LinkTypes, schemas.TestObject,
|
||||
schemas.PersonList, schemas.PersonObject,
|
||||
schemas.PrimitiveArrays]});
|
||||
|
||||
realm.write(function() {
|
||||
var obj = realm.create('LinkTypesObject', {
|
||||
objectCol: {doubleCol: 1},
|
||||
objectCol1: {doubleCol: 2},
|
||||
arrayCol: [{doubleCol: 3}, {doubleCol: 4}],
|
||||
});
|
||||
let obj, prim;
|
||||
realm.write(() => {
|
||||
obj = realm.create('LinkTypesObject', {});
|
||||
prim = realm.create('PrimitiveArrays', {});
|
||||
let person = realm.create('PersonObject', {name: 'a', age: 2.0});
|
||||
let personList = realm.create('PersonList', {list: [person]}).list;
|
||||
|
||||
array = obj.arrayCol;
|
||||
TestCase.assertThrowsContaining(() => obj.arrayCol = [0],
|
||||
"JS value must be of type 'object', got (0)");
|
||||
TestCase.assertThrowsContaining(() => obj.arrayCol = [null],
|
||||
"JS value must be of type 'object', got (null)");
|
||||
TestCase.assertThrowsContaining(() => obj.arrayCol = [person],
|
||||
"Object of type (PersonObject) does not match List type (TestObject)");
|
||||
TestCase.assertThrowsContaining(() => obj.arrayCol = personList,
|
||||
"LinkTypesObject.arrayCol must be of type 'TestObject[]', got 'object' (a)");
|
||||
obj.arrayCol = [realm.create('TestObject', {doubleCol: 1.0})]
|
||||
TestCase.assertEqual(obj.arrayCol[0].doubleCol, 1.0);
|
||||
obj.arrayCol = obj.arrayCol;
|
||||
TestCase.assertEqual(obj.arrayCol[0].doubleCol, 1.0);
|
||||
|
||||
TestCase.assertThrowsContaining(() => prim.bool = [person],
|
||||
"PrimitiveArrays.bool must be of type 'boolean[]', got 'object' ([PersonObject{");
|
||||
TestCase.assertThrowsContaining(() => prim.int = [person],
|
||||
"PrimitiveArrays.int must be of type 'number[]', got 'object' ([PersonObject{");
|
||||
TestCase.assertThrowsContaining(() => prim.float = [person],
|
||||
"PrimitiveArrays.float must be of type 'number[]', got 'object' ([PersonObject{");
|
||||
TestCase.assertThrowsContaining(() => prim.double = [person],
|
||||
"PrimitiveArrays.double must be of type 'number[]', got 'object' ([PersonObject{");
|
||||
TestCase.assertThrowsContaining(() => prim.string = [person],
|
||||
"PrimitiveArrays.string must be of type 'string[]', got 'object' ([PersonObject{");
|
||||
TestCase.assertThrowsContaining(() => prim.data = [person],
|
||||
"PrimitiveArrays.data must be of type 'binary[]', got 'object' ([PersonObject{");
|
||||
TestCase.assertThrowsContaining(() => prim.date = [person],
|
||||
"PrimitiveArrays.date must be of type 'date[]', got 'object' ([PersonObject{");
|
||||
TestCase.assertThrowsContaining(() => prim.optBool = [person],
|
||||
"PrimitiveArrays.optBool must be of type 'boolean?[]', got 'object' ([PersonObject{");
|
||||
TestCase.assertThrowsContaining(() => prim.optInt = [person],
|
||||
"PrimitiveArrays.optInt must be of type 'number?[]', got 'object' ([PersonObject{");
|
||||
TestCase.assertThrowsContaining(() => prim.optFloat = [person],
|
||||
"PrimitiveArrays.optFloat must be of type 'number?[]', got 'object' ([PersonObject{");
|
||||
TestCase.assertThrowsContaining(() => prim.optDouble = [person],
|
||||
"PrimitiveArrays.optDouble must be of type 'number?[]', got 'object' ([PersonObject{");
|
||||
TestCase.assertThrowsContaining(() => prim.optString = [person],
|
||||
"PrimitiveArrays.optString must be of type 'string?[]', got 'object' ([PersonObject{");
|
||||
TestCase.assertThrowsContaining(() => prim.optData = [person],
|
||||
"PrimitiveArrays.optData must be of type 'binary?[]', got 'object' ([PersonObject{");
|
||||
TestCase.assertThrowsContaining(() => prim.optDate = [person],
|
||||
"PrimitiveArrays.optDate must be of type 'date?[]', got 'object' ([PersonObject{");
|
||||
|
||||
function testAssign(name, value) {
|
||||
prim[name] = [value];
|
||||
TestCase.assertSimilar(prim[name].type, prim[name][0], value, undefined, 1);
|
||||
}
|
||||
|
||||
testAssign('bool', true);
|
||||
testAssign('int', 1);
|
||||
testAssign('float', 1.1);
|
||||
testAssign('double', 1.1);
|
||||
testAssign('string', 'a');
|
||||
testAssign('data', DATA1);
|
||||
testAssign('date', DATE1);
|
||||
|
||||
function testAssignNull(name, expected) {
|
||||
TestCase.assertThrowsContaining(() => prim[name] = [null],
|
||||
`PrimitiveArrays.${name} must be of type '${expected}[]', got 'object' ([null])`,
|
||||
undefined, 1);
|
||||
TestCase.assertEqual(prim[name].length, 1,
|
||||
"List should not have been cleared by invalid assignment", 1);
|
||||
}
|
||||
|
||||
testAssignNull('bool', 'boolean');
|
||||
testAssignNull('int', 'number');
|
||||
testAssignNull('float', 'number');
|
||||
testAssignNull('double', 'number');
|
||||
testAssignNull('string', 'string');
|
||||
testAssignNull('data', 'binary');
|
||||
testAssignNull('date', 'date');
|
||||
|
||||
testAssign('optBool', true);
|
||||
testAssign('optInt', 1);
|
||||
testAssign('optFloat', 1.1);
|
||||
testAssign('optDouble', 1.1);
|
||||
testAssign('optString', 'a');
|
||||
testAssign('optData', DATA1);
|
||||
testAssign('optDate', DATE1);
|
||||
|
||||
testAssign('optBool', null);
|
||||
testAssign('optInt', null);
|
||||
testAssign('optFloat', null);
|
||||
testAssign('optDouble', null);
|
||||
testAssign('optString', null);
|
||||
testAssign('optData', null);
|
||||
testAssign('optDate', null);
|
||||
});
|
||||
|
||||
TestCase.assertEqual(undefined, array.ablasdf);
|
||||
TestCase.assertThrowsContaining(() => obj.arrayCol = [],
|
||||
"Cannot modify managed objects outside of a write transaction.");
|
||||
TestCase.assertThrowsContaining(() => prim.bool = [],
|
||||
"Cannot modify managed objects outside of a write transaction.");
|
||||
},
|
||||
|
||||
testListEnumerate: function() {
|
||||
var realm = new Realm({schema: [schemas.LinkTypes, schemas.TestObject]});
|
||||
var obj;
|
||||
const realm = new Realm({schema: [schemas.LinkTypes, schemas.TestObject]});
|
||||
let obj;
|
||||
|
||||
realm.write(function() {
|
||||
realm.write(() => {
|
||||
obj = realm.create('LinkTypesObject', {
|
||||
objectCol: {doubleCol: 1},
|
||||
objectCol1: {doubleCol: 2},
|
||||
@ -155,19 +391,18 @@ module.exports = {
|
||||
});
|
||||
});
|
||||
|
||||
var index;
|
||||
for (index in obj.arrayCol) {
|
||||
for (const index in obj.arrayCol) {
|
||||
TestCase.assertTrue(false, "No objects should have been enumerated: " + index);
|
||||
}
|
||||
|
||||
realm.write(function() {
|
||||
realm.write(() => {
|
||||
obj.arrayCol = [{doubleCol: 0}, {doubleCol: 1}];
|
||||
TestCase.assertEqual(obj.arrayCol.length, 2);
|
||||
});
|
||||
TestCase.assertEqual(obj.arrayCol.length, 2);
|
||||
|
||||
var count = 0;
|
||||
var keys = Object.keys(obj.arrayCol);
|
||||
for (index in obj.arrayCol) {
|
||||
let count = 0;
|
||||
let keys = Object.keys(obj.arrayCol);
|
||||
for (const index in obj.arrayCol) {
|
||||
TestCase.assertEqual(count++, +index);
|
||||
TestCase.assertEqual(keys[index], index);
|
||||
}
|
||||
@ -177,11 +412,11 @@ module.exports = {
|
||||
},
|
||||
|
||||
testListPush: function() {
|
||||
var realm = new Realm({schema: [schemas.LinkTypes, schemas.TestObject]});
|
||||
var array;
|
||||
const realm = new Realm({schema: [schemas.LinkTypes, schemas.TestObject]});
|
||||
let array;
|
||||
|
||||
realm.write(function() {
|
||||
var obj = realm.create('LinkTypesObject', {
|
||||
realm.write(() => {
|
||||
let obj = realm.create('LinkTypesObject', {
|
||||
objectCol: {doubleCol: 1},
|
||||
objectCol1: {doubleCol: 2},
|
||||
arrayCol: [{doubleCol: 3}],
|
||||
@ -199,23 +434,22 @@ module.exports = {
|
||||
TestCase.assertEqual(array[2].doubleCol, 1);
|
||||
TestCase.assertEqual(array[3].doubleCol, 2);
|
||||
|
||||
TestCase.assertThrows(function() {
|
||||
array.push();
|
||||
});
|
||||
TestCase.assertEqual(array.push(), 4);
|
||||
TestCase.assertEqual(array.length, 4);
|
||||
});
|
||||
|
||||
TestCase.assertEqual(array.length, 4);
|
||||
TestCase.assertThrows(function() {
|
||||
TestCase.assertThrowsContaining(() => {
|
||||
array.push([1]);
|
||||
}, 'can only push in a write transaction');
|
||||
}, "Cannot modify managed objects outside of a write transaction.");
|
||||
},
|
||||
|
||||
testListPop: function() {
|
||||
var realm = new Realm({schema: [schemas.LinkTypes, schemas.TestObject]});
|
||||
var array;
|
||||
const realm = new Realm({schema: [schemas.LinkTypes, schemas.TestObject]});
|
||||
let array;
|
||||
|
||||
realm.write(function() {
|
||||
var obj = realm.create('LinkTypesObject', {
|
||||
realm.write(() => {
|
||||
let obj = realm.create('LinkTypesObject', {
|
||||
objectCol: {doubleCol: 1},
|
||||
objectCol1: {doubleCol: 2},
|
||||
arrayCol: [{doubleCol: 3}, {doubleCol: 4}],
|
||||
@ -228,22 +462,19 @@ module.exports = {
|
||||
|
||||
TestCase.assertEqual(array.pop(), undefined);
|
||||
|
||||
TestCase.assertThrows(function() {
|
||||
array.pop(1);
|
||||
});
|
||||
TestCase.assertThrowsContaining(() => array.pop(1), 'Invalid argument');
|
||||
});
|
||||
|
||||
TestCase.assertThrows(function() {
|
||||
array.pop();
|
||||
}, 'can only pop in a write transaction');
|
||||
TestCase.assertThrowsContaining(() => array.pop(),
|
||||
"Cannot modify managed objects outside of a write transaction.");
|
||||
},
|
||||
|
||||
testListUnshift: function() {
|
||||
var realm = new Realm({schema: [schemas.LinkTypes, schemas.TestObject]});
|
||||
var array;
|
||||
const realm = new Realm({schema: [schemas.LinkTypes, schemas.TestObject]});
|
||||
let array;
|
||||
|
||||
realm.write(function() {
|
||||
var obj = realm.create('LinkTypesObject', {
|
||||
realm.write(() => {
|
||||
let obj = realm.create('LinkTypesObject', {
|
||||
objectCol: {doubleCol: 1},
|
||||
objectCol1: {doubleCol: 2},
|
||||
arrayCol: [{doubleCol: 3}],
|
||||
@ -260,20 +491,22 @@ module.exports = {
|
||||
TestCase.assertEqual(array.length, 4);
|
||||
TestCase.assertEqual(array[0].doubleCol, 1);
|
||||
TestCase.assertEqual(array[1].doubleCol, 2);
|
||||
|
||||
TestCase.assertEqual(array.unshift(), 4);
|
||||
TestCase.assertEqual(array.length, 4);
|
||||
});
|
||||
|
||||
TestCase.assertEqual(array.length, 4);
|
||||
TestCase.assertThrows(function() {
|
||||
array.unshift({doubleCol: 1});
|
||||
}, 'can only unshift in a write transaction');
|
||||
TestCase.assertThrowsContaining(() => array.unshift({doubleCol: 1}),
|
||||
'Cannot modify managed objects outside of a write transaction.');
|
||||
},
|
||||
|
||||
testListShift: function() {
|
||||
var realm = new Realm({schema: [schemas.LinkTypes, schemas.TestObject]});
|
||||
var array;
|
||||
const realm = new Realm({schema: [schemas.LinkTypes, schemas.TestObject]});
|
||||
let array;
|
||||
|
||||
realm.write(function() {
|
||||
var obj = realm.create('LinkTypesObject', {
|
||||
realm.write(() => {
|
||||
let obj = realm.create('LinkTypesObject', {
|
||||
objectCol: {doubleCol: 1},
|
||||
objectCol1: {doubleCol: 2},
|
||||
arrayCol: [{doubleCol: 3}, {doubleCol: 4}],
|
||||
@ -286,29 +519,27 @@ module.exports = {
|
||||
|
||||
TestCase.assertEqual(array.shift(), undefined);
|
||||
|
||||
TestCase.assertThrows(function() {
|
||||
array.shift(1);
|
||||
});
|
||||
TestCase.assertThrowsContaining(() => array.shift(1), 'Invalid argument');
|
||||
});
|
||||
|
||||
TestCase.assertThrows(function() {
|
||||
TestCase.assertThrowsContaining(() => {
|
||||
array.shift();
|
||||
}, 'can only shift in a write transaction');
|
||||
}, "Cannot modify managed objects outside of a write transaction.");
|
||||
},
|
||||
|
||||
testListSplice: function() {
|
||||
var realm = new Realm({schema: [schemas.LinkTypes, schemas.TestObject]});
|
||||
var array;
|
||||
const realm = new Realm({schema: [schemas.LinkTypes, schemas.TestObject]});
|
||||
let array;
|
||||
|
||||
realm.write(function() {
|
||||
var obj = realm.create('LinkTypesObject', {
|
||||
realm.write(() => {
|
||||
let obj = realm.create('LinkTypesObject', {
|
||||
objectCol: {doubleCol: 1},
|
||||
objectCol1: {doubleCol: 2},
|
||||
arrayCol: [{doubleCol: 3}, {doubleCol: 4}],
|
||||
});
|
||||
|
||||
array = obj.arrayCol;
|
||||
var removed;
|
||||
let removed;
|
||||
|
||||
removed = array.splice(0, 0, obj.objectCol, obj.objectCol1);
|
||||
TestCase.assertEqual(removed.length, 0);
|
||||
@ -355,26 +586,26 @@ module.exports = {
|
||||
TestCase.assertEqual(removed.length, 1);
|
||||
TestCase.assertEqual(array.length, 0);
|
||||
|
||||
TestCase.assertThrows(function() {
|
||||
TestCase.assertThrowsContaining(() => {
|
||||
array.splice('cat', 1);
|
||||
});
|
||||
}, "Value 'cat' not convertible to a number");
|
||||
|
||||
TestCase.assertThrows(function() {
|
||||
TestCase.assertThrowsContaining(() => {
|
||||
array.splice(0, 0, 0);
|
||||
});
|
||||
}, "JS value must be of type 'object', got (0)");
|
||||
});
|
||||
|
||||
TestCase.assertThrows(function() {
|
||||
TestCase.assertThrowsContaining(() => {
|
||||
array.splice(0, 0, {doubleCol: 1});
|
||||
}, 'can only splice in a write transaction');
|
||||
}, "Cannot modify managed objects outside of a write transaction");
|
||||
},
|
||||
|
||||
testListDeletions: function() {
|
||||
var realm = new Realm({schema: [schemas.LinkTypes, schemas.TestObject]});
|
||||
var object;
|
||||
var array;
|
||||
const realm = new Realm({schema: [schemas.LinkTypes, schemas.TestObject]});
|
||||
let object;
|
||||
let array;
|
||||
|
||||
realm.write(function() {
|
||||
realm.write(() => {
|
||||
object = realm.create('LinkTypesObject', {
|
||||
objectCol: {doubleCol: 1},
|
||||
objectCol1: {doubleCol: 2},
|
||||
@ -385,7 +616,7 @@ module.exports = {
|
||||
});
|
||||
|
||||
try {
|
||||
realm.write(function() {
|
||||
realm.write(() => {
|
||||
realm.delete(array[0]);
|
||||
TestCase.assertEqual(array.length, 1);
|
||||
TestCase.assertEqual(array[0].doubleCol, 4);
|
||||
@ -398,22 +629,20 @@ module.exports = {
|
||||
TestCase.assertEqual(array.length, 2);
|
||||
TestCase.assertEqual(array[0].doubleCol, 3);
|
||||
|
||||
realm.write(function() {
|
||||
realm.write(() => {
|
||||
realm.delete(object);
|
||||
});
|
||||
|
||||
TestCase.assertThrows(function() {
|
||||
array[0];
|
||||
});
|
||||
TestCase.assertThrowsContaining(() => array[0], 'invalidated');
|
||||
},
|
||||
|
||||
testLiveUpdatingResults: function() {
|
||||
var realm = new Realm({schema: [schemas.LinkTypes, schemas.TestObject]});
|
||||
var objects = realm.objects('TestObject');
|
||||
var array;
|
||||
const realm = new Realm({schema: [schemas.LinkTypes, schemas.TestObject]});
|
||||
let objects = realm.objects('TestObject');
|
||||
let array;
|
||||
|
||||
realm.write(function() {
|
||||
var obj = realm.create('LinkTypesObject', {
|
||||
realm.write(() => {
|
||||
let obj = realm.create('LinkTypesObject', {
|
||||
objectCol: {doubleCol: 1},
|
||||
objectCol1: {doubleCol: 2},
|
||||
arrayCol: [{doubleCol: 3}, {doubleCol: 4}],
|
||||
@ -426,7 +655,7 @@ module.exports = {
|
||||
TestCase.assertEqual(objects.length, 4);
|
||||
|
||||
try {
|
||||
realm.write(function() {
|
||||
realm.write(() => {
|
||||
array.push({doubleCol: 5});
|
||||
TestCase.assertEqual(objects.length, 5);
|
||||
|
||||
@ -449,50 +678,50 @@ module.exports = {
|
||||
},
|
||||
|
||||
testListSnapshot: function() {
|
||||
var realm = new Realm({schema: [schemas.LinkTypes, schemas.TestObject]});
|
||||
var objects = realm.objects('TestObject');
|
||||
var array;
|
||||
const realm = new Realm({schema: [schemas.LinkTypes, schemas.TestObject]});
|
||||
let objects = realm.objects('TestObject');
|
||||
let array;
|
||||
|
||||
realm.write(function() {
|
||||
var obj = realm.create('LinkTypesObject', [[1], [2], [[3], [4]]]);
|
||||
realm.write(() => {
|
||||
let obj = realm.create('LinkTypesObject', [[1], [2], [[3], [4]], [[5], [6]]]);
|
||||
array = obj.arrayCol;
|
||||
});
|
||||
|
||||
var objectsCopy = objects.snapshot();
|
||||
var arrayCopy = array.snapshot();
|
||||
let objectsCopy = objects.snapshot();
|
||||
let arrayCopy = array.snapshot();
|
||||
|
||||
TestCase.assertEqual(objectsCopy.length, 4);
|
||||
TestCase.assertEqual(objectsCopy.length, 6);
|
||||
TestCase.assertEqual(arrayCopy.length, 2);
|
||||
|
||||
realm.write(function() {
|
||||
realm.write(() => {
|
||||
array.push([5]);
|
||||
TestCase.assertEqual(objectsCopy.length, 4);
|
||||
TestCase.assertEqual(objectsCopy.length, 6);
|
||||
TestCase.assertEqual(arrayCopy.length, 2);
|
||||
|
||||
TestCase.assertEqual(objectsCopy.snapshot().length, 4);
|
||||
TestCase.assertEqual(objectsCopy.snapshot().length, 6);
|
||||
TestCase.assertEqual(arrayCopy.snapshot().length, 2);
|
||||
|
||||
TestCase.assertEqual(objects.snapshot().length, 5);
|
||||
TestCase.assertEqual(objects.snapshot().length, 7);
|
||||
TestCase.assertEqual(array.snapshot().length, 3);
|
||||
|
||||
realm.delete(array[0]);
|
||||
TestCase.assertEqual(objectsCopy.length, 4);
|
||||
TestCase.assertEqual(objectsCopy.length, 6);
|
||||
TestCase.assertEqual(arrayCopy.length, 2);
|
||||
TestCase.assertEqual(arrayCopy[0], null);
|
||||
|
||||
realm.deleteAll();
|
||||
TestCase.assertEqual(objectsCopy.length, 4);
|
||||
TestCase.assertEqual(objectsCopy.length, 6);
|
||||
TestCase.assertEqual(arrayCopy.length, 2);
|
||||
TestCase.assertEqual(arrayCopy[1], null);
|
||||
});
|
||||
},
|
||||
|
||||
testListFiltered: function() {
|
||||
var realm = new Realm({schema: [schemas.PersonObject, schemas.PersonList]});
|
||||
var list;
|
||||
const realm = new Realm({schema: [schemas.PersonObject, schemas.PersonList]});
|
||||
let list;
|
||||
|
||||
realm.write(function() {
|
||||
var object = realm.create('PersonList', {list: [
|
||||
realm.write(() => {
|
||||
let object = realm.create('PersonList', {list: [
|
||||
{name: 'Ari', age: 10},
|
||||
{name: 'Tim', age: 11},
|
||||
{name: 'Bjarne', age: 12},
|
||||
@ -515,10 +744,11 @@ module.exports = {
|
||||
{name: 'Target', properties: {value: 'int'}},
|
||||
{name: 'Mid', properties: {value: 'int', link: 'Target'}},
|
||||
{name: 'List', properties: {list: {type: 'list', objectType: 'Mid'}}},
|
||||
schemas.PrimitiveArrays
|
||||
];
|
||||
const realm = new Realm({schema: schema});
|
||||
|
||||
let list;
|
||||
let list, prim;
|
||||
realm.write(() => {
|
||||
list = realm.create('List', {list: [
|
||||
{value: 3, link: {value: 1}},
|
||||
@ -528,13 +758,31 @@ module.exports = {
|
||||
realm.create('List', {list: [
|
||||
{value: 4, link: {value: 4}},
|
||||
]});
|
||||
prim = realm.create('PrimitiveArrays', {
|
||||
bool: [true, false],
|
||||
int: [3, 1, 2],
|
||||
float: [3, 1, 2],
|
||||
double: [3, 1, 2],
|
||||
string: ['c', 'a', 'b'],
|
||||
data: [DATA3, DATA1, DATA2],
|
||||
date: [DATE3, DATE1, DATE2],
|
||||
optBool: [true, false, null],
|
||||
optInt: [3, 1, 2, null],
|
||||
optFloat: [3, 1, 2, null],
|
||||
optDouble: [3, 1, 2, null],
|
||||
optString: ['c', 'a', 'b', null],
|
||||
optData: [DATA3, DATA1, DATA2, null],
|
||||
optDate: [DATE3, DATE1, DATE2, null],
|
||||
});
|
||||
});
|
||||
|
||||
const values = (results) => results.map((o) => o.value);
|
||||
|
||||
TestCase.assertThrows(() => list.sorted());
|
||||
TestCase.assertThrows(() => list.sorted('nonexistent property'));
|
||||
TestCase.assertThrows(() => list.sorted('link'));
|
||||
// TestCase.assertThrowsContaining(() => list.sorted());
|
||||
TestCase.assertThrowsContaining(() => list.sorted('nonexistent property'),
|
||||
"Cannot sort on key path 'nonexistent property': property 'Mid.nonexistent property' does not exist.");
|
||||
TestCase.assertThrowsContaining(() => list.sorted('link'),
|
||||
"Cannot sort on key path 'link': property 'Mid.link' of type 'object' cannot be the final property in the key path.");
|
||||
|
||||
TestCase.assertArraysEqual(values(list.sorted([])), [3, 1, 2]);
|
||||
|
||||
@ -551,24 +799,50 @@ module.exports = {
|
||||
TestCase.assertArraysEqual(values(list.sorted(['link.value'])), [3, 2, 1]);
|
||||
TestCase.assertArraysEqual(values(list.sorted([['link.value', false]])), [3, 2, 1]);
|
||||
TestCase.assertArraysEqual(values(list.sorted([['link.value', true]])), [1, 2, 3]);
|
||||
|
||||
TestCase.assertThrowsContaining(() => prim.int.sorted('value', true),
|
||||
"Cannot sort on key path 'value': arrays of 'int' can only be sorted on 'self'");
|
||||
TestCase.assertThrowsContaining(() => prim.int.sorted('!ARRAY_VALUE', true),
|
||||
"Cannot sort on key path '!ARRAY_VALUE': arrays of 'int' can only be sorted on 'self'");
|
||||
|
||||
TestCase.assertArraysEqual(prim.int.sorted([]), [3, 1, 2]);
|
||||
TestCase.assertArraysEqual(prim.int.sorted(), [1, 2, 3]);
|
||||
TestCase.assertArraysEqual(prim.int.sorted(false), [1, 2, 3]);
|
||||
TestCase.assertArraysEqual(prim.int.sorted(true), [3, 2, 1]);
|
||||
|
||||
TestCase.assertArraysEqual(prim.optInt.sorted([]), [3, 1, 2, null]);
|
||||
TestCase.assertArraysEqual(prim.optInt.sorted(), [null, 1, 2, 3]);
|
||||
TestCase.assertArraysEqual(prim.optInt.sorted(false), [null, 1, 2, 3]);
|
||||
TestCase.assertArraysEqual(prim.optInt.sorted(true), [3, 2, 1, null]);
|
||||
|
||||
TestCase.assertArraysEqual(prim.bool.sorted(), [false, true]);
|
||||
TestCase.assertArraysEqual(prim.float.sorted(), [1, 2, 3]);
|
||||
TestCase.assertArraysEqual(prim.double.sorted(), [1, 2, 3]);
|
||||
TestCase.assertArraysEqual(prim.string.sorted(), ['a', 'b', 'c']);
|
||||
TestCase.assertArraysEqual(prim.data.sorted(), [DATA1, DATA2, DATA3]);
|
||||
TestCase.assertArraysEqual(prim.date.sorted(), [DATE1, DATE2, DATE3]);
|
||||
TestCase.assertArraysEqual(prim.optBool.sorted(), [null, false, true]);
|
||||
TestCase.assertArraysEqual(prim.optFloat.sorted(), [null, 1, 2, 3]);
|
||||
TestCase.assertArraysEqual(prim.optDouble.sorted(), [null, 1, 2, 3]);
|
||||
TestCase.assertArraysEqual(prim.optString.sorted(), [null, 'a', 'b', 'c']);
|
||||
TestCase.assertArraysEqual(prim.optData.sorted(), [null, DATA1, DATA2, DATA3]);
|
||||
TestCase.assertArraysEqual(prim.optDate.sorted(), [null, DATE1, DATE2, DATE3]);
|
||||
},
|
||||
|
||||
testArrayMethods: function() {
|
||||
var realm = new Realm({schema: [schemas.PersonObject, schemas.PersonList]});
|
||||
var object;
|
||||
const realm = new Realm({schema: [schemas.PersonObject, schemas.PersonList, schemas.PrimitiveArrays]});
|
||||
let object, prim;
|
||||
|
||||
realm.write(function() {
|
||||
realm.write(() => {
|
||||
object = realm.create('PersonList', {list: [
|
||||
{name: 'Ari', age: 10},
|
||||
{name: 'Tim', age: 11},
|
||||
{name: 'Bjarne', age: 12},
|
||||
]});
|
||||
prim = realm.create('PrimitiveArrays', {int: [10, 11, 12]});
|
||||
});
|
||||
|
||||
[
|
||||
object.list,
|
||||
realm.objects('PersonObject'),
|
||||
].forEach(function(list) {
|
||||
for (const list of [object.list, realm.objects('PersonObject')]) {
|
||||
TestCase.assertEqual(list.slice().length, 3);
|
||||
TestCase.assertEqual(list.slice(-1).length, 1);
|
||||
TestCase.assertEqual(list.slice(-1)[0].age, 12);
|
||||
@ -581,43 +855,43 @@ module.exports = {
|
||||
TestCase.assertEqual(list.join(' '), 'Ari Tim Bjarne');
|
||||
}
|
||||
|
||||
var count = 0;
|
||||
list.forEach(function(p, i) {
|
||||
let count = 0;
|
||||
list.forEach((p, i) => {
|
||||
TestCase.assertEqual(p.name, list[i].name);
|
||||
count++;
|
||||
});
|
||||
TestCase.assertEqual(count, list.length);
|
||||
|
||||
TestCase.assertArraysEqual(list.map(function(p) {return p.age}), [10, 11, 12]);
|
||||
TestCase.assertTrue(list.some(function(p) {return p.age > 10}));
|
||||
TestCase.assertTrue(list.every(function(p) {return p.age > 0}));
|
||||
TestCase.assertArraysEqual(list.map(p => p.age), [10, 11, 12]);
|
||||
TestCase.assertTrue(list.some(p => p.age > 10));
|
||||
TestCase.assertTrue(list.every(p => p.age > 0));
|
||||
|
||||
var person = list.find(function(p) {return p.name == 'Tim'});
|
||||
let person = list.find(p => p.name == 'Tim');
|
||||
TestCase.assertEqual(person.name, 'Tim');
|
||||
|
||||
var index = list.findIndex(function(p) {return p.name == 'Tim'});
|
||||
let index = list.findIndex(p => p.name == 'Tim');
|
||||
TestCase.assertEqual(index, 1);
|
||||
TestCase.assertEqual(list.indexOf(list[index]), index);
|
||||
|
||||
TestCase.assertEqual(list.reduce(function(n, p) {return n + p.age}, 0), 33);
|
||||
TestCase.assertEqual(list.reduceRight(function(n, p) {return n + p.age}, 0), 33);
|
||||
TestCase.assertEqual(list.reduce((n, p) => n + p.age, 0), 33);
|
||||
TestCase.assertEqual(list.reduceRight((n, p) => n + p.age, 0), 33);
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
var iteratorMethodNames = ['entries', 'keys', 'values'];
|
||||
let iteratorMethodNames = ['entries', 'keys', 'values'];
|
||||
|
||||
iteratorMethodNames.push(Symbol.iterator);
|
||||
|
||||
iteratorMethodNames.forEach(function(methodName) {
|
||||
var iterator = list[methodName]();
|
||||
var count = 0;
|
||||
var result;
|
||||
iteratorMethodNames.forEach(methodName => {
|
||||
let iterator = list[methodName]();
|
||||
let count = 0;
|
||||
let result;
|
||||
|
||||
// This iterator should itself be iterable.
|
||||
// TestCase.assertEqual(iterator[iteratorSymbol](), iterator);
|
||||
TestCase.assertEqual(iterator[Symbol.iterator](), iterator);
|
||||
|
||||
while ((result = iterator.next()) && !result.done) {
|
||||
var value = result.value;
|
||||
let value = result.value;
|
||||
|
||||
switch (methodName) {
|
||||
case 'entries':
|
||||
@ -640,14 +914,83 @@ module.exports = {
|
||||
TestCase.assertEqual(result.value, undefined);
|
||||
TestCase.assertEqual(count, list.length);
|
||||
});
|
||||
}
|
||||
|
||||
const list = prim.int;
|
||||
TestCase.assertEqual(list.slice().length, 3);
|
||||
TestCase.assertEqual(list.slice(-1).length, 1);
|
||||
TestCase.assertEqual(list.slice(-1)[0], 12);
|
||||
TestCase.assertEqual(list.slice(1, 3).length, 2);
|
||||
TestCase.assertEqual(list.slice(1, 3)[1], 12);
|
||||
|
||||
TestCase.assertEqual(list.join(' '), '10 11 12');
|
||||
|
||||
let count = 0;
|
||||
list.forEach((v, i) => {
|
||||
TestCase.assertEqual(v, i + 10);
|
||||
count++;
|
||||
});
|
||||
TestCase.assertEqual(count, list.length);
|
||||
|
||||
TestCase.assertArraysEqual(list.map(p => p + 1), [11, 12, 13]);
|
||||
TestCase.assertTrue(list.some(p => p > 10));
|
||||
TestCase.assertTrue(list.every(p => p > 0));
|
||||
|
||||
let value = list.find(p => p == 11);
|
||||
TestCase.assertEqual(value, 11)
|
||||
|
||||
let index = list.findIndex(p => p == 11);
|
||||
TestCase.assertEqual(index, 1);
|
||||
TestCase.assertEqual(list.indexOf(list[index]), index);
|
||||
|
||||
TestCase.assertEqual(list.reduce((n, p) => n + p, 0), 33);
|
||||
TestCase.assertEqual(list.reduceRight((n, p) => n + p, 0), 33);
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
let iteratorMethodNames = ['entries', 'keys', 'values'];
|
||||
|
||||
iteratorMethodNames.push(Symbol.iterator);
|
||||
|
||||
iteratorMethodNames.forEach(methodName => {
|
||||
let iterator = list[methodName]();
|
||||
let count = 0;
|
||||
let result;
|
||||
|
||||
// This iterator should itself be iterable.
|
||||
// TestCase.assertEqual(iterator[iteratorSymbol](), iterator);
|
||||
TestCase.assertEqual(iterator[Symbol.iterator](), iterator);
|
||||
|
||||
while ((result = iterator.next()) && !result.done) {
|
||||
let value = result.value;
|
||||
|
||||
switch (methodName) {
|
||||
case 'entries':
|
||||
TestCase.assertEqual(value.length, 2);
|
||||
TestCase.assertEqual(value[0], count);
|
||||
TestCase.assertEqual(value[1], list[count]);
|
||||
break;
|
||||
case 'keys':
|
||||
TestCase.assertEqual(value, count);
|
||||
break;
|
||||
default:
|
||||
TestCase.assertEqual(value.name, list[count].name);
|
||||
break;
|
||||
}
|
||||
|
||||
count++;
|
||||
}
|
||||
|
||||
TestCase.assertEqual(result.done, true);
|
||||
TestCase.assertEqual(result.value, undefined);
|
||||
TestCase.assertEqual(count, list.length);
|
||||
});
|
||||
},
|
||||
|
||||
testIsValid: function() {
|
||||
var realm = new Realm({schema: [schemas.PersonObject, schemas.PersonList]});
|
||||
var object;
|
||||
var list;
|
||||
realm.write(function() {
|
||||
const realm = new Realm({schema: [schemas.PersonObject, schemas.PersonList]});
|
||||
let object;
|
||||
let list;
|
||||
realm.write(() => {
|
||||
object = realm.create('PersonList', {list: [
|
||||
{name: 'Ari', age: 10},
|
||||
{name: 'Tim', age: 11},
|
||||
@ -659,8 +1002,6 @@ module.exports = {
|
||||
});
|
||||
|
||||
TestCase.assertEqual(list.isValid(), false);
|
||||
TestCase.assertThrows(function() {
|
||||
list.length;
|
||||
});
|
||||
TestCase.assertThrowsContaining(() => list.length, 'invalidated');
|
||||
},
|
||||
};
|
||||
|
@ -88,8 +88,8 @@ module.exports = {
|
||||
renamed: 'string',
|
||||
prop1: 'int',
|
||||
}
|
||||
}],
|
||||
schemaVersion: 1,
|
||||
}],
|
||||
schemaVersion: 1,
|
||||
migration: function(oldRealm, newRealm) {
|
||||
var oldObjects = oldRealm.objects('TestObject');
|
||||
var newObjects = newRealm.objects('TestObject');
|
||||
@ -136,8 +136,8 @@ module.exports = {
|
||||
renamed: 'string',
|
||||
prop1: 'int',
|
||||
}
|
||||
}],
|
||||
schemaVersion: 1,
|
||||
}],
|
||||
schemaVersion: 1,
|
||||
migration: function(oldRealm, newRealm) {
|
||||
var oldSchema = oldRealm.schema;
|
||||
var newSchema = newRealm.schema;
|
||||
@ -324,4 +324,44 @@ module.exports = {
|
||||
|
||||
realm.close();
|
||||
},
|
||||
|
||||
testMigrateToListOfInts: function() {
|
||||
let realm = new Realm({schema: [{name: 'TestObject', properties: {values: 'IntObject[]'}},
|
||||
{name: 'IntObject', properties: {value: 'int'}}]});
|
||||
realm.write(function() {
|
||||
realm.create('TestObject', {values: [{value: 1}, {value: 2}, {value: 3}]});
|
||||
realm.create('TestObject', {values: [{value: 1}, {value: 4}, {value: 5}]});
|
||||
});
|
||||
realm.close();
|
||||
|
||||
realm = new Realm({
|
||||
schema: [{name: 'TestObject', properties: {values: 'int[]'}}],
|
||||
schemaVersion: 1,
|
||||
migration: function(oldRealm, newRealm) {
|
||||
const oldObjects = oldRealm.objects('TestObject');
|
||||
const newObjects = newRealm.objects('TestObject');
|
||||
TestCase.assertEqual(oldObjects.length, 2);
|
||||
TestCase.assertEqual(newObjects.length, 2);
|
||||
|
||||
for (let i = 0; i < oldObjects.length; ++i) {
|
||||
TestCase.assertEqual(oldObjects[i].values.length, 3);
|
||||
TestCase.assertEqual(newObjects[i].values.length, 0);
|
||||
newObjects[i].values = oldObjects[i].values.map(o => o.value);
|
||||
TestCase.assertEqual(newObjects[i].values.length, 3);
|
||||
}
|
||||
newRealm.deleteModel('IntObject');
|
||||
}
|
||||
});
|
||||
|
||||
const objects = realm.objects('TestObject');
|
||||
TestCase.assertEqual(objects.length, 2);
|
||||
TestCase.assertEqual(objects[0].values.length, 3);
|
||||
TestCase.assertEqual(objects[1].values.length, 3);
|
||||
TestCase.assertEqual(objects[0].values[0], 1);
|
||||
TestCase.assertEqual(objects[0].values[1], 2);
|
||||
TestCase.assertEqual(objects[0].values[2], 3);
|
||||
TestCase.assertEqual(objects[1].values[0], 1);
|
||||
TestCase.assertEqual(objects[1].values[1], 4);
|
||||
TestCase.assertEqual(objects[1].values[2], 5);
|
||||
},
|
||||
};
|
||||
|
@ -18,226 +18,158 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
var Realm = require('realm');
|
||||
var TestCase = require('./asserts');
|
||||
var schemas = require('./schemas');
|
||||
const Realm = require('realm');
|
||||
const TestCase = require('./asserts');
|
||||
const schemas = require('./schemas');
|
||||
|
||||
var RANDOM_DATA = new Uint8Array([
|
||||
const RANDOM_DATA = new Uint8Array([
|
||||
0xd8, 0x21, 0xd6, 0xe8, 0x00, 0x57, 0xbc, 0xb2, 0x6a, 0x15, 0x77, 0x30, 0xac, 0x77, 0x96, 0xd9,
|
||||
0x67, 0x1e, 0x40, 0xa7, 0x6d, 0x52, 0x83, 0xda, 0x07, 0x29, 0x9c, 0x70, 0x38, 0x48, 0x4e, 0xff,
|
||||
]);
|
||||
|
||||
module.exports = {
|
||||
testBasicTypesPropertyGetters: function() {
|
||||
var realm = new Realm({schema: [schemas.BasicTypes]});
|
||||
var object;
|
||||
const allTypesValues = {
|
||||
boolCol: true,
|
||||
intCol: 1,
|
||||
floatCol: 1.1,
|
||||
doubleCol: 1.11,
|
||||
stringCol: 'string',
|
||||
dateCol: new Date(1),
|
||||
dataCol: RANDOM_DATA,
|
||||
objectCol: {doubleCol: 2.2},
|
||||
|
||||
var basicTypesValues = {
|
||||
boolCol: true,
|
||||
intCol: 1,
|
||||
floatCol: 1.1,
|
||||
doubleCol: 1.11,
|
||||
stringCol: 'string',
|
||||
dateCol: new Date(1),
|
||||
dataCol: RANDOM_DATA,
|
||||
};
|
||||
optBoolCol: true,
|
||||
optIntCol: 1,
|
||||
optFloatCol: 1.1,
|
||||
optDoubleCol: 1.11,
|
||||
optStringCol: 'string',
|
||||
optDateCol: new Date(1),
|
||||
optDataCol: RANDOM_DATA,
|
||||
|
||||
boolArrayCol: [true],
|
||||
intArrayCol: [1],
|
||||
floatArrayCol: [1.1],
|
||||
doubleArrayCol: [1.11],
|
||||
stringArrayCol: ['string'],
|
||||
dateArrayCol: [new Date(1)],
|
||||
dataArrayCol: [RANDOM_DATA],
|
||||
objectArrayCol: [{doubleCol: 2.2}],
|
||||
|
||||
optBoolArrayCol: [true],
|
||||
optIntArrayCol: [1],
|
||||
optFloatArrayCol: [1.1],
|
||||
optDoubleArrayCol: [1.11],
|
||||
optStringArrayCol: ['string'],
|
||||
optDateArrayCol: [new Date(1)],
|
||||
optDataArrayCol: [RANDOM_DATA],
|
||||
};
|
||||
const nullPropertyValues = (() => {
|
||||
let values = {}
|
||||
for (let name in allTypesValues) {
|
||||
if (name.includes('opt')) {
|
||||
values[name] = name.includes('Array') ? [null] : null;
|
||||
}
|
||||
else {
|
||||
values[name] = allTypesValues[name];
|
||||
}
|
||||
}
|
||||
return values;
|
||||
})();
|
||||
|
||||
module.exports = {
|
||||
testAllPropertyGetters: function() {
|
||||
const realm = new Realm({schema: [schemas.AllTypes, schemas.TestObject, schemas.LinkToAllTypes]});
|
||||
let object, nullObject;
|
||||
|
||||
realm.write(function() {
|
||||
object = realm.create('BasicTypesObject', basicTypesValues);
|
||||
object = realm.create('AllTypesObject', allTypesValues);
|
||||
nullObject = realm.create('AllTypesObject', nullPropertyValues);
|
||||
});
|
||||
|
||||
for (const name in schemas.BasicTypes.properties) {
|
||||
const prop = schemas.BasicTypes.properties[name];
|
||||
const type = typeof prop == 'object' ? prop.type : prop;
|
||||
TestCase.assertSimilar(type, object[name], basicTypesValues[name]);
|
||||
const objectSchema = realm.schema[0];
|
||||
for (const name of Object.keys(objectSchema.properties)) {
|
||||
const type = objectSchema.properties[name].type;
|
||||
if (type === 'linkingObjects') {
|
||||
TestCase.assertEqual(object[name].length, 0);
|
||||
TestCase.assertEqual(nullObject[name].length, 0);
|
||||
continue;
|
||||
}
|
||||
|
||||
TestCase.assertSimilar(type, object[name], allTypesValues[name]);
|
||||
TestCase.assertSimilar(type, nullObject[name], nullPropertyValues[name]);
|
||||
}
|
||||
|
||||
TestCase.assertEqual(object.nonexistent, undefined);
|
||||
},
|
||||
testNullableBasicTypesPropertyGetters: function() {
|
||||
var realm = new Realm({schema: [schemas.NullableBasicTypes]});
|
||||
var object, nullObject;
|
||||
|
||||
var basicTypesValues = {
|
||||
boolCol: true,
|
||||
intCol: 1,
|
||||
floatCol: 1.1,
|
||||
doubleCol: 1.11,
|
||||
stringCol: 'string',
|
||||
dateCol: new Date(1),
|
||||
dataCol: RANDOM_DATA,
|
||||
};
|
||||
testAllTypesPropertySetters: function() {
|
||||
const realm = new Realm({schema: [schemas.AllTypes, schemas.TestObject, schemas.LinkToAllTypes]});
|
||||
let obj;
|
||||
|
||||
realm.write(function() {
|
||||
object = realm.create('NullableBasicTypesObject', basicTypesValues);
|
||||
|
||||
nullObject = realm.create('NullableBasicTypesObject', {
|
||||
boolCol: null,
|
||||
intCol: null,
|
||||
floatCol: null,
|
||||
doubleCol: null,
|
||||
stringCol: null,
|
||||
dateCol: null,
|
||||
dataCol: null,
|
||||
});
|
||||
obj = realm.create('AllTypesObject', allTypesValues);
|
||||
});
|
||||
|
||||
for (var name in schemas.BasicTypes.properties) {
|
||||
var prop = schemas.BasicTypes.properties[name];
|
||||
var type = typeof prop == 'object' ? prop.type : prop;
|
||||
|
||||
TestCase.assertEqual(nullObject[name], null);
|
||||
|
||||
if (type == 'float' || type == 'double') {
|
||||
TestCase.assertEqualWithTolerance(object[name], basicTypesValues[name], 0.000001);
|
||||
}
|
||||
else if (type == 'data') {
|
||||
TestCase.assertArraysEqual(new Uint8Array(object[name]), RANDOM_DATA);
|
||||
}
|
||||
else if (type == 'date') {
|
||||
TestCase.assertEqual(object[name].getTime(), basicTypesValues[name].getTime());
|
||||
}
|
||||
else {
|
||||
TestCase.assertEqual(object[name], basicTypesValues[name]);
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
testBasicTypesPropertySetters: function() {
|
||||
var realm = new Realm({schema: [schemas.BasicTypes]});
|
||||
var obj;
|
||||
|
||||
var basicTypesValues = {
|
||||
boolCol: true,
|
||||
intCol: 1,
|
||||
floatCol: 1.1,
|
||||
doubleCol: 1.11,
|
||||
stringCol: 'string',
|
||||
dateCol: new Date(1),
|
||||
dataCol: new ArrayBuffer(),
|
||||
};
|
||||
|
||||
realm.write(function() {
|
||||
obj = realm.create('BasicTypesObject', basicTypesValues);
|
||||
TestCase.assertThrows(function() {
|
||||
obj.boolCol = false;
|
||||
obj.intCol = 2;
|
||||
obj.floatCol = 2.2;
|
||||
obj.doubleCol = 2.22;
|
||||
obj.stringCol = 'STRING';
|
||||
obj.dateCol = new Date(2);
|
||||
obj.dataCol = RANDOM_DATA;
|
||||
});
|
||||
|
||||
TestCase.assertEqual(obj.boolCol, false, 'wrong bool value');
|
||||
TestCase.assertEqual(obj.intCol, 2, 'wrong int value');
|
||||
TestCase.assertEqualWithTolerance(obj.floatCol, 2.2, 0.000001, 'wrong float value');
|
||||
TestCase.assertEqualWithTolerance(obj.doubleCol, 2.22, 0.000001, 'wrong double value');
|
||||
TestCase.assertEqual(obj.stringCol, 'STRING', 'wrong string value');
|
||||
TestCase.assertEqual(obj.dateCol.getTime(), 2, 'wrong date value');
|
||||
TestCase.assertArraysEqual(new Uint8Array(obj.dataCol), RANDOM_DATA, 'wrong data value');
|
||||
|
||||
realm.write(function() {
|
||||
TestCase.assertThrows(function() {
|
||||
obj.boolCol = 'cat';
|
||||
});
|
||||
TestCase.assertThrows(function() {
|
||||
obj.intCol = 'dog';
|
||||
});
|
||||
|
||||
TestCase.assertThrows(function() {
|
||||
obj.boolCol = null;
|
||||
});
|
||||
TestCase.assertThrows(function() {
|
||||
obj.boolCol = undefined;
|
||||
});
|
||||
TestCase.assertThrows(function() {
|
||||
obj.intCol = null;
|
||||
});
|
||||
TestCase.assertThrows(function() {
|
||||
obj.intCol = undefined;
|
||||
});
|
||||
TestCase.assertThrows(function() {
|
||||
obj.floatCol = null;
|
||||
});
|
||||
TestCase.assertThrows(function() {
|
||||
obj.floatCol = undefined;
|
||||
});
|
||||
TestCase.assertThrows(function() {
|
||||
obj.doubleCol = null;
|
||||
});
|
||||
TestCase.assertThrows(function() {
|
||||
obj.doubleCol = undefined;
|
||||
});
|
||||
TestCase.assertThrows(function() {
|
||||
obj.stringCol = null;
|
||||
});
|
||||
TestCase.assertThrows(function() {
|
||||
obj.stringCol = undefined;
|
||||
});
|
||||
TestCase.assertThrows(function() {
|
||||
obj.dateCol = null;
|
||||
});
|
||||
TestCase.assertThrows(function() {
|
||||
obj.dateCol = undefined;
|
||||
});
|
||||
TestCase.assertThrows(function() {
|
||||
obj.dataCol = null;
|
||||
});
|
||||
TestCase.assertThrows(function() {
|
||||
obj.dataCol = undefined;
|
||||
});
|
||||
});
|
||||
|
||||
TestCase.assertThrows(function() {
|
||||
obj.boolCol = true;
|
||||
}, 'can only set property values in a write transaction');
|
||||
|
||||
TestCase.assertEqual(obj.boolCol, false, 'bool value changed outside transaction');
|
||||
},
|
||||
testNullableBasicTypesPropertySetters: function() {
|
||||
var realm = new Realm({schema: [schemas.NullableBasicTypes]});
|
||||
var obj, obj1;
|
||||
|
||||
var basicTypesValues = {
|
||||
boolCol: true,
|
||||
intCol: 1,
|
||||
floatCol: 1.1,
|
||||
doubleCol: 1.11,
|
||||
stringCol: 'string',
|
||||
dateCol: new Date(1),
|
||||
dataCol: RANDOM_DATA,
|
||||
};
|
||||
TestCase.assertEqual(obj.boolCol, true, 'bool value changed outside transaction');
|
||||
|
||||
realm.write(function() {
|
||||
obj = realm.create('NullableBasicTypesObject', basicTypesValues);
|
||||
obj1 = realm.create('NullableBasicTypesObject', basicTypesValues);
|
||||
TestCase.assertThrows(() => obj.boolCol = 'cat');
|
||||
TestCase.assertThrows(() => obj.intCol = 'dog');
|
||||
|
||||
for (var name in schemas.NullableBasicTypes.properties) {
|
||||
obj[name] = null;
|
||||
obj1[name] = undefined;
|
||||
// Non-optional properties should complain about null
|
||||
for (const name of ['boolCol', 'intCol', 'floatCol', 'doubleCol', 'stringCol', 'dataCol', 'dateCol']) {
|
||||
TestCase.assertThrows(() => obj[name] = null, `Setting ${name} to null should throw`);
|
||||
TestCase.assertThrows(() => obj[name] = undefined, `Setting ${name} to undefined should throw`);
|
||||
}
|
||||
|
||||
// Optional properties should allow it
|
||||
for (const name of ['optBoolCol', 'optIntCol', 'optFloatCol', 'optDoubleCol',
|
||||
'optStringCol', 'optDataCol', 'optDateCol', 'objectCol']) {
|
||||
obj[name] = null;
|
||||
TestCase.assertEqual(obj[name], null);
|
||||
obj[name] = undefined;
|
||||
TestCase.assertEqual(obj[name], null);
|
||||
}
|
||||
|
||||
function tryAssign(name, value) {
|
||||
var prop = schemas.AllTypes.properties[name];
|
||||
var type = typeof prop == 'object' ? prop.type : prop;
|
||||
obj[name] = value;
|
||||
TestCase.assertSimilar(type, obj[name], value, undefined, 1);
|
||||
}
|
||||
|
||||
tryAssign('boolCol', false);
|
||||
tryAssign('intCol', 10);
|
||||
tryAssign('floatCol', 2.2);
|
||||
tryAssign('doubleCol', 3.3);
|
||||
tryAssign('stringCol', 'new str');
|
||||
tryAssign('dateCol', new Date(2));
|
||||
tryAssign('dataCol', RANDOM_DATA);
|
||||
|
||||
tryAssign('optBoolCol', null);
|
||||
tryAssign('optIntCol', null);
|
||||
tryAssign('optFloatCol', null);
|
||||
tryAssign('optDoubleCol', null);
|
||||
tryAssign('optStringCol', null);
|
||||
tryAssign('optDateCol', null);
|
||||
tryAssign('optDataCol', null);
|
||||
|
||||
tryAssign('optBoolCol', false);
|
||||
tryAssign('optIntCol', 10);
|
||||
tryAssign('optFloatCol', 2.2);
|
||||
tryAssign('optDoubleCol', 3.3);
|
||||
tryAssign('optStringCol', 'new str');
|
||||
tryAssign('optDateCol', new Date(2));
|
||||
tryAssign('optDataCol', RANDOM_DATA);
|
||||
|
||||
});
|
||||
|
||||
for (var name in schemas.NullableBasicTypes.properties) {
|
||||
TestCase.assertEqual(obj[name], null);
|
||||
TestCase.assertEqual(obj1[name], null);
|
||||
}
|
||||
|
||||
realm.write(function() {
|
||||
TestCase.assertThrows(function() {
|
||||
obj.boolCol = 'cat';
|
||||
});
|
||||
TestCase.assertThrows(function() {
|
||||
obj.intCol = 'dog';
|
||||
});
|
||||
});
|
||||
|
||||
TestCase.assertThrows(function() {
|
||||
obj.boolCol = null;
|
||||
}, 'can only set property values in a write transaction');
|
||||
},
|
||||
|
||||
testLinkTypesPropertyGetters: function() {
|
||||
var realm = new Realm({schema: [schemas.LinkTypes, schemas.TestObject]});
|
||||
const realm = new Realm({schema: [schemas.LinkTypes, schemas.TestObject]});
|
||||
var obj = null;
|
||||
|
||||
realm.write(function() {
|
||||
@ -261,8 +193,9 @@ module.exports = {
|
||||
TestCase.assertEqual(arrayVal.length, 1);
|
||||
TestCase.assertEqual(arrayVal[0].doubleCol, 3);
|
||||
},
|
||||
|
||||
testLinkTypesPropertySetters: function() {
|
||||
var realm = new Realm({schema: [schemas.LinkTypes, schemas.TestObject]});
|
||||
const realm = new Realm({schema: [schemas.LinkTypes, schemas.TestObject]});
|
||||
var objects = realm.objects('TestObject');
|
||||
var obj;
|
||||
|
||||
@ -328,33 +261,25 @@ module.exports = {
|
||||
});
|
||||
TestCase.assertEqual(obj.objectCol.doubleCol, 3);
|
||||
},
|
||||
|
||||
testEnumerablePropertyNames: function() {
|
||||
var realm = new Realm({schema: [schemas.BasicTypes]});
|
||||
var object;
|
||||
const realm = new Realm({schema: [schemas.AllTypes, schemas.TestObject, schemas.LinkToAllTypes]});
|
||||
let object;
|
||||
|
||||
realm.write(function() {
|
||||
object = realm.create('BasicTypesObject', {
|
||||
boolCol: true,
|
||||
intCol: 1,
|
||||
floatCol: 1.1,
|
||||
doubleCol: 1.11,
|
||||
stringCol: 'string',
|
||||
dateCol: new Date(1),
|
||||
dataCol: RANDOM_DATA,
|
||||
});
|
||||
});
|
||||
realm.write(() => object = realm.create('AllTypesObject', allTypesValues));
|
||||
|
||||
var propNames = Object.keys(schemas.BasicTypes.properties);
|
||||
const propNames = Object.keys(schemas.AllTypes.properties);
|
||||
TestCase.assertArraysEqual(Object.keys(object), propNames, 'Object.keys');
|
||||
|
||||
for (var key in object) {
|
||||
for (let key in object) {
|
||||
TestCase.assertEqual(key, propNames.shift());
|
||||
}
|
||||
|
||||
TestCase.assertEqual(propNames.length, 0);
|
||||
},
|
||||
|
||||
testDataProperties: function() {
|
||||
var realm = new Realm({schema: [schemas.DefaultValues, schemas.TestObject]});
|
||||
const realm = new Realm({schema: [schemas.DefaultValues, schemas.TestObject]});
|
||||
var object;
|
||||
|
||||
// Should be be able to set a data property with a typed array.
|
||||
@ -432,7 +357,7 @@ module.exports = {
|
||||
},
|
||||
|
||||
testObjectConstructor: function() {
|
||||
var realm = new Realm({schema: [schemas.TestObject]});
|
||||
const realm = new Realm({schema: [schemas.TestObject]});
|
||||
|
||||
realm.write(function() {
|
||||
var obj = realm.create('TestObject', {doubleCol: 1});
|
||||
@ -444,7 +369,7 @@ module.exports = {
|
||||
},
|
||||
|
||||
testIsValid: function() {
|
||||
var realm = new Realm({schema: [schemas.TestObject]});
|
||||
const realm = new Realm({schema: [schemas.TestObject]});
|
||||
var obj;
|
||||
realm.write(function() {
|
||||
obj = realm.create('TestObject', {doubleCol: 1});
|
||||
@ -458,9 +383,9 @@ module.exports = {
|
||||
obj.doubleCol;
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
testObjectSchema: function() {
|
||||
var realm = new Realm({schema: [schemas.TestObject]});
|
||||
const realm = new Realm({schema: [schemas.TestObject]});
|
||||
var obj;
|
||||
realm.write(function() {
|
||||
obj = realm.create('TestObject', {doubleCol: 1});
|
||||
@ -503,7 +428,7 @@ module.exports = {
|
||||
TestCase.assertEqual(realm_v3.objects('Date')[0].nullDate.getTime(), 1462500087955);
|
||||
TestCase.assertEqual(realm_v3.objects('Date')[1].currentDate.getTime(), -10000);
|
||||
TestCase.assertEqual(realm_v3.objects('Date')[1].nullDate, null);
|
||||
|
||||
|
||||
// test different dates
|
||||
var realm = new Realm({schema: [schemas.DateObject]});
|
||||
realm.write(function() {
|
||||
|
@ -95,7 +95,8 @@ module.exports = {
|
||||
},
|
||||
|
||||
testRealmConstructorSchemaValidation: function() {
|
||||
TestCase.assertThrowsContaining(() => new Realm({schema: schemas.AllTypes}), "schema must be of type 'array', got");
|
||||
TestCase.assertThrowsContaining(() => new Realm({schema: schemas.AllTypes}),
|
||||
"schema must be of type 'array', got");
|
||||
TestCase.assertThrowsContaining(() => new Realm({schema: ['SomeType']}),
|
||||
"Failed to read ObjectSchema: JS value must be of type 'object', got (SomeType)");
|
||||
TestCase.assertThrowsContaining(() => new Realm({schema: [{}]}),
|
||||
@ -142,7 +143,7 @@ module.exports = {
|
||||
}]});
|
||||
}, "Property 'InvalidObject.link' declared as origin of linking objects property 'InvalidObject.linkingObjects' links to type 'IntObject'")
|
||||
},
|
||||
|
||||
|
||||
testRealmConstructorInMemory: function() {
|
||||
// open in-memory realm instance
|
||||
const realm1 = new Realm({inMemory: true, schema: [schemas.TestObject]});
|
||||
@ -166,7 +167,7 @@ module.exports = {
|
||||
// Open the same in-memory realm again and verify that it is now empty
|
||||
const realm3 = new Realm({inMemory: true});
|
||||
TestCase.assertEqual(realm3.schema.length, 0);
|
||||
|
||||
|
||||
// try to open the same realm in persistent mode (should fail as you cannot mix modes)
|
||||
TestCase.assertThrowsContaining(() => new Realm({}), 'already opened with different inMemory settings.');
|
||||
},
|
||||
@ -296,23 +297,10 @@ module.exports = {
|
||||
});
|
||||
},
|
||||
|
||||
testRealmCreateOptionals: function() {
|
||||
const realm = new Realm({schema: [schemas.NullableBasicTypes, schemas.LinkTypes, schemas.TestObject]});
|
||||
let basic, links;
|
||||
realm.write(() => {
|
||||
basic = realm.create('NullableBasicTypesObject', {});
|
||||
links = realm.create('LinkTypesObject', {});
|
||||
});
|
||||
for (const name in schemas.NullableBasicTypes.properties) {
|
||||
TestCase.assertEqual(basic[name], null);
|
||||
}
|
||||
TestCase.assertEqual(links.objectCol, null);
|
||||
TestCase.assertEqual(links.arrayCol.length, 0);
|
||||
},
|
||||
|
||||
testRealmCreateUpsert: function() {
|
||||
const realm = new Realm({schema: [schemas.IntPrimary, schemas.StringPrimary, schemas.AllTypes, schemas.TestObject, schemas.LinkToAllTypes]});
|
||||
realm.write(() => {
|
||||
const realm = new Realm({schema: [schemas.AllPrimaryTypes, schemas.TestObject,
|
||||
schemas.StringPrimary]});
|
||||
realm.write(function() {
|
||||
const values = {
|
||||
primaryCol: '0',
|
||||
boolCol: true,
|
||||
@ -326,12 +314,12 @@ module.exports = {
|
||||
arrayCol: [],
|
||||
};
|
||||
|
||||
const obj0 = realm.create('AllTypesObject', values);
|
||||
const obj0 = realm.create('AllPrimaryTypesObject', values);
|
||||
|
||||
TestCase.assertThrowsContaining(() => realm.create('AllTypesObject', values),
|
||||
"Attempting to create an object of type 'AllTypesObject' with an existing primary key value '0'.");
|
||||
TestCase.assertThrowsContaining(() => realm.create('AllPrimaryTypesObject', values),
|
||||
"Attempting to create an object of type 'AllPrimaryTypesObject' with an existing primary key value ''0''.");
|
||||
|
||||
const obj1 = realm.create('AllTypesObject', {
|
||||
const obj1 = realm.create('AllPrimaryTypesObject', {
|
||||
primaryCol: '1',
|
||||
boolCol: false,
|
||||
intCol: 2,
|
||||
@ -344,10 +332,10 @@ module.exports = {
|
||||
arrayCol: [{doubleCol: 2}],
|
||||
}, true);
|
||||
|
||||
const objects = realm.objects('AllTypesObject');
|
||||
const objects = realm.objects('AllPrimaryTypesObject');
|
||||
TestCase.assertEqual(objects.length, 2);
|
||||
|
||||
realm.create('AllTypesObject', {
|
||||
realm.create('AllPrimaryTypesObject', {
|
||||
primaryCol: '0',
|
||||
boolCol: false,
|
||||
intCol: 2,
|
||||
@ -371,13 +359,13 @@ module.exports = {
|
||||
TestCase.assertEqual(obj0.objectCol, null);
|
||||
TestCase.assertEqual(obj0.arrayCol.length, 1);
|
||||
|
||||
realm.create('AllTypesObject', {primaryCol: '0'}, true);
|
||||
realm.create('AllTypesObject', {primaryCol: '1'}, true);
|
||||
realm.create('AllPrimaryTypesObject', {primaryCol: '0'}, true);
|
||||
realm.create('AllPrimaryTypesObject', {primaryCol: '1'}, true);
|
||||
TestCase.assertEqual(obj0.stringCol, '2');
|
||||
TestCase.assertEqual(obj0.objectCol, null);
|
||||
TestCase.assertEqual(obj1.objectCol.doubleCol, 0);
|
||||
|
||||
realm.create('AllTypesObject', {
|
||||
realm.create('AllPrimaryTypesObject', {
|
||||
primaryCol: '0',
|
||||
stringCol: '3',
|
||||
objectCol: {doubleCol: 0},
|
||||
@ -393,13 +381,13 @@ module.exports = {
|
||||
TestCase.assertEqual(obj0.objectCol.doubleCol, 0);
|
||||
TestCase.assertEqual(obj0.arrayCol.length, 1);
|
||||
|
||||
realm.create('AllTypesObject', {primaryCol: '0', objectCol: undefined}, true);
|
||||
realm.create('AllTypesObject', {primaryCol: '1', objectCol: null}, true);
|
||||
realm.create('AllPrimaryTypesObject', {primaryCol: '0', objectCol: undefined}, true);
|
||||
realm.create('AllPrimaryTypesObject', {primaryCol: '1', objectCol: null}, true);
|
||||
TestCase.assertEqual(obj0.objectCol, null);
|
||||
TestCase.assertEqual(obj1.objectCol, null);
|
||||
|
||||
// test with string primaries
|
||||
const obj =realm.create('StringPrimaryObject', {
|
||||
const obj = realm.create('StringPrimaryObject', {
|
||||
primaryCol: '0',
|
||||
valueCol: 0
|
||||
});
|
||||
@ -784,8 +772,9 @@ module.exports = {
|
||||
},
|
||||
|
||||
testSchema: function() {
|
||||
const originalSchema = [schemas.TestObject, schemas.BasicTypes, schemas.NullableBasicTypes, schemas.IndexedTypes, schemas.IntPrimary,
|
||||
schemas.PersonObject, schemas.LinkTypes, schemas.LinkingObjectsObject];
|
||||
const originalSchema = [schemas.TestObject, schemas.AllTypes, schemas.LinkToAllTypes,
|
||||
schemas.IndexedTypes, schemas.IntPrimary, schemas.PersonObject,
|
||||
schemas.LinkTypes, schemas.LinkingObjectsObject];
|
||||
|
||||
const schemaMap = {};
|
||||
originalSchema.forEach(objectSchema => {
|
||||
@ -801,45 +790,67 @@ module.exports = {
|
||||
const schema = realm.schema;
|
||||
TestCase.assertEqual(schema.length, originalSchema.length);
|
||||
|
||||
function isString(val) {
|
||||
return typeof val === 'string' || val instanceof String;
|
||||
}
|
||||
const normalizeProperty = (val) => {
|
||||
let prop;
|
||||
if (typeof val !== 'string' && !(val instanceof String)) {
|
||||
prop = val;
|
||||
prop.optional = val.optional || false;
|
||||
prop.indexed = val.indexed || false;
|
||||
}
|
||||
else {
|
||||
prop = {type: val, indexed: false, optional: false};
|
||||
}
|
||||
if (prop.type.includes('?')) {
|
||||
prop.optional = true;
|
||||
prop.type = prop.type.replace('?', '');
|
||||
}
|
||||
if (prop.type.includes('[]')) {
|
||||
prop.objectType = prop.type.replace('[]', '');
|
||||
prop.type = 'list';
|
||||
}
|
||||
return prop;
|
||||
};
|
||||
|
||||
function verifyObjectSchema(returned) {
|
||||
let original = schemaMap[returned.name];
|
||||
for (const objectSchema of schema) {
|
||||
let original = schemaMap[objectSchema.name];
|
||||
if (original.schema) {
|
||||
original = original.schema;
|
||||
}
|
||||
|
||||
TestCase.assertEqual(returned.primaryKey, original.primaryKey);
|
||||
for (const propName in returned.properties) {
|
||||
const prop1 = returned.properties[propName];
|
||||
const prop2 = original.properties[propName];
|
||||
if (prop1.type == 'object') {
|
||||
TestCase.assertEqual(prop1.objectType, isString(prop2) ? prop2 : prop2.objectType);
|
||||
TestCase.assertEqual(prop1.optional, true);
|
||||
TestCase.assertEqual(objectSchema.primaryKey, original.primaryKey);
|
||||
for (const propName in objectSchema.properties) {
|
||||
TestCase.assertDefined(original.properties[propName], `schema has unexpected property ${propName}`);
|
||||
|
||||
const actual = objectSchema.properties[propName];
|
||||
const expected = normalizeProperty(original.properties[propName]);
|
||||
TestCase.assertEqual(actual.name, propName);
|
||||
TestCase.assertEqual(actual.indexed, expected.indexed);
|
||||
|
||||
if (actual.type == 'object') {
|
||||
TestCase.assertEqual(actual.objectType, expected.type === 'object' ? expected.objectType : expected.type);
|
||||
TestCase.assertEqual(actual.optional, true);
|
||||
TestCase.assertUndefined(actual.property);
|
||||
}
|
||||
else if (prop1.type == 'list') {
|
||||
TestCase.assertEqual(prop1.objectType, prop2.objectType);
|
||||
TestCase.assertEqual(prop1.optional, undefined);
|
||||
else if (actual.type == 'list') {
|
||||
TestCase.assertEqual(actual.type, expected.type);
|
||||
TestCase.assertEqual(actual.objectType, expected.objectType);
|
||||
TestCase.assertEqual(actual.optional, expected.optional);
|
||||
TestCase.assertUndefined(actual.property);
|
||||
}
|
||||
else if (prop1.type == 'linking objects') {
|
||||
TestCase.assertEqual(prop1.objectType, prop2.objectType);
|
||||
TestCase.assertEqual(prop1.property, prop2.property);
|
||||
TestCase.assertEqual(prop1.optional, undefined);
|
||||
else if (actual.type == 'linkingObjects') {
|
||||
TestCase.assertEqual(actual.type, expected.type);
|
||||
TestCase.assertEqual(actual.objectType, expected.objectType);
|
||||
TestCase.assertEqual(actual.property, expected.property);
|
||||
TestCase.assertEqual(actual.optional, false);
|
||||
}
|
||||
else {
|
||||
TestCase.assertEqual(prop1.type, isString(prop2) ? prop2 : prop2.type);
|
||||
TestCase.assertEqual(prop1.optional, prop2.optional || undefined);
|
||||
TestCase.assertEqual(actual.type, expected.type);
|
||||
TestCase.assertEqual(actual.optional, expected.optional);
|
||||
TestCase.assertUndefined(actual.property);
|
||||
TestCase.assertUndefined(actual.objectType);
|
||||
}
|
||||
|
||||
TestCase.assertEqual(prop1.indexed, prop2.indexed || undefined);
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < originalSchema.length; i++) {
|
||||
verifyObjectSchema(schema[i]);
|
||||
}
|
||||
},
|
||||
|
||||
testCopyBundledRealmFiles: function() {
|
||||
@ -869,7 +880,7 @@ module.exports = {
|
||||
const p1 = realm.create('PersonObject', { name: 'Ari', age: 10 });
|
||||
p1.age = "Ten";
|
||||
});
|
||||
}, new Error("PersonObject.age must be of type 'number', got (Ten)"));
|
||||
}, new Error("PersonObject.age must be of type 'number', got 'string' ('Ten')"));
|
||||
},
|
||||
|
||||
testErrorMessageFromInvalidCreate: function() {
|
||||
@ -879,7 +890,7 @@ module.exports = {
|
||||
realm.write(() => {
|
||||
const p1 = realm.create('PersonObject', { name: 'Ari', age: 'Ten' });
|
||||
});
|
||||
}, new Error("PersonObject.age must be of type 'number', got (Ten)"));
|
||||
}, new Error("PersonObject.age must be of type 'number', got 'string' ('Ten')"));
|
||||
},
|
||||
|
||||
testValidTypesForListProperties: function() {
|
||||
@ -934,7 +945,7 @@ module.exports = {
|
||||
realm.cancelTransaction();
|
||||
TestCase.assertTrue(!realm.isInTransaction);
|
||||
},
|
||||
|
||||
|
||||
testCompact: function() {
|
||||
let wasCalled = false;
|
||||
const count = 1000;
|
||||
|
@ -299,7 +299,7 @@ module.exports = {
|
||||
},
|
||||
|
||||
testResultsInvalidation: function() {
|
||||
var realm = new Realm({schema: [schemas.TestObject]});
|
||||
let realm = new Realm({schema: [schemas.TestObject]});
|
||||
realm.write(function() {
|
||||
for (var i = 10; i > 0; i--) {
|
||||
realm.create('TestObject', [i]);
|
||||
@ -322,7 +322,7 @@ module.exports = {
|
||||
realm.close();
|
||||
realm = new Realm({
|
||||
schemaVersion: 1,
|
||||
schema: [schemas.TestObject, schemas.BasicTypes]
|
||||
schema: [schemas.TestObject, schemas.DateObject]
|
||||
});
|
||||
|
||||
resultsVariants.forEach(function(objects) {
|
||||
|
@ -33,7 +33,7 @@ PersonObject.schema = {
|
||||
properties: {
|
||||
name: 'string',
|
||||
age: 'double',
|
||||
married: {type: 'bool', default: false},
|
||||
married: {type: 'bool', default: false},
|
||||
children: {type: 'list', objectType: 'PersonObject'},
|
||||
parents: {type: 'linkingObjects', objectType: 'PersonObject', property: 'children'},
|
||||
}
|
||||
@ -51,7 +51,7 @@ exports.PersonObject = PersonObject;
|
||||
exports.PersonList = {
|
||||
name: 'PersonList',
|
||||
properties: {
|
||||
list: {type: 'list', objectType: 'PersonObject'},
|
||||
list: 'PersonObject[]',
|
||||
}
|
||||
};
|
||||
|
||||
@ -68,26 +68,82 @@ exports.BasicTypes = {
|
||||
}
|
||||
};
|
||||
|
||||
exports.NullableBasicTypes = {
|
||||
name: 'NullableBasicTypesObject',
|
||||
exports.AllTypes = {
|
||||
name: 'AllTypesObject',
|
||||
properties: {
|
||||
boolCol: {type: 'bool', optional: true},
|
||||
intCol: {type: 'int', optional: true},
|
||||
floatCol: {type: 'float', optional: true},
|
||||
doubleCol: {type: 'double', optional: true},
|
||||
stringCol: {type: 'string', optional: true},
|
||||
dateCol: {type: 'date', optional: true},
|
||||
dataCol: {type: 'data', optional: true},
|
||||
boolCol: 'bool',
|
||||
intCol: 'int',
|
||||
floatCol: 'float',
|
||||
doubleCol: 'double',
|
||||
stringCol: 'string',
|
||||
dateCol: 'date',
|
||||
dataCol: 'data',
|
||||
objectCol: 'TestObject',
|
||||
|
||||
optBoolCol: 'bool?',
|
||||
optIntCol: 'int?',
|
||||
optFloatCol: 'float?',
|
||||
optDoubleCol: 'double?',
|
||||
optStringCol: 'string?',
|
||||
optDateCol: 'date?',
|
||||
optDataCol: 'data?',
|
||||
|
||||
boolArrayCol: 'bool[]',
|
||||
intArrayCol: 'int[]',
|
||||
floatArrayCol: 'float[]',
|
||||
doubleArrayCol: 'double[]',
|
||||
stringArrayCol: 'string[]',
|
||||
dateArrayCol: 'date[]',
|
||||
dataArrayCol: 'data[]',
|
||||
objectArrayCol: 'TestObject[]',
|
||||
|
||||
optBoolArrayCol: 'bool?[]',
|
||||
optIntArrayCol: 'int?[]',
|
||||
optFloatArrayCol: 'float?[]',
|
||||
optDoubleArrayCol: 'double?[]',
|
||||
optStringArrayCol: 'string?[]',
|
||||
optDateArrayCol: 'date?[]',
|
||||
optDataArrayCol: 'data?[]',
|
||||
|
||||
linkingObjectsCol: {type: 'linkingObjects', objectType: 'LinkToAllTypesObject', property: 'allTypesCol'},
|
||||
}
|
||||
};
|
||||
|
||||
exports.AllPrimaryTypes = {
|
||||
name: 'AllPrimaryTypesObject',
|
||||
primaryKey: 'primaryCol',
|
||||
properties: {
|
||||
primaryCol: 'string',
|
||||
boolCol: 'bool',
|
||||
intCol: 'int',
|
||||
floatCol: 'float',
|
||||
doubleCol: 'double',
|
||||
stringCol: 'string',
|
||||
dateCol: 'date',
|
||||
dataCol: 'data',
|
||||
objectCol: 'TestObject',
|
||||
arrayCol: {type: 'list', objectType: 'TestObject'},
|
||||
}
|
||||
};
|
||||
|
||||
exports.LinkToAllTypes = {
|
||||
name: 'LinkToAllTypesObject',
|
||||
properties: {
|
||||
allTypesCol: 'AllTypesObject',
|
||||
}
|
||||
}
|
||||
|
||||
exports.IndexedTypes = {
|
||||
name: 'IndexedTypesObject',
|
||||
properties: {
|
||||
boolCol: {type: 'bool', indexed: true},
|
||||
intCol: {type: 'int', indexed: true},
|
||||
stringCol: {type: 'string', indexed: true},
|
||||
dateCol: {type: 'date', indexed: true},
|
||||
boolCol: {type: 'bool', indexed: true},
|
||||
intCol: {type: 'int', indexed: true},
|
||||
stringCol: {type: 'string', indexed: true},
|
||||
dateCol: {type: 'date', indexed: true},
|
||||
optBoolCol: {type: 'bool?', indexed: true},
|
||||
optIntCol: {type: 'int?', indexed: true},
|
||||
optStringCol: {type: 'string?', indexed: true},
|
||||
optDateCol: {type: 'date?', indexed: true},
|
||||
}
|
||||
};
|
||||
|
||||
@ -95,9 +151,31 @@ exports.IndexedTypes = {
|
||||
exports.LinkTypes = {
|
||||
name: 'LinkTypesObject',
|
||||
properties: {
|
||||
objectCol: 'TestObject',
|
||||
objectCol: 'TestObject',
|
||||
objectCol1: {type: 'object', objectType: 'TestObject'},
|
||||
arrayCol: {type: 'list', objectType: 'TestObject'},
|
||||
arrayCol: 'TestObject[]',
|
||||
arrayCol1: {type: 'list', objectType: 'TestObject'},
|
||||
}
|
||||
};
|
||||
|
||||
exports.PrimitiveArrays = {
|
||||
name: 'PrimitiveArrays',
|
||||
properties: {
|
||||
bool: 'bool[]',
|
||||
int: 'int[]',
|
||||
float: 'float[]',
|
||||
double: 'double[]',
|
||||
string: 'string[]',
|
||||
date: 'date[]',
|
||||
data: 'data[]',
|
||||
|
||||
optBool: 'bool?[]',
|
||||
optInt: 'int?[]',
|
||||
optFloat: 'float?[]',
|
||||
optDouble: 'double?[]',
|
||||
optString: 'string?[]',
|
||||
optDate: 'date?[]',
|
||||
optData: 'data?[]',
|
||||
}
|
||||
};
|
||||
|
||||
@ -126,44 +204,19 @@ exports.StringOnly = {
|
||||
}
|
||||
};
|
||||
|
||||
exports.AllTypes = {
|
||||
name: 'AllTypesObject',
|
||||
primaryKey: 'primaryCol',
|
||||
properties: {
|
||||
primaryCol: 'string',
|
||||
boolCol: 'bool',
|
||||
intCol: 'int',
|
||||
floatCol: 'float',
|
||||
doubleCol: 'double',
|
||||
stringCol: 'string',
|
||||
dateCol: 'date',
|
||||
dataCol: 'data',
|
||||
objectCol: 'TestObject',
|
||||
arrayCol: {type: 'list', objectType: 'TestObject'},
|
||||
linkingObjectsCol: {type: 'linkingObjects', objectType: 'LinkToAllTypesObject', property: 'allTypesCol'},
|
||||
}
|
||||
};
|
||||
|
||||
exports.LinkToAllTypes = {
|
||||
name: 'LinkToAllTypesObject',
|
||||
properties: {
|
||||
allTypesCol: 'AllTypesObject',
|
||||
}
|
||||
}
|
||||
|
||||
exports.DefaultValues = {
|
||||
name: 'DefaultValuesObject',
|
||||
properties: {
|
||||
boolCol: {type: 'bool', default: true},
|
||||
intCol: {type: 'int', default: -1},
|
||||
floatCol: {type: 'float', default: -1.1},
|
||||
doubleCol: {type: 'double', default: -1.11},
|
||||
stringCol: {type: 'string', default: 'defaultString'},
|
||||
dateCol: {type: 'date', default: new Date(1.111)},
|
||||
dataCol: {type: 'data', default: new ArrayBuffer(1)},
|
||||
objectCol: {type: 'TestObject', default: {doubleCol: 1}},
|
||||
nullObjectCol: {type: 'TestObject', default: null},
|
||||
arrayCol: {type: 'list', objectType: 'TestObject', default: [{doubleCol: 2}]},
|
||||
boolCol: {type: 'bool', default: true},
|
||||
intCol: {type: 'int', default: -1},
|
||||
floatCol: {type: 'float', default: -1.1},
|
||||
doubleCol: {type: 'double', default: -1.11},
|
||||
stringCol: {type: 'string', default: 'defaultString'},
|
||||
dateCol: {type: 'date', default: new Date(1.111)},
|
||||
dataCol: {type: 'data', default: new ArrayBuffer(1)},
|
||||
objectCol: {type: 'TestObject', default: {doubleCol: 1}},
|
||||
nullObjectCol: {type: 'TestObject', default: null},
|
||||
arrayCol: {type: 'TestObject[]', default: [{doubleCol: 2}]},
|
||||
}
|
||||
};
|
||||
|
||||
@ -203,7 +256,7 @@ exports.DateObject = {
|
||||
name: 'Date',
|
||||
properties: {
|
||||
currentDate: 'date',
|
||||
nullDate: { type: 'date', optional: true }
|
||||
nullDate: 'date?'
|
||||
}
|
||||
};
|
||||
|
||||
@ -211,7 +264,7 @@ exports.LinkingObjectsObject = {
|
||||
name: 'LinkingObjectsObject',
|
||||
properties: {
|
||||
value: 'int',
|
||||
links: {type: 'list', objectType: 'LinkingObjectsObject'},
|
||||
links: 'LinkingObjectsObject[]',
|
||||
linkingObjects: {type: 'linkingObjects', objectType: 'LinkingObjectsObject', property: 'links'}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user