Skip to content

Optimize Parse object #195

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion example/lib/data/base/api_response.dart
Original file line number Diff line number Diff line change
@@ -5,7 +5,7 @@ import 'api_error.dart';
class ApiResponse {
ApiResponse(this.success, this.statusCode, this.results, this.error)
: count = results?.length ?? 0,
result = results?.first;
result = results?.isNotEmpty ?? false ? results.first : null;

final bool success;
final int statusCode;
2 changes: 1 addition & 1 deletion example/lib/main.dart
Original file line number Diff line number Diff line change
@@ -304,7 +304,7 @@ class _MyAppState extends State<MyApp> {
if (result.success) {
if (result.result is ParseObject) {
final ParseObject parseObject = result.result;
print(parseObject.className);
print(parseObject.parseClassName);
}
}
}
Original file line number Diff line number Diff line change
@@ -34,7 +34,7 @@ void main() {
test('add DietPlan from API', () async {
// Given
final DietPlan expected = getDummyDietPlan();
expected.getObjectData()['objectId'] = null;
expected['objectId'] = null;

// When
final ApiResponse response = await repository.add(expected);
@@ -51,11 +51,11 @@ void main() {
// Given
final List<DietPlan> actual = List<DietPlan>();
final DietPlan item1 = getDummyDietPlan();
item1.getObjectData()['objectId'] = null;
item1['objectId'] = null;
item1.protein = 5;
actual.add(item1);
final DietPlan item2 = getDummyDietPlan();
item2.getObjectData()['objectId'] = null;
item2['objectId'] = null;
item2.protein = 6;
actual.add(item2);

@@ -74,7 +74,7 @@ void main() {
test('getById DietPlan from API', () async {
// Given
final DietPlan dummy = getDummyDietPlan();
dummy.getObjectData()['objectId'] = null;
dummy['objectId'] = null;

// When
final ApiResponse response = await repository.add(dummy);
@@ -95,7 +95,7 @@ void main() {
test('getNewerThan DietPlan from API', () async {
// Given
final DietPlan dummy = getDummyDietPlan();
dummy.getObjectData()['objectId'] = null;
dummy['objectId'] = null;

// When
final ApiResponse baseResponse = await repository.add(dummy);
@@ -120,11 +120,11 @@ void main() {
final List<DietPlan> actual = List<DietPlan>();

final DietPlan item1 = getDummyDietPlan();
item1.getObjectData()['objectId'] = null;
item1['objectId'] = null;
item1.protein = 5;
actual.add(item1);
final DietPlan item2 = getDummyDietPlan();
item2.getObjectData()['objectId'] = null;
item2['objectId'] = null;
item2.protein = 6;
actual.add(item2);

@@ -142,7 +142,7 @@ void main() {
test('update DietPlan from API', () async {
// Given
final DietPlan expected = getDummyDietPlan();
expected.getObjectData()['objectId'] = null;
expected['objectId'] = null;
final ApiResponse response = await repository.add(expected);
final DietPlan initialResponse = response.result;

@@ -165,11 +165,11 @@ void main() {
final List<DietPlan> actual = List<DietPlan>();

final DietPlan item1 = getDummyDietPlan();
item1.getObjectData()['objectId'] = null;
item1['objectId'] = null;
item1.protein = 7;
actual.add(item1);
final DietPlan item2 = getDummyDietPlan();
item2.getObjectData()['objectId'] = null;
item2['objectId'] = null;
item2.protein = 8;
actual.add(item2);
await repository.addAll(actual);
Original file line number Diff line number Diff line change
@@ -126,7 +126,7 @@ void main() {
// Given
final DietPlan expected = getDummyDietPlan();
// ignore: invalid_use_of_protected_member
expected.getObjectData()[keyVarUpdatedAt] = DateTime.now();
expected[keyVarUpdatedAt] = DateTime.now();
final ApiResponse response = await repository.add(expected);

// When
2 changes: 1 addition & 1 deletion lib/src/network/parse_live_query.dart
Original file line number Diff line number Diff line change
@@ -48,7 +48,7 @@ class LiveQuery {
_liveQueryURL = _liveQueryURL.replaceAll('http', 'ws');
}

final String _className = query.object.className;
final String _className = query.object.parseClassName;
query.limiters.clear(); //Remove limits in LiveQuery
final String _where = query._buildQuery().replaceAll('where=', '');

4 changes: 2 additions & 2 deletions lib/src/network/parse_query.dart
Original file line number Diff line number Diff line change
@@ -246,7 +246,7 @@ class QueryBuilder<T extends ParseObject> {
// Add a constraint to the query that requires a particular key's value match another QueryBuilder
// ignore: always_specify_types
void whereMatchesQuery(String column, QueryBuilder query) {
final String inQuery = query._buildQueryRelational(query.object.className);
final String inQuery = query._buildQueryRelational(query.object.parseClassName);

queries.add(MapEntry<String, dynamic>(
_SINGLE_QUERY, '\"$column\":{\"\$inQuery\":$inQuery}'));
@@ -255,7 +255,7 @@ class QueryBuilder<T extends ParseObject> {
//Add a constraint to the query that requires a particular key's value does not match another QueryBuilder
// ignore: always_specify_types
void whereDoesNotMatchQuery(String column, QueryBuilder query) {
final String inQuery = query._buildQueryRelational(query.object.className);
final String inQuery = query._buildQueryRelational(query.object.parseClassName);

queries.add(MapEntry<String, dynamic>(
_SINGLE_QUERY, '\"$column\":{\"\$notInQuery\":$inQuery}'));
121 changes: 94 additions & 27 deletions lib/src/objects/parse_base.dart
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
part of flutter_parse_sdk;

abstract class ParseBase {
String className;
String parseClassName;
Type type;

String setClassName(String className) => this.className = className;

String getClassName() => className;
bool _dirty = false; // reserved property
final Map<String, dynamic> _unsavedChanges = Map<String, dynamic>();
final Map<String, dynamic> _savingChanges = Map<String, dynamic>();

/// Stores all the values of a class
Map<String, dynamic> _objectData = Map<String, dynamic>();
@@ -16,6 +15,40 @@ abstract class ParseBase {

set objectId(String objectId) => set<String>(keyVarObjectId, objectId);

bool isDirty({String key}) {
if (key != null) {
return _unsavedChanges[key] != null;
}
return _isDirty(true);
}

bool _isDirty(bool considerChildren) {
if (_dirty || _unsavedChanges.isNotEmpty) {
return true;
}

if (considerChildren) {
return _areChildrenDirty(Set<dynamic>());
}
return false;
}

bool _areChildrenDirty(Set<dynamic> seenObjects) {
if (seenObjects.contains(this)) {
return false;
}
seenObjects.add(this);
if (_dirty || _unsavedChanges.isNotEmpty) {
return true;
}
_getObjectData().forEach((String key, dynamic value) {
if (value is ParseObject && value._areChildrenDirty(seenObjects)) {
return true;
}
});
return false;
}

/// Returns [DateTime] createdAt
DateTime get createdAt {
if (get<dynamic>(keyVarCreatedAt) is String) {
@@ -40,7 +73,7 @@ abstract class ParseBase {
@protected
Map<String, dynamic> toJson({bool full, bool forApiRQ = false}) {
final Map<String, dynamic> map = <String, dynamic>{
keyVarClassName: className,
keyVarClassName: parseClassName,
};

if (objectId != null) {
@@ -55,7 +88,8 @@ abstract class ParseBase {
map[keyVarUpdatedAt] = _parseDateFormat.format(updatedAt);
}

getObjectData().forEach((String key, dynamic value) {
final Map<String, dynamic> target = forApiRQ ? _unsavedChanges : _getObjectData();
target.forEach((String key, dynamic value) {
if (!map.containsKey(key)) {
map[key] = parseEncode(value, full: full);
}
@@ -81,7 +115,7 @@ abstract class ParseBase {
}

objectData.forEach((String key, dynamic value) {
if (key == className || key == '__type') {
if (key == parseClassName || key == '__type') {
// NO OP
} else if (key == keyVarObjectId) {
objectId = value;
@@ -98,9 +132,9 @@ abstract class ParseBase {
set<DateTime>(keyVarUpdatedAt, value);
}
} else if (key == keyVarAcl) {
getObjectData()[keyVarAcl] = ParseACL().fromJson(value);
this[keyVarAcl] = ParseACL().fromJson(value);
} else {
getObjectData()[key] = parseDecode(value);
this[key] = parseDecode(value);
}
});

@@ -113,17 +147,32 @@ abstract class ParseBase {

/// Sets all the objects variables
@protected
void setObjectData(Map<String, dynamic> objectData) =>
void _setObjectData(Map<String, dynamic> objectData) =>
_objectData = objectData;

/// Returns the objects variables
@protected
Map<String, dynamic> getObjectData() => _objectData ?? Map<String, dynamic>();
Map<String, dynamic> _getObjectData() => _objectData ?? Map<String, dynamic>();

bool containsValue(Object value) {
return _getObjectData().containsValue(value);
}

bool containsKey(Object key) {
return _getObjectData().containsKey(key);
}

dynamic operator [](Object key) {
get<dynamic>(key);
}

void operator []=(String key, dynamic value) {
set<dynamic>(key, value);
}
/// Saves in storage
Future<void> saveInStorage(String key) async {
final String objectJson = json.encode(toJson(full: true));
await ParseCoreData().getStore()
ParseCoreData().getStore()
..setString(key, objectJson);
}

@@ -134,25 +183,29 @@ abstract class ParseBase {
/// needed or not, set to false
void set<T>(String key, T value, {bool forceUpdate = true}) {
if (value != null) {
if (getObjectData().containsKey(key)) {
if (_getObjectData().containsKey(key)) {
if (_getObjectData()[key] == value) {
return;
}
if (forceUpdate) {
getObjectData()[key] = value;
_getObjectData()[key] = value;
}
} else {
getObjectData()[key] = value;
_getObjectData()[key] = value;
}
_unsavedChanges[key] = value;
}
}

///Set the [ParseACL] governing this object.
void setACL<ParseACL>(ParseACL acl) {
getObjectData()[keyVarAcl] = acl;
_getObjectData()[keyVarAcl] = acl;
}

///Access the [ParseACL] governing this object.
ParseACL getACL() {
if (getObjectData().containsKey(keyVarAcl)) {
return getObjectData()[keyVarAcl];
if (_getObjectData().containsKey(keyVarAcl)) {
return _getObjectData()[keyVarAcl];
} else {
return ParseACL();
}
@@ -164,12 +217,12 @@ abstract class ParseBase {
/// getType<int> and an int will be returned, null, or a defaultValue if
/// provided
dynamic get<T>(String key, {T defaultValue}) {
if (getObjectData().containsKey(key)) {
if (T != null && getObjectData()[key] is T) {
final T data = getObjectData()[key];
if (_getObjectData().containsKey(key)) {
if (T != null && _getObjectData()[key] is T) {
final T data = _getObjectData()[key];
return data;
} else {
return getObjectData()[key];
return _getObjectData()[key];
}
} else {
return defaultValue;
@@ -184,7 +237,7 @@ abstract class ParseBase {
await unpin();
final Map<String, dynamic> objectMap = parseEncode(this, full: true);
final String json = jsonEncode(objectMap);
await ParseCoreData().getStore()
ParseCoreData().getStore()
..setString(objectId, json);
return true;
} else {
@@ -197,7 +250,7 @@ abstract class ParseBase {
/// Replicates Android SDK pin process and saves object to storage
Future<bool> unpin({String key}) async {
if (objectId != null) {
await ParseCoreData().getStore()
ParseCoreData().getStore()
..remove(key ?? objectId);
return true;
}
@@ -210,7 +263,7 @@ abstract class ParseBase {
/// Replicates Android SDK pin process and saves object to storage
dynamic fromPin(String objectId) async {
if (objectId != null) {
final CoreStore coreStore = await ParseCoreData().getStore();
final CoreStore coreStore = ParseCoreData().getStore();
final String itemFromStore = await coreStore.getString(objectId);

if (itemFromStore != null) {
@@ -220,5 +273,19 @@ abstract class ParseBase {
return null;
}

Map<String, dynamic> toPointer() => encodeObject(className, objectId);
Map<String, dynamic> toPointer() => encodeObject(parseClassName, objectId);

/// Deprecated
@Deprecated('Prefer to use parseClassName')
String className;
@Deprecated('Prefer to use parseClassName')
String getClassName() => parseClassName;
@Deprecated('Prefer to use parseClassName')
String setClassName(String className) => parseClassName = className;
@protected @Deprecated('Prefer to use _getObjectData method, or operator [] for certain key.')
Map<String, dynamic> getObjectData() => _getObjectData();

@protected @Deprecated('Prefer to use _setObjectData method, or operator [] for certain key.')
void setObjectData(Map<String, dynamic> objectData) =>
_setObjectData(objectData);
}
8 changes: 4 additions & 4 deletions lib/src/objects/parse_config.dart
Original file line number Diff line number Diff line change
@@ -18,9 +18,9 @@ class ParseConfig extends ParseObject {
final String uri = '${ParseCoreData().serverUrl}/config';
final Response result = await _client.get(uri);
return handleResponse<ParseConfig>(
this, result, ParseApiRQ.getConfigs, _debug, className);
this, result, ParseApiRQ.getConfigs, _debug, parseClassName);
} on Exception catch (e) {
return handleException(e, ParseApiRQ.getConfigs, _debug, className);
return handleException(e, ParseApiRQ.getConfigs, _debug, parseClassName);
}
}

@@ -31,9 +31,9 @@ class ParseConfig extends ParseObject {
final String body = '{\"params\":{\"$key\": \"${parseEncode(value)}\"}}';
final Response result = await _client.put(uri, body: body);
return handleResponse<ParseConfig>(
this, result, ParseApiRQ.addConfig, _debug, className);
this, result, ParseApiRQ.addConfig, _debug, parseClassName);
} on Exception catch (e) {
return handleException(e, ParseApiRQ.addConfig, _debug, className);
return handleException(e, ParseApiRQ.addConfig, _debug, parseClassName);
}
}
}
Loading