Merge pull request #658 from realm/al-sync

Node and fine grained notification support
This commit is contained in:
Ari Lazier 2016-11-15 11:00:08 -08:00 committed by GitHub
commit 7a4bf0e5ec
104 changed files with 4031 additions and 935 deletions

7
.dir-locals.el Normal file
View File

@ -0,0 +1,7 @@
;; Project specific Emacs settings
((nil . ((c-basic-offset . 4)
(indent-tabs-mode . nil)
(fill-column . 80)
(c-file-style . "ellemtel")
(c-file-offsets . ((innamespace . 0)))
(show-trailing-whitespace . t))))

View File

@ -6,4 +6,5 @@ build/
node_modules/
vendor/
/tests/test-runners/
/tests/test-runners/
/object-server-for-testing/

View File

@ -1,10 +1,19 @@
{
"root": true,
"env": {
"es6": true,
"node": true
},
"extends": "eslint:recommended",
"rules": {
"comma-dangle": 0,
"no-empty": 0,
"no-unused-vars": 1,
"no-unused-vars": [
"warn", {
"varsIgnorePattern": "^_",
"argsIgnorePattern": "^_"
}
],
"strict": [2, "global"]
}
}

12
.gitignore vendored
View File

@ -18,6 +18,10 @@
# core
core
core-*
sync
sync-*
node-sync
node-sync-*
# sh build.sh config
/Realm/config.mk
@ -98,6 +102,8 @@ build/
# node.js
node_modules/
npm-debug.log
/compiled/
*.lock
# Android/IJ
/android/
@ -108,3 +114,9 @@ local.properties
# Visual Studio Code
.vscode
tsconfig.json
# Tests
object-server-for-testing/
tests/realm-object-server/
vendor/sync
vendor/sync*

View File

@ -1,10 +1,11 @@
x.x.x Release notes (yyyy-MM-dd)
0.15.0 Release notes (2016-11-15)
=============================================================
### Breaking changes
* None
### Enhancements
* None
* Support for fine grained notifications
* Updated test and examples for react-natve v0.37.0
### Bugfixes
* None

View File

@ -1,37 +1,8 @@
FROM node:6
FROM ubuntu:xenial
# Make debugging quicker.
RUN apt-get update && apt-get install -y gdb vim
RUN apt-get update && \
apt-get install -y curl && \
curl -sL https://deb.nodesource.com/setup_4.x | bash - && \
apt-get install -y nodejs gcc-4.9 python build-essential
# Add non-root user.
RUN useradd -ms /bin/bash user
# Make our workspace directory and work from there.
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
# Get the node_modules setup before anything else.
COPY package.json .
RUN npm install
# Make sure core is downloaded.
COPY scripts/download-core.sh scripts/
RUN scripts/download-core.sh node
# Copy only what we need to build.
COPY src/ src/
# Build the Debug version of the module.
RUN src/node/build-node.sh Debug
# Copy everything else needed to run tests.
COPY lib/ lib/
COPY scripts/ scripts/
COPY tests/ tests/
# Switch to the non-root user.
RUN chown -R user .
USER user
# Default to running the Node tests
CMD ["node", "tests"]
ENV NPM_CONFIG_UNSAFE_PERM true

View File

@ -41,7 +41,7 @@ Prerequisites:
First clone this repository:
```
git clone https://github.com/realm/realm-js.git
git clone https://github.com/realm/realm-js-private.git
```
Then in the cloned directory:
@ -60,6 +60,15 @@ To build for Android:
- `./gradlew publishAndroid`
- The compiled version of the Android module is here: `<project-root>/android`
To build for Node:
- `REALM_CORE_PREFIX=/path/to/realm-core REALM_SYNC_PREFIX=/path/to/realm-sync npm install`
The `REALM_*_PREFIX` paths need to be absolute. Make sure to run `sh build.sh build-node` in both core and sync. Optionally export `REALMJS_USE_DEBUG_CORE=true` to link against the debug version of the realm binaries.
To build the Developer Edition of the module, pass `--developer_edition` to `npm install` or change the default value in `binding.gyp`.
`node-pre-gyp` is used for packaging. Run `scripts/build-node-pre-gyp.sh` with the same arguments and environment variables as `npm install` on all platforms (e.g. Linux and Darwin). Then, edit `package.json` to remove the `--build-from-source` option from the install script and run `npm pack`. Lastly, either create a fat package by merging all the tarballs created so far into one, or upload the `node-pre-gyp` tarballs to the CDN the `binary` section of `package.json` points to.
## Code of Conduct
This project adheres to the Contributor Covenant [code of conduct](https://realm.io/conduct/).

View File

@ -5,12 +5,14 @@
},
"DVTSourceControlWorkspaceBlueprintWorkingCopyStatesKey" : {
"40F53A12E4AE40C654358321B91166ABD3E910A6" : 0,
"F6F96CA34C5878B0A9123C7C37855491A5E599DA" : 0
"F6F96CA34C5878B0A9123C7C37855491A5E599DA" : 0,
"8F3C415DA79CDA7D23734F285B95F9F9A3C0CB81" : 0
},
"DVTSourceControlWorkspaceBlueprintIdentifierKey" : "5EE721F9-041C-4877-9E73-A925C9DB080A",
"DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey" : {
"40F53A12E4AE40C654358321B91166ABD3E910A6" : "realm-js\/",
"F6F96CA34C5878B0A9123C7C37855491A5E599DA" : "realm-js\/vendor\/GCDWebServer\/"
"F6F96CA34C5878B0A9123C7C37855491A5E599DA" : "realm-js\/vendor\/GCDWebServer\/",
"8F3C415DA79CDA7D23734F285B95F9F9A3C0CB81" : "realm-js\/src\/object-store\/"
},
"DVTSourceControlWorkspaceBlueprintNameKey" : "Realm",
"DVTSourceControlWorkspaceBlueprintVersion" : 204,
@ -21,6 +23,11 @@
"DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "40F53A12E4AE40C654358321B91166ABD3E910A6"
},
{
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/realm\/realm-object-store.git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "8F3C415DA79CDA7D23734F285B95F9F9A3C0CB81"
},
{
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/swisspol\/GCDWebServer.git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",

122
binding.gyp Normal file
View File

@ -0,0 +1,122 @@
{
"variables": {
"realm_node_build_as_library%": "0",
"realm_download_binaries%": "1"
},
"includes": [
"src/node/gyp/target_defaults.gypi",
"src/node/gyp/realm.gyp"
],
"targets": [
{
"target_name": "realm",
"dependencies": [
"object-store"
],
"sources": [
"src/node/platform.cpp",
"src/js_realm.cpp"
],
"include_dirs": [
"src"
],
"conditions": [
["realm_node_build_as_library", {
"type": "static_library",
"export_dependent_settings": [ "object-store" ]
}, {
"sources": [
"src/node/node_init.cpp"
]
}]
]
},
{
"target_name": "object-store",
"dependencies": [ "realm-core" ],
"type": "static_library",
"include_dirs": [
"src/object-store/src",
"src/object-store/src/impl",
"src/object-store/src/impl/apple",
"src/object-store/src/parser",
"src/object-store/external/pegtl"
],
"sources": [
"src/object-store/src/collection_notifications.cpp",
"src/object-store/src/index_set.cpp",
"src/object-store/src/list.cpp",
"src/object-store/src/object_schema.cpp",
"src/object-store/src/object_store.cpp",
"src/object-store/src/results.cpp",
"src/object-store/src/schema.cpp",
"src/object-store/src/shared_realm.cpp",
"src/object-store/src/thread_confined.cpp",
"src/object-store/src/impl/collection_change_builder.cpp",
"src/object-store/src/impl/collection_notifier.cpp",
"src/object-store/src/impl/handover.cpp",
"src/object-store/src/impl/list_notifier.cpp",
"src/object-store/src/impl/realm_coordinator.cpp",
"src/object-store/src/impl/results_notifier.cpp",
"src/object-store/src/impl/transact_log_handler.cpp",
"src/object-store/src/impl/weak_realm_notifier.cpp",
"src/object-store/src/parser/parser.cpp",
"src/object-store/src/parser/query_builder.cpp",
"src/object-store/src/util/format.cpp",
"src/object-store/src/util/thread_id.cpp",
],
"conditions": [
["OS=='linux'", {
"sources": [
"src/object-store/src/impl/android/external_commit_helper.cpp",
]
}],
["OS=='mac'", {
"sources": [
"src/object-store/src/impl/apple/external_commit_helper.cpp",
"src/object-store/src/impl/apple/keychain_helper.cpp"
]
}],
["realm_enable_sync", {
"dependencies": [ "realm-sync" ],
"sources": [
"src/object-store/src/sync/sync_manager.cpp",
"src/object-store/src/sync/sync_user.cpp",
"src/object-store/src/sync/sync_session.cpp",
"src/object-store/src/sync/impl/sync_file.cpp",
"src/object-store/src/sync/impl/sync_metadata.cpp"
],
}]
],
"all_dependent_settings": {
"include_dirs": [
"src/object-store/src",
"src/object-store/src/impl",
"src/object-store/src/impl/apple",
"src/object-store/src/parser",
"src/object-store/external/pegtl"
]
},
"export_dependent_settings": [
"<@(_dependencies)" # re-export settings related to linking the realm binaries
]
}
],
"conditions": [
["not realm_node_build_as_library", {
"targets": [
{
"target_name": "action_after_build",
"type": "none",
"dependencies": [ "<(module_name)" ],
"copies": [
{
"files": [ "<(PRODUCT_DIR)/<(module_name).node" ],
"destination": "<(module_path)"
}
]
}
]
}]
]
}

5
dependencies.list Normal file
View File

@ -0,0 +1,5 @@
PACKAGE_NAME=realm-js
VERSION=0.14.3-6
REALM_CORE_VERSION=2.1.4
REALM_SYNC_VERSION=1.0.0-BETA-3.3
REALM_OBJECT_SERVER_VERSION=1.0.0-BETA-2.1

View File

@ -259,6 +259,39 @@ class Collection {
* @since 0.11.0
*/
reduceRight(callback, initialValue) {}
/**
* Add a listener `callback` which will be called when a **live** collection instance changes.
* @param {function(collection, changes)} callback - A function to be called when changes occur.
* The callback function is called with two arguments:
* - `collection`: the collection instance that changed,
* - `changes`: a dictionary with keys `insertions`, `modifications` and `deletions`,
* each containing a list of indices that were inserted, updated or deleted respectively.
* @throws {Error} If `callback` is not a function.
* @example
* wines.addListener((collection, changes) => {
* // collection === wines
* console.log(`${changes.insertions.length} insertions`);
* console.log(`${changes.modifications.length} modifications`);
* console.log(`${changes.deletions.length} deletions`);
* console.log(`new size of collection: ${collection.length}`);
* });
*/
addListener(callback) {}
/**
* Remove the listener `callback` from the collection instance.
* @param {function(collection, changes)} callback - Callback function that was previously
* added as a listener through the {@link Collection#addListener addListener} method.
* @throws {Error} If `callback` is not a function.
*/
removeListener(callback) {}
/**
* Remove all `callback` listeners from the collection instance.
*/
removeAllListeners(name) {}
}
/**

View File

@ -7,7 +7,7 @@
},
"source": {
"include": ["docs"],
"exclude": ["docs/jsdoc-template", "docs/output"]
"exclude": ["docs/jsdoc-template", "docs/output", "docs/sync.js"]
},
"tags": {
"allowUnknownTags": false

View File

@ -189,6 +189,10 @@ Realm.defaultPath;
* object types in this Realm. **Required** when first creating a Realm at this `path`.
* @property {number} [schemaVersion] - **Required** (and must be incremented) after
* changing the `schema`.
* @property {Object} [sync] - Sync configuration parameters with the following
* child properties:
* - `user` - A `User` object obtained by calling `Realm.Sync.User.login`
* - `url` - A `string` which contains a valid Realm Sync url
*/
/**

View File

@ -20,7 +20,9 @@
* Instances of this class are typically **live** collections returned by
* {@link Realm#objects objects()} that will update as new objects are either
* added to or deleted from the Realm that match the underlying query. Results returned by
* {@link Realm.Results#snapshot snapshot()}, however, are will **not** live update.
* {@link Realm.Results#snapshot snapshot()}, however, will **not** live update
* (and listener callbacks added through {@link Realm.Results#addListener addListener()}
* will thus never be called).
* @extends Realm.Collection
* @memberof Realm
*/

129
docs/sync.js Normal file
View File

@ -0,0 +1,129 @@
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
/**
* @memberof Realm
*/
class Sync {
/**
* Set a global listener function.
* @param {string} local_path - The path to the directory where realm files are stored [deprecated]
* @param {string} server_url - The sync server to listen to
* @param {SyncUser} admin_user - An admin user obtained by calling `new Realm.Sync.User.Admin`
* @param {function(realm_name)} filter_callback - Return true to recieve changes for the given realm
* @param {function(realm_name, realm, change_set)} change_callback - called on any realm changes with
* the following arguments:
* - `realm_name` - path of the Realm on which changes occurred
* - `realm` - a `Realm` object for the changed Realm
* - `change_set` - a dictionary of object names to arays of indexes indicating the indexes of objects of each type
* which have been added, removed, or modified
*/
static setGlobalListener(local_path, server_url, admin_user, filter_callback, change_callback) {}
/**
* Set the sync log level.
* @param {string} log_level
*/
static setLogLevel(log_level) {}
}
/**
* @typedef Realm.Sync~LogLevel
* @type {("error"|"info"|"debug")}
*/
/**
* Class that describes authentication errors in the Realm Object Server
* @memberof Realm.Sync
*/
class AuthError extends Error {
/**
* The numerical code for this error.
* @type {number}
*/
get code() {}
/**
* The unique help URI that describes this error.
* @type {string}
*/
get type() {}
}
/**
* Class for logging in and managing Sync users.
* @memberof Realm.Sync
*/
class User {
/**
* Login a sync user with username and password.
* @param {string} server - authentication server
* @param {string} username
* @param {string} password
* @param {function(error, user)} callback - called with the following arguments:
* - `error` - an Error object is provided on failure
* - `user` - a valid User object on success
*/
login(server, username, password, callback) {}
/**
* Login a sync user using an external login provider.
* @param {string} server - authentication server
* @param {string} provider - The provider type
* @param {string} providerToken - The access token for the given provider
* @param {function(error, User)} callback - called with the following arguments:
* - `error` - an Error object is provided on failure
* - `user` - a valid User object on success
*/
loginWithProvider(server, provider, providerToken, callback) {}
/**
* Create a new user using the username/password provider
* @param {string} server - authentication server
* @param {string} username
* @param {string} password
* @param {function(error, User)} callback - called with the following arguments:
* - `error` - an Error object is provided on failure
* - `user` - a valid User object on success
*/
create(server, username, password, callback) {}
/**
* Create an admin user for the given authentication server with an existing token
* @param {string} server - authentication server
* @param {string} adminToken - existing admin token
* @return {User} - admin user populated with the given token and server
*/
adminUser(server, adminToken) {}
/**
* A dictionary containing users that are currently logged in.
* The keys in the dictionary are user identities, values are corresponding User objects.
* @type {object}
*/
get all() {}
/**
* Get the currently logged in user.
* Throws error if > 1 user logged in, returns undefined if no users logged in.
* @type {User}
*/
get current() {}
}

View File

@ -7,7 +7,8 @@
android:allowBackup="true"
android:label="@string/app_name"
android:icon="@mipmap/ic_launcher"
android:theme="@style/AppTheme">
android:theme="@style/AppTheme"
android:name=".MainApplication">
<activity
android:name=".MainActivity"
android:label="@string/app_name"

View File

@ -1,13 +1,6 @@
package io.realm.react.example;
import com.facebook.react.ReactActivity;
import com.facebook.react.ReactPackage;
import com.facebook.react.shell.MainReactPackage;
import java.util.Arrays;
import java.util.List;
import io.realm.react.RealmReactPackage;
public class MainActivity extends ReactActivity {
@ -20,24 +13,4 @@ public class MainActivity extends ReactActivity {
return "ReactExample";
}
/**
* Returns whether dev mode should be enabled.
* This enables e.g. the dev menu.
*/
@Override
protected boolean getUseDeveloperSupport() {
return BuildConfig.DEBUG;
}
/**
* A list of packages used by the app. If the app uses additional views
* or modules besides the default ones, add more packages here.
*/
@Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage(),
new RealmReactPackage()
);
}
}

View File

@ -0,0 +1,40 @@
package io.realm.react.example;
import android.app.Application;
import com.facebook.react.ReactApplication;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.react.shell.MainReactPackage;
import java.util.Arrays;
import java.util.List;
import io.realm.react.RealmReactPackage;
public class MainApplication extends Application implements ReactApplication {
private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
@Override
protected boolean getUseDeveloperSupport() {
return BuildConfig.DEBUG;
}
@Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage(),
new RealmReactPackage()
);
}
};
/**
* A list of packages used by the app. If the app uses additional views
* or modules besides the default ones, add more packages here.
*/
@Override
public ReactNativeHost getReactNativeHost() {
return mReactNativeHost;
}
}

View File

@ -5,7 +5,7 @@ buildscript {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:1.3.1'
classpath 'com.android.tools.build:gradle:2.2.2'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files

View File

@ -1,5 +1,6 @@
#Fri Nov 11 18:15:12 GMT 2016
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.4-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip

View File

@ -34,6 +34,7 @@ TodoList.schema = {
name: 'TodoList',
properties: {
name: 'string',
creationDate: 'date',
items: {type: 'list', objectType: 'Todo'},
},
};

View File

@ -38,15 +38,18 @@ export default class TodoApp extends React.Component {
constructor(props) {
super(props);
let todoLists = realm.objects('TodoList');
if (todoLists.length < 1) {
// This is a Results object, which will live-update.
this.todoLists = realm.objects('TodoList').sorted('creationDate');
if (this.todoLists.length < 1) {
realm.write(() => {
realm.create('TodoList', {name: 'Todo List'});
realm.create('TodoList', {name: 'Todo List', creationDate: new Date()});
});
}
this.todoLists.addListener((name, changes) => {
console.log("changed: " + JSON.stringify(changes));
});
console.log("registered listener");
// This is a Results object, which will live-update.
this.todoLists = todoLists;
// Bind all the methods that we will be passing as props.
this.renderScene = this.renderScene.bind(this);
@ -79,7 +82,6 @@ export default class TodoApp extends React.Component {
component: TodoListView,
passProps: {
ref: 'listView',
items: this.todoLists,
extraItems: extraItems,
onPressItem: this._onPressTodoList,
},
@ -105,7 +107,8 @@ export default class TodoApp extends React.Component {
}
renderScene(route) {
return <route.component {...route.passProps} />
console.log(this.todoLists);
return <route.component items={this.todoLists} {...route.passProps} />
}
_addNewTodoItem(list) {
@ -128,7 +131,7 @@ export default class TodoApp extends React.Component {
}
realm.write(() => {
realm.create('TodoList', {name: ''});
realm.create('TodoList', {name: '', creationDate: new Date()});
});
this._setEditingRow(items.length - 1);

View File

@ -71,6 +71,55 @@
remoteGlobalIDString = 13B07F861A680F5B00A75B9A;
remoteInfo = ReactExample;
};
02F8FE011DD6B23100BD85F8 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 2D2A283A1D9B042B00D4039D;
remoteInfo = "RCTImage-tvOS";
};
02F8FE051DD6B23100BD85F8 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 2D2A28471D9B043800D4039D;
remoteInfo = "RCTLinking-tvOS";
};
02F8FE091DD6B23100BD85F8 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 2D2A28541D9B044C00D4039D;
remoteInfo = "RCTNetwork-tvOS";
};
02F8FE0D1DD6B23100BD85F8 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 2D2A28611D9B046600D4039D;
remoteInfo = "RCTSettings-tvOS";
};
02F8FE111DD6B23100BD85F8 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 2D2A287B1D9B048500D4039D;
remoteInfo = "RCTText-tvOS";
};
02F8FE161DD6B23100BD85F8 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 2D2A28881D9B049200D4039D;
remoteInfo = "RCTWebSocket-tvOS";
};
02F8FE191DD6B23100BD85F8 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 2D2A28131D9B038B00D4039D;
remoteInfo = "React-tvOS";
};
139105C01AF99BAD00B5F7CC /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */;
@ -214,6 +263,7 @@
isa = PBXGroup;
children = (
00C302C01ABCB91800DB3ED1 /* libRCTImage.a */,
02F8FE021DD6B23100BD85F8 /* libRCTImage-tvOS.a */,
);
name = Products;
sourceTree = "<group>";
@ -222,6 +272,7 @@
isa = PBXGroup;
children = (
00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */,
02F8FE0A1DD6B23100BD85F8 /* libRCTNetwork-tvOS.a */,
);
name = Products;
sourceTree = "<group>";
@ -249,6 +300,7 @@
isa = PBXGroup;
children = (
139105C11AF99BAD00B5F7CC /* libRCTSettings.a */,
02F8FE0E1DD6B23100BD85F8 /* libRCTSettings-tvOS.a */,
);
name = Products;
sourceTree = "<group>";
@ -257,6 +309,7 @@
isa = PBXGroup;
children = (
139FDEF41B06529B00C62182 /* libRCTWebSocket.a */,
02F8FE171DD6B23100BD85F8 /* libRCTWebSocket-tvOS.a */,
);
name = Products;
sourceTree = "<group>";
@ -278,6 +331,7 @@
isa = PBXGroup;
children = (
146834041AC3E56700842450 /* libReact.a */,
02F8FE1A1DD6B23100BD85F8 /* libReact-tvOS.a */,
);
name = Products;
sourceTree = "<group>";
@ -286,6 +340,7 @@
isa = PBXGroup;
children = (
78C398B91ACF4ADC00677621 /* libRCTLinking.a */,
02F8FE061DD6B23100BD85F8 /* libRCTLinking-tvOS.a */,
);
name = Products;
sourceTree = "<group>";
@ -314,6 +369,7 @@
isa = PBXGroup;
children = (
832341B51AAA6A8300B99B32 /* libRCTText.a */,
02F8FE121DD6B23100BD85F8 /* libRCTText-tvOS.a */,
);
name = Products;
sourceTree = "<group>";
@ -505,6 +561,55 @@
remoteRef = 00C302E31ABCB9EE00DB3ED1 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
02F8FE021DD6B23100BD85F8 /* libRCTImage-tvOS.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = "libRCTImage-tvOS.a";
remoteRef = 02F8FE011DD6B23100BD85F8 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
02F8FE061DD6B23100BD85F8 /* libRCTLinking-tvOS.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = "libRCTLinking-tvOS.a";
remoteRef = 02F8FE051DD6B23100BD85F8 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
02F8FE0A1DD6B23100BD85F8 /* libRCTNetwork-tvOS.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = "libRCTNetwork-tvOS.a";
remoteRef = 02F8FE091DD6B23100BD85F8 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
02F8FE0E1DD6B23100BD85F8 /* libRCTSettings-tvOS.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = "libRCTSettings-tvOS.a";
remoteRef = 02F8FE0D1DD6B23100BD85F8 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
02F8FE121DD6B23100BD85F8 /* libRCTText-tvOS.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = "libRCTText-tvOS.a";
remoteRef = 02F8FE111DD6B23100BD85F8 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
02F8FE171DD6B23100BD85F8 /* libRCTWebSocket-tvOS.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = "libRCTWebSocket-tvOS.a";
remoteRef = 02F8FE161DD6B23100BD85F8 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
02F8FE1A1DD6B23100BD85F8 /* libReact-tvOS.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = "libReact-tvOS.a";
remoteRef = 02F8FE191DD6B23100BD85F8 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
139105C11AF99BAD00B5F7CC /* libRCTSettings.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
@ -684,6 +789,8 @@
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CC = "$(SRCROOT)/../../../scripts/ccache-clang.sh";
CXX = "$(SRCROOT)/../../../scripts/ccache-clang++.sh";
DEAD_CODE_STRIPPING = NO;
HEADER_SEARCH_PATHS = (
"$(inherited)",
@ -701,6 +808,8 @@
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CC = "$(SRCROOT)/../../../scripts/ccache-clang.sh";
CXX = "$(SRCROOT)/../../../scripts/ccache-clang++.sh";
HEADER_SEARCH_PATHS = (
"$(inherited)",
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
@ -717,6 +826,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CC = "$(SRCROOT)/../../../scripts/ccache-clang.sh";
CLANG_CXX_LANGUAGE_STANDARD = "c++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
@ -732,6 +842,7 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
CXX = "$(SRCROOT)/../../../scripts/ccache-clang++.sh";
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
@ -764,6 +875,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CC = "$(SRCROOT)/../../../scripts/ccache-clang.sh";
CLANG_CXX_LANGUAGE_STANDARD = "c++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
@ -779,6 +891,7 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = YES;
CXX = "$(SRCROOT)/../../../scripts/ccache-clang++.sh";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;

View File

@ -6,8 +6,8 @@
"start": "react-native start"
},
"dependencies": {
"react": "~15.2.0",
"react-native": "^0.31.0",
"react": "15.3.2",
"react-native": "^0.37.0",
"realm": "file:../.."
}
}

View File

@ -1,5 +1,2 @@
{
"env": {
"commonjs": true
}
}

View File

@ -1,7 +1,7 @@
{
"env": {
"es6": true,
"worker": true
"worker": true,
"node": false
},
"globals": {
"global": true

View File

@ -52,7 +52,7 @@ export function fireMutationListeners(realmId) {
}
}
export function createCollection(prototype, realmId, info, mutable) {
export function createCollection(prototype, realmId, info, _mutable) {
let collection = Object.create(prototype);
let size;

View File

@ -145,7 +145,7 @@ Object.defineProperties(Realm, {
set: util.setterForProperty('defaultPath'),
},
schemaVersion: {
value: function(path, encryptionKey) {
value: function(_path, _encryptionKey) {
return rpc.callMethod(undefined, Realm[keys.id], 'schemaVersion', Array.from(arguments));
}
},

View File

@ -21,7 +21,7 @@
import * as base64 from './base64';
import { keys, objectTypes } from './constants';
const {id: idKey, realm: realmKey} = keys;
const {id: idKey, realm: _realmKey} = keys;
const registeredCallbacks = [];
const typeConverters = {};

28
lib/errors.js Normal file
View File

@ -0,0 +1,28 @@
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
'use strict';
class AuthError extends Error {
constructor(problem) {
super(problem.title);
Object.assign(this, problem);
}
}
exports['AuthError'] = AuthError;

67
lib/extensions.js Normal file
View File

@ -0,0 +1,67 @@
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
'use strict';
module.exports = function(realmConstructor) {
// Add the specified Array methods to the Collection prototype.
Object.defineProperties(realmConstructor.Collection.prototype, require('./collection-methods'));
// Add sync methods
if (realmConstructor.Sync) {
Object.defineProperties(realmConstructor.Sync.User, require('./user-methods')(realmConstructor));
realmConstructor.Sync.AuthError = require('./errors').AuthError;
if (realmConstructor.Sync.cleanup) {
// FIXME: DOES THIS WORK ON BOTH NODE AND REACT NATIVE?
process.on('exit', realmConstructor.Sync.cleanup);
}
}
// TODO: Remove this now useless object.
var types = Object.freeze({
'BOOL': 'bool',
'INT': 'int',
'FLOAT': 'float',
'DOUBLE': 'double',
'STRING': 'string',
'DATE': 'date',
'DATA': 'data',
'OBJECT': 'object',
'LIST': 'list',
});
Object.defineProperty(realmConstructor, 'Types', {
get: function() {
if (typeof console != 'undefined') {
/* global console */
/* eslint-disable no-console */
var stack = new Error().stack.split("\n").slice(2).join("\n");
var msg = '`Realm.Types` is deprecated! Please specify the type name as lowercase string instead!\n'+stack;
if (console.warn != undefined) {
console.warn(msg);
}
else {
console.log(msg);
}
/* eslint-enable no-console */
}
return types;
},
configurable: true
});
}

View File

@ -18,7 +18,10 @@
'use strict';
var arrayMethods = require('./collection-methods');
function node_require(module) {
return require(module);
}
var realmConstructor;
if (typeof Realm != 'undefined') {
@ -30,46 +33,16 @@ if (typeof Realm != 'undefined') {
realmConstructor = require('./browser').default; // (exported as ES6 module)
// eslint-disable-next-line
} else if (typeof process == 'object' && (('' + process) == '[object process]' || typeof jest == 'object')) {
// If process is defined, we're running in node.
// Prevent React Native packager from seeing this module.
var bindings = 'bindings';
realmConstructor = require(bindings)('realm').Realm;
var binary = node_require('node-pre-gyp');
var path = node_require('path');
var binding_path = binary.find(path.resolve(path.join(__dirname,'../package.json')));
realmConstructor = require(binding_path).Realm;
} else {
throw new Error('Missing Realm constructor - please ensure RealmReact framework is included!');
}
// Add the specified Array methods to the Collection prototype.
Object.defineProperties(realmConstructor.Collection.prototype, arrayMethods);
// TODO: Remove this now useless object.
var types = Object.freeze({
'BOOL': 'bool',
'INT': 'int',
'FLOAT': 'float',
'DOUBLE': 'double',
'STRING': 'string',
'DATE': 'date',
'DATA': 'data',
'OBJECT': 'object',
'LIST': 'list',
});
Object.defineProperty(realmConstructor, 'Types', {
get: function() {
if (typeof console != 'undefined') {
/* global console */
/* eslint-disable no-console */
var stack = new Error().stack.split("\n").slice(2).join("\n");
var msg = '`Realm.Types` is deprecated! Please specify the type name as lowercase string instead!\n'+stack;
if (console.warn != undefined) {
console.warn(msg);
}
else {
console.log(msg);
}
/* eslint-enable no-console */
}
return types;
},
configurable: true
});
require('./extensions')(realmConstructor);
module.exports = realmConstructor;

144
lib/user-methods.js Normal file
View File

@ -0,0 +1,144 @@
'use strict';
const AuthError = require('./errors').AuthError;
function node_require(module) {
return require(module);
}
var post;
if (typeof fetch !== 'undefined') {
post = function(options, callback) {
options.method = 'POST';
// eslint-disable-next-line no-undef
fetch(options.url, options)
.then((response) => {
if (response.status != 200) {
callback(undefined, {statusCode: response.status});
}
else {
return response.text();
}
})
.then((body) => {
callback(undefined, {statusCode: 200}, JSON.parse(body));
})
.catch((error) => {
callback(error);
});
}
}
else {
post = function(options, callback) {
node_require('needle').post(options.url, options.body, options, callback);
}
}
const url_parse = require("url-parse");
const postHeaders = {
'content-type': 'application/json;charset=utf-8',
'accept': 'application/json'
};
function auth_url(server) {
if (server.charAt(server.length-1) != '/') {
return server + '/auth';
}
return server + 'auth';
}
module.exports = function(realmConstructor) {
function _authenticate(server, json, callback) {
json.app_id = '';
var options = {
url: auth_url(server),
body: JSON.stringify(json),
headers: postHeaders,
open_timeout: 5000
};
post(options, function(error, response, body) {
if (error) {
callback(error);
}
else if (response.statusCode != 200) {
callback(new AuthError(JSON.parse(body)));
}
else {
// TODO: validate JSON
const token = body.refresh_token.token;
const identity = body.refresh_token.token_data.identity;
callback(undefined, realmConstructor.Sync.User.createUser(server, identity, token, false));
}
});
}
var methods = {};
methods['adminUser'] = function(token) {
var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
return v.toString(16);
});
var user = realmConstructor.Sync.User.createUser('', uuid, token, true);
return user;
}
methods['register'] = function(server, username, password, callback) {
_authenticate(server, {
provider: 'password',
user_info: { password: password, register: true },
data: username
}, callback);
}
methods['login'] = function(server, username, password, callback) {
_authenticate(server, {
provider: 'password',
user_info: { password: password },
data: username
}, callback);
}
methods['registerWithProvider'] = function(server, provider, providerToken, callback) {
_authenticate(server, {
provider: provider,
data: providerToken
}, callback);
}
methods['_authenticateRealm'] = function(fileUrl, realmUrl, callback) {
var options = {
url: auth_url(this.server),
body: JSON.stringify({
data: this.token,
path: url_parse(realmUrl).pathname,
provider: 'realm',
app_id: ''
}),
headers: postHeaders
};
post(options, function(error, response, body) {
if (error) {
callback(error);
}
else if (response.statusCode != 200) {
callback(new AuthError('Bad response: ' + response.statusCode));
}
else {
// TODO: validate JSON
callback(undefined, {
token: body.access_token.token,
file_url: url_parse(fileUrl).pathname,
resolved_realm_url: 'realm://' + url_parse(realmUrl).host + body.access_token.token_data.path
});
}
});
}
for (var name in methods) {
methods[name] = {value: methods[name], configurable: true, writable: true}
}
return methods;
}

View File

@ -1,7 +1,7 @@
{
"name": "realm",
"description": "Realm is a mobile database: an alternative to SQLite and key-value stores",
"version": "0.14.3",
"version": "0.15.0-rc",
"license": "Apache-2.0",
"homepage": "https://realm.io",
"keywords": [
@ -14,7 +14,8 @@
"sqlite",
"asyncstorage",
"rocksdb",
"leveldb"
"leveldb",
"realm"
],
"author": {
"name": "Realm",
@ -35,29 +36,34 @@
"react-native",
"scripts",
"src",
"vendor"
"vendor",
"binding.gyp",
"dependencies.list"
],
"scripts": {
"get-version": "echo $npm_package_version",
"set-version": "scripts/set-version.sh",
"get-core-version": "scripts/download-core.sh --version",
"get-sync-version": "scripts/download-core.sh --versionSync",
"jsdoc": "rm -rf docs/output && jsdoc -c docs/conf.json",
"lint": "eslint",
"test": "scripts/test.sh",
"install": "node-pre-gyp install --fallback-to-build",
"prepublish": "scripts/prepublish.sh"
},
"dependencies": {
"bindings": "^1.2.1",
"nan": "^2.3.3",
"node-gyp": "^3.3.1",
"sync-request": "^3.0.1"
"needle": "^1.3.0",
"node-pre-gyp": "^0.6.30",
"sync-request": "^3.0.1",
"url-parse": "^1.1.7"
},
"devDependencies": {
"babel-eslint": "^6.0.4",
"eslint": "^2.10.2",
"eslint-plugin-react": "^5.1.1",
"eslint": "^3.2.2",
"eslint-plugin-jasmine": "^2.1.0",
"eslint-plugin-react": "^6.7.0",
"jsdoc": "^3.4.0",
"mockery": "^1.7.0",
"semver": "^5.1.0"
},
"rnpm": {
@ -71,5 +77,11 @@
},
"engines": {
"node": ">=4"
},
"binary": {
"module_name": "realm",
"module_path": "./compiled/{node_abi}_{platform}_{arch}/",
"host": "https://static.realm.io",
"remote_path": "/node-pre-gyp"
}
}

View File

@ -33,12 +33,14 @@ import org.apache.tools.ant.filters.ReplaceTokens
// After that we build native code from src/main/jni with module path pointing at third-party-ndk.
ext.coreVersion = "npm --silent run get-core-version".execute().text.trim()
ext.syncVersion = "npm --silent run get-sync-version".execute().text.trim()
def currentVersion = "npm --silent run get-version".execute().text.trim()
def downloadsDir = new File("$projectDir/downloads")
def jscDownloadDir = new File("$projectDir/src/main/jni/jsc")
def coreDownloadDir = new File("$projectDir/src/main/jni")
def publishDir = new File("$projectDir/../../android/")
// to build with sync run: ./gradlew assembleDebug -PbuildWithSync=true
ext.buildSync = project.hasProperty('buildWithSync') ? project.getProperty('buildWithSync').toBoolean() : false
task generateVersionClass(type: Copy) {
from 'src/main/templates/Version.java'
@ -64,10 +66,18 @@ task downloadJSCHeaders(type: Download) {
}
task downloadRealmCore(type: Download) {
src "https://static.realm.io/downloads/core/realm-core-android-${project.coreVersion}.tar.gz"
if (project.buildSync) {
src "https://static.realm.io/downloads/sync/realm-sync-android-${project.syncVersion}.tar.gz"
} else {
src "https://static.realm.io/downloads/core/realm-core-android-${project.coreVersion}.tar.gz"
}
onlyIfNewer true
overwrite false
dest new File(downloadsDir, "realm-core-android-${project.coreVersion}.tar.gz")
overwrite true
if (project.buildSync) {
dest new File(downloadsDir, "realm-core-android-${project.syncVersion}.tar.gz")
} else {
dest new File(downloadsDir, "realm-core-android-${project.coreVersion}.tar.gz")
}
}
task prepareRealmCore(dependsOn: downloadRealmCore, type:Copy) {
@ -139,6 +149,8 @@ task buildReactNdkLib(dependsOn: [downloadJSCHeaders,prepareRealmCore], type: Ex
inputs.file('src/main/jni')
outputs.dir("$buildDir/realm-react-ndk/all")
commandLine getNdkBuildFullPath(),
'-e',
project.buildSync ? 'BUILD_TYPE_SYNC=1' : 'BUILD_TYPE_SYNC=0',
'NDK_PROJECT_PATH=null',
"NDK_APPLICATION_MK=$projectDir/src/main/jni/Application.mk",
'NDK_OUT=' + temporaryDir,

View File

@ -1,5 +1,13 @@
LOCAL_PATH:= $(call my-dir)
ifeq ($(strip $(BUILD_TYPE_SYNC)),1)
include $(CLEAR_VARS)
LOCAL_MODULE := realm-android-sync-$(TARGET_ARCH_ABI)
LOCAL_EXPORT_C_INCLUDES := core/include
LOCAL_SRC_FILES := core/librealm-sync-android-$(TARGET_ARCH_ABI).a
include $(PREBUILT_STATIC_LIBRARY)
endif
include $(CLEAR_VARS)
LOCAL_MODULE := realm-android-$(TARGET_ARCH_ABI)
LOCAL_EXPORT_C_INCLUDES := core/include
@ -14,34 +22,43 @@ include $(BUILD_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := librealmreact
LOCAL_SRC_FILES := \
src/js_realm.cpp \
src/rpc.cpp \
src/jsc/jsc_init.cpp \
src/android/platform.cpp \
src/android/io_realm_react_RealmReactModule.cpp \
src/android/jsc_override.cpp \
src/object-store/src/collection_notifications.cpp \
src/object-store/src/index_set.cpp \
src/object-store/src/list.cpp \
src/object-store/src/object_schema.cpp \
src/object-store/src/object_store.cpp \
src/object-store/src/results.cpp \
src/object-store/src/schema.cpp \
src/object-store/src/shared_realm.cpp \
src/object-store/src/parser/parser.cpp \
src/object-store/src/parser/query_builder.cpp \
src/object-store/src/impl/collection_change_builder.cpp \
src/object-store/src/impl/collection_notifier.cpp \
src/object-store/src/impl/list_notifier.cpp \
src/object-store/src/impl/realm_coordinator.cpp \
src/object-store/src/impl/results_notifier.cpp \
src/object-store/src/impl/transact_log_handler.cpp \
src/object-store/src/impl/android/external_commit_helper.cpp \
src/object-store/src/impl/android/weak_realm_notifier.cpp \
src/object-store/src/util/format.cpp \
src/object-store/src/util/thread_id.cpp \
vendor/base64.cpp
LOCAL_SRC_FILES := vendor/base64.cpp
LOCAL_SRC_FILES += src/js_realm.cpp
LOCAL_SRC_FILES += src/rpc.cpp
LOCAL_SRC_FILES += src/jsc/jsc_init.cpp
LOCAL_SRC_FILES += src/android/io_realm_react_RealmReactModule.cpp
LOCAL_SRC_FILES += src/android/jsc_override.cpp
LOCAL_SRC_FILES += src/android/platform.cpp
LOCAL_SRC_FILES += src/object-store/src/impl/collection_change_builder.cpp
LOCAL_SRC_FILES += src/object-store/src/impl/collection_notifier.cpp
LOCAL_SRC_FILES += src/object-store/src/impl/handover.cpp
LOCAL_SRC_FILES += src/object-store/src/impl/list_notifier.cpp
LOCAL_SRC_FILES += src/object-store/src/impl/realm_coordinator.cpp
LOCAL_SRC_FILES += src/object-store/src/impl/results_notifier.cpp
LOCAL_SRC_FILES += src/object-store/src/impl/transact_log_handler.cpp
LOCAL_SRC_FILES += src/object-store/src/impl/weak_realm_notifier.cpp
LOCAL_SRC_FILES += src/object-store/src/impl/android/external_commit_helper.cpp
LOCAL_SRC_FILES += src/object-store/src/parser/parser.cpp
LOCAL_SRC_FILES += src/object-store/src/parser/query_builder.cpp
LOCAL_SRC_FILES += src/object-store/src/util/format.cpp
LOCAL_SRC_FILES += src/object-store/src/util/thread_id.cpp
LOCAL_SRC_FILES += src/object-store/src/collection_notifications.cpp
LOCAL_SRC_FILES += src/object-store/src/index_set.cpp
LOCAL_SRC_FILES += src/object-store/src/list.cpp
LOCAL_SRC_FILES += src/object-store/src/object_schema.cpp
LOCAL_SRC_FILES += src/object-store/src/object_store.cpp
LOCAL_SRC_FILES += src/object-store/src/placeholder.cpp
LOCAL_SRC_FILES += src/object-store/src/results.cpp
LOCAL_SRC_FILES += src/object-store/src/schema.cpp
LOCAL_SRC_FILES += src/object-store/src/shared_realm.cpp
LOCAL_SRC_FILES += src/object-store/src/thread_confined.cpp
ifeq ($(strip $(BUILD_TYPE_SYNC)),1)
LOCAL_SRC_FILES += src/object-store/src/sync/sync_manager.cpp
LOCAL_SRC_FILES += src/object-store/src/sync/sync_session.cpp
LOCAL_SRC_FILES += src/object-store/src/sync/sync_user.cpp
LOCAL_SRC_FILES += src/object-store/src/sync/impl/sync_file.cpp
LOCAL_SRC_FILES += src/object-store/src/sync/impl/sync_metadata.cpp
endif
LOCAL_C_INCLUDES := src
LOCAL_C_INCLUDES += src/jsc
@ -53,9 +70,18 @@ LOCAL_C_INCLUDES += vendor
LOCAL_C_INCLUDES += $(JAVA_HOME)/include
LOCAL_C_INCLUDES += $(JAVA_HOME)/include/darwin
LOCAL_C_INCLUDES += core/include
ifeq ($(strip $(BUILD_TYPE_SYNC)),1)
LOCAL_C_INCLUDES += src/object-store/src/sync
endif
LOCAL_ALLOW_UNDEFINED_SYMBOLS := true
ifeq ($(strip $(BUILD_TYPE_SYNC)),1)
LOCAL_STATIC_LIBRARIES := realm-android-sync-$(TARGET_ARCH_ABI)
LOCAL_STATIC_LIBRARIES += realm-android-$(TARGET_ARCH_ABI)
else
LOCAL_STATIC_LIBRARIES := realm-android-$(TARGET_ARCH_ABI)
endif
LOCAL_SHARED_LIBRARIES := libjsc
include $(BUILD_SHARED_LIBRARY)

View File

@ -5,7 +5,7 @@ APP_PLATFORM := android-9
APP_MK_DIR := $(dir $(lastword $(MAKEFILE_LIST)))
NDK_MODULE_PATH := $(APP_MK_DIR)$(HOST_DIRSEP)$(THIRD_PARTY_NDK_DIR)$(HOST_DIRSEP)$(APP_MK_DIR)first-party
NDK_MODULE_PATH := $(APP_MK_DIR)$(HOST_DIRSEP)$(THIRD_PARTY_NDK_DIR)$(HOST_DIRSEP)$(APP_MK_DIR)
APP_STL := gnustl_static
APP_CPPFLAGS := -std=c++14
@ -18,5 +18,8 @@ APP_CPPFLAGS += -fomit-frame-pointer
APP_LDFLAGS := -Wl,--build-id
APP_LDFLAGS += -llog
APP_LDFLAGS += -landroid
ifeq ($(strip $(BUILD_TYPE_SYNC)),1)
APP_LDFLAGS += -lz
endif
NDK_TOOLCHAIN_VERSION := 4.9

View File

@ -247,6 +247,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CC = "$(SRCROOT)/../../scripts/ccache-clang.sh";
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "c++14";
CLANG_CXX_LIBRARY = "libc++";
@ -263,6 +264,7 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
CXX = "$(SRCROOT)/../../scripts/ccache-clang++.sh";
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
@ -296,6 +298,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CC = "$(SRCROOT)/../../scripts/ccache-clang.sh";
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "c++14";
CLANG_CXX_LIBRARY = "libc++";
@ -312,6 +315,7 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
CXX = "$(SRCROOT)/../../scripts/ccache-clang++.sh";
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
@ -341,11 +345,12 @@
HEADER_SEARCH_PATHS = (
"$(inherited)",
"$(SRCROOT)/../../src/**",
"$(SRCROOT)/../../vendor",
"$(SRCROOT)/../../vendor/core/include",
"$(SRCROOT)/../../vendor/GCDWebServer/GCDWebServer/**",
"$(SRCROOT)/../../../react-native/React/**",
"$(SRCROOT)/../../tests/react-test-app/node_modules/react-native/React/**",
"$(SRCROOT)/../../examples/ReactExample/node_modules/react-native/React/**",
"$(SRCROOT)/../../vendor",
);
OTHER_LIBTOOLFLAGS = "$(BUILT_PRODUCTS_DIR)/libRealmJS.a $(BUILT_PRODUCTS_DIR)/libGCDWebServers.a";
PRODUCT_NAME = "$(TARGET_NAME)";
@ -359,11 +364,12 @@
HEADER_SEARCH_PATHS = (
"$(inherited)",
"$(SRCROOT)/../../src/**",
"$(SRCROOT)/../../vendor",
"$(SRCROOT)/../../vendor/core/include",
"$(SRCROOT)/../../vendor/GCDWebServer/GCDWebServer/**",
"$(SRCROOT)/../../../react-native/React/**",
"$(SRCROOT)/../../tests/react-test-app/node_modules/react-native/React/**",
"$(SRCROOT)/../../examples/ReactExample/node_modules/react-native/React/**",
"$(SRCROOT)/../../vendor",
);
OTHER_LIBTOOLFLAGS = "$(BUILT_PRODUCTS_DIR)/libRealmJS.a";
PRODUCT_NAME = "$(TARGET_NAME)";

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0720"
LastUpgradeVersion = "0800"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"

44
scripts/build-node-pre-gyp.sh Executable file
View File

@ -0,0 +1,44 @@
#!/bin/bash
node_versions=${@:-4.4.7 5.12.0 6.5.0}
topdir=$(cd $(dirname "$0")/..; pwd)
die() {
echo $1
exit 1
}
mkdir -p ${topdir}/out
: ${NVM_DIR=$topdir/.nvm}
if [ ! -d "$NVM_DIR" ]; then
(
git clone https://github.com/creationix/nvm.git "$NVM_DIR"
cd "$NVM_DIR"
git checkout `git describe --abbrev=0 --tags --match "v[0-9]*" origin`
)
fi
if [ -f "$NVM_DIR/nvm.sh" ]; then
. "$NVM_DIR/nvm.sh"
else
# we must be on mac and nvm was installed with brew
# TODO: change the mac slaves to use manual nvm installation
. "$(brew --prefix nvm)/nvm.sh"
fi
for node_version in ${node_versions}; do
(
rm -rf node_modules build
nvm install ${node_version} || die "Could not install nodejs v${node_version}"
nvm use ${node_version} || die "Could not load nodejs v${node_version}"
npm install --build-from-source "$EXTRA_NPM_ARGUMENTS" || die "Could not build module"
#./scripts/test.sh node || die "Unit tests for nodejs v${node_version} failed"
./node_modules/.bin/node-pre-gyp package || die "Could not package module"
cp build/stage/node-pre-gyp/*.tar.gz ${topdir}/out/
)
done

11
scripts/ccache-clang++.sh Executable file
View File

@ -0,0 +1,11 @@
#!/bin/sh
if type -p /usr/local/bin/ccache >/dev/null 2>&1; then
export CCACHE_MAXSIZE=10G
export CCACHE_CPP2=true
export CCACHE_HARDLINK=true
export CCACHE_SLOPPINESS=file_macro,time_macros,include_file_mtime,include_file_ctime,file_stat_matches
exec /usr/local/bin/ccache /usr/bin/clang++ "$@"
else
exec clang++ "$@"
fi

11
scripts/ccache-clang.sh Executable file
View File

@ -0,0 +1,11 @@
#!/bin/sh
if type -p /usr/local/bin/ccache >/dev/null 2>&1; then
export CCACHE_MAXSIZE=10G
export CCACHE_CPP2=true
export CCACHE_HARDLINK=true
export CCACHE_SLOPPINESS=file_macro,time_macros,include_file_mtime,include_file_ctime,file_stat_matches
exec /usr/local/bin/ccache /usr/bin/clang "$@"
else
exec clang "$@"
fi

18
scripts/docker-test.sh Executable file
View File

@ -0,0 +1,18 @@
#!/bin/sh
# This is a wrapper script around ./scripts/test.sh which uses docker. The
# arguments are the same, as they are passed directly to test.sh.
# It can be used to locally check and debug the linux build process
# outside of CI.
set -e
./scripts/docker_build_wrapper.sh ci/realm-js:build .
exec docker run --rm \
-u $(id -u) \
-e HOME=/tmp \
-v $(pwd):/source \
-w /source \
ci/realm-js:build \
./scripts/test.sh $@

103
scripts/docker_build_wrapper.sh Executable file
View File

@ -0,0 +1,103 @@
#!/bin/bash
# This is a wrapper script around `docker build`. It provides a mechanism
# for using cached image layers from upstream repositories as well as an
# automatic push back to the docker registry.
set -e
script_path="$(pushd "$(dirname "$0")" >/dev/null; pwd)"
src_path="$(pushd "${script_path}/.." >/dev/null; pwd)"
die() { echo "$@" 1>&2 ; exit 1; }
info() { echo "===> $*"; }
docker_build() {
declare name="$1"; shift
declare path="$1"; shift
declare args="$*"
if [ "${DOCKER_REGISTRY}" != "" ]; then
remote_name="${DOCKER_REGISTRY}/${name}"
fi
info "Building ${name} image..."
if [ "${DOCKER_REGISTRY}" != "" ]; then
docker_pull "${remote_name}" && docker tag "${remote_name}" "${name}" || true
fi
old_id=$(docker images -q "${name}")
info "Old ${name} image id: ${old_id}"
if [ "${DOCKERFILE}" != "" ]; then
docker build ${args} -t "${name}" -f "${DOCKERFILE}" "${path}" || \
die "Building ${name} image failed"
else
docker build ${args} -t "${name}" "${path}" || \
die "Building ${name} image failed"
fi
new_id=$(docker images -q "${name}")
info "New ${name} image id: ${new_id}"
if [ "${DEBUG}" ] && [ "${new_id}" != "${old_id}" ]; then
info "History for old id $old_id:"
if [ "${old_id}" != "" ]; then
docker history "$old_id"
fi
info "History for new id $new_id:"
docker history "$new_id"
fi
if [ "${DOCKER_PUSH:-0}" != "0" ] && [ "${DOCKER_REGISTRY}" != "" ]; then
docker tag "${name}" "${remote_name}"
docker_push "${remote_name}"
fi
}
# Due to https://github.com/docker/docker/issues/20316, we use
# https://github.com/tonistiigi/buildcache to generate a cache of the layer
# metadata for later builds.
my_buildcache() {
if [ "$DOCKER_REGISTRY" != "" ]; then
docker_path="/var/lib/docker"
# Stupid hack for our AWS nodes, which have docker data on another volume
if [ -d "/mnt/docker" ]; then
docker_path="/mnt/docker"
fi
docker pull "${DOCKER_REGISTRY}/ci/buildcache" >/dev/null && \
docker run --rm \
-v /var/run/docker.sock:/var/run/docker.sock \
-v ${docker_path}:/var/lib/docker \
"${DOCKER_REGISTRY}/ci/buildcache" "$@"
fi
}
docker_pull() {
info "Attempting to pull '$1' image from registry..."
(
tmpdir=$(mktemp -d)
docker pull "$1"
(
# In addition, pull the cached metadata and load it (buildcache)
cd "${tmpdir}" && docker pull "$1-cache" && docker save "$1-cache" | \
tar -xf - && docker load -i ./*/layer.tar
)
rm -rf "${tmpdir}"
) || true
}
docker_push() {
info "Pushing '$1' image to registry..."
docker push "$1"
# Create a cache of the layer metdata we need and push it as an image
#docker rmi $1-cache 2>/dev/null || true
my_buildcache save -g /var/lib/docker "$1" | gunzip -c | \
docker import - "$1-cache" && \
docker push "$1-cache"
}
docker_build $@

View File

@ -3,26 +3,50 @@
set -e
set -o pipefail
source_root="$(dirname "$0")"
# Set to "latest" for the latest build.
: ${REALM_CORE_VERSION:=1.5.0}
: ${REALM_CORE_VERSION:=$(sed -n 's/^REALM_CORE_VERSION=\(.*\)$/\1/p' ${source_root}/../dependencies.list)}
: ${REALM_SYNC_VERSION:=$(sed -n 's/^REALM_SYNC_VERSION=\(.*\)$/\1/p' ${source_root}/../dependencies.list)}
if [ "$1" = '--version' ]; then
echo "$REALM_CORE_VERSION"
exit 0
elif [ "$1" = '--versionSync' ]; then
echo "$REALM_SYNC_VERSION"
exit 0
fi
# The 'node' argument will result in realm-node build being downloaded.
if [ "$1" = 'node' ]; then
ENABLE_SYNC="$2"
CORE_DIR="core-node"
SYNC_DIR='node-sync'
if [ "$(uname)" = 'Darwin' ]; then
PLATFORM_TAG="node-osx-"
SYNC_PLATFORM_TAG="node-cocoa-"
CORE_DOWNLOAD_FILE="realm-core-node-osx-$REALM_CORE_VERSION.tar.gz"
SYNC_DOWNLOAD_FILE="realm-sync-$SYNC_PLATFORM_TAG$REALM_SYNC_VERSION.zip"
else
PLATFORM_TAG="node-linux-"
SYNC_PLATFORM_TAG="node-cocoa-"
CORE_DOWNLOAD_FILE="realm-core-node-linux-$REALM_CORE_VERSION.tar.gz"
SYNC_DOWNLOAD_FILE=""
fi
SYNC_EXTRACT="unzip"
EXTRACTED_DIR="realm-sync-node-cocoa-$REALM_SYNC_VERSION"
else
ENABLE_SYNC="yes" # FIXME: This means that both core and sync will be downloaded for non "node" targets.
# Should be 0 or 1. We do not need to download both
CORE_DIR='core'
CORE_DOWNLOAD_FILE="realm-core-$REALM_CORE_VERSION.tar.xz"
PLATFORM_TAG=""
SYNC_DIR='sync'
CORE_DOWNLOAD_FILE="realm-core-$PLATFORM_TAG$REALM_CORE_VERSION.tar.xz"
SYNC_DOWNLOAD_FILE="realm-sync-cocoa-$SYNC_PLATFORM_TAG$REALM_SYNC_VERSION.tar.xz"
SYNC_EXTRACT="tar -xvf"
EXTRACTED_DIR="core"
fi
# Start current working directory at the root of the project.
@ -34,57 +58,119 @@ die() {
}
download_core() {
echo "Downloading dependency: $CORE_DIR $REALM_CORE_VERSION"
local DIR=$1
local VERSION=$2
local DOWNLOAD_FILE=$3
local SERVER_DIR=$4
local UNTAR=$5
local UNTARRED_DIR=$6
echo "Downloading dependency: $DIR $VERSION"
local TMP_DIR="${TMPDIR:-/tmp}/core_bin"
local CORE_TAR="$TMP_DIR/$CORE_DOWNLOAD_FILE"
local CORE_TMP_TAR="$CORE_TAR.tmp"
local TMP_DIR="${TMPDIR:-/tmp}/$DIR"
local TAR="$TMP_DIR/$DOWNLOAD_FILE"
local TMP_TAR="$TAR.tmp"
mkdir -p "$TMP_DIR"
if [ ! -f "$CORE_TAR" ]; then
curl -f -L -s "https://static.realm.io/downloads/core/$CORE_DOWNLOAD_FILE" -o "$CORE_TMP_TAR" ||
die "Downloading $CORE_DIR failed. Please try again once you have an Internet connection."
mv "$CORE_TMP_TAR" "$CORE_TAR"
if [ ! -f "$TAR" ]; then
echo "https://static.realm.io/downloads/$SERVER_DIR/$DOWNLOAD_FILE"
curl -f -L -s "https://static.realm.io/downloads/$SERVER_DIR/$DOWNLOAD_FILE" -o "$TMP_TAR" ||
die "Downloading $DIR failed. Please try again once you have an Internet connection."
mv "$TMP_TAR" "$TAR"
else
echo "Using cached $CORE_DIR from TMPDIR"
echo "Using cached $DIR from TMPDIR"
fi
(
cd "$TMP_DIR"
rm -rf "$CORE_DIR"
tar -xzf "$CORE_TAR"
mv core "$CORE_DIR-$REALM_CORE_VERSION"
rm -rf "$DIR"
eval "$UNTAR" "$TAR"
mv "$UNTARRED_DIR" "$DIR-$VERSION"
)
rm -rf "$CORE_DIR-$REALM_CORE_VERSION" "$CORE_DIR"
mv "$TMP_DIR/$CORE_DIR-$REALM_CORE_VERSION" .
ln -s "$CORE_DIR-$REALM_CORE_VERSION" "$CORE_DIR"
(
cd vendor
rm -rf "$DIR-$VERSION" "$DIR"
mv "$TMP_DIR/$DIR-$VERSION" .
ln -s "$DIR-$VERSION" "$DIR"
)
}
check_release_notes() {
grep -Fqi "$REALM_CORE_VERSION RELEASE NOTES" "$@"
}
if [ ! -e "$CORE_DIR" ]; then
download_core
elif [ -d "$CORE_DIR" -a -d ../realm-core -a ! -L "$CORE_DIR" ]; then
# Allow newer versions than expected for local builds as testing
# with unreleased versions is one of the reasons to use a local build
if ! check_release_notes "$CORE_DIR/release_notes.txt"; then
die "Local build of core is out of date."
DOWNLOAD_CORE=1
if ! [ -z "$REALM_CORE_PREFIX" ]; then
DOWNLOAD_CORE=0
echo "Skipping the core download because REALM_CORE_PREFIX is defined."
fi
if [ "$ENABLE_SYNC" == 1 ]; then
DOWNLOAD_CORE=0
echo "Skipping the core download because ENABLE_SYNC is true."
fi
if [ "$DOWNLOAD_CORE" == 1 ]; then
if [ ! -e "vendor/$CORE_DIR" ]; then
download_core $CORE_DIR $REALM_CORE_VERSION $CORE_DOWNLOAD_FILE core "tar -xzf" core
elif [ -d "vendor/$CORE_DIR" -a -d ../realm-core -a ! -L "vendor/$CORE_DIR" ]; then
# Allow newer versions than expected for local builds as testing
# with unreleased versions is one of the reasons to use a local build
if ! check_release_notes "vendor/$CORE_DIR/CHANGELOG.txt"; then
die "Local build of core is out of date."
else
echo "The core library seems to be up to date."
fi
elif [ ! -L "vendor/$CORE_DIR" ]; then
echo "vendor/$CORE_DIR is not a symlink. Deleting..."
rm -rf "vendor/$CORE_DIR"
download_core $CORE_DIR $REALM_CORE_VERSION $CORE_DOWNLOAD_FILE core "tar -xzf" core
# With a prebuilt version we only want to check the first non-empty
# line so that checking out an older commit will download the
# appropriate version of core if the already-present version is too new
elif ! grep -m 1 . "vendor/$CORE_DIR/CHANGELOG.txt" | check_release_notes; then
download_core $CORE_DIR $REALM_CORE_VERSION $CORE_DOWNLOAD_FILE core "tar -xzf" core
else
echo "The core library seems to be up to date."
fi
elif [ ! -L "$CORE_DIR" ]; then
echo "$CORE_DIR is not a symlink. Deleting..."
rm -rf "$CORE_DIR"
download_core
# With a prebuilt version we only want to check the first non-empty
# line so that checking out an older commit will download the
# appropriate version of core if the already-present version is too new
elif ! grep -m 1 . "$CORE_DIR/release_notes.txt" | check_release_notes; then
download_core
else
echo "The core library seems to be up to date."
fi
DOWNLOAD_SYNC=1
if ! [ -z "$REALM_SYNC_PREFIX" ]; then
DOWNLOAD_SYNC=0
echo "Skipping the sync download because REALM_SYNC_PREFIX is defined."
fi
if [ "$ENABLE_SYNC" == 0 ]; then
DOWNLOAD_SYNC=0
echo "Skipping the sync download because ENABLE_SYNC is false."
fi
if [ "$DOWNLOAD_SYNC" == 1 ]; then
if [ -n "$SYNC_DOWNLOAD_FILE" ];then
if [ ! -e "vendor/$SYNC_DIR" ]; then
download_core $SYNC_DIR $REALM_SYNC_VERSION $SYNC_DOWNLOAD_FILE sync "$SYNC_EXTRACT" $EXTRACTED_DIR
elif [ -d "vendor/$SYNC_DIR" -a -d ../realm-sync -a ! -L "vendor/$SYNC_DIR" ]; then
# Allow newer versions than expected for local builds as testing
# with unreleased versions is one of the reasons to use a local build
if ! check_release_notes "vendor/$SYNC_DIR/version.txt"; then
die "Local build of sync is out of date."
else
echo "The sync library seems to be up to date."
fi
elif [ ! -L "vendor/$SYNC_DIR" ]; then
echo "vendor/$SYNC_DIR is not a symlink. Deleting..."
rm -rf "vendor/$SYNC_DIR"
download_core $SYNC_DIR $REALM_SYNC_VERSION $SYNC_DOWNLOAD_FILE sync "$SYNC_EXTRACT" $EXTRACTED_DIR
# With a prebuilt version we only want to check the first non-empty
# line so that checking out an older commit will download the
# appropriate version of core if the already-present version is too new
elif ! grep -m 1 . "vendor/$SYNC_DIR/version.txt"; then
download_core $SYNC_DIR $REALM_SYNC_VERSION $SYNC_DOWNLOAD_FILE sync "$SYNC_EXTRACT" $EXTRACTED_DIR
else
echo "The sync library seems to be up to date."
fi
fi
fi

View File

@ -0,0 +1,25 @@
#!/bin/bash
set -eo pipefail
[ "$(uname -s)" != "Darwin" ] && exit
. dependencies.list
if [ -f object-server-for-testing/node_modules/realm-object-server/CHANGELOG.md ]; then
current_version=$(head -n1 object-server-for-testing/node_modules/realm-object-server/CHANGELOG.md | cut -d" " -f2)
if [ "$REALM_OBJECT_SERVER_VERSION" = "$current_version" ]; then
echo -e "yes\n" | object-server-for-testing/reset-server-realms.command
exit
fi
fi
object_server_bundle="realm-object-server-bundled_node_darwin-$REALM_OBJECT_SERVER_VERSION.tar.gz"
curl -f -L "https://static.realm.io/downloads/object-server/$object_server_bundle" -o "$object_server_bundle"
rm -rf object-server-for-testing
mkdir object-server-for-testing
tar -C object-server-for-testing -xf "$object_server_bundle"
rm "$object_server_bundle"
echo -e "enterprise:\n skip_setup: true\n" >> "object-server-for-testing/object-server/configuration.yml"
touch "object-server-for-testing/object-server/do_not_open_browser"

View File

@ -8,5 +8,5 @@ cd "$(dirname "$0")/.."
if [ -n "$REALM_BUILD_ANDROID" ]; then
rm -rf android
(cd react-native/android && ./gradlew publishAndroid)
(cd react-native/android && ./gradlew publishAndroid -PbuildWithSync=true)
fi

View File

@ -3,57 +3,50 @@
set -o pipefail
set -e
export REALM_SWIFT_VERSION=3.0.1
if [[ -z "$DEVELOPER_DIR" ]]; then
export DEVELOPER_DIR="$(xcode-select -p)"
fi
while pgrep -q Simulator; do
# Kill all the current simulator processes as they may be from a
# different Xcode version
pkill Simulator 2>/dev/null || true
# CoreSimulatorService doesn't exit when sent SIGTERM
pkill -9 Simulator 2>/dev/null || true
done
# Shut down simulators until there's no booted ones left
# Only do one at a time because devices sometimes show up multiple times
while xcrun simctl list | grep -q Booted; do
xcrun simctl list | grep Booted | sed 's/.* (\(.*\)) (Booted)/\1/' | head -n 1 | xargs xcrun simctl shutdown
done
# Clean up all available simulators
(
previous_device=''
IFS=$'\n' # make newlines the only separator
for LINE in $(xcrun simctl list); do
if [[ $LINE =~ unavailable || $LINE =~ disconnected ]]; then
# skip unavailable simulators
continue
fi
# Run until we get a result since switching simulator versions often causes CoreSimulatorService to throw an exception.
devices=""
until [ "$devices" != "" ]; do
devices="$(xcrun simctl list devices -j || true)"
done
if [[ $LINE =~ "--" ]]; then
# Reset the last seen device so we won't consider devices with the same name to be duplicates
# if they appear in different sections.
previous_device=""
continue
fi
# Shut down booted simulators
echo "$devices" | ruby -rjson -e 'puts JSON.parse($stdin.read)["devices"].flat_map { |d| d[1] }.select { |d| d["state"] == "Booted" && d["availability"] == "(available)" }.map { |d| d["udid"] }' | while read udid; do
echo "shutting down simulator with ID: $udid"
xcrun simctl shutdown $udid
done
regex='^(.*) [(]([0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12})[)]'
if [[ $LINE =~ $regex ]]; then
device="${BASH_REMATCH[1]}"
guid="${BASH_REMATCH[2]}"
# Erase all available simulators
echo "erasing simulators"
echo "$devices" | ruby -rjson -e 'puts JSON.parse($stdin.read)["devices"].flat_map { |d| d[1] }.select { |d| d["availability"] == "(available)" }.map { |d| d["udid"] }' | while read udid; do
xcrun simctl erase $udid &
done
wait
# Delete the simulator if it's a duplicate of the last seen one
# Otherwise delete all contents and settings for it
if [[ $device == $previous_device ]]; then
xcrun simctl delete $guid
else
xcrun simctl erase $guid
previous_device="$device"
fi
fi
done
)
xcrun simctl boot "iPhone 5" # React Native seems to want to test with this device
if [[ -a "${DEVELOPER_DIR}/Applications/iOS Simulator.app" ]]; then
open "${DEVELOPER_DIR}/Applications/iOS Simulator.app"
elif [[ -a "${DEVELOPER_DIR}/Applications/Simulator.app" ]]; then
if [[ -a "${DEVELOPER_DIR}/Applications/Simulator.app" ]]; then
open "${DEVELOPER_DIR}/Applications/Simulator.app"
fi
# Wait until the boot completes
echo "waiting for simulator to boot..."
until xcrun simctl list devices -j | ruby -rjson -e 'exit JSON.parse($stdin.read)["devices"].flat_map { |d| d[1] }.any? { |d| d["availability"] == "(available)" && d["state"] == "Booted" }'; do
sleep 1
done
# Wait until the simulator is fully booted by waiting for it to launch SpringBoard
xcrun simctl launch booted com.apple.springboard >/dev/null 2>&1 || true
echo "simulator booted"

View File

@ -11,6 +11,7 @@ CONFIGURATION="${2:-"Release"}"
DESTINATION=
PATH="/opt/android-sdk-linux/platform-tools:$PATH"
SRCROOT=$(cd "$(dirname "$0")/.." && pwd)
XCPRETTY=true
# Start current working directory at the root of the project.
cd "$SRCROOT"
@ -23,12 +24,34 @@ if [[ $TARGET = *-android ]]; then
export REALM_BUILD_ANDROID=1
fi
SERVER_PID=0
PACKAGER_OUT="$SRCROOT/packager_out.txt"
LOGCAT_OUT="$SRCROOT/logcat_out.txt"
download_server() {
sh ./scripts/download-object-server.sh
}
start_server() {
sh ./object-server-for-testing/start-object-server.command &
SERVER_PID=$!
}
stop_server() {
if [[ ${SERVER_PID} > 0 ]] ; then
kill ${SERVER_PID}
fi
}
cleanup() {
# Kill all child processes.
# Kill started object server
stop_server
# Kill all other child processes.
pkill -P $$ || true
# Kill react native packager
pkill node || true
rm -f "$PACKAGER_OUT" "$LOGCAT_OUT"
}
@ -58,13 +81,15 @@ start_packager() {
}
xctest() {
${SRCROOT}/scripts/reset-simulators.sh
local dest="$(xcrun simctl list devices | grep -v unavailable | grep -m 1 -o '[0-9A-F\-]\{36\}')"
if [ -n "$XCPRETTY" ]; then
xcodebuild -scheme "$1" -configuration "$CONFIGURATION" -sdk iphonesimulator -destination id="$dest" test | xcpretty -c --no-utf --report junit --output build/reports/junit.xml
mkdir -p build
xcodebuild -scheme "$1" -configuration "$CONFIGURATION" -sdk iphonesimulator -destination id="$dest" build test | tee build/build.log | xcpretty -c --no-utf --report junit --output build/reports/junit.xml || {
echo "The raw xcodebuild output is available in build/build.log"
exit 1
}
else
xcodebuild -scheme "$1" -configuration "$CONFIGURATION" -sdk iphonesimulator -destination id="$dest" test
xcodebuild -scheme "$1" -configuration "$CONFIGURATION" -sdk iphonesimulator -destination id="$dest" build test
fi
}
@ -75,9 +100,12 @@ trap cleanup EXIT
# Use a consistent version of Node if possible.
if [ -s "${HOME}/.nvm/nvm.sh" ]; then
. "${HOME}/.nvm/nvm.sh"
nvm use 5.4.0 || true
nvm use 4.4.7 || true
fi
# Remove cached packages
rm -rf ~/.yarn-cache/npm-realm-*
case "$TARGET" in
"eslint")
[[ $CONFIGURATION == 'Debug' ]] && exit 0
@ -94,35 +122,36 @@ case "$TARGET" in
xctest RealmJS
;;
"react-tests")
if ! [ -z "${JENKINS_HOME}" ]; then
${SRCROOT}/scripts/reset-simulators.sh
fi
pushd tests/react-test-app
if [ -f ../../target=node_modules/react_tests_node_modules.zip ]; then
unzip -q ../../target=node_modules/react_tests_node_modules.zip
fi
npm install
open_chrome
start_packager
pushd ios
xctest ReactTestApp
xctest ReactTestApp || xctest ReactTestApp
;;
"react-example")
pushd examples/ReactExample
if [ -f ../../target=node_modules/react_example_node_modules.zip ]; then
unzip -q ../../target=node_modules/react_example_node_modules.zip
if ! [ -z "${JENKINS_HOME}" ]; then
${SRCROOT}/scripts/reset-simulators.sh
fi
pushd examples/ReactExample
npm install
open_chrome
start_packager
pushd ios
xctest ReactExample
xctest ReactExample || xctest ReactExample
;;
"react-tests-android")
[[ $CONFIGURATION == 'Debug' ]] && exit 0
XCPRETTY=false
pushd tests/react-test-app
@ -154,20 +183,40 @@ case "$TARGET" in
cat tests.xml
;;
"node")
npm install
scripts/download-core.sh node
src/node/build-node.sh $CONFIGURATION
if [ "$(uname)" = 'Darwin' ]; then
download_server
start_server
npm_tests_cmd="npm run test"
npm install --build-from-source --realm_enable_sync
else
npm_tests_cmd="npm run test-nosync"
npm install --build-from-source
fi
# Change to a temp directory.
cd "$(mktemp -q -d -t realm.node.XXXXXX)"
trap "rm -rf '$PWD'" EXIT
node "$SRCROOT/tests"
pushd "$SRCROOT/tests"
npm install
eval $npm_tests_cmd
popd
stop_server
;;
"node-nosync")
npm install --build-from-source
# Change to a temp directory.
cd "$(mktemp -q -d -t realm.node.XXXXXX)"
trap "rm -rf '$PWD'" EXIT
pushd "$SRCROOT/tests"
npm install
npm run test-nosync
popd
;;
"test-runners")
npm install
scripts/download-core.sh node
src/node/build-node.sh $CONFIGURATION
npm install --build-from-source
for runner in ava mocha jest; do
pushd "$SRCROOT/tests/test-runners/$runner"
@ -181,6 +230,27 @@ case "$TARGET" in
cmake -DCMAKE_BUILD_TYPE=$CONFIGURATION .
make run-tests
;;
"download-object-server")
. dependencies.list
object_server_bundle="realm-object-server-bundled_node_darwin-$REALM_OBJECT_SERVER_VERSION.tar.gz"
curl -f -L "https://static.realm.io/downloads/object-server/$object_server_bundle" -o "$object_server_bundle"
rm -rf tests/sync-bundle
mkdir -p tests/sync-bundle
tar -C tests/sync-bundle -xf "$object_server_bundle"
rm "$object_server_bundle"
echo -e "enterprise:\n skip_setup: true\n" >> "tests/sync-bundle/object-server/configuration.yml"
touch "tests/sync-bundle/object-server/do_not_open_browser"
;;
"object-server-integration")
echo -e "yes\n" | ./tests/sync-bundle/reset-server-realms.command
pushd "$SRCROOT/tests"
npm install
npm run test-sync-integration
popd
;;
*)
echo "Invalid target '${TARGET}'"
exit 1

View File

@ -7,6 +7,13 @@
objects = {
/* Begin PBXBuildFile section */
02022A581DA476CD000F0C4F /* external_commit_helper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02022A411DA47489000F0C4F /* external_commit_helper.cpp */; };
02022A5A1DA476CD000F0C4F /* weak_realm_notifier.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02022A4A1DA475A9000F0C4F /* weak_realm_notifier.cpp */; };
02022A5B1DA476CD000F0C4F /* placeholder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02022A4C1DA475C0000F0C4F /* placeholder.cpp */; };
02022A671DA47BD7000F0C4F /* parser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02022A611DA47B8B000F0C4F /* parser.cpp */; };
02022A681DA47BD7000F0C4F /* query_builder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02022A631DA47B8B000F0C4F /* query_builder.cpp */; };
02022A7C1DA47EC8000F0C4F /* format.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02022A731DA47EC8000F0C4F /* format.cpp */; };
02022A7D1DA47EC8000F0C4F /* thread_id.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02022A791DA47EC8000F0C4F /* thread_id.cpp */; };
02409DC21BCF11D6005F3B3E /* RealmJSCoreTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 02409DC11BCF11D6005F3B3E /* RealmJSCoreTests.m */; };
02414B881CE68CA200A8669F /* dates-v5.realm in Resources */ = {isa = PBXBuildFile; fileRef = 02414B871CE68CA200A8669F /* dates-v5.realm */; };
02414BA51CE6ABCF00A8669F /* collection_change_builder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02414B991CE6AAEF00A8669F /* collection_change_builder.cpp */; };
@ -18,7 +25,12 @@
027A23131CD3E379000543AE /* libRealmJS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F63FF2B11C1241E500B3B8E0 /* libRealmJS.a */; };
02D041F71CE11159000E4250 /* dates-v3.realm in Resources */ = {isa = PBXBuildFile; fileRef = 02D041F61CE11159000E4250 /* dates-v3.realm */; };
02D8D1F71B601984006DB49D /* JavaScriptCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 02B58CCD1AE99D4D009B348C /* JavaScriptCore.framework */; };
02E008D51D10ABB600F3AA37 /* format.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02E008D21D10AB1B00F3AA37 /* format.cpp */; };
02E315C91DB80DDD00555337 /* sync_manager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02E315C31DB80DDD00555337 /* sync_manager.cpp */; };
02E315CA1DB80DDD00555337 /* sync_session.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02E315C51DB80DDD00555337 /* sync_session.cpp */; };
02E315CB1DB80DDD00555337 /* sync_user.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02E315C71DB80DDD00555337 /* sync_user.cpp */; };
02E315D21DB80DF200555337 /* sync_file.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02E315CE1DB80DF200555337 /* sync_file.cpp */; };
02E315D31DB80DF200555337 /* sync_metadata.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02E315D01DB80DF200555337 /* sync_metadata.cpp */; };
02E315E01DB8233E00555337 /* keychain_helper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02E315DE1DB8233E00555337 /* keychain_helper.cpp */; };
02F59EBF1C88F17D007F774C /* index_set.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02F59EAF1C88F17D007F774C /* index_set.cpp */; };
02F59EC01C88F17D007F774C /* list.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02F59EB11C88F17D007F774C /* list.cpp */; };
02F59EC11C88F17D007F774C /* object_schema.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02F59EB41C88F17D007F774C /* object_schema.cpp */; };
@ -26,34 +38,12 @@
02F59EC31C88F17D007F774C /* results.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02F59EB91C88F17D007F774C /* results.cpp */; };
02F59EC41C88F17D007F774C /* schema.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02F59EBB1C88F17D007F774C /* schema.cpp */; };
02F59EC51C88F17D007F774C /* shared_realm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02F59EBD1C88F17D007F774C /* shared_realm.cpp */; };
02F59ECA1C88F190007F774C /* parser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02F59EC61C88F190007F774C /* parser.cpp */; };
02F59ECB1C88F190007F774C /* query_builder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02F59EC81C88F190007F774C /* query_builder.cpp */; };
02F59ED41C88F1B6007F774C /* external_commit_helper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02F59ECF1C88F1B6007F774C /* external_commit_helper.cpp */; };
02F59ED51C88F1B6007F774C /* weak_realm_notifier.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02F59ED11C88F1B6007F774C /* weak_realm_notifier.cpp */; };
02F59EE21C88F2BB007F774C /* realm_coordinator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02F59EDB1C88F2BA007F774C /* realm_coordinator.cpp */; };
02F59EE31C88F2BB007F774C /* transact_log_handler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02F59EDD1C88F2BB007F774C /* transact_log_handler.cpp */; };
5D25F5A11D6284FD00EBBB30 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = F63FF3301C16434400B3B8E0 /* libz.tbd */; };
5DC74A781D623C9800D77A4F /* handover.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5DC74A751D623C8700D77A4F /* handover.cpp */; };
5DC74A791D623CA200D77A4F /* handover.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5DC74A751D623C8700D77A4F /* handover.cpp */; };
5DC74A7A1D623CA800D77A4F /* thread_confined.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5DC74A721D623C7A00D77A4F /* thread_confined.cpp */; };
5DC74A7B1D623CA800D77A4F /* thread_confined.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5DC74A721D623C7A00D77A4F /* thread_confined.cpp */; };
F60102D31CBB966E00EC01BA /* js_realm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 029048071C0428DF00ABDED4 /* js_realm.cpp */; };
F60102D41CBB96AB00EC01BA /* index_set.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02F59EAF1C88F17D007F774C /* index_set.cpp */; };
F60102D51CBB96AE00EC01BA /* list.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02F59EB11C88F17D007F774C /* list.cpp */; };
F60102D61CBB96B400EC01BA /* object_schema.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02F59EB41C88F17D007F774C /* object_schema.cpp */; };
F60102D71CBB96B800EC01BA /* object_store.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02F59EB61C88F17D007F774C /* object_store.cpp */; };
F60102D81CBB96BD00EC01BA /* results.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02F59EB91C88F17D007F774C /* results.cpp */; };
F60102D91CBB96C100EC01BA /* schema.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02F59EBB1C88F17D007F774C /* schema.cpp */; };
F60102DA1CBB96C300EC01BA /* shared_realm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02F59EBD1C88F17D007F774C /* shared_realm.cpp */; };
F60102DB1CBB96C600EC01BA /* parser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02F59EC61C88F190007F774C /* parser.cpp */; };
F60102DC1CBB96C900EC01BA /* query_builder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02F59EC81C88F190007F774C /* query_builder.cpp */; };
F60102DD1CBB96CC00EC01BA /* external_commit_helper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02F59ECF1C88F1B6007F774C /* external_commit_helper.cpp */; };
F60102E01CBB96D900EC01BA /* realm_coordinator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02F59EDB1C88F2BA007F774C /* realm_coordinator.cpp */; };
F60102E11CBB96DD00EC01BA /* transact_log_handler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02F59EDD1C88F2BB007F774C /* transact_log_handler.cpp */; };
F60102EA1CBCAFC300EC01BA /* node_dummy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F6267BCA1CADC49200AC36B1 /* node_dummy.cpp */; };
F61378791C18EAC5008BFC51 /* js in Resources */ = {isa = PBXBuildFile; fileRef = F61378781C18EAAC008BFC51 /* js */; };
F620F0581CB766DA0082977B /* node_init.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F620F0571CB766DA0082977B /* node_init.cpp */; };
F620F0751CB9F60C0082977B /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F620F0741CB9F60C0082977B /* CoreFoundation.framework */; };
F63117F01CEB0D5F00ECB2DE /* weak_realm_notifier.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F63117EE1CEB0D5900ECB2DE /* weak_realm_notifier.cpp */; };
F63FF2C61C12469E00B3B8E0 /* jsc_init.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 029048011C0428DF00ABDED4 /* jsc_init.cpp */; };
F63FF2C91C12469E00B3B8E0 /* js_realm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 029048071C0428DF00ABDED4 /* js_realm.cpp */; };
F63FF2CD1C12469E00B3B8E0 /* rpc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0290480F1C0428DF00ABDED4 /* rpc.cpp */; };
@ -72,17 +62,8 @@
F63FF3251C1642BB00B3B8E0 /* GCDWebServerErrorResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = F63FF3161C1642BB00B3B8E0 /* GCDWebServerErrorResponse.m */; };
F63FF3261C1642BB00B3B8E0 /* GCDWebServerFileResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = F63FF3181C1642BB00B3B8E0 /* GCDWebServerFileResponse.m */; };
F63FF3271C1642BB00B3B8E0 /* GCDWebServerStreamedResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = F63FF31A1C1642BB00B3B8E0 /* GCDWebServerStreamedResponse.m */; };
F64A059B1D10D928004ACDBE /* format.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02E008D21D10AB1B00F3AA37 /* format.cpp */; };
F64A059D1D13710C004ACDBE /* thread_id.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F64A059C1D13710C004ACDBE /* thread_id.cpp */; };
F64A059E1D13710C004ACDBE /* thread_id.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F64A059C1D13710C004ACDBE /* thread_id.cpp */; };
F674784A1CC81F1900F9273C /* platform.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F67478481CC81F1300F9273C /* platform.cpp */; };
F68A278C1BC2722A0063D40A /* RJSModuleLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = F68A278B1BC2722A0063D40A /* RJSModuleLoader.m */; };
F6BCCFE21C8380A400FE31AE /* lib in Resources */ = {isa = PBXBuildFile; fileRef = F6BCCFDF1C83809A00FE31AE /* lib */; };
F6E931BA1CFEAE170016AF14 /* collection_notifications.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02414B961CE6AADD00A8669F /* collection_notifications.cpp */; };
F6E931BB1CFEAE310016AF14 /* collection_change_builder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02414B991CE6AAEF00A8669F /* collection_change_builder.cpp */; };
F6E931BC1CFEAE340016AF14 /* collection_notifier.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02414B9B1CE6AAEF00A8669F /* collection_notifier.cpp */; };
F6E931BD1CFEAE370016AF14 /* list_notifier.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02414B9D1CE6AAEF00A8669F /* list_notifier.cpp */; };
F6E931BE1CFEAE3A0016AF14 /* results_notifier.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02414B9F1CE6AAEF00A8669F /* results_notifier.cpp */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
@ -98,6 +79,27 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
02022A411DA47489000F0C4F /* external_commit_helper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = external_commit_helper.cpp; sourceTree = "<group>"; };
02022A421DA47489000F0C4F /* external_commit_helper.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = external_commit_helper.hpp; sourceTree = "<group>"; };
02022A481DA474A7000F0C4F /* weak_realm_notifier.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = weak_realm_notifier.hpp; sourceTree = "<group>"; };
02022A4A1DA475A9000F0C4F /* weak_realm_notifier.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = weak_realm_notifier.cpp; sourceTree = "<group>"; };
02022A4C1DA475C0000F0C4F /* placeholder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = placeholder.cpp; path = src/placeholder.cpp; sourceTree = "<group>"; };
02022A611DA47B8B000F0C4F /* parser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = parser.cpp; sourceTree = "<group>"; };
02022A621DA47B8B000F0C4F /* parser.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = parser.hpp; sourceTree = "<group>"; };
02022A631DA47B8B000F0C4F /* query_builder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = query_builder.cpp; sourceTree = "<group>"; };
02022A641DA47B8B000F0C4F /* query_builder.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = query_builder.hpp; sourceTree = "<group>"; };
02022A6D1DA47EC8000F0C4F /* event_loop_signal.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = event_loop_signal.hpp; sourceTree = "<group>"; };
02022A6F1DA47EC8000F0C4F /* event_loop_signal.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = event_loop_signal.hpp; sourceTree = "<group>"; };
02022A701DA47EC8000F0C4F /* atomic_shared_ptr.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = atomic_shared_ptr.hpp; sourceTree = "<group>"; };
02022A711DA47EC8000F0C4F /* compiler.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = compiler.hpp; sourceTree = "<group>"; };
02022A721DA47EC8000F0C4F /* event_loop_signal.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = event_loop_signal.hpp; sourceTree = "<group>"; };
02022A731DA47EC8000F0C4F /* format.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = format.cpp; sourceTree = "<group>"; };
02022A741DA47EC8000F0C4F /* format.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = format.hpp; sourceTree = "<group>"; };
02022A761DA47EC8000F0C4F /* event_loop_signal.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = event_loop_signal.hpp; sourceTree = "<group>"; };
02022A781DA47EC8000F0C4F /* event_loop_signal.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = event_loop_signal.hpp; sourceTree = "<group>"; };
02022A791DA47EC8000F0C4F /* thread_id.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = thread_id.cpp; sourceTree = "<group>"; };
02022A7A1DA47EC8000F0C4F /* thread_id.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = thread_id.hpp; sourceTree = "<group>"; };
02022A7B1DA47EC8000F0C4F /* thread_local.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = thread_local.hpp; sourceTree = "<group>"; };
02409DC11BCF11D6005F3B3E /* RealmJSCoreTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RealmJSCoreTests.m; path = ios/RealmJSCoreTests.m; sourceTree = "<group>"; };
02414B871CE68CA200A8669F /* dates-v5.realm */ = {isa = PBXFileReference; lastKnownFileType = file; path = "dates-v5.realm"; sourceTree = "<group>"; };
02414B961CE6AADD00A8669F /* collection_notifications.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = collection_notifications.cpp; path = src/collection_notifications.cpp; sourceTree = "<group>"; };
@ -110,11 +112,13 @@
02414B9E1CE6AAEF00A8669F /* list_notifier.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = list_notifier.hpp; sourceTree = "<group>"; };
02414B9F1CE6AAEF00A8669F /* results_notifier.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = results_notifier.cpp; sourceTree = "<group>"; };
02414BA01CE6AAEF00A8669F /* results_notifier.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = results_notifier.hpp; sourceTree = "<group>"; };
0250D9C01D7647E00012C20C /* js_sync.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = js_sync.hpp; sourceTree = "<group>"; };
025678951CAB392000FB8501 /* jsc_types.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = jsc_types.hpp; sourceTree = "<group>"; };
0270BC5A1B7CFC1300010E03 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
0270BC781B7D020100010E03 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = ios/Info.plist; sourceTree = "<group>"; };
0270BC7A1B7D020100010E03 /* RealmJSTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RealmJSTests.h; path = ios/RealmJSTests.h; sourceTree = "<group>"; };
0270BC7B1B7D020100010E03 /* RealmJSTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = RealmJSTests.mm; path = ios/RealmJSTests.mm; sourceTree = "<group>"; };
02879D8B1DC29D5600777A5D /* package.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = package.json; path = ../package.json; sourceTree = "<group>"; };
029048011C0428DF00ABDED4 /* jsc_init.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = jsc_init.cpp; sourceTree = "<group>"; };
029048021C0428DF00ABDED4 /* jsc_init.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = jsc_init.h; sourceTree = "<group>"; };
029048041C0428DF00ABDED4 /* js_list.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = js_list.hpp; sourceTree = "<group>"; };
@ -127,13 +131,25 @@
029048101C0428DF00ABDED4 /* rpc.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = rpc.hpp; sourceTree = "<group>"; };
029048351C042A3C00ABDED4 /* platform.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = platform.hpp; sourceTree = "<group>"; };
029048381C042A8F00ABDED4 /* platform.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = platform.mm; sourceTree = "<group>"; };
0290934A1CEFA9170009769E /* js_observable.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = js_observable.hpp; sourceTree = "<group>"; };
02A3C7A41BC4341500B1A7BE /* libc++.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = "libc++.tbd"; path = "usr/lib/libc++.tbd"; sourceTree = SDKROOT; };
02B58CBC1AE99CEC009B348C /* RealmJSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RealmJSTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
02B58CCD1AE99D4D009B348C /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
02D041F61CE11159000E4250 /* dates-v3.realm */ = {isa = PBXFileReference; lastKnownFileType = file; path = "dates-v3.realm"; sourceTree = "<group>"; };
02E008D11D10AB1B00F3AA37 /* atomic_shared_ptr.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = atomic_shared_ptr.hpp; sourceTree = "<group>"; };
02E008D21D10AB1B00F3AA37 /* format.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = format.cpp; sourceTree = "<group>"; };
02E008D31D10AB1B00F3AA37 /* format.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = format.hpp; sourceTree = "<group>"; };
02E315C21DB80DDD00555337 /* sync_config.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = sync_config.hpp; path = src/sync/sync_config.hpp; sourceTree = "<group>"; };
02E315C31DB80DDD00555337 /* sync_manager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = sync_manager.cpp; path = src/sync/sync_manager.cpp; sourceTree = "<group>"; };
02E315C41DB80DDD00555337 /* sync_manager.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = sync_manager.hpp; path = src/sync/sync_manager.hpp; sourceTree = "<group>"; };
02E315C51DB80DDD00555337 /* sync_session.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = sync_session.cpp; path = src/sync/sync_session.cpp; sourceTree = "<group>"; };
02E315C61DB80DDD00555337 /* sync_session.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = sync_session.hpp; path = src/sync/sync_session.hpp; sourceTree = "<group>"; };
02E315C71DB80DDD00555337 /* sync_user.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = sync_user.cpp; path = src/sync/sync_user.cpp; sourceTree = "<group>"; };
02E315C81DB80DDD00555337 /* sync_user.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = sync_user.hpp; path = src/sync/sync_user.hpp; sourceTree = "<group>"; };
02E315CD1DB80DF200555337 /* sync_client.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = sync_client.hpp; path = src/sync/impl/sync_client.hpp; sourceTree = "<group>"; };
02E315CE1DB80DF200555337 /* sync_file.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = sync_file.cpp; path = src/sync/impl/sync_file.cpp; sourceTree = "<group>"; };
02E315CF1DB80DF200555337 /* sync_file.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = sync_file.hpp; path = src/sync/impl/sync_file.hpp; sourceTree = "<group>"; };
02E315D01DB80DF200555337 /* sync_metadata.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = sync_metadata.cpp; path = src/sync/impl/sync_metadata.cpp; sourceTree = "<group>"; };
02E315D11DB80DF200555337 /* sync_metadata.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = sync_metadata.hpp; path = src/sync/impl/sync_metadata.hpp; sourceTree = "<group>"; };
02E315DE1DB8233E00555337 /* keychain_helper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = keychain_helper.cpp; sourceTree = "<group>"; };
02E315DF1DB8233E00555337 /* keychain_helper.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = keychain_helper.hpp; sourceTree = "<group>"; };
02F59EAE1C88F17D007F774C /* binding_context.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = binding_context.hpp; path = src/binding_context.hpp; sourceTree = "<group>"; };
02F59EAF1C88F17D007F774C /* index_set.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = index_set.cpp; path = src/index_set.cpp; sourceTree = "<group>"; };
02F59EB01C88F17D007F774C /* index_set.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = index_set.hpp; path = src/index_set.hpp; sourceTree = "<group>"; };
@ -151,26 +167,15 @@
02F59EBC1C88F17D007F774C /* schema.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = schema.hpp; path = src/schema.hpp; sourceTree = "<group>"; };
02F59EBD1C88F17D007F774C /* shared_realm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = shared_realm.cpp; path = src/shared_realm.cpp; sourceTree = "<group>"; };
02F59EBE1C88F17D007F774C /* shared_realm.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = shared_realm.hpp; path = src/shared_realm.hpp; sourceTree = "<group>"; };
02F59EC61C88F190007F774C /* parser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = parser.cpp; sourceTree = "<group>"; };
02F59EC71C88F190007F774C /* parser.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = parser.hpp; sourceTree = "<group>"; };
02F59EC81C88F190007F774C /* query_builder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = query_builder.cpp; sourceTree = "<group>"; };
02F59EC91C88F190007F774C /* query_builder.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = query_builder.hpp; sourceTree = "<group>"; };
02F59ECF1C88F1B6007F774C /* external_commit_helper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = external_commit_helper.cpp; sourceTree = "<group>"; };
02F59ED01C88F1B6007F774C /* external_commit_helper.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = external_commit_helper.hpp; sourceTree = "<group>"; };
02F59ED11C88F1B6007F774C /* weak_realm_notifier.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = weak_realm_notifier.cpp; sourceTree = "<group>"; };
02F59ED21C88F1B6007F774C /* weak_realm_notifier.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = weak_realm_notifier.hpp; sourceTree = "<group>"; };
02F59EDA1C88F2BA007F774C /* external_commit_helper.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = external_commit_helper.hpp; sourceTree = "<group>"; };
02F59EDB1C88F2BA007F774C /* realm_coordinator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = realm_coordinator.cpp; sourceTree = "<group>"; };
02F59EDC1C88F2BB007F774C /* realm_coordinator.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = realm_coordinator.hpp; sourceTree = "<group>"; };
02F59EDD1C88F2BB007F774C /* transact_log_handler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = transact_log_handler.cpp; sourceTree = "<group>"; };
02F59EDE1C88F2BB007F774C /* transact_log_handler.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = transact_log_handler.hpp; sourceTree = "<group>"; };
02F59EDF1C88F2BB007F774C /* weak_realm_notifier_base.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = weak_realm_notifier_base.hpp; sourceTree = "<group>"; };
02F59EE01C88F2BB007F774C /* weak_realm_notifier.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = weak_realm_notifier.hpp; sourceTree = "<group>"; };
5DC74A721D623C7A00D77A4F /* thread_confined.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = thread_confined.cpp; path = src/thread_confined.cpp; sourceTree = "<group>"; };
5DC74A731D623C7A00D77A4F /* thread_confined.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = thread_confined.hpp; path = src/thread_confined.hpp; sourceTree = "<group>"; };
5DC74A751D623C8700D77A4F /* handover.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = handover.cpp; sourceTree = "<group>"; };
5DC74A761D623C8700D77A4F /* handover.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = handover.hpp; sourceTree = "<group>"; };
5DC74A7C1D623CED00D77A4F /* compiler.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = compiler.hpp; sourceTree = "<group>"; };
F60102CF1CBB814A00EC01BA /* node_init.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = node_init.hpp; sourceTree = "<group>"; };
F60102D11CBB865A00EC01BA /* jsc_init.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = jsc_init.hpp; sourceTree = "<group>"; };
F60102E31CBBB19700EC01BA /* node_object_accessor.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = node_object_accessor.hpp; sourceTree = "<group>"; };
@ -200,19 +205,12 @@
F620F0571CB766DA0082977B /* node_init.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = node_init.cpp; sourceTree = "<group>"; };
F620F0591CB7B4C80082977B /* js_object_accessor.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = js_object_accessor.hpp; sourceTree = "<group>"; };
F620F0741CB9F60C0082977B /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/System/Library/Frameworks/CoreFoundation.framework; sourceTree = DEVELOPER_DIR; };
F6242B291D08EE9600BE1E03 /* thread_id.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = thread_id.hpp; sourceTree = "<group>"; };
F6267BC91CADC30000AC36B1 /* js_util.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = js_util.hpp; sourceTree = "<group>"; };
F6267BCA1CADC49200AC36B1 /* node_dummy.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = node_dummy.cpp; sourceTree = "<group>"; };
F62BF8FB1CAC71780022BCDC /* libRealmNode.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libRealmNode.dylib; sourceTree = BUILT_PRODUCTS_DIR; };
F63117EE1CEB0D5900ECB2DE /* weak_realm_notifier.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = weak_realm_notifier.cpp; sourceTree = "<group>"; };
F63117EF1CEB0D5900ECB2DE /* weak_realm_notifier.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = weak_realm_notifier.hpp; sourceTree = "<group>"; };
F63118431CEBA7B700ECB2DE /* external_commit_helper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = external_commit_helper.cpp; sourceTree = "<group>"; };
F63118441CEBA7B700ECB2DE /* external_commit_helper.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = external_commit_helper.hpp; sourceTree = "<group>"; };
F63118451CEBA7B700ECB2DE /* weak_realm_notifier.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = weak_realm_notifier.cpp; sourceTree = "<group>"; };
F63118461CEBA7B700ECB2DE /* weak_realm_notifier.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = weak_realm_notifier.hpp; sourceTree = "<group>"; };
F631184A1CEBA7D800ECB2DE /* external_commit_helper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = external_commit_helper.cpp; sourceTree = "<group>"; };
F631184B1CEBA7D800ECB2DE /* external_commit_helper.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = external_commit_helper.hpp; sourceTree = "<group>"; };
F631184C1CEBA7D800ECB2DE /* weak_realm_notifier.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = weak_realm_notifier.hpp; sourceTree = "<group>"; };
F63FF2B11C1241E500B3B8E0 /* libRealmJS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRealmJS.a; sourceTree = BUILT_PRODUCTS_DIR; };
F63FF2F01C16405C00B3B8E0 /* libGCDWebServers.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libGCDWebServers.a; sourceTree = BUILT_PRODUCTS_DIR; };
F63FF2FD1C1642BB00B3B8E0 /* GCDWebServer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDWebServer.h; sourceTree = "<group>"; };
@ -248,8 +246,6 @@
F63FF32C1C16432E00B3B8E0 /* CFNetwork.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CFNetwork.framework; path = System/Library/Frameworks/CFNetwork.framework; sourceTree = SDKROOT; };
F63FF32E1C16433900B3B8E0 /* libxml2.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libxml2.tbd; path = usr/lib/libxml2.tbd; sourceTree = SDKROOT; };
F63FF3301C16434400B3B8E0 /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; };
F64A059C1D13710C004ACDBE /* thread_id.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = thread_id.cpp; sourceTree = "<group>"; };
F64A059F1D13716B004ACDBE /* thread_local.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = thread_local.hpp; sourceTree = "<group>"; };
F67478481CC81F1300F9273C /* platform.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = platform.cpp; sourceTree = "<group>"; };
F6874A351CAC792D00EEEE36 /* node_types.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = node_types.hpp; sourceTree = "<group>"; };
F6874A3E1CACA5A900EEEE36 /* js_types.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = js_types.hpp; sourceTree = "<group>"; };
@ -266,19 +262,12 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
5D25F5A11D6284FD00EBBB30 /* libz.tbd in Frameworks */,
027A23131CD3E379000543AE /* libRealmJS.a in Frameworks */,
02D8D1F71B601984006DB49D /* JavaScriptCore.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
F62BF8F81CAC71780022BCDC /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
F620F0751CB9F60C0082977B /* CoreFoundation.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
F63FF2ED1C16405C00B3B8E0 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
@ -289,19 +278,74 @@
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
02022A6B1DA47EC8000F0C4F /* util */ = {
isa = PBXGroup;
children = (
02022A6C1DA47EC8000F0C4F /* android */,
02022A6E1DA47EC8000F0C4F /* apple */,
02022A701DA47EC8000F0C4F /* atomic_shared_ptr.hpp */,
02022A711DA47EC8000F0C4F /* compiler.hpp */,
02022A721DA47EC8000F0C4F /* event_loop_signal.hpp */,
02022A731DA47EC8000F0C4F /* format.cpp */,
02022A741DA47EC8000F0C4F /* format.hpp */,
02022A751DA47EC8000F0C4F /* generic */,
02022A771DA47EC8000F0C4F /* node */,
02022A791DA47EC8000F0C4F /* thread_id.cpp */,
02022A7A1DA47EC8000F0C4F /* thread_id.hpp */,
02022A7B1DA47EC8000F0C4F /* thread_local.hpp */,
);
name = util;
path = src/util;
sourceTree = "<group>";
};
02022A6C1DA47EC8000F0C4F /* android */ = {
isa = PBXGroup;
children = (
02022A6D1DA47EC8000F0C4F /* event_loop_signal.hpp */,
);
path = android;
sourceTree = "<group>";
};
02022A6E1DA47EC8000F0C4F /* apple */ = {
isa = PBXGroup;
children = (
02022A6F1DA47EC8000F0C4F /* event_loop_signal.hpp */,
);
path = apple;
sourceTree = "<group>";
};
02022A751DA47EC8000F0C4F /* generic */ = {
isa = PBXGroup;
children = (
02022A761DA47EC8000F0C4F /* event_loop_signal.hpp */,
);
path = generic;
sourceTree = "<group>";
};
02022A771DA47EC8000F0C4F /* node */ = {
isa = PBXGroup;
children = (
02022A781DA47EC8000F0C4F /* event_loop_signal.hpp */,
);
path = node;
sourceTree = "<group>";
};
0270BC3D1B7CFBFD00010E03 /* RealmJS */ = {
isa = PBXGroup;
children = (
F6BCCFDF1C83809A00FE31AE /* lib */,
02879D8B1DC29D5600777A5D /* package.json */,
F62A35131C18E6E2004A917D /* iOS */,
F6874A441CAD2ACD00EEEE36 /* JSC */,
F62BF9001CAC72C40022BCDC /* Node */,
F62A35141C18E783004A917D /* Object Store */,
0290934A1CEFA9170009769E /* js_observable.hpp */,
F60102F71CBDA6D400EC01BA /* js_collection.hpp */,
029048041C0428DF00ABDED4 /* js_list.hpp */,
029048061C0428DF00ABDED4 /* js_realm_object.hpp */,
029048071C0428DF00ABDED4 /* js_realm.cpp */,
029048081C0428DF00ABDED4 /* js_realm.hpp */,
0250D9C01D7647E00012C20C /* js_sync.hpp */,
0290480A1C0428DF00ABDED4 /* js_results.hpp */,
0290480C1C0428DF00ABDED4 /* js_schema.hpp */,
F620F0521CAF0B600082977B /* js_class.hpp */,
@ -333,7 +377,6 @@
02B58CBC1AE99CEC009B348C /* RealmJSTests.xctest */,
F63FF2B11C1241E500B3B8E0 /* libRealmJS.a */,
F63FF2F01C16405C00B3B8E0 /* libGCDWebServers.a */,
F62BF8FB1CAC71780022BCDC /* libRealmNode.dylib */,
);
name = Products;
sourceTree = "<group>";
@ -378,19 +421,23 @@
path = data;
sourceTree = "<group>";
};
02E008D01D10AB1B00F3AA37 /* util */ = {
02E315CC1DB80DE000555337 /* sync */ = {
isa = PBXGroup;
children = (
02E008D11D10AB1B00F3AA37 /* atomic_shared_ptr.hpp */,
5DC74A7C1D623CED00D77A4F /* compiler.hpp */,
02E008D21D10AB1B00F3AA37 /* format.cpp */,
02E008D31D10AB1B00F3AA37 /* format.hpp */,
F6242B291D08EE9600BE1E03 /* thread_id.hpp */,
F64A059C1D13710C004ACDBE /* thread_id.cpp */,
F64A059F1D13716B004ACDBE /* thread_local.hpp */,
02E315CD1DB80DF200555337 /* sync_client.hpp */,
02E315CE1DB80DF200555337 /* sync_file.cpp */,
02E315CF1DB80DF200555337 /* sync_file.hpp */,
02E315D01DB80DF200555337 /* sync_metadata.cpp */,
02E315D11DB80DF200555337 /* sync_metadata.hpp */,
02E315C21DB80DDD00555337 /* sync_config.hpp */,
02E315C31DB80DDD00555337 /* sync_manager.cpp */,
02E315C41DB80DDD00555337 /* sync_manager.hpp */,
02E315C51DB80DDD00555337 /* sync_session.cpp */,
02E315C61DB80DDD00555337 /* sync_session.hpp */,
02E315C71DB80DDD00555337 /* sync_user.cpp */,
02E315C81DB80DDD00555337 /* sync_user.hpp */,
);
name = util;
path = src/util;
name = sync;
sourceTree = "<group>";
};
F62A35131C18E6E2004A917D /* iOS */ = {
@ -406,7 +453,8 @@
F62A35141C18E783004A917D /* Object Store */ = {
isa = PBXGroup;
children = (
02E008D01D10AB1B00F3AA37 /* util */,
02E315CC1DB80DE000555337 /* sync */,
02022A6B1DA47EC8000F0C4F /* util */,
F63117EA1CEB0BFA00ECB2DE /* impl */,
F63117EC1CEB0C8100ECB2DE /* parser */,
02414B961CE6AADD00A8669F /* collection_notifications.cpp */,
@ -421,6 +469,7 @@
02F59EB51C88F17D007F774C /* object_schema.hpp */,
02F59EB61C88F17D007F774C /* object_store.cpp */,
02F59EB71C88F17D007F774C /* object_store.hpp */,
02022A4C1DA475C0000F0C4F /* placeholder.cpp */,
02F59EB81C88F17D007F774C /* property.hpp */,
02F59EB91C88F17D007F774C /* results.cpp */,
02F59EBA1C88F17D007F774C /* results.hpp */,
@ -464,7 +513,6 @@
F63118421CEBA7A100ECB2DE /* android */,
F63117EB1CEB0C1B00ECB2DE /* apple */,
F63118491CEBA7BD00ECB2DE /* generic */,
F63117ED1CEB0CC600ECB2DE /* node */,
02414B991CE6AAEF00A8669F /* collection_change_builder.cpp */,
02414B9A1CE6AAEF00A8669F /* collection_change_builder.hpp */,
02414B9B1CE6AAEF00A8669F /* collection_notifier.cpp */,
@ -480,8 +528,8 @@
02F59EDC1C88F2BB007F774C /* realm_coordinator.hpp */,
02F59EDD1C88F2BB007F774C /* transact_log_handler.cpp */,
02F59EDE1C88F2BB007F774C /* transact_log_handler.hpp */,
02F59EDF1C88F2BB007F774C /* weak_realm_notifier_base.hpp */,
02F59EE01C88F2BB007F774C /* weak_realm_notifier.hpp */,
02022A481DA474A7000F0C4F /* weak_realm_notifier.hpp */,
02022A4A1DA475A9000F0C4F /* weak_realm_notifier.cpp */,
);
name = impl;
path = src/impl;
@ -490,10 +538,10 @@
F63117EB1CEB0C1B00ECB2DE /* apple */ = {
isa = PBXGroup;
children = (
02F59ECF1C88F1B6007F774C /* external_commit_helper.cpp */,
02F59ED01C88F1B6007F774C /* external_commit_helper.hpp */,
02F59ED11C88F1B6007F774C /* weak_realm_notifier.cpp */,
02F59ED21C88F1B6007F774C /* weak_realm_notifier.hpp */,
02E315DE1DB8233E00555337 /* keychain_helper.cpp */,
02E315DF1DB8233E00555337 /* keychain_helper.hpp */,
02022A411DA47489000F0C4F /* external_commit_helper.cpp */,
02022A421DA47489000F0C4F /* external_commit_helper.hpp */,
);
path = apple;
sourceTree = "<group>";
@ -501,31 +549,20 @@
F63117EC1CEB0C8100ECB2DE /* parser */ = {
isa = PBXGroup;
children = (
02F59EC61C88F190007F774C /* parser.cpp */,
02F59EC71C88F190007F774C /* parser.hpp */,
02F59EC81C88F190007F774C /* query_builder.cpp */,
02F59EC91C88F190007F774C /* query_builder.hpp */,
02022A611DA47B8B000F0C4F /* parser.cpp */,
02022A621DA47B8B000F0C4F /* parser.hpp */,
02022A631DA47B8B000F0C4F /* query_builder.cpp */,
02022A641DA47B8B000F0C4F /* query_builder.hpp */,
);
name = parser;
path = src/parser;
sourceTree = "<group>";
};
F63117ED1CEB0CC600ECB2DE /* node */ = {
isa = PBXGroup;
children = (
F63117EE1CEB0D5900ECB2DE /* weak_realm_notifier.cpp */,
F63117EF1CEB0D5900ECB2DE /* weak_realm_notifier.hpp */,
);
path = node;
sourceTree = "<group>";
};
F63118421CEBA7A100ECB2DE /* android */ = {
isa = PBXGroup;
children = (
F63118431CEBA7B700ECB2DE /* external_commit_helper.cpp */,
F63118441CEBA7B700ECB2DE /* external_commit_helper.hpp */,
F63118451CEBA7B700ECB2DE /* weak_realm_notifier.cpp */,
F63118461CEBA7B700ECB2DE /* weak_realm_notifier.hpp */,
);
path = android;
sourceTree = "<group>";
@ -535,7 +572,6 @@
children = (
F631184A1CEBA7D800ECB2DE /* external_commit_helper.cpp */,
F631184B1CEBA7D800ECB2DE /* external_commit_helper.hpp */,
F631184C1CEBA7D800ECB2DE /* weak_realm_notifier.hpp */,
);
path = generic;
sourceTree = "<group>";
@ -644,7 +680,7 @@
02B58CB81AE99CEC009B348C /* Sources */,
02B58CB91AE99CEC009B348C /* Frameworks */,
02B58CBA1AE99CEC009B348C /* Resources */,
F613787A1C18F003008BFC51 /* Copy Query Tests */,
02879D8D1DC29F2000777A5D /* Install Node Modules */,
);
buildRules = (
);
@ -655,24 +691,6 @@
productReference = 02B58CBC1AE99CEC009B348C /* RealmJSTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
F62BF8FA1CAC71780022BCDC /* RealmNode */ = {
isa = PBXNativeTarget;
buildConfigurationList = F62BF8FF1CAC71780022BCDC /* Build configuration list for PBXNativeTarget "RealmNode" */;
buildPhases = (
F64A05B71D140311004ACDBE /* Download Core */,
F62BF8F71CAC71780022BCDC /* Sources */,
F62BF8F81CAC71780022BCDC /* Frameworks */,
F63117A41CEA911000ECB2DE /* Build with node-gyp */,
);
buildRules = (
);
dependencies = (
);
name = RealmNode;
productName = RealmNode;
productReference = F62BF8FB1CAC71780022BCDC /* libRealmNode.dylib */;
productType = "com.apple.product-type.library.dynamic";
};
F63FF2B01C1241E500B3B8E0 /* RealmJS */ = {
isa = PBXNativeTarget;
buildConfigurationList = F63FF2C41C1241E500B3B8E0 /* Build configuration list for PBXNativeTarget "RealmJS" */;
@ -718,9 +736,6 @@
02B58CBB1AE99CEC009B348C = {
CreatedOnToolsVersion = 6.3.1;
};
F62BF8FA1CAC71780022BCDC = {
CreatedOnToolsVersion = 7.3;
};
F63FF2B01C1241E500B3B8E0 = {
CreatedOnToolsVersion = 7.1.1;
};
@ -743,7 +758,6 @@
targets = (
F63FF2EF1C16405C00B3B8E0 /* GCDWebServers */,
F63FF2B01C1241E500B3B8E0 /* RealmJS */,
F62BF8FA1CAC71780022BCDC /* RealmNode */,
02B58CBB1AE99CEC009B348C /* RealmJSTests */,
);
};
@ -764,33 +778,19 @@
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
F613787A1C18F003008BFC51 /* Copy Query Tests */ = {
02879D8D1DC29F2000777A5D /* Install Node Modules */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Copy Query Tests";
name = "Install Node Modules";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "DEST=\"$TARGET_BUILD_DIR/$CONTENTS_FOLDER_PATH/js/query-tests.json\"\nrm -f \"$DEST\"\ncp object-store/tests/query.json \"$DEST\"";
};
F63117A41CEA911000ECB2DE /* Build with node-gyp */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Build with node-gyp";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "node/build-node.sh \"$CONFIGURATION\"";
shellScript = "cd \"$TARGET_BUILD_DIR/$CONTENTS_FOLDER_PATH\"\nyarn add needle url-parse";
};
F63FF2C51C12462600B3B8E0 /* Download Core */ = {
isa = PBXShellScriptBuildPhase;
@ -806,20 +806,6 @@
shellPath = /bin/sh;
shellScript = "../scripts/download-core.sh";
};
F64A05B71D140311004ACDBE /* Download Core */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Download Core";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "../scripts/download-core.sh node";
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
@ -833,70 +819,44 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
F62BF8F71CAC71780022BCDC /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
F6E931BA1CFEAE170016AF14 /* collection_notifications.cpp in Sources */,
F60102D31CBB966E00EC01BA /* js_realm.cpp in Sources */,
F60102D61CBB96B400EC01BA /* object_schema.cpp in Sources */,
F60102D41CBB96AB00EC01BA /* index_set.cpp in Sources */,
F60102DB1CBB96C600EC01BA /* parser.cpp in Sources */,
F6E931BB1CFEAE310016AF14 /* collection_change_builder.cpp in Sources */,
F64A059B1D10D928004ACDBE /* format.cpp in Sources */,
5DC74A7B1D623CA800D77A4F /* thread_confined.cpp in Sources */,
F60102D51CBB96AE00EC01BA /* list.cpp in Sources */,
F6E931BC1CFEAE340016AF14 /* collection_notifier.cpp in Sources */,
F60102DC1CBB96C900EC01BA /* query_builder.cpp in Sources */,
F60102DD1CBB96CC00EC01BA /* external_commit_helper.cpp in Sources */,
F63117F01CEB0D5F00ECB2DE /* weak_realm_notifier.cpp in Sources */,
F60102E11CBB96DD00EC01BA /* transact_log_handler.cpp in Sources */,
F6E931BE1CFEAE3A0016AF14 /* results_notifier.cpp in Sources */,
F60102D71CBB96B800EC01BA /* object_store.cpp in Sources */,
F6E931BD1CFEAE370016AF14 /* list_notifier.cpp in Sources */,
F60102DA1CBB96C300EC01BA /* shared_realm.cpp in Sources */,
F60102E01CBB96D900EC01BA /* realm_coordinator.cpp in Sources */,
F64A059E1D13710C004ACDBE /* thread_id.cpp in Sources */,
F60102EA1CBCAFC300EC01BA /* node_dummy.cpp in Sources */,
F60102D81CBB96BD00EC01BA /* results.cpp in Sources */,
F674784A1CC81F1900F9273C /* platform.cpp in Sources */,
F620F0581CB766DA0082977B /* node_init.cpp in Sources */,
5DC74A791D623CA200D77A4F /* handover.cpp in Sources */,
F60102D91CBB96C100EC01BA /* schema.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
F63FF2AD1C1241E500B3B8E0 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
02E008D51D10ABB600F3AA37 /* format.cpp in Sources */,
02E315C91DB80DDD00555337 /* sync_manager.cpp in Sources */,
02022A671DA47BD7000F0C4F /* parser.cpp in Sources */,
02022A681DA47BD7000F0C4F /* query_builder.cpp in Sources */,
02E315E01DB8233E00555337 /* keychain_helper.cpp in Sources */,
02022A581DA476CD000F0C4F /* external_commit_helper.cpp in Sources */,
02022A5A1DA476CD000F0C4F /* weak_realm_notifier.cpp in Sources */,
02022A5B1DA476CD000F0C4F /* placeholder.cpp in Sources */,
5DC74A7A1D623CA800D77A4F /* thread_confined.cpp in Sources */,
02414BA51CE6ABCF00A8669F /* collection_change_builder.cpp in Sources */,
02414BA61CE6ABCF00A8669F /* collection_notifier.cpp in Sources */,
F64A059D1D13710C004ACDBE /* thread_id.cpp in Sources */,
02414BA71CE6ABCF00A8669F /* list_notifier.cpp in Sources */,
02414BA81CE6ABCF00A8669F /* results_notifier.cpp in Sources */,
02414BA91CE6ABCF00A8669F /* collection_notifications.cpp in Sources */,
02F59EE31C88F2BB007F774C /* transact_log_handler.cpp in Sources */,
5DC74A781D623C9800D77A4F /* handover.cpp in Sources */,
F63FF2E81C159C4B00B3B8E0 /* platform.mm in Sources */,
02E315CA1DB80DDD00555337 /* sync_session.cpp in Sources */,
02F59EC31C88F17D007F774C /* results.cpp in Sources */,
F63FF2E21C15921A00B3B8E0 /* base64.cpp in Sources */,
F63FF2C61C12469E00B3B8E0 /* jsc_init.cpp in Sources */,
02F59ECA1C88F190007F774C /* parser.cpp in Sources */,
02F59EC01C88F17D007F774C /* list.cpp in Sources */,
02F59EBF1C88F17D007F774C /* index_set.cpp in Sources */,
02F59ED51C88F1B6007F774C /* weak_realm_notifier.cpp in Sources */,
F63FF2C91C12469E00B3B8E0 /* js_realm.cpp in Sources */,
02F59EC51C88F17D007F774C /* shared_realm.cpp in Sources */,
02F59ECB1C88F190007F774C /* query_builder.cpp in Sources */,
02F59EE21C88F2BB007F774C /* realm_coordinator.cpp in Sources */,
02F59EC41C88F17D007F774C /* schema.cpp in Sources */,
F63FF2CD1C12469E00B3B8E0 /* rpc.cpp in Sources */,
02E315D21DB80DF200555337 /* sync_file.cpp in Sources */,
02F59EC21C88F17D007F774C /* object_store.cpp in Sources */,
02022A7C1DA47EC8000F0C4F /* format.cpp in Sources */,
02022A7D1DA47EC8000F0C4F /* thread_id.cpp in Sources */,
02E315CB1DB80DDD00555337 /* sync_user.cpp in Sources */,
02E315D31DB80DF200555337 /* sync_metadata.cpp in Sources */,
02F59EC11C88F17D007F774C /* object_schema.cpp in Sources */,
02F59ED41C88F1B6007F774C /* external_commit_helper.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -927,6 +887,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CC = "$(SRCROOT)/../scripts/ccache-clang.sh";
CLANG_CXX_LANGUAGE_STANDARD = "c++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
@ -942,7 +903,8 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 0.14.3;
CURRENT_PROJECT_VERSION = 0.15.0;
CXX = "$(SRCROOT)/../scripts/ccache-clang++.sh";
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
@ -988,6 +950,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CC = "$(SRCROOT)/../scripts/ccache-clang.sh";
CLANG_CXX_LANGUAGE_STANDARD = "c++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
@ -1003,7 +966,8 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 0.14.3;
CURRENT_PROJECT_VERSION = 0.15.0;
CXX = "$(SRCROOT)/../scripts/ccache-clang++.sh";
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
@ -1072,85 +1036,25 @@
};
name = Release;
};
F62BF8FC1CAC71780022BCDC /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
DEBUG_INFORMATION_FORMAT = dwarf;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 0.14.3;
EXECUTABLE_PREFIX = lib;
GCC_PREPROCESSOR_DEFINITIONS = (
"REALM_PLATFORM_NODE=1",
"$(inherited)",
);
HEADER_SEARCH_PATHS = (
"$(inherited)",
"$(SRCROOT)/../node_modules/nan",
/usr/local/include/node,
);
LIBRARY_SEARCH_PATHS = (
/usr/local/lib,
"../core-node",
);
OTHER_CPLUSPLUSFLAGS = (
"$(inherited)",
"-isystem",
"../core-node/include",
);
OTHER_LDFLAGS = (
"-lrealm-node-dbg",
"-luv",
"-lv8",
);
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = macosx;
};
name = Debug;
};
F62BF8FE1CAC71780022BCDC /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 0.14.3;
EXECUTABLE_PREFIX = lib;
GCC_PREPROCESSOR_DEFINITIONS = (
"REALM_PLATFORM_NODE=1",
"$(inherited)",
);
HEADER_SEARCH_PATHS = (
"$(inherited)",
"$(SRCROOT)/../node_modules/nan",
/usr/local/include/node,
);
LIBRARY_SEARCH_PATHS = (
/usr/local/lib,
"../core-node",
);
OTHER_CPLUSPLUSFLAGS = (
"$(inherited)",
"-isystem",
"../core-node/include",
);
OTHER_LDFLAGS = (
"-lrealm-node",
"-luv",
"-lv8",
);
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = macosx;
};
name = Release;
};
F63FF2B71C1241E500B3B8E0 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
DEBUG_INFORMATION_FORMAT = dwarf;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
REALM_DEBUG,
REALM_HAVE_CONFIG,
__ASSERTMACROS__,
REALM_ENABLE_SYNC,
);
OTHER_CPLUSPLUSFLAGS = (
"$(inherited)",
"-isystem",
../core/include,
"$(SRCROOT)/../vendor/core/include",
"-isystem",
"$(SRCROOT)/../vendor/sync/include",
);
OTHER_LIBTOOLFLAGS = "$(SRCROOT)/../core/librealm-ios-dbg.a";
OTHER_LIBTOOLFLAGS = "$(SRCROOT)/../vendor/core/librealm-ios-dbg.a $(SRCROOT)/../vendor/sync/librealm-ios-dbg.a";
PRODUCT_NAME = RealmJS;
SKIP_INSTALL = YES;
};
@ -1159,12 +1063,19 @@
F63FF2B91C1241E500B3B8E0 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
GCC_PREPROCESSOR_DEFINITIONS = (
REALM_HAVE_CONFIG,
__ASSERTMACROS__,
REALM_ENABLE_SYNC,
);
OTHER_CPLUSPLUSFLAGS = (
"$(inherited)",
"-isystem",
../core/include,
"$(SRCROOT)/../vendor/core/include",
"-isystem",
"$(SRCROOT)/../vendor/sync/include",
);
OTHER_LIBTOOLFLAGS = "$(SRCROOT)/../core/librealm-ios.a";
OTHER_LIBTOOLFLAGS = "$(SRCROOT)/../vendor/core/librealm-ios.a $(SRCROOT)/../vendor/sync/librealm-ios.a";
PRODUCT_NAME = RealmJS;
SKIP_INSTALL = YES;
};
@ -1212,15 +1123,6 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
F62BF8FF1CAC71780022BCDC /* Build configuration list for PBXNativeTarget "RealmNode" */ = {
isa = XCConfigurationList;
buildConfigurations = (
F62BF8FC1CAC71780022BCDC /* Debug */,
F62BF8FE1CAC71780022BCDC /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
F63FF2C41C1241E500B3B8E0 /* Build configuration list for PBXNativeTarget "RealmJS" */ = {
isa = XCConfigurationList;
buildConfigurations = (

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0720"
LastUpgradeVersion = "0800"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0720"
LastUpgradeVersion = "0800"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"

View File

@ -1,26 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0730"
LastUpgradeVersion = "0800"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "F62BF8FA1CAC71780022BCDC"
BuildableName = "libRealmNode.dylib"
BlueprintName = "RealmNode"
ReferencedContainer = "container:RealmJS.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
@ -29,24 +13,6 @@
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "YES"
customWorkingDirectory = "$(TEMP_DIR)"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<PathRunnable
runnableDebuggingMode = "0"
FilePath = "/usr/local/bin/node">
</PathRunnable>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
@ -56,9 +22,27 @@
ReferencedContainer = "container:RealmJS.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "YES"
customWorkingDirectory = "~/src/realm/sync/realm-js"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<PathRunnable
runnableDebuggingMode = "0"
FilePath = "/usr/local/bin/node">
</PathRunnable>
<CommandLineArguments>
<CommandLineArgument
argument = "$(SRCROOT)/../tests"
argument = "test.js"
isEnabled = "YES">
</CommandLineArgument>
</CommandLineArguments>
@ -71,15 +55,6 @@
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "F62BF8FA1CAC71780022BCDC"
BuildableName = "libRealmNode.dylib"
BlueprintName = "RealmNode"
ReferencedContainer = "container:RealmJS.xcodeproj">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">

View File

@ -93,7 +93,7 @@ void remove_realm_files_from_directory(const std::string &directory)
NSString *fileDir = @(directory.c_str());
for (NSString *path in [manager enumeratorAtPath:fileDir]) {
if (![path containsString:@".realm"]) {
if (![path.pathExtension isEqualToString:@"realm"] && ![path.pathExtension isEqualToString:@"realm.lock"] && ![path.pathExtension isEqualToString:@"realm.management"]) {
continue;
}
if (![manager removeItemAtPath:[fileDir stringByAppendingPathComponent:path] error:nil]) {

View File

@ -19,6 +19,10 @@
#pragma once
#include "js_class.hpp"
#include "js_types.hpp"
#include "js_observable.hpp"
#include "collection_notifications.hpp"
namespace realm {
namespace js {
@ -27,9 +31,40 @@ namespace js {
class Collection {};
template<typename T>
struct CollectionClass : ClassDefinition<T, Collection> {
struct CollectionClass : ClassDefinition<T, Collection, ObservableClass<T>> {
using ContextType = typename T::Context;
using ValueType = typename T::Value;
using ObjectType = typename T::Object;
using Object = js::Object<T>;
using Value = js::Value<T>;
std::string const name = "Collection";
static inline ValueType create_collection_change_set(ContextType ctx, const CollectionChangeSet &change_set);
};
template<typename T>
typename T::Value CollectionClass<T>::create_collection_change_set(ContextType ctx, const CollectionChangeSet &change_set)
{
ObjectType object = Object::create_empty(ctx);
std::vector<ValueType> deletions, insertions, modifications;
for (auto index : change_set.deletions.as_indexes()) {
deletions.push_back(Value::from_number(ctx, index));
}
Object::set_property(ctx, object, "deletions", Object::create_array(ctx, deletions));
for (auto index : change_set.insertions.as_indexes()) {
insertions.push_back(Value::from_number(ctx, index));
}
Object::set_property(ctx, object, "insertions", Object::create_array(ctx, insertions));
for (auto index : change_set.modifications.as_indexes()) {
modifications.push_back(Value::from_number(ctx, index));
}
Object::set_property(ctx, object, "modifications", Object::create_array(ctx, modifications));
return object;
}
} // js
} // realm

View File

@ -33,10 +33,20 @@ namespace realm {
namespace js {
template<typename T>
struct ListClass : ClassDefinition<T, realm::List, CollectionClass<T>> {
class List : public realm::List {
public:
List(std::shared_ptr<Realm> r, const ObjectSchema& s, LinkViewRef l) noexcept : realm::List(r, l) {}
List(const realm::List &l) : realm::List(l) {}
std::vector<std::pair<Protected<typename T::Function>, NotificationToken>> m_notification_tokens;
};
template<typename T>
struct ListClass : ClassDefinition<T, realm::js::List<T>, CollectionClass<T>> {
using ContextType = typename T::Context;
using ObjectType = typename T::Object;
using ValueType = typename T::Value;
using FunctionType = typename T::Function;
using Object = js::Object<T>;
using Value = js::Value<T>;
using ReturnValue = js::ReturnValue<T>;
@ -58,7 +68,12 @@ struct ListClass : ClassDefinition<T, realm::List, CollectionClass<T>> {
static void filtered(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void sorted(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void is_valid(ContextType, ObjectType, size_t, const ValueType [], ReturnValue &);
// observable
static void add_listener(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void remove_listener(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void remove_all_listeners(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
std::string const name = "List";
MethodMap<T> const methods = {
@ -71,6 +86,9 @@ struct ListClass : ClassDefinition<T, realm::List, CollectionClass<T>> {
{"filtered", wrap<filtered>},
{"sorted", wrap<sorted>},
{"isValid", wrap<is_valid>},
{"addListener", wrap<add_listener>},
{"removeListener", wrap<remove_listener>},
{"removeAllListeners", wrap<remove_all_listeners>},
};
PropertyMap<T> const properties = {
@ -82,7 +100,7 @@ struct ListClass : ClassDefinition<T, realm::List, CollectionClass<T>> {
template<typename T>
typename T::Object ListClass<T>::create_instance(ContextType ctx, realm::List list) {
return create_object<T, ListClass<T>>(ctx, new realm::List(std::move(list)));
return create_object<T, ListClass<T>>(ctx, new realm::js::List<T>(std::move(list)));
}
template<typename T>
@ -230,6 +248,55 @@ template<typename T>
void ListClass<T>::is_valid(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
return_value.set(get_internal<T, ListClass<T>>(this_object)->is_valid());
}
template<typename T>
void ListClass<T>::add_listener(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
validate_argument_count(argc, 1);
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>::call(protected_ctx, protected_callback, protected_this, 2, arguments);
});
list->m_notification_tokens.emplace_back(protected_callback, std::move(token));
}
template<typename T>
void ListClass<T>::remove_listener(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
validate_argument_count(argc, 1);
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++;
}
}
}
template<typename T>
void ListClass<T>::remove_all_listeners(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
validate_argument_count(argc, 0);
auto list = get_internal<T, ListClass<T>>(this_object);
list->m_notification_tokens.clear();
}
} // js
} // realm

35
src/js_observable.hpp Normal file
View File

@ -0,0 +1,35 @@
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
#pragma once
#include "js_class.hpp"
namespace realm {
namespace js {
// Empty class that merely serves as useful type for now.
class Observable {};
template<typename T>
struct ObservableClass : ClassDefinition<T, Observable> {
std::string const name = "Observable";
};
} // js
} // realm

View File

@ -28,6 +28,13 @@
#include "js_list.hpp"
#include "js_results.hpp"
#include "js_schema.hpp"
#include "js_observable.hpp"
#if REALM_ENABLE_SYNC
#include "js_sync.hpp"
#include "sync/sync_config.hpp"
#include "sync/sync_manager.hpp"
#endif
#include "shared_realm.hpp"
#include "binding_context.hpp"
@ -99,8 +106,10 @@ class RealmDelegate : public BindingContext {
Protected<GlobalContextType> m_context;
std::list<Protected<FunctionType>> m_notifications;
std::weak_ptr<realm::Realm> m_realm;
void notify(const char *notification_name) {
HANDLESCOPE
SharedRealm realm = m_realm.lock();
if (!realm) {
throw std::runtime_error("Realm no longer exists");
@ -111,7 +120,8 @@ class RealmDelegate : public BindingContext {
arguments[0] = realm_object;
arguments[1] = Value::from_string(m_context, notification_name);
for (auto &callback : m_notifications) {
std::list<Protected<FunctionType>> notifications_copy(m_notifications);
for (auto &callback : notifications_copy) {
Function<T>::call(m_context, callback, realm_object, 2, arguments);
}
}
@ -124,7 +134,7 @@ void set_default_path(std::string path);
void delete_all_realms();
template<typename T>
class RealmClass : public ClassDefinition<T, SharedRealm> {
class RealmClass : public ClassDefinition<T, SharedRealm, ObservableClass<T>> {
using GlobalContextType = typename T::GlobalContext;
using ContextType = typename T::Context;
using FunctionType = typename T::Function;
@ -162,23 +172,23 @@ public:
static void schema_version(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void clear_test_state(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void copy_bundled_realm_files(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
// static properties
static void get_default_path(ContextType, ObjectType, ReturnValue &);
static void set_default_path(ContextType, ObjectType, ValueType value);
std::string const name = "Realm";
MethodMap<T> const static_methods = {
{"schemaVersion", wrap<schema_version>},
{"clearTestState", wrap<clear_test_state>},
{"copyBundledRealmFiles", wrap<copy_bundled_realm_files>},
};
PropertyMap<T> const static_properties = {
{"defaultPath", {wrap<get_default_path>, wrap<set_default_path>}},
};
MethodMap<T> const methods = {
{"objects", wrap<objects>},
{"objectForPrimaryKey", wrap<object_for_primary_key>},
@ -191,14 +201,14 @@ public:
{"removeAllListeners", wrap<remove_all_listeners>},
{"close", wrap<close>},
};
PropertyMap<T> const properties = {
{"path", {wrap<get_path>, nullptr}},
{"schemaVersion", {wrap<get_schema_version>, nullptr}},
{"schema", {wrap<get_schema>, nullptr}},
{"readOnly", {wrap<get_read_only>, nullptr}},
};
private:
static std::string validated_notification_name(ContextType ctx, const ValueType &value) {
std::string name = Value::validated_to_string(ctx, value, "notification name");
@ -207,13 +217,13 @@ public:
}
return name;
}
static const ObjectSchema& validated_object_schema_for_value(ContextType ctx, const SharedRealm &realm, const ValueType &value) {
std::string object_type;
if (Value::is_constructor(ctx, value)) {
FunctionType constructor = Value::to_constructor(ctx, value);
auto delegate = get_delegate<T>(realm.get());
for (auto &pair : delegate->m_constructors) {
if (FunctionType(pair.second) == constructor) {
@ -238,7 +248,7 @@ public:
}
return *object_schema;
}
static std::string normalize_path(std::string path) {
if (path.size() && path[0] != '/' && path[0] != '.') {
return default_realm_file_directory() + "/" + path;
@ -261,9 +271,15 @@ inline typename T::Function RealmClass<T>::create_constructor(ContextType ctx) {
Object::set_property(ctx, realm_constructor, "Results", results_constructor, attributes);
Object::set_property(ctx, realm_constructor, "Object", realm_object_constructor, attributes);
#if REALM_ENABLE_SYNC
FunctionType sync_constructor = SyncClass<T>::create_constructor(ctx);
Object::set_property(ctx, realm_constructor, "Sync", sync_constructor, attributes);
#endif
Object::set_global(ctx, "Realm", realm_constructor);
return realm_constructor;
}
static inline void convert_outdated_datetime_columns(const SharedRealm &realm) {
realm::util::Optional<int> old_file_format_version = realm->file_format_upgraded_from_version();
if (old_file_format_version && old_file_format_version < 5) {
@ -275,7 +291,7 @@ static inline void convert_outdated_datetime_columns(const SharedRealm &realm) {
if (!realm->is_in_transaction()) {
realm->begin_transaction();
}
for (size_t row_index = 0; row_index < table->size(); row_index++) {
if (table->is_null(property.table_column, row_index)) {
continue;
@ -318,7 +334,7 @@ void RealmClass<T>::constructor(ContextType ctx, ObjectType this_object, size_t
else {
config.path = js::default_path();
}
static const String read_only_string = "readOnly";
ValueType read_only_value = Object::get_property(ctx, object, read_only_string);
if (!Value::is_undefined(ctx, read_only_value) && Value::validated_to_boolean(ctx, read_only_value, "readOnly")) {
@ -338,7 +354,7 @@ void RealmClass<T>::constructor(ContextType ctx, ObjectType this_object, size_t
if (!Value::is_undefined(ctx, version_value)) {
config.schema_version = Value::validated_to_number(ctx, version_value, "schemaVersion");
}
else {
else if (schema_updated) {
config.schema_version = 0;
}
@ -353,7 +369,7 @@ void RealmClass<T>::constructor(ContextType ctx, ObjectType this_object, size_t
create_object<T, RealmClass<T>>(ctx, old_realm_ptr),
create_object<T, RealmClass<T>>(ctx, realm_ptr)
};
try {
Function<T>::call(ctx, migration_function, 2, arguments);
}
@ -363,7 +379,7 @@ void RealmClass<T>::constructor(ContextType ctx, ObjectType this_object, size_t
realm_ptr->reset();
throw;
}
old_realm->close();
old_realm_ptr->reset();
realm_ptr->reset();
@ -376,12 +392,15 @@ void RealmClass<T>::constructor(ContextType ctx, ObjectType this_object, size_t
std::string encryption_key = NativeAccessor::to_binary(ctx, encryption_key_value);
config.encryption_key = std::vector<char>(encryption_key.begin(), encryption_key.end());
}
#if REALM_ENABLE_SYNC
SyncClass<T>::populate_sync_config(ctx, Value::validated_to_object(ctx, Object::get_global(ctx, "Realm")), object, config);
#endif
}
}
else {
throw std::runtime_error("Invalid arguments when constructing 'Realm'");
}
config.path = normalize_path(config.path);
ensure_directory_exists_for_file(config.path);
@ -403,7 +422,7 @@ void RealmClass<T>::constructor(ContextType ctx, ObjectType this_object, size_t
js_binding_context->m_defaults = std::move(defaults);
js_binding_context->m_constructors = std::move(constructors);
}
// Fix for datetime -> timestamp conversion
convert_outdated_datetime_columns(realm);
@ -413,7 +432,7 @@ void RealmClass<T>::constructor(ContextType ctx, ObjectType this_object, size_t
template<typename T>
void RealmClass<T>::schema_version(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
validate_argument_count(argc, 1, 2);
realm::Realm::Config config;
config.path = normalize_path(Value::validated_to_string(ctx, arguments[0]));
if (argc == 2) {
@ -421,7 +440,7 @@ void RealmClass<T>::schema_version(ContextType ctx, ObjectType this_object, size
std::string encryptionKey = NativeAccessor::to_binary(ctx, encryptionKeyValue);
config.encryption_key = std::vector<char>(encryptionKey.begin(), encryptionKey.end());
}
auto version = realm::Realm::get_schema_version(config);
if (version == ObjectStore::NotVersioned) {
return_value.set(-1);
@ -431,12 +450,20 @@ void RealmClass<T>::schema_version(ContextType ctx, ObjectType this_object, size
}
}
template<typename T>
void RealmClass<T>::clear_test_state(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
validate_argument_count(argc, 0);
#if REALM_ENABLE_SYNC
for(auto &user : SyncManager::shared().all_users()) {
user->log_out();
}
SyncManager::shared().reset_for_testing();
SyncManager::shared().configure_file_system(default_realm_file_directory(), SyncManager::MetadataMode::NoEncryption);
#endif
delete_all_realms();
}
template<typename T>
void RealmClass<T>::copy_bundled_realm_files(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
validate_argument_count(argc, 0);

View File

@ -31,10 +31,25 @@ namespace realm {
namespace js {
template<typename T>
struct ResultsClass : ClassDefinition<T, realm::Results, CollectionClass<T>> {
class Results : public realm::Results {
public:
Results(Results const& r) : realm::Results(r) {};
Results(realm::Results const& r) : realm::Results(r) {};
Results(Results&&) = default;
Results& operator=(Results&&) = default;
Results& operator=(Results const&) = default;
using realm::Results::Results;
std::vector<std::pair<Protected<typename T::Function>, NotificationToken>> m_notification_tokens;
};
template<typename T>
struct ResultsClass : ClassDefinition<T, realm::js::Results<T>, CollectionClass<T>> {
using ContextType = typename T::Context;
using ObjectType = typename T::Object;
using ValueType = typename T::Value;
using FunctionType = typename T::Function;
using Object = js::Object<T>;
using Value = js::Value<T>;
using ReturnValue = js::ReturnValue<T>;
@ -56,6 +71,11 @@ struct ResultsClass : ClassDefinition<T, realm::Results, CollectionClass<T>> {
static void sorted(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void is_valid(ContextType, ObjectType, size_t, const ValueType [], ReturnValue &);
// observable
static void add_listener(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void remove_listener(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void remove_all_listeners(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
std::string const name = "Results";
MethodMap<T> const methods = {
@ -63,6 +83,9 @@ struct ResultsClass : ClassDefinition<T, realm::Results, CollectionClass<T>> {
{"filtered", wrap<filtered>},
{"sorted", wrap<sorted>},
{"isValid", wrap<is_valid>},
{"addListener", wrap<add_listener>},
{"removeListener", wrap<remove_listener>},
{"removeAllListeners", wrap<remove_all_listeners>},
};
PropertyMap<T> const properties = {
@ -74,13 +97,13 @@ struct ResultsClass : ClassDefinition<T, realm::Results, CollectionClass<T>> {
template<typename T>
typename T::Object ResultsClass<T>::create_instance(ContextType ctx, realm::Results results) {
return create_object<T, ResultsClass<T>>(ctx, new realm::Results(std::move(results)));
return create_object<T, ResultsClass<T>>(ctx, new realm::js::Results<T>(std::move(results)));
}
template<typename T>
typename T::Object ResultsClass<T>::create_instance(ContextType ctx, SharedRealm realm, const ObjectSchema &object_schema) {
auto table = ObjectStore::table_for_object_type(realm->read_group(), object_schema.name);
return create_object<T, ResultsClass<T>>(ctx, new realm::Results(realm, *table));
return create_object<T, ResultsClass<T>>(ctx, new realm::js::Results<T>(realm, *table));
}
template<typename T>
@ -155,8 +178,8 @@ typename T::Object ResultsClass<T>::create_sorted(ContextType ctx, const U &coll
}
auto table = realm::ObjectStore::table_for_object_type(realm->read_group(), object_schema.name);
auto results = new realm::Results(realm, collection.get_query(),
{*table, std::move(columns), std::move(ascending)});
auto results = new realm::js::Results<T>(realm, collection.get_query(),
{*table, std::move(columns), std::move(ascending)});
return create_object<T, ResultsClass<T>>(ctx, results);
}
@ -209,6 +232,55 @@ template<typename T>
void ResultsClass<T>::is_valid(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
return_value.set(get_internal<T, ResultsClass<T>>(this_object)->is_valid());
}
template<typename T>
void ResultsClass<T>::add_listener(ContextType ctx, 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]);
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) {
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>::call(protected_ctx, protected_callback, protected_this, 2, arguments);
});
results->m_notification_tokens.emplace_back(protected_callback, std::move(token));
}
template<typename T>
void ResultsClass<T>::remove_listener(ContextType ctx, 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]);
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++;
}
}
}
template<typename T>
void ResultsClass<T>::remove_all_listeners(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
validate_argument_count(argc, 0);
auto results = get_internal<T, ResultsClass<T>>(this_object);
results->m_notification_tokens.clear();
}
} // js
} // realm

314
src/js_sync.hpp Normal file
View File

@ -0,0 +1,314 @@
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
#pragma once
#include <list>
#include <map>
#include <set>
#include "platform.hpp"
#include "js_class.hpp"
#include "js_collection.hpp"
#include "sync/sync_manager.hpp"
#include "sync/sync_config.hpp"
#include "sync/sync_session.hpp"
#include "sync/sync_user.hpp"
#include "realm/util/logger.hpp"
#include "realm/util/uri.hpp"
namespace realm {
namespace js {
using SharedUser = std::shared_ptr<realm::SyncUser>;
template<typename T>
class UserClass : public ClassDefinition<T, SharedUser> {
using GlobalContextType = typename T::GlobalContext;
using ContextType = typename T::Context;
using FunctionType = typename T::Function;
using ObjectType = typename T::Object;
using ValueType = typename T::Value;
using String = js::String<T>;
using Object = js::Object<T>;
using Value = js::Value<T>;
using Function = js::Function<T>;
using ReturnValue = js::ReturnValue<T>;
using NativeAccessor = realm::NativeAccessor<ValueType, ContextType>;
public:
std::string const name = "User";
static FunctionType create_constructor(ContextType);
static void get_server(ContextType, ObjectType, ReturnValue &);
static void get_identity(ContextType, ObjectType, ReturnValue &);
static void get_token(ContextType, ObjectType, ReturnValue &);
static void is_admin(ContextType, ObjectType, ReturnValue &);
PropertyMap<T> const properties = {
{"server", {wrap<get_server>, nullptr}},
{"identity", {wrap<get_identity>, nullptr}},
{"token", {wrap<get_token>, nullptr}},
{"isAdmin", {wrap<is_admin>, nullptr}},
};
static void create_user(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
MethodMap<T> const static_methods = {
{"createUser", wrap<create_user>}
};
static void current_user(ContextType ctx, ObjectType object, ReturnValue &return_value);
static void all_users(ContextType ctx, ObjectType object, ReturnValue &return_value);
PropertyMap<T> const static_properties = {
{"current", {wrap<current_user>, nullptr}},
{"all", {wrap<all_users>, nullptr}},
};
static void logout(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
MethodMap<T> const methods = {
{"logout", wrap<logout>}
};
};
template<typename T>
void UserClass<T>::get_server(ContextType ctx, ObjectType object, ReturnValue &return_value) {
std::string server = get_internal<T, UserClass<T>>(object)->get()->server_url();
return_value.set(server);
}
template<typename T>
void UserClass<T>::get_identity(ContextType ctx, ObjectType object, ReturnValue &return_value) {
std::string identity = get_internal<T, UserClass<T>>(object)->get()->identity();
return_value.set(identity);
}
template<typename T>
void UserClass<T>::get_token(ContextType ctx, ObjectType object, ReturnValue &return_value) {
std::string token = get_internal<T, UserClass<T>>(object)->get()->refresh_token();
return_value.set(token);
}
template<typename T>
void UserClass<T>::is_admin(ContextType ctx, ObjectType object, ReturnValue &return_value) {
return_value.set(get_internal<T, UserClass<T>>(object)->get()->is_admin());
}
template<typename T>
void UserClass<T>::create_user(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
validate_argument_count(argc, 3, 4);
SharedUser *user = new SharedUser(SyncManager::shared().get_user(
Value::validated_to_string(ctx, arguments[1]),
Value::validated_to_string(ctx, arguments[2]),
(std::string)Value::validated_to_string(ctx, arguments[0]),
Value::validated_to_boolean(ctx, arguments[3])));
return_value.set(create_object<T, UserClass<T>>(ctx, user));
}
template<typename T>
void UserClass<T>::all_users(ContextType ctx, ObjectType object, ReturnValue &return_value) {
auto users = Object::create_empty(ctx);
for (auto user : SyncManager::shared().all_users()) {
if (user->state() == SyncUser::State::Active) {
Object::set_property(ctx, users, user->identity(), create_object<T, UserClass<T>>(ctx, new SharedUser(user)), ReadOnly | DontDelete);
}
}
return_value.set(users);
}
template<typename T>
void UserClass<T>::current_user(ContextType ctx, ObjectType object, ReturnValue &return_value) {
SharedUser *current = nullptr;
for (auto user : SyncManager::shared().all_users()) {
if (user->state() == SyncUser::State::Active) {
if (current != nullptr) {
throw std::runtime_error("More than one user logged in currently.");
}
current = new SharedUser(user);
}
}
if (current != nullptr) {
return_value.set(create_object<T, UserClass<T>>(ctx, current));
}
else {
return_value.set_undefined();
}
}
template<typename T>
void UserClass<T>::logout(ContextType ctx, ObjectType object, size_t, const ValueType[], ReturnValue &) {
get_internal<T, UserClass<T>>(object)->get()->log_out();
}
template<typename T>
class SyncClass : public ClassDefinition<T, void *> {
using GlobalContextType = typename T::GlobalContext;
using ContextType = typename T::Context;
using FunctionType = typename T::Function;
using ObjectType = typename T::Object;
using ValueType = typename T::Value;
using String = js::String<T>;
using Object = js::Object<T>;
using Value = js::Value<T>;
using Function = js::Function<T>;
using ReturnValue = js::ReturnValue<T>;
using NativeAccessor = realm::NativeAccessor<ValueType, ContextType>;
public:
std::string const name = "Sync";
static FunctionType create_constructor(ContextType);
static void set_sync_log_level(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void set_verify_servers_ssl_certificate(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
// private
static void refresh_access_token(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void populate_sync_config(ContextType, ObjectType realm_constructor, ObjectType config_object, Realm::Config&);
// static properties
static void get_is_developer_edition(ContextType, ObjectType, ReturnValue &);
MethodMap<T> const static_methods = {
{"refreshAccessToken", wrap<refresh_access_token>},
{"setLogLevel", wrap<set_sync_log_level>},
{"setVerifyServersSslCertificate", wrap<set_verify_servers_ssl_certificate>}
};
};
template<typename T>
inline typename T::Function SyncClass<T>::create_constructor(ContextType ctx) {
// setup synced realmFile paths
ensure_directory_exists_for_file(default_realm_file_directory());
SyncManager::shared().configure_file_system(default_realm_file_directory(), SyncManager::MetadataMode::NoEncryption);
FunctionType sync_constructor = ObjectWrap<T, SyncClass<T>>::create_constructor(ctx);
PropertyAttributes attributes = ReadOnly | DontEnum | DontDelete;
Object::set_property(ctx, sync_constructor, "User", ObjectWrap<T, UserClass<T>>::create_constructor(ctx), attributes);
realm::SyncManager::shared().set_error_handler([=](int error_code, std::string message) {
std::cout << error_code << " " << message << std::endl;
});
return sync_constructor;
}
template<typename T>
void SyncClass<T>::set_sync_log_level(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
validate_argument_count(argc, 1);
std::string log_level = Value::validated_to_string(ctx, arguments[0]);
std::istringstream in(log_level); // Throws
in.imbue(std::locale::classic()); // Throws
in.unsetf(std::ios_base::skipws);
util::Logger::Level log_level_2 = util::Logger::Level();
in >> log_level_2; // Throws
if (!in || !in.eof())
throw std::runtime_error("Bad log level");
realm::SyncManager::shared().set_log_level(log_level_2);
}
template<typename T>
void SyncClass<T>::set_verify_servers_ssl_certificate(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
validate_argument_count(argc, 1);
bool verify_servers_ssl_certificate = Value::validated_to_boolean(ctx, arguments[0]);
realm::SyncManager::shared().set_client_should_validate_ssl(verify_servers_ssl_certificate);
}
template<typename T>
void SyncClass<T>::refresh_access_token(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
validate_argument_count(argc, 2);
static const String token_string = "token";
static const String file_url_string = "file_url";
static const String realm_url_string = "resolved_realm_url";
ObjectType json_arguments = Value::validated_to_object(ctx, arguments[1]);
std::string token = Object::validated_get_string(ctx, json_arguments, token_string);
std::string file_url = Object::validated_get_string(ctx, json_arguments, file_url_string);
std::string realm_url = Object::validated_get_string(ctx, json_arguments, realm_url_string);
if (auto session = SyncManager::shared().get_existing_active_session(file_url)) {
session->refresh_access_token(token, realm_url);
return_value.set(true);
}
else {
return_value.set(false);
}
}
template<typename T>
void SyncClass<T>::populate_sync_config(ContextType ctx, ObjectType realm_constructor, ObjectType config_object, Realm::Config& config) {
ValueType sync_config_value = Object::get_property(ctx, config_object, "sync");
if (!Value::is_undefined(ctx, sync_config_value)) {
auto sync_config_object = Value::validated_to_object(ctx, sync_config_value);
ObjectType sync_constructor = Object::validated_get_object(ctx, realm_constructor, std::string("Sync"));
Protected<ValueType> refresh(ctx, Object::validated_get_function(ctx, sync_constructor, std::string("refreshAccessToken")));
Protected<ObjectType> protected_sync(ctx, sync_constructor);
Protected<typename T::GlobalContext> protected_ctx(Context<T>::get_global_context(ctx));
auto handler = [=](const std::string& path, const realm::SyncConfig& config, std::shared_ptr<SyncSession>) {
HANDLESCOPE
if (config.user->is_admin()) {
// FIXME: This log-in callback is called while the object store still holds some sync-related locks.
// Notify the object store of the access token asynchronously to avoid the deadlock that would result
// from reentering the object store here.
auto thread = std::thread([path, config]{
auto session = SyncManager::shared().get_existing_active_session(path);
session->refresh_access_token(config.user->refresh_token(), config.realm_url);
});
thread.detach();
}
else {
ObjectType user_constructor = Object::validated_get_object(ctx, protected_sync, std::string("User"));
FunctionType authenticate = Object::validated_get_function(ctx, user_constructor, std::string("_authenticateRealm"));
ValueType arguments[3];
arguments[0] = Value::from_string(protected_ctx, path.c_str());
arguments[1] = Value::from_string(protected_ctx, config.realm_url.c_str());
arguments[2] = refresh;
ObjectType user = create_object<T, UserClass<T>>(ctx, new SharedUser(config.user));
Function::call(protected_ctx, authenticate, user, 3, arguments);
}
};
ObjectType user = Object::validated_get_object(ctx, sync_config_object, "user");
SharedUser shared_user = *get_internal<T, UserClass<T>>(user);
if (shared_user->state() != SyncUser::State::Active) {
throw std::runtime_error("User is no longer valid.");
}
std::string raw_realm_url = Object::validated_get_string(ctx, sync_config_object, "url");
// FIXME - use make_shared
config.sync_config = std::shared_ptr<SyncConfig>(
new SyncConfig(shared_user, raw_realm_url, SyncSessionStopPolicy::AfterChangesUploaded, handler)
);
config.schema_mode = SchemaMode::Additive;
config.path = realm::SyncManager::shared().path_for_realm(shared_user->identity(), raw_realm_url);
}
}
} // js
} // realm

View File

@ -133,6 +133,11 @@ struct Function {
using ValueType = typename T::Value;
static ValueType call(ContextType, const FunctionType &, const ObjectType &, size_t, const ValueType[]);
template<size_t N> static ValueType call(ContextType ctx, const FunctionType &function,
const ObjectType &this_object, const ValueType (&arguments)[N])
{
return call(ctx, function, this_object, N, arguments);
}
static ValueType call(ContextType ctx, const FunctionType &function, size_t argument_count, const ValueType arguments[]) {
return call(ctx, function, {}, argument_count, arguments);
}
@ -165,6 +170,9 @@ struct Object {
static void set_property(ContextType, const ObjectType &, uint32_t, const ValueType &);
static std::vector<String<T>> get_property_names(ContextType, const ObjectType &);
static void set_global(ContextType, const String<T> &, const ValueType &);
static ValueType get_global(ContextType, const String<T> &);
template<typename P>
static ValueType validated_get_property(ContextType ctx, const ObjectType &object, const P &property, const char *message = nullptr) {
if (!has_property(ctx, object, property)) {
@ -247,6 +255,10 @@ class Protected {
bool operator!=(const ValueType &) const;
bool operator==(const Protected<ValueType> &) const;
bool operator!=(const Protected<ValueType> &) const;
struct Comparator {
bool operator()(const Protected<ValueType>& a, const Protected<ValueType>& b) const;
};
};
template<typename T>

View File

@ -26,7 +26,6 @@ extern "C" {
JSObjectRef RJSConstructorCreate(JSContextRef ctx);
void RJSInitializeInContext(JSContextRef ctx);
void RJSClearTestState(void);
#ifdef __cplusplus
}

View File

@ -143,5 +143,17 @@ inline void jsc::Object::set_internal(const JSObjectRef &object, typename ClassT
*wrap = ptr;
}
template<>
inline void jsc::Object::set_global(JSContextRef ctx, const jsc::String &key, const JSValueRef &value) {
JSObjectRef global_object = JSContextGetGlobalObject(ctx);
jsc::Object::set_property(ctx, global_object, key, value, js::ReadOnly | js::DontEnum | js::DontDelete);
}
template<>
inline JSValueRef jsc::Object::get_global(JSContextRef ctx, const jsc::String &key) {
JSObjectRef global_object = JSContextGetGlobalObject(ctx);
return jsc::Object::get_property(ctx, global_object, key);
}
} // js
} // realm

View File

@ -47,10 +47,17 @@ class Protected<JSGlobalContextRef> {
operator bool() const {
return m_context != nullptr;
}
struct Comparator {
bool operator() (const Protected<JSGlobalContextRef>& a, const Protected<JSGlobalContextRef>& b) const {
return a.m_context == b.m_context;
}
};
};
template<>
class Protected<JSValueRef> {
protected:
JSGlobalContextRef m_context;
JSValueRef m_value;
@ -75,6 +82,21 @@ class Protected<JSValueRef> {
operator bool() const {
return m_value != nullptr;
}
struct Comparator {
bool operator() (const Protected<JSValueRef>& a, const Protected<JSValueRef>& b) const {
if (a.m_context != b.m_context) {
return false;
}
return JSValueIsStrictEqual(a.m_context, a.m_value, b.m_value);
}
};
Protected<JSValueRef>& operator=(Protected<JSValueRef> other) {
std::swap(m_context, other.m_context);
std::swap(m_value, other.m_value);
return *this;
}
};
template<>
@ -89,6 +111,12 @@ class Protected<JSObjectRef> : public Protected<JSValueRef> {
JSValueRef value = static_cast<JSValueRef>(*this);
return (JSObjectRef)value;
}
Protected<JSObjectRef>& operator=(Protected<JSObjectRef> other) {
std::swap(m_context, other.m_context);
std::swap(m_value, other.m_value);
return *this;
}
};
} // js

View File

@ -24,6 +24,8 @@
#include "js_types.hpp"
#define HANDLESCOPE
namespace realm {
namespace jsc {

View File

@ -1,85 +0,0 @@
{
"targets": [
{
"target_name": "realm",
"sources": [
"node_init.cpp",
"platform.cpp",
"../js_realm.cpp",
"../object-store/src/collection_notifications.cpp",
"../object-store/src/index_set.cpp",
"../object-store/src/list.cpp",
"../object-store/src/object_schema.cpp",
"../object-store/src/object_store.cpp",
"../object-store/src/results.cpp",
"../object-store/src/schema.cpp",
"../object-store/src/shared_realm.cpp",
"../object-store/src/thread_confined.cpp",
"../object-store/src/impl/collection_change_builder.cpp",
"../object-store/src/impl/collection_notifier.cpp",
"../object-store/src/impl/handover.cpp",
"../object-store/src/impl/list_notifier.cpp",
"../object-store/src/impl/realm_coordinator.cpp",
"../object-store/src/impl/results_notifier.cpp",
"../object-store/src/impl/transact_log_handler.cpp",
"../object-store/src/impl/node/weak_realm_notifier.cpp",
"../object-store/src/parser/parser.cpp",
"../object-store/src/parser/query_builder.cpp",
"../object-store/src/util/format.cpp",
"../object-store/src/util/thread_id.cpp"
],
"include_dirs": [
"..",
"../object-store/src",
"../object-store/src/impl",
"../object-store/src/impl/apple",
"../object-store/src/parser",
"../object-store/external/pegtl",
"../../core-node/include",
"<!(node -e 'require(\"nan\")')"
],
"library_dirs": [
"$(srcdir)/../../core-node"
],
"defines": [
"REALM_HAVE_CONFIG",
"REALM_PLATFORM_NODE=1"
],
"cflags_cc": [
"-fexceptions",
"-frtti",
"-std=c++14",
"-Wno-missing-field-initializers",
"-Wno-return-type"
],
"libraries": ["-lrealm-node"],
"xcode_settings": {
"CLANG_CXX_LANGUAGE_STANDARD": "c++14",
"CLANG_CXX_LIBRARY": "libc++",
"GCC_ENABLE_CPP_EXCEPTIONS": "YES",
"GCC_ENABLE_CPP_RTTI": "YES",
"MACOSX_DEPLOYMENT_TARGET": "10.8",
"OTHER_LDFLAGS": ["-framework", "Foundation"]
},
"conditions": [
[
"OS=='linux'", {
"sources": [
"../object-store/src/impl/android/external_commit_helper.cpp",
]
}
],
["OS=='mac'", {
"sources": [
"../object-store/src/impl/apple/external_commit_helper.cpp"
]
}]
],
"configurations": {
"Debug": {
"defines": ["DEBUG=1"]
}
}
}
]
}

View File

@ -1,28 +0,0 @@
#!/bin/bash
set -o pipefail
set -e
cd "$(dirname "$0")"
CONFIGURATION="${1:-"Release"}"
PATH="$(cd ../../node_modules/.bin && pwd):$PATH"
if [ -s "${HOME}/.nvm/nvm.sh" ]; then
. "${HOME}/.nvm/nvm.sh"
nvm use 5.4.0 || true
fi
node-gyp configure
# Being explicit about debug mode rather than relying on defaults.
if [[ $CONFIGURATION == 'Debug' ]]; then
node-gyp build --debug
else
node-gyp build --no-debug
fi
# Link to the appropriate module in the build directory.
cd ../..
mkdir -p build
ln -fs "../src/node/build/$CONFIGURATION/realm.node" build/

111
src/node/gyp/realm.gyp Normal file
View File

@ -0,0 +1,111 @@
{
"variables": {
"use_realm_debug": "<!(echo $REALMJS_USE_DEBUG_CORE)"
},
"conditions": [
["OS=='mac'", {
"variables": {
"realm_enable_sync%": "0"
}
}, {
"variables": {
"realm_enable_sync%": "0"
}
}]
],
"targets": [
{
"target_name": "realm-core",
"type": "none",
"direct_dependent_settings": {
"conditions": [
["use_realm_debug!=''", {
"libraries": [ "-lrealm-node-dbg" ],
"defines": [ "REALM_DEBUG=1" ]
}, {
"libraries": [ "-lrealm-node" ]
}]
]
},
"all_dependent_settings": {
"defines": [ "REALM_HAVE_CONFIG", "REALM_PLATFORM_NODE=1", "REALM_ENABLE_SYNC=<(realm_enable_sync)" ]
},
"variables": {
"prefix": "<!(echo $REALM_CORE_PREFIX)"
},
"conditions": [
["prefix!=''", {
"all_dependent_settings": {
"include_dirs": [ "<(prefix)/src" ],
},
"direct_dependent_settings": {
"library_dirs": [ "<(prefix)/src/realm" ]
}
}, {
"dependencies": [ "vendored-realm" ]
}]
]
},
{
"target_name": "realm-sync",
"type": "none",
"dependencies": [ "realm-core" ], # sync headers include core headers
"direct_dependent_settings": {
"conditions": [
["use_realm_debug!=''", {
"libraries": [ "-lrealm-sync-node-dbg" ]
}, {
"libraries": [ "-lrealm-sync-node" ]
}]
]
},
"export_dependent_settings": [ "realm-core" ], # depending on sync is tantamount to depending on core
"variables": {
"prefix": "<!(echo $REALM_SYNC_PREFIX)"
},
"conditions": [
["prefix!=''", {
"all_dependent_settings": {
"include_dirs": [ "<(prefix)/src" ],
},
"direct_dependent_settings": {
"library_dirs": [ "<(prefix)/src/realm" ]
}
}, {
"dependencies": [ "vendored-realm" ]
}]
],
},
{
"variables": {
"vendor_dir%": "<(module_root_dir)/vendor",
},
"target_name": "vendored-realm",
"type": "none",
"conditions": [
["realm_enable_sync", {
"all_dependent_settings": {
"include_dirs": [ "<(module_root_dir)/vendor/node-sync/include" ],
"library_dirs": [ "<(module_root_dir)/vendor/node-sync/osx" ]
}
}, {
"all_dependent_settings": {
"include_dirs": [ "<(module_root_dir)/vendor/core-node/include" ],
"library_dirs": [ "<(module_root_dir)/vendor/core-node" ]
},
}],
["realm_download_binaries", {
"actions": [
{
"action_name": "download-realm",
"inputs": [ ],
"outputs": [ "<(module_root_dir)/vendor/core-node" ],
"action": [ "<(module_root_dir)/scripts/download-core.sh", "node", "<(realm_enable_sync)" ]
}
]
}]
]
}
]
}

View File

@ -0,0 +1,40 @@
{
"target_defaults": {
"variables": {
"warning-flags": [
"-Wno-missing-field-initializers",
"-Wno-return-type",
"-Wno-unused-result",
"-Wno-deprecated-declarations",
"-Wundef"
]
},
"cflags_cc!": [ # turn off default flags on older nodes on linux
"-fno-exceptions",
"-fno-rtti",
"-std=gnu++0x"
],
"cflags_cc": [
"-fexceptions",
"-frtti",
"-std=c++14",
"<@(warning-flags)"
],
"include_dirs": [
"<!(node -e \"require('nan')\")"
],
"conditions": [
["OS=='mac'", {
"xcode_settings": {
"CLANG_CXX_LANGUAGE_STANDARD": "c++14",
"CLANG_CXX_LIBRARY": "libc++",
"GCC_ENABLE_CPP_EXCEPTIONS": "YES",
"GCC_ENABLE_CPP_RTTI": "YES",
"MACOSX_DEPLOYMENT_TARGET": "10.8",
"OTHER_LDFLAGS": ["-framework Foundation"],
"WARNING_CFLAGS": [ "<@(warning-flags)" ]
}
}]
]
}
}

View File

@ -49,6 +49,8 @@ class ObjectWrap : public Nan::ObjectWrap {
return Nan::New(js_template);
}
static void construct(const v8::FunctionCallbackInfo<v8::Value>&);
static bool has_instance(v8::Isolate* isolate, const v8::Local<v8::Value> &value) {
return get_template()->HasInstance(value);
}
@ -73,27 +75,26 @@ class ObjectWrap : public Nan::ObjectWrap {
static v8::Local<v8::FunctionTemplate> create_template();
static void setup_method(v8::Local<v8::FunctionTemplate>, const std::string &, Nan::FunctionCallback);
static void setup_static_method(v8::Local<v8::FunctionTemplate>, const std::string &, Nan::FunctionCallback);
static void setup_method(v8::Local<v8::FunctionTemplate>, const std::string &, v8::FunctionCallback);
static void setup_static_method(v8::Local<v8::FunctionTemplate>, const std::string &, v8::FunctionCallback);
template<typename TargetType>
static void setup_property(v8::Local<TargetType>, const std::string &, const PropertyType &);
static void construct(Nan::NAN_METHOD_ARGS_TYPE);
static void get_indexes(Nan::NAN_INDEX_ENUMERATOR_ARGS_TYPE);
static void set_property(v8::Local<v8::String>, v8::Local<v8::Value>, Nan::NAN_PROPERTY_SETTER_ARGS_TYPE);
static void get_indexes(const v8::PropertyCallbackInfo<v8::Array>&);
static void set_property(v8::Local<v8::String>, v8::Local<v8::Value>, const v8::PropertyCallbackInfo<v8::Value>&);
static void set_readonly_property(v8::Local<v8::String> property, v8::Local<v8::Value> value, Nan::NAN_SETTER_ARGS_TYPE info) {
static void set_readonly_property(v8::Local<v8::String> property, v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<void>& info) {
std::string message = std::string("Cannot assign to read only property '") + std::string(String(property)) + "'";
Nan::ThrowError(message.c_str());
}
static void set_readonly_index(uint32_t index, v8::Local<v8::Value> value, Nan::NAN_INDEX_SETTER_ARGS_TYPE info) {
static void set_readonly_index(uint32_t index, v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<v8::Value>& info) {
std::string message = std::string("Cannot assign to read only index ") + util::to_string(index);
Nan::ThrowError(message.c_str());
}
static void get_nonexistent_property(v8::Local<v8::String>, Nan::NAN_PROPERTY_GETTER_ARGS_TYPE) {
static void get_nonexistent_property(v8::Local<v8::String>, const v8::PropertyCallbackInfo<v8::Value>&) {
// Do nothing. This function exists only to prevent a crash where it is used.
}
};
@ -109,7 +110,7 @@ class ObjectWrap<void> {
};
// This helper function is needed outside the scope of the ObjectWrap class as well.
static inline std::vector<v8::Local<v8::Value>> get_arguments(const Nan::FunctionCallbackInfo<v8::Value> &info) {
static inline std::vector<v8::Local<v8::Value>> get_arguments(const v8::FunctionCallbackInfo<v8::Value> &info) {
int count = info.Length();
std::vector<v8::Local<v8::Value>> arguments;
arguments.reserve(count);
@ -156,7 +157,7 @@ template<typename ClassType>
inline v8::Local<v8::FunctionTemplate> ObjectWrap<ClassType>::create_template() {
Nan::EscapableHandleScope scope;
v8::Local<v8::FunctionTemplate> tpl = Nan::New<v8::FunctionTemplate>(construct);
v8::Local<v8::FunctionTemplate> tpl = v8::FunctionTemplate::New(v8::Isolate::GetCurrent(), construct);
v8::Local<v8::ObjectTemplate> instance_tpl = tpl->InstanceTemplate();
v8::Local<v8::String> name = Nan::New(s_class.name).ToLocalChecked();
@ -181,21 +182,21 @@ inline v8::Local<v8::FunctionTemplate> ObjectWrap<ClassType>::create_template()
if (s_class.index_accessor.getter) {
auto &index_accessor = s_class.index_accessor;
Nan::SetIndexedPropertyHandler(instance_tpl, index_accessor.getter, index_accessor.setter ?: set_readonly_index, 0, 0, get_indexes);
instance_tpl->SetIndexedPropertyHandler(index_accessor.getter, index_accessor.setter ?: set_readonly_index, 0, 0, get_indexes);
}
if (s_class.string_accessor.getter || s_class.index_accessor.getter || s_class.index_accessor.setter) {
// Use our own wrapper for the setter since we want to throw for negative indices.
auto &string_accessor = s_class.string_accessor;
Nan::SetNamedPropertyHandler(instance_tpl, string_accessor.getter ?: get_nonexistent_property, set_property, 0, 0, string_accessor.enumerator);
instance_tpl->SetNamedPropertyHandler(string_accessor.getter ?: get_nonexistent_property, set_property, 0, 0, string_accessor.enumerator);
}
return scope.Escape(tpl);
}
template<typename ClassType>
inline void ObjectWrap<ClassType>::setup_method(v8::Local<v8::FunctionTemplate> tpl, const std::string &name, Nan::FunctionCallback callback) {
inline void ObjectWrap<ClassType>::setup_method(v8::Local<v8::FunctionTemplate> tpl, const std::string &name, v8::FunctionCallback callback) {
v8::Local<v8::Signature> signature = Nan::New<v8::Signature>(tpl);
v8::Local<v8::FunctionTemplate> fn_tpl = Nan::New<v8::FunctionTemplate>(callback, v8::Local<v8::Value>(), signature);
v8::Local<v8::FunctionTemplate> fn_tpl = v8::FunctionTemplate::New(v8::Isolate::GetCurrent(), callback, v8::Local<v8::Value>(), signature);
v8::Local<v8::String> fn_name = Nan::New(name).ToLocalChecked();
// The reason we use this rather than Nan::SetPrototypeMethod is DontEnum.
@ -204,8 +205,8 @@ inline void ObjectWrap<ClassType>::setup_method(v8::Local<v8::FunctionTemplate>
}
template<typename ClassType>
inline void ObjectWrap<ClassType>::setup_static_method(v8::Local<v8::FunctionTemplate> tpl, const std::string &name, Nan::FunctionCallback callback) {
v8::Local<v8::FunctionTemplate> fn_tpl = Nan::New<v8::FunctionTemplate>(callback);
inline void ObjectWrap<ClassType>::setup_static_method(v8::Local<v8::FunctionTemplate> tpl, const std::string &name, v8::FunctionCallback callback) {
v8::Local<v8::FunctionTemplate> fn_tpl = v8::FunctionTemplate::New(v8::Isolate::GetCurrent(), callback);
v8::Local<v8::String> fn_name = Nan::New(name).ToLocalChecked();
tpl->Set(fn_name, fn_tpl, v8::PropertyAttribute::DontEnum);
@ -218,11 +219,11 @@ inline void ObjectWrap<ClassType>::setup_property(v8::Local<TargetType> target,
v8::Local<v8::String> prop_name = Nan::New(name).ToLocalChecked();
v8::PropertyAttribute attributes = v8::PropertyAttribute(v8::DontEnum | v8::DontDelete);
Nan::SetAccessor(target, prop_name, property.getter, property.setter ?: set_readonly_property, v8::Local<v8::Value>(), v8::DEFAULT, attributes);
target->SetAccessor(prop_name, property.getter, property.setter ?: set_readonly_property, v8::Local<v8::Value>(), v8::DEFAULT, attributes);
}
template<typename ClassType>
inline void ObjectWrap<ClassType>::construct(Nan::NAN_METHOD_ARGS_TYPE info) {
inline void ObjectWrap<ClassType>::construct(const v8::FunctionCallbackInfo<v8::Value>& info) {
if (!info.IsConstructCall()) {
Nan::ThrowError("Constructor must be called with new");
}
@ -248,7 +249,7 @@ inline void ObjectWrap<ClassType>::construct(Nan::NAN_METHOD_ARGS_TYPE info) {
}
template<typename ClassType>
inline void ObjectWrap<ClassType>::get_indexes(Nan::NAN_INDEX_ENUMERATOR_ARGS_TYPE info) {
inline void ObjectWrap<ClassType>::get_indexes(const v8::PropertyCallbackInfo<v8::Array>& info) {
uint32_t length;
try {
length = Object::validated_get_length(info.GetIsolate(), info.This());
@ -267,7 +268,7 @@ inline void ObjectWrap<ClassType>::get_indexes(Nan::NAN_INDEX_ENUMERATOR_ARGS_TY
}
template<typename ClassType>
inline void ObjectWrap<ClassType>::set_property(v8::Local<v8::String> property, v8::Local<v8::Value> value, Nan::NAN_PROPERTY_SETTER_ARGS_TYPE info) {
inline void ObjectWrap<ClassType>::set_property(v8::Local<v8::String> property, v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<v8::Value>& info) {
if (s_class.index_accessor.getter || s_class.index_accessor.setter) {
try {
// Negative indices are passed into this string property interceptor, so check for them here.
@ -294,7 +295,7 @@ template<typename ClassType>
class ObjectWrap<node::Types, ClassType> : public node::ObjectWrap<ClassType> {};
template<node::MethodType F>
void wrap(Nan::NAN_METHOD_ARGS_TYPE info) {
void wrap(const v8::FunctionCallbackInfo<v8::Value>& info) {
v8::Isolate* isolate = info.GetIsolate();
node::ReturnValue return_value(info.GetReturnValue());
auto arguments = node::get_arguments(info);
@ -308,7 +309,7 @@ void wrap(Nan::NAN_METHOD_ARGS_TYPE info) {
}
template<node::PropertyType::GetterType F>
void wrap(v8::Local<v8::String> property, Nan::NAN_GETTER_ARGS_TYPE info) {
void wrap(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
v8::Isolate* isolate = info.GetIsolate();
node::ReturnValue return_value(info.GetReturnValue());
try {
@ -320,7 +321,7 @@ void wrap(v8::Local<v8::String> property, Nan::NAN_GETTER_ARGS_TYPE info) {
}
template<node::PropertyType::SetterType F>
void wrap(v8::Local<v8::String> property, v8::Local<v8::Value> value, Nan::NAN_SETTER_ARGS_TYPE info) {
void wrap(v8::Local<v8::String> property, v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<void>& info) {
v8::Isolate* isolate = info.GetIsolate();
try {
F(isolate, info.This(), value);
@ -331,7 +332,7 @@ void wrap(v8::Local<v8::String> property, v8::Local<v8::Value> value, Nan::NAN_S
}
template<node::IndexPropertyType::GetterType F>
void wrap(uint32_t index, Nan::NAN_INDEX_GETTER_ARGS_TYPE info) {
void wrap(uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& info) {
v8::Isolate* isolate = info.GetIsolate();
node::ReturnValue return_value(info.GetReturnValue());
try {
@ -347,7 +348,7 @@ void wrap(uint32_t index, Nan::NAN_INDEX_GETTER_ARGS_TYPE info) {
}
template<node::IndexPropertyType::SetterType F>
void wrap(uint32_t index, v8::Local<v8::Value> value, Nan::NAN_INDEX_SETTER_ARGS_TYPE info) {
void wrap(uint32_t index, v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<v8::Value>& info) {
v8::Isolate* isolate = info.GetIsolate();
try {
if (F(isolate, info.This(), index, value)) {
@ -361,7 +362,7 @@ void wrap(uint32_t index, v8::Local<v8::Value> value, Nan::NAN_INDEX_SETTER_ARGS
}
template<node::StringPropertyType::GetterType F>
void wrap(v8::Local<v8::String> property, Nan::NAN_PROPERTY_GETTER_ARGS_TYPE info) {
void wrap(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
v8::Isolate* isolate = info.GetIsolate();
node::ReturnValue return_value(info.GetReturnValue());
try {
@ -373,7 +374,7 @@ void wrap(v8::Local<v8::String> property, Nan::NAN_PROPERTY_GETTER_ARGS_TYPE inf
}
template<node::StringPropertyType::SetterType F>
void wrap(v8::Local<v8::String> property, v8::Local<v8::Value> value, Nan::NAN_PROPERTY_SETTER_ARGS_TYPE info) {
void wrap(v8::Local<v8::String> property, v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<v8::Value>& info) {
v8::Isolate* isolate = info.GetIsolate();
try {
if (F(isolate, info.This(), property, value)) {
@ -387,7 +388,7 @@ void wrap(v8::Local<v8::String> property, v8::Local<v8::Value> value, Nan::NAN_P
}
template<node::StringPropertyType::EnumeratorType F>
void wrap(Nan::NAN_PROPERTY_ENUMERATOR_ARGS_TYPE info) {
void wrap(const v8::PropertyCallbackInfo<v8::Array>& info) {
auto names = F(info.GetIsolate(), info.This());
int count = (int)names.size();
v8::Local<v8::Array> array = Nan::New<v8::Array>(count);

View File

@ -17,6 +17,7 @@
////////////////////////////////////////////////////////////////////////////
#include "node_init.hpp"
#include "js_realm.hpp"
namespace realm {
namespace node {

View File

@ -27,5 +27,3 @@
#include "node_exception.hpp"
#include "node_return_value.hpp"
#include "node_object_accessor.hpp"
#include "js_realm.hpp"

View File

@ -154,6 +154,16 @@ inline void node::Object::set_internal(const v8::Local<v8::Object> &object, type
auto wrap = Nan::ObjectWrap::Unwrap<node::ObjectWrap<ClassType>>(object);
*wrap = ptr;
}
template<>
inline void node::Object::set_global(v8::Isolate* isolate, const node::String &key, const v8::Local<v8::Value> &value) {
Object::set_property(isolate, isolate->GetCurrentContext()->Global(), key, value);
}
template<>
inline v8::Local<v8::Value> node::Object::get_global(v8::Isolate* isolate, const node::String &key) {
return Object::get_property(isolate, isolate->GetCurrentContext()->Global(), key);
}
} // js
} // realm

View File

@ -35,7 +35,7 @@ class Protected {
operator v8::Local<MemberType>() const {
return Nan::New(m_value);
}
operator bool() const {
explicit operator bool() const {
return m_value.isEmpty();
}
bool operator==(const v8::Local<MemberType> &other) const {
@ -50,6 +50,12 @@ class Protected {
bool operator!=(const Protected<MemberType> &other) const {
return m_value != other.m_value;
}
struct Comparator {
bool operator()(const Protected<MemberType>& a, const Protected<MemberType>& b) const {
return Nan::New(a.m_value)->StrictEquals(Nan::New(b.m_value));
}
};
};
} // node
@ -88,5 +94,13 @@ class Protected<node::Types::Function> : public node::Protected<v8::Function> {
Protected(v8::Isolate* isolate, v8::Local<v8::Function> object) : node::Protected<v8::Function>(object) {}
};
template<typename T>
struct GlobalCopyablePersistentTraits {
typedef v8::Persistent<T, GlobalCopyablePersistentTraits<T>> CopyablePersistent;
static const bool kResetInDestructor = false;
template<typename S, typename M>
static inline void Copy(const v8::Persistent<S, M> &source, CopyablePersistent *dest) {}
};
} // js
} // realm

View File

@ -25,10 +25,10 @@ namespace js {
template<>
class ReturnValue<node::Types> {
Nan::ReturnValue<v8::Value> m_value;
v8::ReturnValue<v8::Value> m_value;
public:
ReturnValue(Nan::ReturnValue<v8::Value> value) : m_value(value) {}
ReturnValue(v8::ReturnValue<v8::Value> value) : m_value(value) {}
void set(const v8::Local<v8::Value> &value) {
m_value.Set(value);

View File

@ -23,7 +23,10 @@
#include <map>
#include <string>
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wundef"
#include <nan.h>
#pragma GCC diagnostic pop
#include "js_types.hpp"
@ -31,6 +34,8 @@
#define REALM_V8_ARRAY_BUFFER_API 1
#endif
#define HANDLESCOPE Nan::HandleScope handle_scope;
namespace realm {
namespace node {
@ -42,15 +47,15 @@ struct Types {
using String = v8::Local<v8::String>;
using Function = v8::Local<v8::Function>;
using ConstructorCallback = Nan::FunctionCallback;
using FunctionCallback = Nan::FunctionCallback;
using PropertyGetterCallback = Nan::GetterCallback;
using PropertySetterCallback = Nan::SetterCallback;
using IndexPropertyGetterCallback = Nan::IndexGetterCallback;
using IndexPropertySetterCallback = Nan::IndexSetterCallback;
using StringPropertyGetterCallback = Nan::PropertyGetterCallback;
using StringPropertySetterCallback = Nan::PropertySetterCallback;
using StringPropertyEnumeratorCallback = Nan::PropertyEnumeratorCallback;
using ConstructorCallback = v8::FunctionCallback;
using FunctionCallback = v8::FunctionCallback;
using PropertyGetterCallback = v8::AccessorGetterCallback;
using PropertySetterCallback = v8::AccessorSetterCallback;
using IndexPropertyGetterCallback = v8::IndexedPropertyGetterCallback;
using IndexPropertySetterCallback = v8::IndexedPropertySetterCallback;
using StringPropertyGetterCallback = v8::NamedPropertyGetterCallback;
using StringPropertySetterCallback = v8::NamedPropertySetterCallback;
using StringPropertyEnumeratorCallback = v8::NamedPropertyEnumeratorCallback;
};
template<typename ClassType>

View File

@ -129,7 +129,7 @@ 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 (isnan(number)) {
if (std::isnan(number)) {
throw std::invalid_argument("Value not convertible to a number.");
}
return number;

@ -1 +1 @@
Subproject commit c3e4be6d6cd0bf3e010e169b9793087e60eb52bb
Subproject commit 1c2f3a7607467ed904a81a4824d0cbda33cc0ebb

View File

@ -1,7 +1,9 @@
{
"env": {
"commonjs": true
"jasmine": true
},
"plugins": ["jasmine"],
"extends": "plugin:jasmine/recommended",
"globals": {
"ArrayBuffer": false,
"DataView": false,
@ -14,5 +16,12 @@
"Uint32Array": false,
"Uint8Array": false,
"Uint8ClampedArray": false
},
"rules": {
"no-invalid-this": "off",
"no-magic-numbers": "off",
"jasmine/no-spec-dupes": ["error", "branch"],
"jasmine/no-suite-dupes": ["error", "branch"],
"no-console": "off"
}
}

2
tests/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/sync-bundle/
/junitresults-*.xml

View File

@ -23,14 +23,13 @@
const fs = require('fs');
const path = require('path');
const mockery = require('mockery');
function runTests() {
const Realm = require('realm');
const RealmTests = require('./js');
RealmTests.registerTests({
WorkerTests: require('./js/worker-tests'),
AsyncTests: require('./js/async-tests'),
});
const testNames = RealmTests.getTestNames();
@ -79,10 +78,6 @@ function runTests() {
}
if (require.main == module) {
mockery.enable();
mockery.warnOnUnregistered(false);
mockery.registerMock('realm', require('..'));
runTests().then(
(passed) => {
if (!passed) {

View File

@ -27,5 +27,6 @@
- (JSValue *)loadModuleFromURL:(NSURL *)url error:(NSError **)error;
- (JSValue *)loadJSONFromURL:(NSURL *)url error:(NSError **)error;
- (JSValue *)loadGlobalModule:(NSString *)name relativeToURL:(NSURL *)url error:(NSError **)error;
@end

View File

@ -75,6 +75,9 @@ static NSString * const RJSModuleLoaderErrorDomain = @"RJSModuleLoaderErrorDomai
- (JSValue *)loadModuleFromURL:(NSURL *)url error:(NSError **)error {
url = url.absoluteURL;
url = url.standardizedURL ?: url;
if (url.pathExtension.length == 0) {
url = [url URLByAppendingPathExtension:@"js"];
}
NSString *path = url.path;
JSValue *exports = self.modules[path];
@ -156,7 +159,10 @@ static NSString * const RJSModuleLoaderErrorDomain = @"RJSModuleLoaderErrorDomai
- (JSValue *)loadJSONFromURL:(NSURL *)url error:(NSError **)error {
url = url.absoluteURL;
url = url.standardizedURL ?: url;
if (url.pathExtension.length == 0) {
url = [url URLByAppendingPathExtension:@"js"];
}
NSString *path = url.path;
JSValue *exports = self.modules[path];
if (exports) {
@ -195,6 +201,18 @@ static NSString * const RJSModuleLoaderErrorDomain = @"RJSModuleLoaderErrorDomai
BOOL isDirectory;
if ([fileManager fileExistsAtPath:moduleURL.path isDirectory:&isDirectory] && isDirectory) {
NSURL *packageURL = [moduleURL URLByAppendingPathComponent:@"package.json"];
NSDictionary *package;
if ([fileManager fileExistsAtPath:packageURL.path]) {
NSError *error;
NSData *data = [NSData dataWithContentsOfURL:packageURL options:0 error:&error];
package = data ? [NSJSONSerialization JSONObjectWithData:data options:0 error:&error] : nil;
NSAssert(package, @"%@", error);
}
moduleURL = [moduleURL URLByAppendingPathComponent:package[@"main"] ?: @"index.js"];
return [self loadModuleFromURL:moduleURL error:error];
}

View File

@ -31,12 +31,23 @@
+ (XCTestSuite *)defaultTestSuite {
XCTestSuite *suite = [super defaultTestSuite];
JSContext *context = [[JSContext alloc] init];
// We need a JS context from a UIWebView so it has setTimeout, Promise, etc.
UIWebView *webView = [[UIWebView alloc] init];
JSContext *context = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
RJSModuleLoader *moduleLoader = [[RJSModuleLoader alloc] initWithContext:context];
NSURL *realmURL = [[NSBundle bundleForClass:self] URLForResource:@"index" withExtension:@"js" subdirectory:@"lib"];
NSURL *scriptURL = [[NSBundle bundleForClass:self] URLForResource:@"index" withExtension:@"js" subdirectory:@"js"];
NSError *error;
// The ES6 global Promise constructor was added in iOS 8.
if (![context[@"Promise"] isObject]) {
JSValue *promiseModule = [moduleLoader loadGlobalModule:@"es6-promise" relativeToURL:scriptURL error:&error];
NSAssert(promiseModule, @"%@", error);
context[@"Promise"] = promiseModule[@"Promise"];
}
// Create Realm constructor in the JS context.
RJSInitializeInContext(context.JSGlobalContextRef);
@ -73,25 +84,46 @@
JSContext *context = testObject.context;
context.exception = nil;
[testObject invokeMethod:@"runTest" withArguments:@[NSStringFromClass(self.class), method]];
JSValue *promise = [testObject invokeMethod:@"runTest" withArguments:@[NSStringFromClass(self.class), method]];
JSValue *exception = context.exception;
if (exception) {
JSValue *message = [exception hasProperty:@"message"] ? exception[@"message"] : exception;
NSString *source = [exception hasProperty:@"sourceURL"] ? [exception[@"sourceURL"] toString] : nil;
NSUInteger line = [exception hasProperty:@"line"] ? [exception[@"line"] toUInt32] - 1 : 0;
NSURL *sourceURL = nil;
if (context.exception) {
[self recordException:context.exception];
return;
}
if (source) {
NSString *path = [NSString pathWithComponents:@[[@(__FILE__) stringByDeletingLastPathComponent], @"..", @"js", source.lastPathComponent]];
sourceURL = [NSURL URLWithString:path];
}
if ([promise isObject]) {
XCTestExpectation *expectation = [self expectationWithDescription:@"Promise resolved or rejected"];
[self recordFailureWithDescription:message.description
inFile:sourceURL ? sourceURL.absoluteString : @(__FILE__)
atLine:sourceURL ? line : __LINE__
expected:YES];
JSValue *onFulfilled = [JSValue valueWithObject:^() {
[expectation fulfill];
} inContext:context];
JSValue *onRejected = [JSValue valueWithObject:^(JSValue *error) {
[self recordException:error];
[expectation fulfill];
} inContext:context];
[promise invokeMethod:@"then" withArguments:@[onFulfilled, onRejected]];
[self waitForExpectationsWithTimeout:5.0 handler:NULL];
}
}
- (void)recordException:(JSValue *)exception {
JSValue *message = [exception hasProperty:@"message"] ? exception[@"message"] : exception;
NSString *source = [exception hasProperty:@"sourceURL"] ? [exception[@"sourceURL"] toString] : nil;
NSUInteger line = [exception hasProperty:@"line"] ? [exception[@"line"] toUInt32] - 1 : 0;
NSURL *sourceURL = nil;
if (source) {
NSString *path = [NSString pathWithComponents:@[[@(__FILE__) stringByDeletingLastPathComponent], @"..", @"js", source.lastPathComponent]];
sourceURL = [NSURL URLWithString:path];
}
[self recordFailureWithDescription:message.description
inFile:sourceURL ? sourceURL.absoluteString : @(__FILE__)
atLine:sourceURL ? line : __LINE__
expected:YES];
}
@end

View File

@ -49,6 +49,19 @@ module.exports = {
}
},
assertArray: function(value, length, errorMessage) {
if (!Array.isArray(value)) {
throw new TestFailureError(errorMessage || `Value ${value} is not an array`);
}
},
assertArrayLength: function(value, length, errorMessage) {
this.assertArray(value);
if (value.length !== length) {
throw new TestFailureError(errorMessage || `Value ${value} is not an array of length ${length}`);
}
},
assertArraysEqual: function(val1, val2, errorMessage) {
var len1 = val1.length;
var len2 = val2.length;
@ -106,7 +119,23 @@ module.exports = {
assertTrue: function(condition, errorMessage) {
if (!condition) {
throw new TestFailureError(errorMessage || 'Condition expected to be true');
throw new TestFailureError(errorMessage || `Condition ${condition} expected to be true`);
}
},
assertInstanceOf: function(object, type, errorMessage) {
if (!(object instanceof type)) {
throw new TestFailureError(errorMessage || `Object ${object} expected to be of type ${type}`);
}
},
assertType: function(value, type) {
this.assertEqual(typeof value, type, `Value ${value} expected to be of type ${type}`);
},
assertUndefined: function(value, errorMessage) {
if (value !== undefined) {
throw new TestFailureError(errorMessage || `Value ${value} expected to be undefined`);
}
},

360
tests/js/async-tests.js Normal file
View File

@ -0,0 +1,360 @@
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
/* eslint-env es6, node */
'use strict';
const Realm = require('realm');
const TestCase = require('./asserts');
const schemas = require('./schemas');
const Worker = require('./worker');
function createNotificationTest(config, getObservable, addListener, removeListener, messages, expectedCount) {
return new Promise((resolve, reject) => {
let realm = new Realm(config);
let observable = getObservable(realm);
let worker = new Worker(__dirname + '/worker-tests-script.js');
// Test will fail if it does not receive a change event within a second.
let timer = setTimeout(() => {
reject(new Error('Timed out waiting for change notification'));
}, 5000);
let cleanup = () => {
clearTimeout(timer);
worker.terminate();
};
var count = 0;
var listener = addListener(observable, () => count++, resolve, reject, cleanup);
messages.push(['echo', 'resolve']);
var messageIndex = 0;
worker.onmessage = (message) => {
if (message.error) {
reject(message.error);
cleanup();
}
else if (message.result == 'resolve') {
if (count != expectedCount) {
reject('Notification count ' + count + ' not equal to expected count ' + expectedCount);
}
else {
resolve();
}
cleanup();
}
else {
if (message.result == 'removeListener') {
removeListener(observable, listener);
}
worker.postMessage(messages[messageIndex++]);
}
};
worker.postMessage(messages[messageIndex++]);
});
}
function createCollectionChangeTest(config, createCollection, messages, expected, removeAll) {
return createNotificationTest(
config,
createCollection,
(collection, increment, resolve, reject, cleanup) => {
var listener = (object, changes) => {
try {
var notificationCount = increment();
TestCase.assertArraysEqual(changes.insertions, expected[notificationCount][0]);
TestCase.assertArraysEqual(changes.deletions, expected[notificationCount][1]);
TestCase.assertArraysEqual(changes.modifications, expected[notificationCount][2]);
} catch (e) {
reject(e);
cleanup();
}
};
collection.addListener(listener);
return listener;
},
removeAll ? (observable) => observable.removeAllListeners() :
(observable, listener) => observable.removeListener(listener),
messages,
expected.length
);
}
const ListObject = {
name: 'ListObject',
properties: {
list: {type: 'list', objectType: 'TestObject'},
}
};
const PrimaryListObject = {
name: 'PrimaryListObject',
properties: {
list: {type: 'list', objectType: 'IntPrimaryObject'},
}
};
module.exports = {
testChangeNotifications() {
var config = { schema: [schemas.TestObject] };
return createNotificationTest(
config,
(realm) => realm,
(realm, increment, resolve, reject, cleanup) => realm.addListener('change', () => {
try {
var objects = realm.objects('TestObject');
TestCase.assertEqual(objects.length, 1);
TestCase.assertEqual(objects[0].doubleCol, 42);
increment();
} catch (e) {
reject(e);
cleanup();
}
}),
undefined,
[[config, 'create', 'TestObject', [{doubleCol: 42}]]],
1
);
},
testResultsAddNotifications() {
var config = { schema: [schemas.TestObject] };
return createCollectionChangeTest(
config,
(realm) => realm.objects('TestObject'),
[
[config, 'create', 'TestObject', [{ doubleCol: 1 }]],
[config, 'create', 'TestObject', [{ doubleCol: 2 }, { doubleCol: 3 }]]
],
[
[[], [], []],
[[0], [], []],
[[1, 2], [], []],
]
);
},
testResultsRemoveNotifications() {
var config = { schema: [schemas.TestObject] };
return createCollectionChangeTest(
config,
(realm) => realm.objects('TestObject'),
[
[config, 'create', 'TestObject', [{ doubleCol: 1 }]],
['echo', 'removeListener'],
[config, 'create', 'TestObject', [{ doubleCol: 2 }, { doubleCol: 3 }]]
],
[
[[], [], []],
[[0], [], []],
]
);
},
testResultsRemoveAllNotifications() {
var config = { schema: [schemas.TestObject] };
return createCollectionChangeTest(
config,
(realm) => realm.objects('TestObject'),
[
[config, 'create', 'TestObject', [{ doubleCol: 1 }]],
['echo', 'removeListener'],
[config, 'create', 'TestObject', [{ doubleCol: 2 }, { doubleCol: 3 }]]
],
[
[[], [], []],
[[0], [], []],
],
true
);
},
testResultsDeleteNotifications() {
var config = { schema: [schemas.TestObject] };
return createCollectionChangeTest(
config,
function(realm) {
return realm.objects('TestObject');
},
[
[config, 'create', 'TestObject', [[0], [1], [2], [3], [4]]],
[config, 'delete', 'TestObject', [4]],
[config, 'delete', 'TestObject', [0, 2]]
],
[
[[], [], []],
[[0, 1, 2, 3, 4], [], []],
[[], [4], []],
[[0], [0, 2, 3], []]
]
);
},
testResultsUpdateNotifications() {
var config = { schema: [schemas.IntPrimary] };
return createCollectionChangeTest(
config,
(realm) => realm.objects('IntPrimaryObject'),
[
[config, 'create', 'IntPrimaryObject', [[0, '0'], [1, '1'], [2, '2']]],
[config, 'update', 'IntPrimaryObject', [[0, '00'], [2, '22']]]
],
[
[[], [], []],
[[0, 1, 2], [], []],
[[], [], [0, 2]]
]
);
},
testListAddNotifications() {
var config = { schema: [schemas.TestObject, ListObject] };
return createCollectionChangeTest(
config,
function(realm) {
let listObject;
realm.write(() => {
listObject = realm.create('ListObject', {list: []})
});
return listObject.list;
},
[
[config, 'list_method', 'ListObject', 'list', 'push', {doubleCol: 0}, {doubleCol: 1}]
],
[
[[], [], []],
[[0, 1], [], []]
]
);
},
testListRemoveNotifications() {
var config = { schema: [schemas.TestObject, ListObject] };
return createCollectionChangeTest(
config,
function(realm) {
let listObject;
realm.write(() => {
listObject = realm.create('ListObject', {list: []})
});
return listObject.list;
},
[
[config, 'list_method', 'ListObject', 'list', 'push', {doubleCol: 0}, {doubleCol: 1}],
['echo', 'removeListener'],
[config, 'list_method', 'ListObject', 'list', 'push', {doubleCol: 0}, {doubleCol: 1}],
],
[
[[], [], []],
[[0, 1], [], []]
]
);
},
testListRemoveAllNotifications() {
var config = { schema: [schemas.TestObject, ListObject] };
return createCollectionChangeTest(
config,
function(realm) {
let listObject;
realm.write(() => {
listObject = realm.create('ListObject', {list: []})
});
return listObject.list;
},
[
[config, 'list_method', 'ListObject', 'list', 'push', {doubleCol: 0}, {doubleCol: 1}],
['echo', 'removeListener'],
[config, 'list_method', 'ListObject', 'list', 'push', {doubleCol: 0}, {doubleCol: 1}],
],
[
[[], [], []],
[[0, 1], [], []]
],
true
);
},
testListDeleteNotifications() {
var config = { schema: [schemas.TestObject, ListObject] };
return createCollectionChangeTest(
config,
function(realm) {
let listObject;
realm.write(() => {
listObject = realm.create('ListObject', {list: [[0], [1], [2]]})
});
return listObject.list;
},
[
[config, 'list_method', 'ListObject', 'list', 'splice', 1, 2]
],
[
[[], [], []],
[[], [1, 2], []]
]
);
},
testListSpliceNotifications() {
var config = { schema: [schemas.TestObject, ListObject] };
return createCollectionChangeTest(
config,
function(realm) {
let listObject;
realm.write(() => {
listObject = realm.create('ListObject', {list: [[0], [1], [2]]})
});
return listObject.list;
},
[
[config, 'list_method', 'ListObject', 'list', 'splice', 1, 1, [2]]
],
[
[[], [], []],
[[1], [1], []]
]
);
},
testListUpdateNotifications() {
var config = { schema: [schemas.IntPrimary, PrimaryListObject] };
return createCollectionChangeTest(
config,
function(realm) {
let listObject;
realm.write(() => {
listObject = realm.create('PrimaryListObject', {list: [[0, '0'], [1, '1']]})
});
return listObject.list;
},
[
[config, 'update', 'IntPrimaryObject', [[1, '11']]]
],
[
[[], [], []],
[[], [], [1]]
]
);
},
};

View File

@ -1,5 +1,8 @@
{
"name": "realm-tests",
"version": "0.0.1",
"private": true
"private": true,
"dependencies": {
"es6-promise": "^3.2.1"
}
}

View File

@ -1 +0,0 @@
../../src/object-store/tests/query.json

381
tests/js/query-tests.json Normal file
View File

@ -0,0 +1,381 @@
{
"dateTests" : {
"schema" : [{
"name": "DateObject",
"properties": [{ "name": "date", "type": "date" }]
}],
"objects": [
{ "type": "DateObject", "value": [10000] },
{ "type": "DateObject", "value": [10001] },
{ "type": "DateObject", "value": [10002] }
],
"tests": [
["QueryCount", 2, "DateObject", "date < $0", [2, "date"]],
["QueryCount", 3, "DateObject", "date <= $0", [2, "date"]],
["QueryCount", 2, "DateObject", "date > $0", [0, "date"]],
["QueryCount", 3, "DateObject", "date >= $0", [0, "date"]],
["QueryCount", 1, "DateObject", "date == $0", [0, "date"]],
["QueryCount", 2, "DateObject", "date != $0", [0, "date"]],
["QueryThrows", "DateObject", "date == 'not a date'"],
["QueryThrows", "DateObject", "date == 1"],
["QueryThrows", "DateObject", "date == $0", 1]
]
},
"boolTests" : {
"schema" : [{
"name": "BoolObject",
"properties": [{ "name": "boolCol", "type": "bool" }]
}],
"objects": [
{ "type": "BoolObject", "value": [false] },
{ "type": "BoolObject", "value": [true] },
{ "type": "BoolObject", "value": [true] }
],
"tests": [
["QueryCount", 2, "BoolObject", "boolCol == true"],
["QueryCount", 1, "BoolObject", "boolCol==false"],
["QueryCount", 1, "BoolObject", "boolCol != true"],
["QueryCount", 2, "BoolObject", "true == boolCol"],
["QueryCount", 2, "BoolObject", "boolCol == TRUE"],
["QueryCount", 1, "BoolObject", "boolCol == FALSE"],
["QueryCount", 2, "BoolObject", "boolCol == $0", true],
["QueryCount", 1, "BoolObject", "boolCol == $0", false],
["QueryCount", 0, "BoolObject", "boolCol == true && boolCol == false"],
["QueryCount", 3, "BoolObject", "boolCol == true || boolCol == false"],
["QueryThrows", "BoolObject", "boolCol == 0"],
["QueryThrows", "BoolObject", "boolCol == 1"],
["QueryThrows", "BoolObject", "boolCol == 'not a bool'"],
["QueryThrows", "BoolObject", "boolCol == $0", "not a bool"],
["QueryThrows", "BoolObject", "boolCol > true"],
["QueryThrows", "BoolObject", "boolCol >= true"],
["QueryThrows", "BoolObject", "boolCol < true"],
["QueryThrows", "BoolObject", "boolCol <= true"],
["QueryThrows", "BoolObject", "boolCol BEGINSWITH true"],
["QueryThrows", "BoolObject", "boolCol CONTAINS true"],
["QueryThrows", "BoolObject", "boolCol ENDSWITH true"]
]
},
"intTests" : {
"schema" : [{
"name": "IntObject",
"properties": [{ "name": "intCol", "type": "int" }]
}],
"objects": [
{ "type": "IntObject", "value": [-1] },
{ "type": "IntObject", "value": [0] },
{ "type": "IntObject", "value": [100] }
],
"tests": [
["QueryCount", 1, "IntObject", "intCol == -1"],
["QueryCount", 1, "IntObject", "intCol==0"],
["QueryCount", 0, "IntObject", "1 == intCol"],
["QueryCount", 2, "IntObject", "intCol != 0"],
["QueryCount", 2, "IntObject", "intCol > -1"],
["QueryCount", 3, "IntObject", "intCol >= -1"],
["QueryCount", 2, "IntObject", "intCol < 100"],
["QueryCount", 3, "IntObject", "intCol <= 100"],
["QueryCount", 1, "IntObject", "intCol > 0x1F"],
["QueryCount", 1, "IntObject", "intCol == $0", 100],
["QueryThrows", "IntObject", "intCol == 'not an int'"],
["QueryThrows", "IntObject", "intCol == true"],
["QueryThrows", "IntObject", "intCol == $0", "not an int"],
["QueryThrows", "IntObject", "intCol BEGINSWITH 1"],
["QueryThrows", "IntObject", "intCol CONTAINS 1"],
["QueryThrows", "IntObject", "intCol ENDSWITH 1"]
]
},
"floatTests" : {
"schema" : [{
"name": "FloatObject",
"properties": [{ "name": "floatCol", "type": "float" }]
}],
"objects": [
{ "type": "FloatObject", "value": [-1.001] },
{ "type": "FloatObject", "value": [0.0] },
{ "type": "FloatObject", "value": [100.2] }
],
"tests": [
["QueryCount", 1, "FloatObject", "floatCol == -1.001"],
["QueryCount", 1, "FloatObject", "floatCol = 0"],
["QueryCount", 0, "FloatObject", "1 == floatCol"],
["QueryCount", 2, "FloatObject", "floatCol != 0"],
["QueryCount", 2, "FloatObject", "floatCol > -1.001"],
["QueryCount", 3, "FloatObject", "floatCol >= -1.001"],
["QueryCount", 2, "FloatObject", "floatCol < 100.2"],
["QueryCount", 3, "FloatObject", "floatCol <= 100.2"],
["QueryCount", 1, "FloatObject", "floatCol > 0x1F"],
["QueryCount", 1, "FloatObject", "floatCol == $0", 100.2],
["QueryThrows", "FloatObject", "floatCol == 'not a float'"],
["QueryThrows", "FloatObject", "floatCol == true"],
["QueryThrows", "FloatObject", "floatCol == $0", "not a float"],
["QueryThrows", "FloatObject", "floatCol BEGINSWITH 1"],
["QueryThrows", "FloatObject", "floatCol CONTAINS 1"],
["QueryThrows", "FloatObject", "floatCol ENDSWITH 1"],
["Disabled", "QueryThrows", "FloatObject", "floatCol = 3.5e+38"],
["Disabled", "QueryThrows", "FloatObject", "floatCol = -3.5e+38"]
]
},
"doubleTests" : {
"schema" : [{
"name": "DoubleObject",
"properties": [{ "name": "doubleCol", "type": "double" }]
}],
"objects": [
{ "type": "DoubleObject", "value": [-1.001] },
{ "type": "DoubleObject", "value": [0.0] },
{ "type": "DoubleObject", "value": [100.2] }
],
"tests": [
["QueryCount", 1, "DoubleObject", "doubleCol == -1.001"],
["QueryCount", 1, "DoubleObject", "doubleCol == 0"],
["QueryCount", 0, "DoubleObject", "1 == doubleCol"],
["QueryCount", 2, "DoubleObject", "doubleCol != 0"],
["QueryCount", 2, "DoubleObject", "doubleCol > -1.001"],
["QueryCount", 3, "DoubleObject", "doubleCol >= -1.001"],
["QueryCount", 2, "DoubleObject", "doubleCol < 100.2"],
["QueryCount", 3, "DoubleObject", "doubleCol <= 100.2"],
["QueryCount", 1, "DoubleObject", "doubleCol > 0x1F"],
["QueryCount", 1, "DoubleObject", "doubleCol == $0", 100.2],
["QueryThrows", "DoubleObject", "doubleCol == 'not a double'"],
["QueryThrows", "DoubleObject", "doubleCol == true"],
["QueryThrows", "DoubleObject", "doubleCol == $0", "not a double"],
["QueryThrows", "DoubleObject", "doubleCol BEGINSWITH 1"],
["QueryThrows", "DoubleObject", "doubleCol CONTAINS 1"],
["QueryThrows", "DoubleObject", "doubleCol ENDSWITH 1"]
]
},
"stringTests" : {
"schema" : [{
"name": "StringObject",
"properties": [{ "name": "stringCol", "type": "string" }]
}],
"objects": [
{ "type": "StringObject", "value": ["A"] },
{ "type": "StringObject", "value": ["a"] },
{ "type": "StringObject", "value": ["a"] },
{ "type": "StringObject", "value": ["C"] },
{ "type": "StringObject", "value": ["c"] },
{ "type": "StringObject", "value": ["abc"] },
{ "type": "StringObject", "value": ["ABC"] },
{ "type": "StringObject", "value": [""] },
{ "type": "StringObject", "value": ["\\\"\\n\\0\\r\\\\'"] }
],
"tests": [
["QueryCount", 2, "StringObject", "stringCol == 'a'"],
["QueryCount", 1, "StringObject", "'c' == stringCol"],
["QueryCount", 2, "StringObject", "stringCol == \"a\""],
["QueryCount", 1, "StringObject", "stringCol=='abc'"],
["QueryCount", 1, "StringObject", "stringCol == ''"],
["QueryCount", 8, "StringObject", "stringCol != ''"],
["QueryCount", 1, "StringObject", "stringCol == \"\\\"\\n\\0\\r\\\\'\""],
["QueryCount", 3, "StringObject", "stringCol BEGINSWITH 'a'"],
["QueryCount", 1, "StringObject", "stringCol beginswith 'ab'"],
["QueryCount", 0, "StringObject", "stringCol BEGINSWITH 'abcd'"],
["QueryCount", 2, "StringObject", "stringCol BEGINSWITH 'A'"],
["QueryCount", 2, "StringObject", "stringCol ENDSWITH 'c'"],
["QueryCount", 1, "StringObject", "stringCol endswith 'bc'"],
["QueryCount", 9, "StringObject", "stringCol ENDSWITH ''"],
["QueryCount", 1, "StringObject", "stringCol CONTAINS 'b'"],
["QueryCount", 2, "StringObject", "stringCol contains 'c'"],
["QueryCount", 9, "StringObject", "stringCol CONTAINS ''"],
["QueryCount", 2, "StringObject", "stringCol == $0", "a"],
["QueryCount", 2, "StringObject", "stringCol ENDSWITH $0", "c"],
["QueryThrows", "StringObject", "stringCol == true"],
["QueryThrows", "StringObject", "stringCol == 123"],
["QueryThrows", "StringObject", "stringCol CONTAINS $0", 1],
["QueryCount", 3, "StringObject", "stringCol ==[c] 'a'"],
["QueryCount", 5, "StringObject", "stringCol BEGINSWITH[c] 'A'"],
["QueryCount", 4, "StringObject", "stringCol ENDSWITH[c] 'c'"],
["QueryCount", 2, "StringObject", "stringCol CONTAINS[c] 'B'"]
]
},
"binaryTests" : {
"schema" : [{
"name": "BinaryObject",
"properties": [{ "name": "binaryCol", "type": "data" }]
}],
"objects": [
{ "type": "BinaryObject", "value": [[1, 100, 233, 255, 0]] },
{ "type": "BinaryObject", "value": [[1, 100]] },
{ "type": "BinaryObject", "value": [[100]] },
{ "type": "BinaryObject", "value": [[]] },
{ "type": "BinaryObject", "value": [[255, 0]] }
],
"tests": [
["QueryCount", 1, "BinaryObject", "binaryCol == $0", [1, "binaryCol"]],
["QueryCount", 1, "BinaryObject", "$0 == binaryCol", [2, "binaryCol"]],
["QueryCount", 4, "BinaryObject", "binaryCol != $0", [0, "binaryCol"]],
["QueryCount", 1, "BinaryObject", "binaryCol BEGINSWITH $0", [0, "binaryCol"]],
["QueryCount", 2, "BinaryObject", "binaryCol BEGINSWITH $0", [1, "binaryCol"]],
["QueryCount", 2, "BinaryObject", "binaryCol ENDSWITH $0", [4, "binaryCol"]],
["QueryCount", 3, "BinaryObject", "binaryCol CONTAINS $0", [2, "binaryCol"]]
]
},
"objectTests" : {
"schema" : [
{ "name": "IntObject", "properties": [
{ "name": "intCol", "type": "int" }
]},
{ "name": "LinkObject", "properties": [
{ "name": "linkCol", "type": "object", "objectType": "IntObject" }
]}
],
"objects": [
{ "type": "LinkObject", "value": [[1]] },
{ "type": "LinkObject", "value": [[2]] },
{ "type": "LinkObject", "value": [null] }
],
"tests": [
["QueryCount", 1, "LinkObject", "linkCol == $0", [0, "linkCol"]],
["QueryCount", 1, "LinkObject", "$0 == linkCol", [1, "linkCol"]],
["QueryCount", 2, "LinkObject", "linkCol != $0", [0, "linkCol"]],
["QueryCount", 1, "LinkObject", "linkCol = null"],
["QueryCount", 2, "LinkObject", "linkCol != NULL"],
["QueryCount", 1, "LinkObject", "linkCol = $0", null],
["QueryThrows", "LinkObject", "linkCol > $0", [0, "linkCol"]],
["QueryThrows", "LinkObject", "intCol = $0", [0, "linkCol"]]
]
},
"compoundTests" : {
"schema" : [
{ "name": "IntObject",
"properties": [{ "name": "intCol", "type": "int" }],
"primaryKey" : "intCol" }
],
"objects": [
{ "type": "IntObject", "value": [0] },
{ "type": "IntObject", "value": [1] },
{ "type": "IntObject", "value": [2] },
{ "type": "IntObject", "value": [3] }
],
"tests": [
["ObjectSet", [], "IntObject", "intCol == 0 && intCol == 1"],
["ObjectSet", [0, 1], "IntObject", "intCol == 0 || intCol == 1"],
["ObjectSet", [0], "IntObject", "intCol == 0 && intCol != 1"],
["ObjectSet", [2, 3], "IntObject", "intCol >= 2 && intCol < 4"],
["ObjectSet", [0], "IntObject", "intCol == 0 && NOT intCol != 0"],
["ObjectSet", [0, 3], "IntObject", "intCol == 0 || NOT (intCol == 1 || intCol == 2)"],
["ObjectSet", [1], "IntObject", "(intCol == 0 || intCol == 1) && intCol >= 1"],
["ObjectSet", [1], "IntObject", "intCol >= 1 && (intCol == 0 || intCol == 1)"],
["ObjectSet", [0, 1], "IntObject", "intCol == 0 || (intCol == 1 && intCol >= 1)"],
["ObjectSet", [0, 1], "IntObject", "(intCol == 1 && intCol >= 1) || intCol == 0"],
["ObjectSet", [0, 1], "IntObject", "intCol == 0 || intCol == 1 && intCol >= 1"],
["ObjectSet", [0, 1, 2],"IntObject", "intCol == 0 || intCol == 1 || intCol <= 2"],
["ObjectSet", [0, 1], "IntObject", "intCol == 1 && intCol >= 1 || intCol == 0"],
["ObjectSet", [0, 1], "IntObject", "intCol == 1 || intCol == 0 && intCol <= 0 && intCol >= 0"],
["ObjectSet", [0, 1], "IntObject", "intCol == 0 || NOT (intCol == 3 && intCol >= 0) && intCol == 1"]
]
},
"keyPathTests" : {
"schema" : [
{
"name": "BasicTypesObject",
"properties": [
{ "name": "intCol", "type": "int" },
{ "name": "floatCol", "type": "float" },
{ "name": "doubleCol", "type": "double" },
{ "name": "stringCol", "type": "string" },
{ "name": "dateCol", "type": "date" },
{ "name": "dataCol", "type": "data" }
]
},
{
"name": "LinkTypesObject",
"primaryKey": "primaryKey",
"properties": [
{ "name": "primaryKey", "type": "int" },
{ "name": "basicLink", "type": "object", "objectType": "BasicTypesObject" },
{ "name": "linkLink", "type": "object", "objectType": "LinkTypesObject" }
]
}],
"objects": [
{ "type": "LinkTypesObject", "value": [0, [1, 0.1, 0.001, "1", 1, [1, 10, 100]], null] },
{ "type": "LinkTypesObject", "value": [1, null, [2, [1, 0.1, 0.001, "1", 1, [1, 10, 100]], null]] },
{ "type": "LinkTypesObject", "value": [3, null, [4, [2, 0.2, 0.002, "2", 2, [2, 20, 200]], null]] }
],
"tests": [
["ObjectSet", [0, 2], "LinkTypesObject", "basicLink.intCol == 1"],
["ObjectSet", [1], "LinkTypesObject", "linkLink.basicLink.intCol == 1"],
["ObjectSet", [1, 3], "LinkTypesObject", "linkLink.basicLink.intCol > 0"],
["ObjectSet", [0, 2], "LinkTypesObject", "basicLink.floatCol == 0.1"],
["ObjectSet", [1], "LinkTypesObject", "linkLink.basicLink.floatCol == 0.1"],
["ObjectSet", [1, 3], "LinkTypesObject", "linkLink.basicLink.floatCol > 0"]
]
},
"optionalTests" : {
"schema" : [
{
"name": "OptionalTypesObject",
"primaryKey": "primaryKey",
"properties": [
{ "name": "primaryKey", "type": "int" },
{ "name": "intCol", "type": "int", "optional": true },
{ "name": "floatCol", "type": "float", "optional": true },
{ "name": "doubleCol", "type": "double", "optional": true },
{ "name": "stringCol", "type": "string", "optional": true },
{ "name": "dateCol", "type": "date", "optional": true },
{ "name": "dataCol", "type": "data", "optional": true }
]
},
{
"name": "LinkTypesObject",
"primaryKey": "primaryKey",
"properties": [
{ "name": "primaryKey", "type": "int" },
{ "name": "basicLink", "type": "object", "objectType": "OptionalTypesObject" }
]
}],
"objects": [
{ "type": "LinkTypesObject", "value": [0, [0, 1, 0.1, 0.001, "1", 1, [1, 10, 100]]] },
{ "type": "LinkTypesObject", "value": [1, [1, null, null, null, null, null, null]] }
],
"tests": [
["ObjectSet", [1], "OptionalTypesObject", "intCol == null"],
["ObjectSet", [1], "OptionalTypesObject", "null == intCol"],
["ObjectSet", [0], "OptionalTypesObject", "intCol != null"],
["ObjectSet", [1], "OptionalTypesObject", "floatCol == null"],
["ObjectSet", [0], "OptionalTypesObject", "floatCol != null"],
["ObjectSet", [1], "OptionalTypesObject", "doubleCol == null"],
["ObjectSet", [0], "OptionalTypesObject", "doubleCol != null"],
["ObjectSet", [1], "OptionalTypesObject", "stringCol == null"],
["ObjectSet", [0], "OptionalTypesObject", "stringCol != null"],
["ObjectSet", [1], "OptionalTypesObject", "dateCol == null"],
["ObjectSet", [0], "OptionalTypesObject", "dateCol != null"],
["ObjectSet", [1], "OptionalTypesObject", "dataCol == null"],
["ObjectSet", [0], "OptionalTypesObject", "dataCol != null"],
["ObjectSet", [1], "LinkTypesObject", "basicLink.intCol == null"],
["ObjectSet", [0], "LinkTypesObject", "basicLink.intCol != null"],
["ObjectSet", [1], "LinkTypesObject", "basicLink.floatCol == null"],
["ObjectSet", [0], "LinkTypesObject", "basicLink.floatCol != null"],
["ObjectSet", [1], "LinkTypesObject", "basicLink.doubleCol == null"],
["ObjectSet", [0], "LinkTypesObject", "basicLink.doubleCol != null"],
["ObjectSet", [1], "LinkTypesObject", "basicLink.stringCol == null"],
["ObjectSet", [0], "LinkTypesObject", "basicLink.stringCol != null"],
["ObjectSet", [1], "LinkTypesObject", "basicLink.dateCol == null"],
["ObjectSet", [0], "LinkTypesObject", "basicLink.dateCol != null"],
["QueryThrows", "LinkTypesObject", "basicLink.dataCol == null"]
]
}
}

View File

@ -33,7 +33,7 @@ module.exports = {
testRealmConstructorPath: function() {
TestCase.assertThrows(function() {
new Realm('/invalidpath');
new Realm('');
}, 'Realm cannot be created with an invalid path');
TestCase.assertThrows(function() {
new Realm('test1.realm', 'invalidArgument');

View File

@ -18,8 +18,6 @@
'use strict';
var Realm = require('realm');
exports.TestObject = {
name: 'TestObject',
properties: {

331
tests/js/user-tests.js Normal file
View File

@ -0,0 +1,331 @@
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
/* eslint-env es6, node */
'use strict';
const Realm = require('realm');
const TestCase = require('./asserts');
function uuid() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
function assertIsUser(user, isAdmin) {
TestCase.assertType(user, 'object');
TestCase.assertType(user.token, 'string');
TestCase.assertType(user.identity, 'string');
TestCase.assertInstanceOf(user, Realm.Sync.User);
if (isAdmin !== undefined) {
TestCase.assertEqual(user.isAdmin, isAdmin);
}
}
function assertIsSameUser(value, user) {
assertIsUser(value);
TestCase.assertEqual(value.token, user.token);
TestCase.assertEqual(value.identity, user.identity);
TestCase.assertEqual(value.isAdmin, user.isAdmin);
}
function assertIsError(error, code) {
TestCase.assertInstanceOf(error, Error, 'The API should return an Error');
if (code) {
TestCase.assertEqual(error.code, code);
}
}
function assertIsAuthError(error, code, type) {
TestCase.assertInstanceOf(error, Realm.Sync.AuthError, 'The API should return an AuthError');
if (code) {
TestCase.assertEqual(error.code, code);
}
if (type) {
TestCase.assertEqual(error.type, type);
}
}
function failOnError(error) {
if (error) {
throw new Error(`Error ${error} was not expected`);
}
}
// Test the given requestFunc, passing it the given callback after it's been wrapped appropriately.
// This function makes sure that errors thrown in the async callback rejects the promise (making tests actually run).
function callbackTest(requestFunc, callback) {
return new Promise((resolve, reject) => {
function callbackWrapper() {
try {
callback.apply(this, Array.from(arguments));
resolve();
}
catch (e) {
reject(e);
}
}
requestFunc(callbackWrapper);
});
}
module.exports = {
testLogout() {
var username = uuid();
return callbackTest((callback) => Realm.Sync.User.register('http://localhost:9080', username, 'password', callback), (error, user) => {
failOnError(error);
assertIsUser(user);
assertIsSameUser(user, Realm.Sync.User.current);
user.logout();
// Is now logged out.
TestCase.assertUndefined(Realm.Sync.User.current);
// Can we open a realm with the registered user?
TestCase.assertThrows(function() {
var _realm = new Realm({sync: {user: user, url: 'realm://localhost:9080/~/test'}});
});
})
},
testRegisterUser() {
var username = uuid();
return callbackTest((callback) => Realm.Sync.User.register('http://localhost:9080', username, 'password', callback), (error, user) => {
failOnError(error);
assertIsUser(user);
// Can we open a realm with the registered user?
var realm = new Realm({sync: {user: user, url: 'realm://localhost:9080/~/test'}});
TestCase.assertInstanceOf(realm, Realm);
});
},
testRegisterExistingUser() {
var username = uuid();
return callbackTest((callback) => Realm.Sync.User.register('http://localhost:9080', username, 'password', callback), (error, user) => {
failOnError(error);
assertIsUser(user);
Realm.Sync.User.register('http://localhost:9080', username, 'password', (error, user) => {
assertIsAuthError(error, 613, 'https://realm.io/docs/object-server/problems/existing-account');
TestCase.assertUndefined(user);
});
});
},
testRegisterMissingUsername() {
return callbackTest((callback) => Realm.Sync.User.register('http://localhost:9080', undefined, 'password', callback), (error, user) => {
assertIsAuthError(error, 602, 'https://realm.io/docs/object-server/problems/missing-parameters');
TestCase.assertUndefined(user);
});
},
testRegisterMissingPassword() {
var username = uuid();
return callbackTest((callback) => Realm.Sync.User.register('http://localhost:9080', username, undefined, callback), (error, user) => {
assertIsAuthError(error, 602, 'https://realm.io/docs/object-server/problems/missing-parameters');
TestCase.assertUndefined(user);
});
},
testRegisterServerOffline() {
var username = uuid();
// Because it waits for answer this takes some time..
return callbackTest((callback) => Realm.Sync.User.register('http://fake_host.local', username, 'password', callback), (error, user) => {
assertIsError(error, 'ECONNRESET');
TestCase.assertUndefined(user);
});
},
testLogin() {
var username = uuid();
// Create user, logout the new user, then login
return callbackTest((callback) => Realm.Sync.User.register('http://localhost:9080', username, 'password', callback), (error, user) => {
failOnError(error);
user.logout();
Realm.Sync.User.login('http://localhost:9080', username, 'password', (error, user) => {
failOnError(error);
assertIsUser(user);
// Can we open a realm with the logged-in user?
var realm = new Realm({sync: {user: user, url: 'realm://localhost:9080/~/test'}});
TestCase.assertInstanceOf(realm, Realm);
realm.close();
});
});
},
testLoginMissingUsername() {
return callbackTest((callback) => Realm.Sync.User.login('http://localhost:9080', undefined, 'password', callback), (error, user) => {
assertIsAuthError(error, 602, 'https://realm.io/docs/object-server/problems/missing-parameters');
TestCase.assertUndefined(user);
});
},
testLoginMissingPassword() {
var username = uuid();
return callbackTest((callback) => Realm.Sync.User.login('http://localhost:9080', username, undefined, callback), (error, user) => {
assertIsAuthError(error, 602, 'https://realm.io/docs/object-server/problems/missing-parameters');
TestCase.assertUndefined(user);
});
},
testLoginNonExistingUser() {
return callbackTest((callback) => Realm.Sync.User.login('http://localhost:9080', 'does_not', 'exist', callback), (error, user) => {
assertIsAuthError(error, 612, 'https://realm.io/docs/object-server/problems/unknown-account');
TestCase.assertUndefined(user);
});
},
testLoginServerOffline() {
var username = uuid();
// Because it waits for answer this takes some time..
return callbackTest((callback) => Realm.Sync.User.register('http://fake_host.local', username, 'password', callback), (error, user) => {
assertIsError(error);
TestCase.assertUndefined(user);
});
},
testAll() {
return new Promise((resolve, _reject) => {
let all;
all = Realm.Sync.User.all;
TestCase.assertArrayLength(Object.keys(all), 0);
callbackTest((callback) => Realm.Sync.User.register('http://localhost:9080', uuid(), 'password', callback), (error, user1) => {
failOnError(error);
all = Realm.Sync.User.all;
TestCase.assertArrayLength(Object.keys(all), 1);
assertIsSameUser(all[user1.identity], user1);
Realm.Sync.User.register('http://localhost:9080', uuid(), 'password', (error, user2) => {
failOnError(error);
all = Realm.Sync.User.all;
TestCase.assertArrayLength(Object.keys(all), 2);
// NOTE: the list of users is in latest-first order.
assertIsSameUser(all[user2.identity], user2);
assertIsSameUser(all[user1.identity], user1);
user2.logout();
all = Realm.Sync.User.all;
TestCase.assertArrayLength(Object.keys(all), 1);
assertIsSameUser(all[user1.identity], user1);
user1.logout();
all = Realm.Sync.User.all;
TestCase.assertArrayLength(Object.keys(all), 0);
resolve();
});
});
});
},
testCurrent() {
return new Promise((resolve, _reject) => {
TestCase.assertUndefined(Realm.Sync.User.current);
callbackTest((callback) => Realm.Sync.User.register('http://localhost:9080', uuid(), 'password', callback), (error, user1) => {
failOnError(error);
assertIsSameUser(Realm.Sync.User.current, user1);
Realm.Sync.User.register('http://localhost:9080', uuid(), 'password', (error, user2) => {
failOnError(error);
TestCase.assertThrows(() => Realm.Sync.User.current, 'We expect Realm.Sync.User.current to throw if > 1 user.');
user2.logout();
assertIsSameUser(Realm.Sync.User.current, user1);
user1.logout();
TestCase.assertUndefined(Realm.Sync.User.current);
resolve();
});
});
});
},
testSynchronizeChangesWithTwoClientsAndOneUser() {
// Test Schema
class Foo {}
Foo.schema = {
name: 'Foo',
properties: {
string: 'string',
bars: { type: 'list', objectType: 'Bar' },
},
};
class Bar {}
Bar.schema = {
name: 'Bar',
properties: { integer: 'int' },
};
const schema = [Foo.schema, Bar.schema];
// Create a user, open two clients at different local paths, synchronize changes
const username = uuid();
return new Promise((resolve) => {
Realm.Sync.User.register('http://localhost:9080', username, 'password', (error ,user) => {
failOnError(error);
const clientA = new Realm({
path: 'testSynchronizeChangesWithTwoClientsAndOneUser_clientA.realm',
schema: schema,
sync: {
user: user,
url: 'http://localhost:9080/~/test',
},
});
const clientB = new Realm({
path: 'testSynchronizeChangesWithTwoClientsAndOneUser_clientB.realm',
schema: schema,
sync: {
user: user,
url: 'http://localhost:9080/~/test',
},
});
clientB.addListener('change', () => {
const foos = clientB.objects('Foo');
if (foos.length > 0) {
TestCase.assertEqual(foos.length, 1);
TestCase.assertEqual(foos[0].string, 'Hello, World!');
resolve();
}
});
clientA.write(() => {
clientA.create('Foo', { string: 'Hello, World!' });
});
});
});
},
};

View File

@ -21,17 +21,7 @@
'use strict';
const Realm = require('../..');
const handlers = {
create(options) {
let realm = new Realm(options.config);
realm.write(() => {
realm.create(options.type, options.properties);
});
}
};
const Realm = require('realm');
process.on('message', (message) => {
process.send(handleMessage(message));
@ -39,14 +29,33 @@ process.on('message', (message) => {
function handleMessage(message) {
let error, result;
if (message[0] == 'echo') {
return {result: message[1]}
}
try {
let handler = handlers[message.action];
if (handler) {
result = handler(message);
} else {
throw new Error('Unknown worker action: ' + message.action);
}
let realm = new Realm(message[0]);
realm.write(() => {
if (message[1] == 'create') {
result = message[3].map((value) => realm.create(message[2], value));
}
else if (message[1] == 'delete') {
let objects = realm.objects(message[2]);
objects = message[3].map((index) => objects[index]);
realm.delete(objects);
}
else if (message[1] == 'update') {
result = message[3].map((value) => realm.create(message[2], value, true));
}
else if (message[1] == 'list_method') {
var listObject = realm.objects(message[2])[0];
var list = listObject[message[3]];
result = list[message[4]].apply(list, message.slice(5));
}
else {
throw new Error('Unknown realm method: ' + message[1]);
}
});
} catch (e) {
console.warn(e);
error = e.message;

View File

@ -1,76 +0,0 @@
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
/* eslint-env es6, node */
'use strict';
const Realm = require('realm');
const TestCase = require('./asserts');
const schemas = require('./schemas');
const Worker = require('./worker');
module.exports = {
testChangeNotifications() {
return new Promise((resolve, reject) => {
let config = {schema: [schemas.TestObject]};
let realm = new Realm(config);
let objects = realm.objects('TestObject');
let worker = new Worker(__dirname + '/worker-tests-script.js');
// Test will fail if it does not receive a change event within a second.
let timer = setTimeout(() => {
reject(new Error('Timed out waiting for change notification'));
}, 1000);
let cleanup = () => {
clearTimeout(timer);
worker.terminate();
};
// Test will pass if it receives a change event and the Realm changed.
realm.addListener('change', () => {
try {
TestCase.assertEqual(objects.length, 1);
TestCase.assertEqual(objects[0].doubleCol, 42);
resolve();
} catch (e) {
reject(e);
} finally {
cleanup();
}
});
worker.onmessage = (message) => {
if (message.error) {
cleanup();
reject(message.error);
}
};
worker.postMessage({
action: 'create',
config: config,
type: 'TestObject',
properties: {
doubleCol: 42,
}
});
});
}
};

View File

@ -31,7 +31,9 @@ class Worker {
});
}
postMessage(message) {
this._process.send(message);
if (this._process) {
this._process.send(message);
}
}
terminate() {
if (this._process) {

21
tests/package.json Normal file
View File

@ -0,0 +1,21 @@
{
"name": "realm-tests-jasmine",
"version": "0.0.1",
"private": true,
"dependencies": {
"es6-promise": "^3.2.1",
"jasmine": "^2.5.1",
"jasmine-console-reporter": "^1.2.7",
"jasmine-reporters": "^2.2.0",
"needle": "^1.3.0",
"terminate": "^1.0.8",
"tmp": "^0.0.30",
"url-parse": "^1.1.7"
},
"scripts": {
"test": "jasmine spec/unit_tests.js spec/sync_tests.js",
"test-nosync": "jasmine spec/unit_tests.js",
"test-sync-integration": "jasmine spec/sync_integration_tests.js",
"postinstall": "rm -f node_modules/realm && ln -s ../.. node_modules/realm"
}
}

View File

@ -73,6 +73,62 @@
remoteGlobalIDString = 13B07F861A680F5B00A75B9A;
remoteInfo = ReactTests;
};
02022A7E1DA47F06000F0C4F /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = F60690521CA2831E0003FB26 /* RealmReact.xcodeproj */;
proxyType = 1;
remoteGlobalIDString = F60690121CA2766F0003FB26;
remoteInfo = RealmReact;
};
02CBBBEF1DC2B3C00057A570 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 2D2A283A1D9B042B00D4039D;
remoteInfo = "RCTImage-tvOS";
};
02CBBBF31DC2B3C00057A570 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 2D2A28471D9B043800D4039D;
remoteInfo = "RCTLinking-tvOS";
};
02CBBBF71DC2B3C00057A570 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 2D2A28541D9B044C00D4039D;
remoteInfo = "RCTNetwork-tvOS";
};
02CBBBFB1DC2B3C00057A570 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 2D2A28611D9B046600D4039D;
remoteInfo = "RCTSettings-tvOS";
};
02CBBBFF1DC2B3C00057A570 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 2D2A287B1D9B048500D4039D;
remoteInfo = "RCTText-tvOS";
};
02CBBC041DC2B3C00057A570 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 2D2A28881D9B049200D4039D;
remoteInfo = "RCTWebSocket-tvOS";
};
02CBBC081DC2B3C00057A570 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 2D2A28131D9B038B00D4039D;
remoteInfo = "React-tvOS";
};
139105C01AF99BAD00B5F7CC /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */;
@ -210,6 +266,7 @@
isa = PBXGroup;
children = (
00C302C01ABCB91800DB3ED1 /* libRCTImage.a */,
02CBBBF01DC2B3C00057A570 /* libRCTImage-tvOS.a */,
);
name = Products;
sourceTree = "<group>";
@ -218,6 +275,7 @@
isa = PBXGroup;
children = (
00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */,
02CBBBF81DC2B3C00057A570 /* libRCTNetwork-tvOS.a */,
);
name = Products;
sourceTree = "<group>";
@ -244,6 +302,7 @@
isa = PBXGroup;
children = (
139105C11AF99BAD00B5F7CC /* libRCTSettings.a */,
02CBBBFC1DC2B3C00057A570 /* libRCTSettings-tvOS.a */,
);
name = Products;
sourceTree = "<group>";
@ -252,6 +311,7 @@
isa = PBXGroup;
children = (
139FDEF41B06529B00C62182 /* libRCTWebSocket.a */,
02CBBC051DC2B3C00057A570 /* libRCTWebSocket-tvOS.a */,
);
name = Products;
sourceTree = "<group>";
@ -273,6 +333,7 @@
isa = PBXGroup;
children = (
146834041AC3E56700842450 /* libReact.a */,
02CBBC091DC2B3C00057A570 /* libReact-tvOS.a */,
);
name = Products;
sourceTree = "<group>";
@ -281,6 +342,7 @@
isa = PBXGroup;
children = (
78C398B91ACF4ADC00677621 /* libRCTLinking.a */,
02CBBBF41DC2B3C00057A570 /* libRCTLinking-tvOS.a */,
);
name = Products;
sourceTree = "<group>";
@ -309,6 +371,7 @@
isa = PBXGroup;
children = (
832341B51AAA6A8300B99B32 /* libRCTText.a */,
02CBBC001DC2B3C00057A570 /* libRCTText-tvOS.a */,
);
name = Products;
sourceTree = "<group>";
@ -368,7 +431,6 @@
isa = PBXNativeTarget;
buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "ReactTestApp" */;
buildPhases = (
F65C4BCA1BC72C5A000A1793 /* Install Realm Node Modules */,
13B07F871A680F5B00A75B9A /* Sources */,
13B07F8C1A680F5B00A75B9A /* Frameworks */,
13B07F8E1A680F5B00A75B9A /* Resources */,
@ -378,6 +440,7 @@
buildRules = (
);
dependencies = (
02022A7F1DA47F06000F0C4F /* PBXTargetDependency */,
);
name = ReactTestApp;
productName = "Hello World";
@ -390,7 +453,7 @@
83CBB9F71A601CBA00E9B192 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0710;
LastUpgradeCheck = 0800;
ORGANIZATIONNAME = Realm;
TargetAttributes = {
00E356ED1AD99517003FC87E = {
@ -500,6 +563,55 @@
remoteRef = 00C302E31ABCB9EE00DB3ED1 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
02CBBBF01DC2B3C00057A570 /* libRCTImage-tvOS.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = "libRCTImage-tvOS.a";
remoteRef = 02CBBBEF1DC2B3C00057A570 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
02CBBBF41DC2B3C00057A570 /* libRCTLinking-tvOS.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = "libRCTLinking-tvOS.a";
remoteRef = 02CBBBF31DC2B3C00057A570 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
02CBBBF81DC2B3C00057A570 /* libRCTNetwork-tvOS.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = "libRCTNetwork-tvOS.a";
remoteRef = 02CBBBF71DC2B3C00057A570 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
02CBBBFC1DC2B3C00057A570 /* libRCTSettings-tvOS.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = "libRCTSettings-tvOS.a";
remoteRef = 02CBBBFB1DC2B3C00057A570 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
02CBBC001DC2B3C00057A570 /* libRCTText-tvOS.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = "libRCTText-tvOS.a";
remoteRef = 02CBBBFF1DC2B3C00057A570 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
02CBBC051DC2B3C00057A570 /* libRCTWebSocket-tvOS.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = "libRCTWebSocket-tvOS.a";
remoteRef = 02CBBC041DC2B3C00057A570 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
02CBBC091DC2B3C00057A570 /* libReact-tvOS.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = "libReact-tvOS.a";
remoteRef = 02CBBC081DC2B3C00057A570 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
139105C11AF99BAD00B5F7CC /* libRCTSettings.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
@ -566,20 +678,6 @@
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
F65C4BCA1BC72C5A000A1793 /* Install Realm Node Modules */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Install Realm Node Modules";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "[ -s \"${HOME}/.nvm/nvm.sh\" ] && . \"${HOME}/.nvm/nvm.sh\" \nif [ -z \"$TEST_SCRIPT\" ]; then\n npm install realm realm-tests\nfi\ncp ../../../src/object-store/tests/query.json ../node_modules/realm-tests/query-tests.json\n";
};
F6EDE5BF1C49007200B1085F /* Bundle React Native code and images */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
@ -623,6 +721,11 @@
target = 13B07F861A680F5B00A75B9A /* ReactTestApp */;
targetProxy = 00E356F41AD99517003FC87E /* PBXContainerItemProxy */;
};
02022A7F1DA47F06000F0C4F /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
name = RealmReact;
targetProxy = 02022A7E1DA47F06000F0C4F /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
@ -704,6 +807,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CC = "$(SRCROOT)/../../../scripts/ccache-clang.sh";
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
@ -713,16 +817,20 @@
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
CXX = "$(SRCROOT)/../../../scripts/ccache-clang++.sh";
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
@ -741,9 +849,10 @@
"$(SRCROOT)/../node_modules/react-native/React/Base/**",
"$(SRCROOT)/../node_modules/react-native/React/Modules",
);
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
OS_ACTIVITY_MODE = disable;
PRODUCT_BUNDLE_IDENTIFIER = "io.realm.$(PRODUCT_NAME:rfc1034identifier)";
SDKROOT = iphoneos;
};
@ -753,6 +862,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CC = "$(SRCROOT)/../../../scripts/ccache-clang.sh";
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
@ -762,15 +872,19 @@
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = YES;
CXX = "$(SRCROOT)/../../../scripts/ccache-clang++.sh";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
@ -783,8 +897,9 @@
"$(SRCROOT)/../node_modules/react-native/React/Base/**",
"$(SRCROOT)/../node_modules/react-native/React/Modules",
);
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MTL_ENABLE_DEBUG_INFO = NO;
OS_ACTIVITY_MODE = disable;
PRODUCT_BUNDLE_IDENTIFIER = "io.realm.$(PRODUCT_NAME:rfc1034identifier)";
SDKROOT = iphoneos;
VALIDATE_PRODUCT = YES;

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0720"
LastUpgradeVersion = "0800"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@ -91,6 +91,13 @@
ReferencedContainer = "container:ReactTests.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<EnvironmentVariables>
<EnvironmentVariable
key = "OS_ACTIVITY_MODE"
value = "disable"
isEnabled = "YES">
</EnvironmentVariable>
</EnvironmentVariables>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>

View File

@ -6,11 +6,11 @@
"start": "react-native start"
},
"dependencies": {
"react": "~15.2.0",
"react-native": "^0.31.0",
"react": "15.3.2",
"react-native": "^0.37.0",
"react-native-fs": "^1.1.0",
"xmlbuilder": "^4.2.1",
"realm": "file:../..",
"realm-tests": "file:../js"
"realm-tests": "file:../js",
"xmlbuilder": "^4.2.1"
}
}

View File

@ -0,0 +1,11 @@
'use strict';
var jasmineReporters = require('jasmine-reporters');
var junitReporter = new jasmineReporters.JUnitXmlReporter({
savePath: '.',
consolidateAll: false
});
jasmine.getEnv().addReporter(junitReporter);
var JasmineConsoleReporter = require('jasmine-console-reporter');
jasmine.getEnv().addReporter(new JasmineConsoleReporter());

Some files were not shown because too many files have changed in this diff Show More