diff --git a/RealmJS.xcodeproj/project.pbxproj b/RealmJS.xcodeproj/project.pbxproj index bc589292..4ea8a1b9 100644 --- a/RealmJS.xcodeproj/project.pbxproj +++ b/RealmJS.xcodeproj/project.pbxproj @@ -40,7 +40,7 @@ 02D8D1F71B601984006DB49D /* JavaScriptCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 02B58CCD1AE99D4D009B348C /* JavaScriptCore.framework */; }; 02EF76861BFFDE37000D5BAD /* test.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02EF76851BFFDE37000D5BAD /* test.cpp */; }; 02EF76881BFFDE9E000D5BAD /* GrammarTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 02EF76871BFFDE9E000D5BAD /* GrammarTests.mm */; }; - F61378791C18EAC5008BFC51 /* lib in Resources */ = {isa = PBXBuildFile; fileRef = F61378781C18EAAC008BFC51 /* lib */; }; + F61378791C18EAC5008BFC51 /* js in Resources */ = {isa = PBXBuildFile; fileRef = F61378781C18EAAC008BFC51 /* js */; }; F636F6C81BCDB3570023F35C /* RealmReact.h in Headers */ = {isa = PBXBuildFile; fileRef = 0270BCCF1B7D067300010E03 /* RealmReact.h */; settings = {ATTRIBUTES = (Public, ); }; }; F63FF2C61C12469E00B3B8E0 /* js_init.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 029048011C0428DF00ABDED4 /* js_init.cpp */; }; F63FF2C71C12469E00B3B8E0 /* js_list.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 029048031C0428DF00ABDED4 /* js_list.cpp */; }; @@ -86,6 +86,7 @@ F63FF3311C16434400B3B8E0 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = F63FF3301C16434400B3B8E0 /* libz.tbd */; }; F68A278C1BC2722A0063D40A /* RJSModuleLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = F68A278B1BC2722A0063D40A /* RJSModuleLoader.m */; }; F6BB7DF21BF681BC00D0A69E /* base64.hpp in Headers */ = {isa = PBXBuildFile; fileRef = F6BB7DF01BF681BC00D0A69E /* base64.hpp */; }; + F6BCCFE21C8380A400FE31AE /* lib in Resources */ = {isa = PBXBuildFile; fileRef = F6BCCFDF1C83809A00FE31AE /* lib */; }; F6C74DF01C732CC500C9DDCD /* RealmAnalytics.h in Headers */ = {isa = PBXBuildFile; fileRef = F6C74DEE1C732CC500C9DDCD /* RealmAnalytics.h */; }; F6C74DF11C732CC500C9DDCD /* RealmAnalytics.mm in Sources */ = {isa = PBXBuildFile; fileRef = F6C74DEF1C732CC500C9DDCD /* RealmAnalytics.mm */; }; /* End PBXBuildFile section */ @@ -207,7 +208,7 @@ 02D0F23A1BF6C95200B4FC45 /* binding_context.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = binding_context.hpp; sourceTree = ""; }; 02EF76851BFFDE37000D5BAD /* test.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = test.cpp; path = "src/object-store/parser/test.cpp"; sourceTree = SOURCE_ROOT; }; 02EF76871BFFDE9E000D5BAD /* GrammarTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = GrammarTests.mm; path = ios/GrammarTests.mm; sourceTree = ""; }; - F61378781C18EAAC008BFC51 /* lib */ = {isa = PBXFileReference; lastKnownFileType = folder; path = lib; sourceTree = ""; }; + F61378781C18EAAC008BFC51 /* js */ = {isa = PBXFileReference; lastKnownFileType = folder; path = js; sourceTree = ""; }; F63FF2B11C1241E500B3B8E0 /* libRealmJS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRealmJS.a; sourceTree = BUILT_PRODUCTS_DIR; }; F63FF2DC1C15659A00B3B8E0 /* RealmJS.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RealmJS.mm; sourceTree = ""; }; F63FF2F01C16405C00B3B8E0 /* libGCDWebServers.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libGCDWebServers.a; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -248,6 +249,7 @@ F68A278B1BC2722A0063D40A /* RJSModuleLoader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RJSModuleLoader.m; path = ios/RJSModuleLoader.m; sourceTree = ""; }; F6BB7DEF1BF681BC00D0A69E /* base64.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = base64.cpp; sourceTree = ""; }; F6BB7DF01BF681BC00D0A69E /* base64.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = base64.hpp; sourceTree = ""; }; + F6BCCFDF1C83809A00FE31AE /* lib */ = {isa = PBXFileReference; lastKnownFileType = folder; path = lib; sourceTree = SOURCE_ROOT; }; F6C3FBBC1BF680EC00E6FFD4 /* json.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = json.hpp; sourceTree = ""; }; F6C74DEE1C732CC500C9DDCD /* RealmAnalytics.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RealmAnalytics.h; path = "react-native/RealmAnalytics.h"; sourceTree = ""; }; F6C74DEF1C732CC500C9DDCD /* RealmAnalytics.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = RealmAnalytics.mm; path = "react-native/RealmAnalytics.mm"; sourceTree = ""; }; @@ -308,6 +310,7 @@ 0270BC3D1B7CFBFD00010E03 /* RealmJS */ = { isa = PBXGroup; children = ( + F6BCCFDF1C83809A00FE31AE /* lib */, F62A35131C18E6E2004A917D /* iOS */, F62A35141C18E783004A917D /* Object Store */, 029048011C0428DF00ABDED4 /* js_init.cpp */, @@ -371,7 +374,7 @@ 02B58CC01AE99CEC009B348C /* RealmJSTests */ = { isa = PBXGroup; children = ( - F61378781C18EAAC008BFC51 /* lib */, + F61378781C18EAAC008BFC51 /* js */, 0270BC781B7D020100010E03 /* Info.plist */, 0270BC7A1B7D020100010E03 /* RealmJSTests.h */, 0270BC7B1B7D020100010E03 /* RealmJSTests.mm */, @@ -735,7 +738,8 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - F61378791C18EAC5008BFC51 /* lib in Resources */, + F6BCCFE21C8380A400FE31AE /* lib in Resources */, + F61378791C18EAC5008BFC51 /* js in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -754,7 +758,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "cp src/object-store/parser/queryTests.json \"$TARGET_BUILD_DIR/$CONTENTS_FOLDER_PATH/lib/query-tests.json\""; + shellScript = "cp src/object-store/parser/queryTests.json \"$TARGET_BUILD_DIR/$CONTENTS_FOLDER_PATH/js/query-tests.json\""; }; F63FF2C51C12462600B3B8E0 /* Download Core */ = { isa = PBXShellScriptBuildPhase; diff --git a/examples/ReactExample/components/todo-list-item.js b/examples/ReactExample/components/todo-list-item.js index 155063cc..38a879ef 100644 --- a/examples/ReactExample/components/todo-list-item.js +++ b/examples/ReactExample/components/todo-list-item.js @@ -40,7 +40,7 @@ export default class TodoListItem extends React.Component { get done() { let items = this.props.item.items; - return items.length > 0 && Array.prototype.every.call(items, (item) => item.done); + return items.length > 0 && items.every((item) => item.done); } get text() { diff --git a/lib/.eslintrc b/lib/.eslintrc index 432f0525..e84df8c3 100644 --- a/lib/.eslintrc +++ b/lib/.eslintrc @@ -1,13 +1,5 @@ { "env": { - "commonjs": true, - "browser": true, - "es6": true - }, - "ecmaFeatures": { - "forOf": false - }, - "globals": { - "global": true + "commonjs": true } } diff --git a/lib/browser/.eslintrc b/lib/browser/.eslintrc new file mode 100644 index 00000000..3fb64fc9 --- /dev/null +++ b/lib/browser/.eslintrc @@ -0,0 +1,18 @@ +{ + "env": { + "es6": true, + "worker": true + }, + "globals": { + "global": true + }, + "parserOptions": { + "ecmaFeatures": { + "forOf": false + }, + "sourceType": "module" + }, + "rules": { + "strict": 0 + } +} diff --git a/lib/base64.js b/lib/browser/base64.js similarity index 96% rename from lib/base64.js rename to lib/browser/base64.js index 24b65db3..49774b12 100644 --- a/lib/base64.js +++ b/lib/browser/base64.js @@ -23,12 +23,7 @@ const CHAR_MAP = {}; Array.from(CHARS, (char, i) => CHAR_MAP[char] = i); -module.exports = { - decode, - encode, -}; - -function decode(base64) { +export function decode(base64) { let length = base64.length; let byteCount = length * 0.75; @@ -56,7 +51,7 @@ function decode(base64) { return buffer; } -function encode(data) { +export function encode(data) { var byteOffset = 0; var buffer; diff --git a/lib/constants.js b/lib/browser/constants.js similarity index 91% rename from lib/constants.js rename to lib/browser/constants.js index 88551970..367ee967 100644 --- a/lib/constants.js +++ b/lib/browser/constants.js @@ -18,9 +18,9 @@ 'use strict'; -let keys = {}; -let objectTypes = {}; -let propTypes = {}; +export const keys = {}; +export const objectTypes = {}; +export const propTypes = {}; [ 'id', @@ -62,9 +62,3 @@ let propTypes = {}; enumerable: true, }); }); - -module.exports = { - keys, - objectTypes, - propTypes -}; diff --git a/lib/realm.js b/lib/browser/index.js similarity index 92% rename from lib/realm.js rename to lib/browser/index.js index 93f8ab40..d160af58 100644 --- a/lib/realm.js +++ b/lib/browser/index.js @@ -18,21 +18,20 @@ 'use strict'; -const constants = require('./constants'); -const lists = require('./lists'); -const objects = require('./objects'); -const results = require('./results'); -const rpc = require('./rpc'); -const util = require('./util'); +import { keys, propTypes, objectTypes } from './constants'; +import * as lists from './lists'; +import * as objects from './objects'; +import * as results from './results'; +import * as rpc from './rpc'; +import * as util from './util'; -const {keys, propTypes, objectTypes} = constants; const listenersKey = Symbol(); rpc.registerTypeConverter(objectTypes.LIST, lists.create); rpc.registerTypeConverter(objectTypes.OBJECT, objects.create); rpc.registerTypeConverter(objectTypes.RESULTS, results.create); -class Realm { +export default class Realm { constructor(config) { let schemas = typeof config == 'object' && config.schema; let constructors = {}; @@ -142,6 +141,12 @@ util.createMethods(Realm.prototype, objectTypes.REALM, [ ], true); Object.defineProperties(Realm, { + List: { + value: lists.List, + }, + Results: { + value: results.Results, + }, Types: { value: Object.freeze(propTypes), }, @@ -159,5 +164,3 @@ Object.defineProperties(Realm, { // The session ID refers to the Realm constructor object in the RPC server. Realm[keys.id] = rpc.createSession(); - -module.exports = Realm; diff --git a/lib/lists.js b/lib/browser/lists.js similarity index 69% rename from lib/lists.js rename to lib/browser/lists.js index 983bffa3..23d2eb32 100644 --- a/lib/lists.js +++ b/lib/browser/lists.js @@ -18,26 +18,24 @@ 'use strict'; -const constants = require('./constants'); -const util = require('./util'); +import { objectTypes } from './constants'; +import { createCollection, createMethods } from './util'; -const {objectTypes} = constants; - -module.exports = { - create, -}; - -class List {} +export class List { + constructor() { + throw new TypeError('Illegal constructor'); + } +} // Non-mutating methods: -util.createMethods(List.prototype, objectTypes.LIST, [ +createMethods(List.prototype, objectTypes.LIST, [ 'filtered', 'sorted', 'snapshot', ]); // Mutating methods: -util.createMethods(List.prototype, objectTypes.LIST, [ +createMethods(List.prototype, objectTypes.LIST, [ 'pop', 'shift', 'push', @@ -45,6 +43,6 @@ util.createMethods(List.prototype, objectTypes.LIST, [ 'splice', ], true); -function create(realmId, info) { - return util.createList(List.prototype, realmId, info, true); +export function create(realmId, info) { + return createCollection(List.prototype, realmId, info, true); } diff --git a/lib/objects.js b/lib/browser/objects.js similarity index 81% rename from lib/objects.js rename to lib/browser/objects.js index a6e34b1e..974b4916 100644 --- a/lib/objects.js +++ b/lib/browser/objects.js @@ -18,18 +18,12 @@ 'use strict'; -const constants = require('./constants'); -const util = require('./util'); +import { keys } from './constants'; +import { getterForProperty, setterForProperty } from './util'; -const {keys} = constants; const registeredConstructors = {}; -module.exports = { - create, - registerConstructors, -}; - -function create(realmId, info) { +export function create(realmId, info) { let schema = info.schema; let constructor = (registeredConstructors[realmId] || {})[schema.name]; let object = constructor ? Object.create(constructor.prototype) : {}; @@ -41,8 +35,8 @@ function create(realmId, info) { schema.properties.forEach((name) => { Object.defineProperty(object, name, { enumerable: true, - get: util.getterForProperty(name), - set: util.setterForProperty(name), + get: getterForProperty(name), + set: setterForProperty(name), }); }); @@ -56,6 +50,6 @@ function create(realmId, info) { return object; } -function registerConstructors(realmId, constructors) { +export function registerConstructors(realmId, constructors) { registeredConstructors[realmId] = constructors; } diff --git a/lib/results.js b/lib/browser/results.js similarity index 69% rename from lib/results.js rename to lib/browser/results.js index dee2ea92..13717328 100644 --- a/lib/results.js +++ b/lib/browser/results.js @@ -18,21 +18,21 @@ 'use strict'; -const constants = require('./constants'); -const util = require('./util'); +import { objectTypes } from './constants'; +import { createCollection, createMethods } from './util'; -module.exports = { - create, -}; +export class Results { + constructor() { + throw new TypeError('Illegal constructor'); + } +} -class Results {} - -util.createMethods(Results.prototype, constants.objectTypes.RESULTS, [ +createMethods(Results.prototype, objectTypes.RESULTS, [ 'filtered', 'sorted', 'snapshot', ]); -function create(realmId, info) { - return util.createList(Results.prototype, realmId, info); +export function create(realmId, info) { + return createCollection(Results.prototype, realmId, info); } diff --git a/lib/rpc.js b/lib/browser/rpc.js similarity index 86% rename from lib/rpc.js rename to lib/browser/rpc.js index f632dfd2..de010510 100644 --- a/lib/rpc.js +++ b/lib/browser/rpc.js @@ -18,12 +18,11 @@ 'use strict'; -const base64 = require('./base64'); -const constants = require('./constants'); +import * as base64 from './base64'; +import { keys, objectTypes } from './constants'; const DEVICE_HOST = 'localhost:8082'; -const {keys, objectTypes} = constants; const {id: idKey, realm: realmKey} = keys; const typeConverters = {}; @@ -38,35 +37,20 @@ if (XMLHttpRequest.__proto__ != global.XMLHttpRequestEventTarget) { global.XMLHttpRequest = fakeXMLHttpRequest; } -module.exports = { - registerTypeConverter, - - createSession, - createRealm, - callMethod, - getProperty, - setProperty, - beginTransaction, - cancelTransaction, - commitTransaction, - - clearTestState, -}; - registerTypeConverter(objectTypes.DATA, (_, {value}) => base64.decode(value)); registerTypeConverter(objectTypes.DATE, (_, {value}) => new Date(value)); registerTypeConverter(objectTypes.DICT, deserializeDict); -function registerTypeConverter(type, handler) { +export function registerTypeConverter(type, handler) { typeConverters[type] = handler; } -function createSession() { +export function createSession() { sessionId = sendRequest('create_session'); return sessionId; } -function createRealm(args) { +export function createRealm(args) { if (args) { args = args.map((arg) => serialize(null, arg)); } @@ -74,7 +58,7 @@ function createRealm(args) { return sendRequest('create_realm', {arguments: args}); } -function callMethod(realmId, id, name, args) { +export function callMethod(realmId, id, name, args) { if (args) { args = args.map((arg) => serialize(realmId, arg)); } @@ -83,29 +67,29 @@ function callMethod(realmId, id, name, args) { return deserialize(realmId, result); } -function getProperty(realmId, id, name) { +export function getProperty(realmId, id, name) { let result = sendRequest('get_property', {realmId, id, name}); return deserialize(realmId, result); } -function setProperty(realmId, id, name, value) { +export function setProperty(realmId, id, name, value) { value = serialize(realmId, value); sendRequest('set_property', {realmId, id, name, value}); } -function beginTransaction(realmId) { +export function beginTransaction(realmId) { sendRequest('begin_transaction', {realmId}); } -function cancelTransaction(realmId) { +export function cancelTransaction(realmId) { sendRequest('cancel_transaction', {realmId}); } -function commitTransaction(realmId) { +export function commitTransaction(realmId) { sendRequest('commit_transaction', {realmId}); } -function clearTestState() { +export function clearTestState() { sendRequest('clear_test_state'); } diff --git a/lib/util.js b/lib/browser/util.js similarity index 87% rename from lib/util.js rename to lib/browser/util.js index 206c5549..e6516cba 100644 --- a/lib/util.js +++ b/lib/browser/util.js @@ -18,22 +18,11 @@ 'use strict'; -const constants = require('./constants'); -const rpc = require('./rpc'); +import { keys } from './constants'; +import * as rpc from './rpc'; -const {keys} = constants; let mutationListeners = {}; -module.exports = { - clearMutationListeners, - fireMutationListeners, - createList, - createMethods, - createMethod, - getterForProperty, - setterForProperty, -}; - function addMutationListener(realmId, callback) { let listeners = mutationListeners[realmId] || (mutationListeners[realmId] = new Set()); listeners.add(callback); @@ -46,18 +35,18 @@ function removeMutationListener(realmId, callback) { } } -function clearMutationListeners() { +export function clearMutationListeners() { mutationListeners = {}; } -function fireMutationListeners(realmId) { +export function fireMutationListeners(realmId) { let listeners = mutationListeners[realmId]; if (listeners) { listeners.forEach((cb) => cb()); } } -function createList(prototype, realmId, info, mutable) { +export function createCollection(prototype, realmId, info, mutable) { let list = Object.create(prototype); let size = 0; @@ -129,7 +118,7 @@ function createList(prototype, realmId, info, mutable) { return list; } -function createMethods(prototype, type, methodNames, mutates) { +export function createMethods(prototype, type, methodNames, mutates) { let props = {}; methodNames.forEach((name) => { @@ -141,7 +130,7 @@ function createMethods(prototype, type, methodNames, mutates) { Object.defineProperties(prototype, props); } -function createMethod(type, name, mutates) { +export function createMethod(type, name, mutates) { return function() { let realmId = this[keys.realm]; let id = this[keys.id]; @@ -163,13 +152,13 @@ function createMethod(type, name, mutates) { }; } -function getterForProperty(name) { +export function getterForProperty(name) { return function() { return rpc.getProperty(this[keys.realm], this[keys.id], name); }; } -function setterForProperty(name) { +export function setterForProperty(name) { return function(value) { let realmId = this[keys.realm]; diff --git a/lib/index.js b/lib/index.js index 05b3b63e..bf0e617d 100644 --- a/lib/index.js +++ b/lib/index.js @@ -18,12 +18,44 @@ 'use strict'; +var realmConstructor; + if (typeof Realm != 'undefined') { // The global Realm constructor should be available on device (using JavaScriptCore). - module.exports = Realm; // eslint-disable-line no-undef -} else if (navigator.userAgent) { + realmConstructor = Realm; // eslint-disable-line no-undef +} else if (typeof navigator != 'undefined' && navigator.userAgent) { // eslint-disable-line no-undef // The userAgent will be defined when running in a browser (such as Chrome debugging mode). - module.exports = require('./realm'); + realmConstructor = require('./browser').default; // (exported as ES6 module) } else { throw new Error('Missing Realm constructor - please ensure RealmReact framework is included!'); } + +var arrayPrototype = Array.prototype; +var arrayMethods = {}; + +[ + 'join', + 'slice', + 'forEach', + 'every', + 'some', + 'find', + 'findIndex', + 'map', + 'reduce', + 'reduceRight', + 'entries', + 'keys', + 'values', +].forEach(function(methodName) { + var method = arrayPrototype[methodName]; + if (method) { + arrayMethods[methodName] = {value: method}; + } +}); + +// Add the specified Array methods to the prototype of List and Results. +Object.defineProperties(realmConstructor.List.prototype, arrayMethods); +Object.defineProperties(realmConstructor.Results.prototype, arrayMethods); + +module.exports = realmConstructor; diff --git a/src/js_init.cpp b/src/js_init.cpp index 4269e54f..5c8d6010 100644 --- a/src/js_init.cpp +++ b/src/js_init.cpp @@ -19,6 +19,8 @@ #include "js_init.h" #include "js_realm.hpp" #include "js_object.hpp" +#include "js_list.hpp" +#include "js_results.hpp" #include "js_util.hpp" #include "js_schema.hpp" #include "platform.hpp" @@ -54,27 +56,36 @@ JSClassRef RJSRealmTypeClass() { return JSClassCreate(&realmTypesDefinition); } +static JSObjectRef UncallableConstructor(JSContextRef ctx, JSObjectRef constructor, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) { + *exception = RJSMakeError(ctx, "Illegal constructor"); + return NULL; +} + static JSValueRef ClearTestState(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef *exception) { RJSClearTestState(); return NULL; } JSObjectRef RJSConstructorCreate(JSContextRef ctx) { + static JSStringRef clearTestStateString = JSStringCreateWithUTF8CString("clearTestState"); + static JSStringRef listString = JSStringCreateWithUTF8CString("List"); + static JSStringRef resultsString = JSStringCreateWithUTF8CString("Results"); + static JSStringRef typeString = JSStringCreateWithUTF8CString("Types"); + JSObjectRef realmObject = JSObjectMake(ctx, RJSRealmConstructorClass(), NULL); - JSObjectRef typesObject = JSObjectMake(ctx, RJSRealmTypeClass(), NULL); - - JSValueRef exception = NULL; - JSStringRef typeString = JSStringCreateWithUTF8CString("Types"); JSPropertyAttributes attributes = kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete; - JSObjectSetProperty(ctx, realmObject, typeString, typesObject, attributes, &exception); - JSStringRelease(typeString); - assert(!exception); - JSStringRef clearTestStateString = JSStringCreateWithUTF8CString("clearTestState"); + JSObjectRef listConstructor = JSObjectMakeConstructor(ctx, RJSListClass(), UncallableConstructor); + RJSValidatedSetProperty(ctx, realmObject, listString, listConstructor, attributes); + + JSObjectRef resultsContructor = JSObjectMakeConstructor(ctx, RJSResultsClass(), UncallableConstructor); + RJSValidatedSetProperty(ctx, realmObject, resultsString, resultsContructor, attributes); + + JSObjectRef typesObject = JSObjectMake(ctx, RJSRealmTypeClass(), NULL); + RJSValidatedSetProperty(ctx, realmObject, typeString, typesObject, attributes); + JSObjectRef clearTestStateFunction = JSObjectMakeFunctionWithCallback(ctx, clearTestStateString, ClearTestState); - JSObjectSetProperty(ctx, realmObject, clearTestStateString, clearTestStateFunction, attributes, &exception); - JSStringRelease(clearTestStateString); - assert(!exception); + RJSValidatedSetProperty(ctx, realmObject, clearTestStateString, clearTestStateFunction, attributes); return realmObject; } diff --git a/src/js_realm.cpp b/src/js_realm.cpp index 2e80f099..a21ab798 100644 --- a/src/js_realm.cpp +++ b/src/js_realm.cpp @@ -217,16 +217,22 @@ JSObjectRef RealmConstructor(JSContextRef ctx, JSObjectRef constructor, size_t a } } +bool RealmHasInstance(JSContextRef ctx, JSObjectRef constructor, JSValueRef value, JSValueRef* exception) { + return JSValueIsObjectOfClass(ctx, value, RJSRealmClass()); +} + +static const JSStaticValue RealmStaticProperties[] = { + {"defaultPath", GetDefaultPath, SetDefaultPath, kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, + {NULL, NULL} +}; + JSClassRef RJSRealmConstructorClass() { JSClassDefinition realmConstructorDefinition = kJSClassDefinitionEmpty; - realmConstructorDefinition.className = "Realm"; + realmConstructorDefinition.attributes = kJSClassAttributeNoAutomaticPrototype; + realmConstructorDefinition.className = "RealmConstructor"; realmConstructorDefinition.callAsConstructor = RealmConstructor; - - JSStaticValue realmStaticProperties[] = { - {"defaultPath", GetDefaultPath, SetDefaultPath, kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, - {NULL, NULL} - }; - realmConstructorDefinition.staticValues = realmStaticProperties; + realmConstructorDefinition.hasInstance = RealmHasInstance; + realmConstructorDefinition.staticValues = RealmStaticProperties; return JSClassCreate(&realmConstructorDefinition); } diff --git a/src/js_util.hpp b/src/js_util.hpp index e08c8d1c..4d7fa311 100644 --- a/src/js_util.hpp +++ b/src/js_util.hpp @@ -201,6 +201,14 @@ static inline size_t RJSValidatedListLength(JSContextRef ctx, JSObjectRef object return RJSValidatedValueToNumber(ctx, lengthValue); } +static inline void RJSValidatedSetProperty(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef value, JSPropertyAttributes attributes = 0) { + JSValueRef exception = NULL; + JSObjectSetProperty(ctx, object, propertyName, value, attributes, &exception); + if (exception) { + throw RJSException(ctx, exception); + } +} + template T stot(const std::string s) { std::istringstream iss(s); diff --git a/tests/ios/RealmJSCoreTests.m b/tests/ios/RealmJSCoreTests.m index 34b9275b..54c9363e 100644 --- a/tests/ios/RealmJSCoreTests.m +++ b/tests/ios/RealmJSCoreTests.m @@ -32,14 +32,21 @@ + (XCTestSuite *)defaultTestSuite { XCTestSuite *suite = [super defaultTestSuite]; JSContext *context = [[JSContext alloc] init]; - JSValue *realmConstructor = [JSValue valueWithJSValueRef:RJSConstructorCreate(context.JSGlobalContextRef) inContext:context]; RJSModuleLoader *moduleLoader = [[RJSModuleLoader alloc] initWithContext:context]; - NSURL *scriptURL = [[NSBundle bundleForClass:self] URLForResource:@"index" withExtension:@"js" subdirectory:@"lib"]; + NSURL *realmURL = [[NSBundle bundleForClass:self] URLForResource:@"index" withExtension:@"js" subdirectory:@"lib"]; + NSURL *scriptURL = [[NSBundle bundleForClass:self] URLForResource:@"index" withExtension:@"js" subdirectory:@"js"]; + NSError *error; + + // Create Realm constructor in the JS context. + RJSInitializeInContext(context.JSGlobalContextRef); + + // Load the Realm module so additional functionality is exposed on Realm objects. + JSValue *realmConstructor = [moduleLoader loadModuleFromURL:realmURL error:&error]; + NSAssert(realmConstructor, @"%@", error); // Expose the Realm constructor as a global 'realm' CommonJS module. [moduleLoader addGlobalModuleObject:realmConstructor forName:@"realm"]; - NSError *error; JSValue *testObject = [moduleLoader loadModuleFromURL:scriptURL error:&error]; NSAssert(testObject, @"%@", error); @@ -76,7 +83,7 @@ NSURL *sourceURL = nil; if (source) { - NSString *path = [NSString pathWithComponents:@[[@(__FILE__) stringByDeletingLastPathComponent], @"..", @"lib", source.lastPathComponent]]; + NSString *path = [NSString pathWithComponents:@[[@(__FILE__) stringByDeletingLastPathComponent], @"..", @"js", source.lastPathComponent]]; sourceURL = [NSURL URLWithString:path]; } diff --git a/tests/lib/asserts.js b/tests/js/asserts.js similarity index 100% rename from tests/lib/asserts.js rename to tests/js/asserts.js diff --git a/tests/lib/base-test.js b/tests/js/base-test.js similarity index 100% rename from tests/lib/base-test.js rename to tests/js/base-test.js diff --git a/tests/lib/encryption-tests.js b/tests/js/encryption-tests.js similarity index 100% rename from tests/lib/encryption-tests.js rename to tests/js/encryption-tests.js diff --git a/tests/lib/index.js b/tests/js/index.js similarity index 100% rename from tests/lib/index.js rename to tests/js/index.js diff --git a/tests/lib/list-tests.js b/tests/js/list-tests.js similarity index 84% rename from tests/lib/list-tests.js rename to tests/js/list-tests.js index 37fcf1dc..b7fcd648 100644 --- a/tests/lib/list-tests.js +++ b/tests/js/list-tests.js @@ -24,7 +24,20 @@ var TestCase = require('./asserts'); var schemas = require('./schemas'); module.exports = BaseTest.extend({ - testArrayLength: function() { + testListConstructor: function() { + var 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.assertThrows(function() { + new Realm.List(); + }); + }, + + testListLength: function() { var realm = new Realm({schema: [schemas.LinkTypes, schemas.TestObject]}); var array; @@ -52,7 +65,7 @@ module.exports = BaseTest.extend({ TestCase.assertEqual(array.length, 2); }, - testArraySubscriptGetters: function() { + testListSubscriptGetters: function() { var realm = new Realm({schema: [schemas.LinkTypes, schemas.TestObject]}); var array; @@ -72,7 +85,7 @@ module.exports = BaseTest.extend({ TestCase.assertEqual(array[-1], undefined); }, - testArraySubscriptSetters: function() { + testListSubscriptSetters: function() { var realm = new Realm({schema: [schemas.LinkTypes, schemas.TestObject]}); var array; @@ -110,7 +123,7 @@ module.exports = BaseTest.extend({ }, 'cannot set list item outside write transaction'); }, - testArrayInvalidProperty: function() { + testListInvalidProperty: function() { var realm = new Realm({schema: [schemas.LinkTypes, schemas.TestObject]}); var array; @@ -127,7 +140,7 @@ module.exports = BaseTest.extend({ TestCase.assertEqual(undefined, array.ablasdf); }, - testArrayEnumerate: function() { + testListEnumerate: function() { var realm = new Realm({schema: [schemas.LinkTypes, schemas.TestObject]}); var obj; @@ -160,7 +173,7 @@ module.exports = BaseTest.extend({ TestCase.assertEqual(keys.length, 2); }, - testPush: function() { + testListPush: function() { var realm = new Realm({schema: [schemas.LinkTypes, schemas.TestObject]}); var array; @@ -194,7 +207,7 @@ module.exports = BaseTest.extend({ }, 'can only push in a write transaction'); }, - testPop: function() { + testListPop: function() { var realm = new Realm({schema: [schemas.LinkTypes, schemas.TestObject]}); var array; @@ -222,7 +235,7 @@ module.exports = BaseTest.extend({ }, 'can only pop in a write transaction'); }, - testUnshift: function() { + testListUnshift: function() { var realm = new Realm({schema: [schemas.LinkTypes, schemas.TestObject]}); var array; @@ -252,7 +265,7 @@ module.exports = BaseTest.extend({ }, 'can only unshift in a write transaction'); }, - testShift: function() { + testListShift: function() { var realm = new Realm({schema: [schemas.LinkTypes, schemas.TestObject]}); var array; @@ -280,7 +293,7 @@ module.exports = BaseTest.extend({ }, 'can only shift in a write transaction'); }, - testSplice: function() { + testListSplice: function() { var realm = new Realm({schema: [schemas.LinkTypes, schemas.TestObject]}); var array; @@ -345,7 +358,7 @@ module.exports = BaseTest.extend({ }, 'can only splice in a write transaction'); }, - testDeletions: function() { + testListDeletions: function() { var realm = new Realm({schema: [schemas.LinkTypes, schemas.TestObject]}); var object; var array; @@ -424,7 +437,7 @@ module.exports = BaseTest.extend({ TestCase.assertEqual(objects.length, 4); }, - testStaticResults: function() { + testListSnapshot: function() { var realm = new Realm({schema: [schemas.LinkTypes, schemas.TestObject]}); var objects = realm.objects('TestObject'); var array; @@ -498,7 +511,7 @@ module.exports = BaseTest.extend({ }); var names = function(results, prop) { - return Array.prototype.map.call(results, function(object) { + return results.map(function(object) { return object.name; }); }; @@ -509,4 +522,61 @@ module.exports = BaseTest.extend({ objects = list.sorted(['age', 'name']); TestCase.assertArraysEqual(names(objects), ['Ari', 'Tim', 'Alex', 'Bjarne']); }, + + testArrayMethods: function() { + var realm = new Realm({schema: [schemas.PersonObject, schemas.PersonList]}); + var object; + + realm.write(function() { + object = realm.create('PersonList', {list: [ + {name: 'Ari', age: 10}, + {name: 'Tim', age: 11}, + {name: 'Bjarne', age: 12}, + ]}); + }); + + [ + object.list, + realm.objects('PersonObject'), + ].forEach(function(list) { + TestCase.assertEqual(list.slice().length, 3); + TestCase.assertEqual(list.slice(-1).length, 1); + TestCase.assertEqual(list.slice(-1)[0].age, 12); + TestCase.assertEqual(list.slice(1, 3).length, 2); + TestCase.assertEqual(list.slice(1, 3)[1].age, 12); + + TestCase.assertEqual(list.join(' '), 'Ari Tim Bjarne'); + + var count = 0; + list.forEach(function(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})); + + var person = list.find(function(p) {return p.name == 'Tim'}); + TestCase.assertEqual(person.name, 'Tim'); + + var index = list.findIndex(function(p) {return p.name == 'Tim'}); + TestCase.assertEqual(index, 1); + + 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); + + // Some of these may not be present in every environment. + if (list.entries) { + TestCase.assertEqual(list.entries().next().value[1].name, 'Ari'); + } + if (list.keys) { + TestCase.assertEqual(list.keys().next().value, 0); + } + if (list.values) { + TestCase.assertEqual(list.values().next().value.name, 'Ari'); + } + }); + }, }); diff --git a/tests/lib/object-tests.js b/tests/js/object-tests.js similarity index 100% rename from tests/lib/object-tests.js rename to tests/js/object-tests.js diff --git a/tests/lib/package.json b/tests/js/package.json similarity index 100% rename from tests/lib/package.json rename to tests/js/package.json diff --git a/tests/lib/query-tests.js b/tests/js/query-tests.js similarity index 99% rename from tests/lib/query-tests.js rename to tests/js/query-tests.js index 1d2c0d81..df0fb503 100644 --- a/tests/lib/query-tests.js +++ b/tests/js/query-tests.js @@ -97,8 +97,8 @@ function runQuerySuite(suite) { throw "Primary key required for object comparison"; } - TestCase.assertArraysEqual(test[1], Array.prototype.map.call(results, function(el) { - return el[primary] + TestCase.assertArraysEqual(test[1], results.map(function(el) { + return el[primary]; })); } else if (test[0] == "QueryThrows") { diff --git a/tests/lib/realm-tests.js b/tests/js/realm-tests.js similarity index 99% rename from tests/lib/realm-tests.js rename to tests/js/realm-tests.js index 476219f4..8555d05b 100644 --- a/tests/lib/realm-tests.js +++ b/tests/js/realm-tests.js @@ -25,6 +25,11 @@ var schemas = require('./schemas'); var util = require('./util'); module.exports = BaseTest.extend({ + testRealmConstructor: function() { + var realm = new Realm({schema: []}); + TestCase.assertTrue(realm instanceof Realm); + }, + testRealmConstructorPath: function() { TestCase.assertThrows(function() { new Realm('/invalidpath'); diff --git a/tests/lib/results-tests.js b/tests/js/results-tests.js similarity index 96% rename from tests/lib/results-tests.js rename to tests/js/results-tests.js index 02a80448..4157d664 100644 --- a/tests/lib/results-tests.js +++ b/tests/js/results-tests.js @@ -24,6 +24,17 @@ var TestCase = require('./asserts'); var schemas = require('./schemas'); module.exports = BaseTest.extend({ + testResultsConstructor: function() { + var realm = new Realm({schema: [schemas.TestObject]}); + var objects = realm.objects('TestObject'); + + TestCase.assertTrue(objects instanceof Realm.Results); + + TestCase.assertThrows(function() { + new Realm.Results(); + }); + }, + testResultsLength: function() { var realm = new Realm({schema: [schemas.TestObject]}); var objects = realm.objects('TestObject'); @@ -35,6 +46,7 @@ module.exports = BaseTest.extend({ }); TestCase.assertEqual(objects.length, 1); }, + testResultsSubscript: function() { var realm = new Realm({schema: [schemas.PersonObject]}); realm.write(function() { @@ -50,6 +62,7 @@ module.exports = BaseTest.extend({ TestCase.assertTrue(Object.getPrototypeOf(people[0]) === schemas.PersonObject.prototype); TestCase.assertTrue(people[0] instanceof schemas.PersonObject); }, + testResultsReadonly: function() { var realm = new Realm({schema: [schemas.TestObject]}); var objects = realm.objects('TestObject'); @@ -71,17 +84,20 @@ module.exports = BaseTest.extend({ objects.length = 0; }); }, + testResultsInvalidProperty: function() { var realm = new Realm({schema: [schemas.TestObject]}); var objects = realm.objects('TestObject'); TestCase.assertEqual(undefined, objects.ablasdf); }, + testResultsInvalidObjectType: function() { var realm = new Realm({schema: [schemas.TestObject]}); TestCase.assertThrows(function() { var objects = realm.objects('NotTestObject'); }); }, + testResultsEnumerate: function() { var realm = new Realm({schema: [schemas.TestObject]}); var objects = realm.objects('TestObject'); @@ -105,6 +121,7 @@ module.exports = BaseTest.extend({ TestCase.assertEqual(count, 1); TestCase.assertEqual(keys.length, 1); }, + testResultsFiltered: function() { var realm = new Realm({schema: [schemas.PersonObject, schemas.DefaultValues, schemas.TestObject]}); realm.write(function() { @@ -149,6 +166,7 @@ module.exports = BaseTest.extend({ realm.objects('PersonObject').filtered("invalidQuery"); }); }, + testResultsSorted: function() { var realm = new Realm({schema: [schemas.IntPrimary]}); var objects = realm.objects('IntPrimaryObject'); @@ -162,7 +180,7 @@ module.exports = BaseTest.extend({ }); var primaries = function(results, prop) { - return Array.prototype.map.call(results, function(object) { + return results.map(function(object) { return object.primaryCol; }); }; diff --git a/tests/lib/schemas.js b/tests/js/schemas.js similarity index 98% rename from tests/lib/schemas.js rename to tests/js/schemas.js index f2dc9478..707d21c5 100644 --- a/tests/lib/schemas.js +++ b/tests/js/schemas.js @@ -39,6 +39,9 @@ PersonObject.schema = { PersonObject.prototype.description = function() { return this.name + ' ' + this.age; }; +PersonObject.prototype.toString = function() { + return this.name; +}; exports.PersonObject = PersonObject; exports.PersonList = { diff --git a/tests/lib/util.js b/tests/js/util.js similarity index 100% rename from tests/lib/util.js rename to tests/js/util.js diff --git a/tests/react-test-app/package.json b/tests/react-test-app/package.json index 20373194..e528b01a 100644 --- a/tests/react-test-app/package.json +++ b/tests/react-test-app/package.json @@ -10,6 +10,6 @@ "react-native-fs": "^1.1.0", "xmlbuilder": "^4.2.1", "realm": "file:../..", - "realm-tests": "file:../lib" + "realm-tests": "file:../js" } }