Index: pkg/observe/lib/src/path_observer.dart |
diff --git a/pkg/observe/lib/src/path_observer.dart b/pkg/observe/lib/src/path_observer.dart |
index 0d631a0c192829fb51726b968157ebbf13c3b554..b7d83172d1606a0b7924772af55fe04d272ab4a9 100644 |
--- a/pkg/observe/lib/src/path_observer.dart |
+++ b/pkg/observe/lib/src/path_observer.dart |
@@ -83,7 +83,10 @@ class PathObserver extends _Observer implements Bindable { |
/// A dot-delimieted property path such as "foo.bar" or "foo.10.bar". |
Jennifer Messerly
2014/03/25 22:32:44
this was an issue before but: do you mind adding a
Siggi Cherem (dart-lang)
2014/03/26 00:32:07
Done.
|
/// The path specifies how to get a particular value from an object graph, where |
-/// the graph can include arrays. |
+/// the graph can include arrays and maps. Each segment of the path describes |
+/// how to take a single step in the object graph. Properties like 'foo' or |
+/// 'bar' are read as properties on objects, or as keys if the object is a Map |
+/// or an [StringIndexer], while integer values are read as indexes in an array. |
Jennifer Messerly
2014/03/25 22:32:44
should be "a [StringIndexer]"
also, this was an i
Siggi Cherem (dart-lang)
2014/03/26 00:32:07
Done.
|
// TODO(jmesserly): consider specialized subclasses for: |
// * empty path |
// * "value" |
@@ -180,7 +183,7 @@ class PropertyPath { |
return 0x1fffffff & (hash + ((0x00003fff & hash) << 15)); |
} |
- /// Returns the current of the path from the provided [obj]ect. |
+ /// Returns the current value of the path from the provided [obj]ect. |
getValueFrom(Object obj) { |
if (!isValid) return null; |
for (var segment in _segments) { |
@@ -233,6 +236,13 @@ bool _changeRecordMatches(record, key) { |
return false; |
} |
+/// Properties in [Map] that need to be read as properties and not as keys in |
+/// the map. We exclude methods ('containsValue', 'containsKey', 'putIfAbsent', |
+/// 'addAll', 'remove', 'clear', 'forEach') because there is no use in reading |
+/// them as part of path-observer segments. |
+const _MAP_PROPERTIES = |
+ const ['keys', 'values', 'length', 'isEmpty', 'isNotEmpty']; |
+ |
_getObjectProperty(object, property) { |
if (object == null) return null; |
@@ -241,21 +251,25 @@ _getObjectProperty(object, property) { |
return object[property]; |
} |
} else if (property is Symbol) { |
- final type = object.runtimeType; |
+ var name = smoke.symbolToName(property); |
Jennifer Messerly
2014/03/25 22:32:44
move this inside the if, below? and make _MAP_PROP
Siggi Cherem (dart-lang)
2014/03/26 00:32:07
Done.
|
+ // Support indexer if available, e.g. Maps or polymer_expressions Scope. |
+ // This is the default syntax used by polymer/nodebind and |
+ // polymer/observe-js PathObserver. |
+ // TODO(sigmund): should we also support using checking dynamically for |
+ // whether the type practically implements the indexer API |
+ // (smoke.hasInstanceMethod(type, const Symbol('[]')))? |
+ if (object is StringIndexer || |
+ object is Map && !_MAP_PROPERTIES.contains(name)) { |
+ return object[name]; |
+ } |
try { |
- if (smoke.hasGetter(type, property) || smoke.hasNoSuchMethod(type)) { |
- return smoke.read(object, property); |
- } |
- // Support indexer if available, e.g. Maps or polymer_expressions Scope. |
- // This is the default syntax used by polymer/nodebind and |
- // polymer/observe-js PathObserver. |
- if (smoke.hasInstanceMethod(type, const Symbol('[]'))) { |
- return object[smoke.symbolToName(property)]; |
- } |
- } on NoSuchMethodError catch (e) { |
+ return smoke.read(object, property); |
+ } on NoSuchMethodError catch (e, s) { |
// Rethrow, unless the type implements noSuchMethod, in which case we |
// interpret the exception as a signal that the method was not found. |
- if (!smoke.hasNoSuchMethod(type)) rethrow; |
+ if (!smoke.hasNoSuchMethod(object.runtimeType)) { |
+ new Completer().completeError(e, s); |
Jennifer Messerly
2014/03/25 22:32:44
hmmm. Upon second look I'm not sure this is the ri
Siggi Cherem (dart-lang)
2014/03/26 00:32:07
No worries, changed it back to sync. Thanks for fi
|
+ } |
} |
} |
@@ -274,19 +288,20 @@ bool _setObjectProperty(object, property, value) { |
return true; |
} |
} else if (property is Symbol) { |
- final type = object.runtimeType; |
+ // Support indexer if available, e.g. Maps or polymer_expressions Scope. |
+ var name = smoke.symbolToName(property); |
+ if (object is StringIndexer || |
+ object is Map && !_MAP_PROPERTIES.contains(name)) { |
+ object[name] = value; |
+ return true; |
+ } |
try { |
- if (smoke.hasSetter(type, property) || smoke.hasNoSuchMethod(type)) { |
- smoke.write(object, property, value); |
- return true; |
- } |
- // Support indexer if available, e.g. Maps or polymer_expressions Scope. |
- if (smoke.hasInstanceMethod(type, const Symbol('[]='))) { |
- object[smoke.symbolToName(property)] = value; |
- return true; |
+ smoke.write(object, property, value); |
+ return true; |
+ } on NoSuchMethodError catch (e, s) { |
+ if (!smoke.hasNoSuchMethod(object.runtimeType)) { |
+ new Completer().completeError(e, s); |
} |
- } on NoSuchMethodError catch (e) { |
- if (!smoke.hasNoSuchMethod(type)) rethrow; |
} |
} |
@@ -470,6 +485,13 @@ class CompoundObserver extends _Observer implements Bindable { |
} |
} |
+/// An object accepted by [PropertyPath] where properties are read and written |
+/// as indexing operations, just like a [Map]. |
+abstract class StringIndexer { |
Siggi Cherem (dart-lang)
2014/03/25 21:44:25
I was not sure about the name. Other ideas/suggest
Jennifer Messerly
2014/03/25 22:32:44
Indexable<K, V>? then do the is check for Indexabl
Siggi Cherem (dart-lang)
2014/03/26 00:32:07
Done.
|
+ operator [](String name); |
+ operator []=(index, value); |
+} |
+ |
const _observerSentinel = const _ObserverSentinel(); |
class _ObserverSentinel { const _ObserverSentinel(); } |