From 77f5f61154c21aecf16fbf1242115efea5382d33 Mon Sep 17 00:00:00 2001 From: Matan Lurey Date: Mon, 21 Nov 2016 09:39:07 -0800 Subject: [PATCH] Revert "Update to pkg/observable 0.17.0" This reverts commit c124c2c2b7448ba649d32d75af93c0fa5e2a3fde. --- benchmark/index.dart | 171 ++++++++++++++++++ benchmark/index.html | 73 ++++++++ benchmark/object_benchmark.dart | 28 +++ benchmark/observable_list_benchmark.dart | 58 ++++++ benchmark/observation_benchmark_base.dart | 120 ++++++++++++ benchmark/path_benchmark.dart | 42 +++++ benchmark/setup_object_benchmark.dart | 15 ++ .../setup_observable_list_benchmark.dart | 26 +++ .../setup_observation_benchmark_base.dart | 76 ++++++++ benchmark/setup_path_benchmark.dart | 23 +++ benchmark/test_observable.dart | 39 ++++ benchmark/test_path_observable.dart | 27 +++ lib/html.dart | 2 +- lib/src/auto_observable.dart | 6 +- lib/src/list_path_observer.dart | 2 +- lib/src/metadata.dart | 2 +- lib/src/observable_box.dart | 2 +- pubspec.yaml | 4 +- test/list_path_observer_test.dart | 2 +- test/path_observer_test.dart | 16 +- 20 files changed, 711 insertions(+), 23 deletions(-) create mode 100644 benchmark/index.dart create mode 100644 benchmark/index.html create mode 100644 benchmark/object_benchmark.dart create mode 100644 benchmark/observable_list_benchmark.dart create mode 100644 benchmark/observation_benchmark_base.dart create mode 100644 benchmark/path_benchmark.dart create mode 100644 benchmark/setup_object_benchmark.dart create mode 100644 benchmark/setup_observable_list_benchmark.dart create mode 100644 benchmark/setup_observation_benchmark_base.dart create mode 100644 benchmark/setup_path_benchmark.dart create mode 100644 benchmark/test_observable.dart create mode 100644 benchmark/test_path_observable.dart diff --git a/benchmark/index.dart b/benchmark/index.dart new file mode 100644 index 0000000..49e7cad --- /dev/null +++ b/benchmark/index.dart @@ -0,0 +1,171 @@ +// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +library observe.test.benchmark.index; + +import 'dart:async'; +import 'dart:html'; +import 'package:benchmark_harness/benchmark_harness.dart'; +import 'package:chart/chart.dart'; +import 'package:observe/mirrors_used.dart' as mu; // Makes output smaller. +import 'package:smoke/mirrors.dart'; +import 'object_benchmark.dart'; +import 'setup_object_benchmark.dart'; +import 'observable_list_benchmark.dart'; +import 'setup_observable_list_benchmark.dart'; +import 'path_benchmark.dart'; +import 'setup_path_benchmark.dart'; + +/// Benchmark names to factory functions. +/// Uses [mu]. +typedef BenchmarkBase BenchmarkFactory( + int objectCount, int mutationCount, String config); +final Map benchmarkFactories = { + 'ObjectBenchmark': (int o, int m, String c) => new ObjectBenchmark(o, m, c), + 'SetupObjectBenchmark': (int o, int m, String c) => + new SetupObjectBenchmark(o, c), + 'ObservableListBenchmark': (int o, int m, String c) => + new ObservableListBenchmark(o, m, c), + 'SetupObservableListBenchmark': (int o, int m, String c) => + new SetupObservableListBenchmark(o, c), + 'PathBenchmark': (int o, int m, String c) => new PathBenchmark(o, m, c), + 'SetupPathBenchmark': (int o, int m, String c) => + new SetupPathBenchmark(o, c), +}; + +/// Benchmark names to possible configs. +final Map> benchmarkConfigs = { + 'ObjectBenchmark': [], + 'SetupObjectBenchmark': [], + 'ObservableListBenchmark': ['splice', 'update', 'push/pop', 'shift/unshift'], + 'SetupObservableListBenchmark': [], + 'PathBenchmark': ['leaf', 'root'], + 'SetupPathBenchmark': [], +}; + +Iterable objectCounts; +Iterable mutationCounts; + +final ButtonElement goButton = querySelector('#go'); +final InputElement objectCountInput = querySelector('#objectCountInput'); +final InputElement mutationCountInput = querySelector('#mutationCountInput'); +final SpanElement mutationCountWrapper = querySelector('#mutationCountWrapper'); +final SpanElement statusSpan = querySelector('#status'); +final DivElement canvasWrapper = querySelector('#canvasWrapper'); +final SelectElement benchmarkSelect = querySelector('#benchmarkSelect'); +final SelectElement configSelect = querySelector('#configSelect'); +final UListElement legendList = querySelector('#legendList'); +final List colors = [ + [0, 0, 255], + [138, 43, 226], + [165, 42, 42], + [100, 149, 237], + [220, 20, 60], + [184, 134, 11] +].map((rgb) => 'rgba(' + rgb.join(',') + ',.7)').toList(); + +main() { + // TODO(jakemac): Use a transformer to generate the smoke config so we can see + // how that affects the benchmark. + useMirrors(); + + benchmarkSelect.onChange.listen((_) => changeBenchmark()); + changeBenchmark(); + + goButton.onClick.listen((_) async { + canvasWrapper.children.clear(); + goButton.disabled = true; + goButton.text = 'Running...'; + legendList.text = ''; + objectCounts = + objectCountInput.value.split(',').map((val) => int.parse(val)); + + if (benchmarkSelect.value.startsWith('Setup')) { + mutationCounts = [0]; + } else { + mutationCounts = + mutationCountInput.value.split(',').map((val) => int.parse(val)); + } + + var i = 0; + mutationCounts.forEach((count) { + var li = document.createElement('li'); + li.text = '$count mutations.'; + li.style.color = colors[i % colors.length]; + legendList.append(li); + i++; + }); + + var results = >[]; + for (int objectCount in objectCounts) { + int x = 0; + for (int mutationCount in mutationCounts) { + statusSpan.text = + 'Testing: $objectCount objects with $mutationCount mutations'; + // Let the status text render before running the next benchmark. + await new Future(() {}); + var factory = benchmarkFactories[benchmarkSelect.value]; + var benchmark = factory(objectCount, mutationCount, configSelect.value); + // Divide by 10 because benchmark_harness returns the amount of time it + // took to run 10 times, not once :(. + var resultMicros = benchmark.measure() / 10; + + if (results.length <= x) results.add([]); + results[x].add(resultMicros / 1000); + x++; + } + } + + drawBenchmarks(results); + }); +} + +void drawBenchmarks(List> results) { + var datasets = []; + for (int i = 0; i < results.length; i++) { + datasets.add({ + 'fillColor': 'rgba(255, 255, 255, 0)', + 'strokeColor': colors[i % colors.length], + 'pointColor': colors[i % colors.length], + 'pointStrokeColor': "#fff", + 'data': results[i], + }); + } + var data = { + 'labels': objectCounts.map((c) => '$c').toList(), + 'datasets': datasets, + }; + + new Line(data, { + 'bezierCurve': false, + }).show(canvasWrapper); + goButton.disabled = false; + goButton.text = 'Run Benchmarks'; + statusSpan.text = ''; +} + +void changeBenchmark() { + var configs = benchmarkConfigs[benchmarkSelect.value]; + configSelect.text = ''; + configs.forEach((config) { + var option = document.createElement('option'); + option.text = config; + configSelect.append(option); + }); + + document.title = benchmarkSelect.value; + + // Don't show the configSelect if there are no configs. + if (configs.isEmpty) { + configSelect.style.display = 'none'; + } else { + configSelect.style.display = 'inline'; + } + + // Don't show the mutation counts box if running a Setup* benchmark. + if (benchmarkSelect.value.startsWith('Setup')) { + mutationCountWrapper.style.display = 'none'; + } else { + mutationCountWrapper.style.display = 'inline'; + } +} diff --git a/benchmark/index.html b/benchmark/index.html new file mode 100644 index 0000000..ce2bf9f --- /dev/null +++ b/benchmark/index.html @@ -0,0 +1,73 @@ + + + + Observation Benchmarks + + + + +

Observation Benchmarks

+ + + + + + +Object Count: + + + Mutation Count: +
+
+
+ + +
+
+
+ Times in ms +
+
+
+
+
    +
+
+
+
+

Object Set Size

+ + + + diff --git a/benchmark/object_benchmark.dart b/benchmark/object_benchmark.dart new file mode 100644 index 0000000..91282be --- /dev/null +++ b/benchmark/object_benchmark.dart @@ -0,0 +1,28 @@ +// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +library observe.test.benchmark.object_benchmark; + +import 'observation_benchmark_base.dart'; +import 'test_observable.dart'; + +class ObjectBenchmark extends ObservationBenchmarkBase { + ObjectBenchmark(int objectCount, int mutationCount, String config) + : super('ObjectBenchmark:$objectCount:$mutationCount:$config', + objectCount, mutationCount, config); + + @override + int mutateObject(TestObservable obj) { + // Modify the first 5 properties. + obj.a++; + obj.b++; + obj.c++; + obj.d++; + obj.e++; + // Return # of modifications. + return 5; + } + + @override + TestObservable newObject() => new TestObservable(); +} diff --git a/benchmark/observable_list_benchmark.dart b/benchmark/observable_list_benchmark.dart new file mode 100644 index 0000000..d0f8ad9 --- /dev/null +++ b/benchmark/observable_list_benchmark.dart @@ -0,0 +1,58 @@ +// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +library observe.test.benchmark.observable_list_benchmark; + +import 'package:observable/observable.dart'; +import 'observation_benchmark_base.dart'; + +class ObservableListBenchmark extends ObservationBenchmarkBase { + final int elementCount = 100; + + ObservableListBenchmark(int objectCount, int mutationCount, String config) + : super('ObservableListBenchmark:$objectCount:$mutationCount:$config', + objectCount, mutationCount, config); + + @override + int mutateObject(ObservableList obj) { + switch (config) { + case 'update': + var size = (elementCount / 10).floor(); + for (var j = 0; j < size; j++) { + obj[j * size]++; + } + return size; + + case 'splice': + var size = (elementCount / 5).floor(); + // No splice equivalent in List, so we hardcode it. + var removed = obj.sublist(size, size * 2); + obj.removeRange(size, size * 2); + obj.insertAll(size * 2, removed); + return size * 2; + + case 'push/pop': + var val = obj.removeLast(); + obj.add(val + 1); + return 2; + + case 'shift/unshift': + var val = obj.removeAt(0); + obj.insert(0, val + 1); + return 2; + + default: + throw new ArgumentError( + 'Invalid config for ObservableListBenchmark: $config'); + } + } + + @override + ObservableList newObject() { + var list = new ObservableList(); + for (int i = 0; i < elementCount; i++) { + list.add(i); + } + return list; + } +} diff --git a/benchmark/observation_benchmark_base.dart b/benchmark/observation_benchmark_base.dart new file mode 100644 index 0000000..f8fc983 --- /dev/null +++ b/benchmark/observation_benchmark_base.dart @@ -0,0 +1,120 @@ +// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +library observe.test.benchmark.observation_benchmark_base; + +import 'dart:async'; +import 'dart:html'; + +import 'package:observable/observable.dart'; +import 'package:observe/observe.dart'; +import 'package:benchmark_harness/benchmark_harness.dart'; + +abstract class ObservationBenchmarkBase + extends BenchmarkBase { + /// The number of objects to create and observe. + final int objectCount; + + /// The number of mutations to perform. + final int mutationCount; + + /// The current configuration. + final String config; + + /// The number of pending mutations left to observe. + int mutations; + + /// The objects we want to observe. + List objects; + + /// The change listeners on all of our objects. + List observers; + + /// The current object being mutated. + int objectIndex; + + ObservationBenchmarkBase( + String name, this.objectCount, this.mutationCount, this.config) + : super(name); + + /// Subclasses should use this method to perform mutations on an object. The + /// return value indicates how many mutations were performed on the object. + int mutateObject(T obj); + + /// Subclasses should use this method to return an observable object to be + /// benchmarked. + T newObject(); + + /// Subclasses should override this to do anything other than a default change + /// listener. It must return either a StreamSubscription or a PathObserver. + /// If overridden this observer should decrement [mutations] each time a + /// change is observed. + newObserver(T obj) { + decrement(_) => mutations--; + if (obj is ObservableList) return obj.listChanges.listen(decrement); + return obj.changes.listen(decrement); + } + + /// Set up each benchmark by creating all the objects and listeners. + @override + void setup() { + mutations = 0; + + objects = []; + observers = []; + objectIndex = 0; + + while (objects.length < objectCount) { + var obj = newObject(); + objects.add(obj); + observers.add(newObserver(obj)); + } + } + + /// Tear down each benchmark and make sure that [mutations] is 0. + @override + void teardown() { + if (mutations != 0) { + window.alert('$mutations mutation sets were not observed!'); + } + mutations = 0; + + while (observers.isNotEmpty) { + var observer = observers.removeLast(); + if (observer is StreamSubscription) { + observer.cancel(); + } else if (observer is PathObserver) { + observer.close(); + } else { + throw 'Unknown observer type ${observer.runtimeType}. Only ' + '[PathObserver] and [StreamSubscription] are supported.'; + } + } + observers = null; + + bool leakedObservers = false; + while (objects.isNotEmpty) { + leakedObservers = objects.removeLast().hasObservers || leakedObservers; + } + if (leakedObservers) window.alert('Observers leaked!'); + objects = null; + } + + /// Run the benchmark + @override + void run() { + var mutationsLeft = mutationCount; + while (mutationsLeft > 0) { + var obj = objects[objectIndex]; + mutationsLeft -= mutateObject(obj); + this.mutations++; + this.objectIndex++; + if (this.objectIndex == this.objects.length) { + this.objectIndex = 0; + } + obj.deliverChanges(); + if (obj is ObservableList) obj.deliverListChanges(); + } + AutoObservable.dirtyCheck(); + } +} diff --git a/benchmark/path_benchmark.dart b/benchmark/path_benchmark.dart new file mode 100644 index 0000000..a612c55 --- /dev/null +++ b/benchmark/path_benchmark.dart @@ -0,0 +1,42 @@ +// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +library observe.test.benchmark.path_benchmark; + +import 'package:observe/observe.dart'; +import 'observation_benchmark_base.dart'; +import 'test_path_observable.dart'; + +class PathBenchmark extends ObservationBenchmarkBase { + final PropertyPath path = new PropertyPath('foo.bar.baz'); + + PathBenchmark(int objectCount, int mutationCount, String config) + : super('PathBenchmark:$objectCount:$mutationCount:$config', objectCount, + mutationCount, config); + + @override + int mutateObject(TestPathObservable obj) { + switch (config) { + case 'leaf': + obj.foo.bar.baz += 1; + // Make sure [obj.foo.bar] delivers its changes synchronously. The base + // class already handles this for [obj]. + obj.foo.bar.deliverChanges(); + return 1; + + case 'root': + obj.foo = new Foo(obj.foo.bar.baz + 1); + return 1; + + default: + throw new ArgumentError('Invalid config for PathBenchmark: $config'); + } + } + + @override + TestPathObservable newObject() => new TestPathObservable(1); + + @override + PathObserver newObserver(TestPathObservable obj) => + new PathObserver(obj, path)..open((_) => mutations--); +} diff --git a/benchmark/setup_object_benchmark.dart b/benchmark/setup_object_benchmark.dart new file mode 100644 index 0000000..6fda9fc --- /dev/null +++ b/benchmark/setup_object_benchmark.dart @@ -0,0 +1,15 @@ +// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +library observe.test.benchmark.setup_object_benchmark; + +import 'setup_observation_benchmark_base.dart'; +import 'test_observable.dart'; + +class SetupObjectBenchmark extends SetupObservationBenchmarkBase { + SetupObjectBenchmark(int objectCount, String config) + : super('SetupObjectBenchmark:$objectCount:$config', objectCount, config); + + @override + TestObservable newObject() => new TestObservable(); +} diff --git a/benchmark/setup_observable_list_benchmark.dart b/benchmark/setup_observable_list_benchmark.dart new file mode 100644 index 0000000..b40aced --- /dev/null +++ b/benchmark/setup_observable_list_benchmark.dart @@ -0,0 +1,26 @@ +// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +library observe.test.benchmark.setup_observable_list_benchmark; + +import 'package:observable/observable.dart'; +import 'setup_observation_benchmark_base.dart'; + +class SetupObservableListBenchmark extends SetupObservationBenchmarkBase { + final int elementCount = 100; + + SetupObservableListBenchmark(int objectCount, String config) + : super('SetupObservableListBenchmark:$objectCount:$config', objectCount, + config); + + @override + ObservableList newObject() { + var list = new ObservableList(); + for (int i = 0; i < elementCount; i++) { + list.add(i); + } + list.deliverChanges(); + list.deliverListChanges(); + return list; + } +} diff --git a/benchmark/setup_observation_benchmark_base.dart b/benchmark/setup_observation_benchmark_base.dart new file mode 100644 index 0000000..2b26b72 --- /dev/null +++ b/benchmark/setup_observation_benchmark_base.dart @@ -0,0 +1,76 @@ +// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +library observe.test.benchmark.setup_observation_benchmark_base; + +import 'dart:async'; +import 'dart:html'; +import 'package:observable/observable.dart'; +import 'package:observe/observe.dart'; +import 'package:benchmark_harness/benchmark_harness.dart'; + +abstract class SetupObservationBenchmarkBase + extends BenchmarkBase { + /// The number of objects to create and observe. + final int objectCount; + + /// The current configuration. + final String config; + + /// The objects we want to observe. + List objects; + + SetupObservationBenchmarkBase(String name, this.objectCount, this.config) + : super(name); + + /// Subclasses should use this method to return an observable object to be + /// benchmarked. + T newObject(); + + /// Subclasses should override this to do anything other than a default change + /// listener. It must return either a StreamSubscription or a PathObserver. + newObserver(T obj) => obj.changes.listen((_) {}); + + /// Set up each benchmark by creating all the objects. + @override + void setup() { + objects = []; + while (objects.length < objectCount) { + objects.add(newObject()); + } + } + + /// Tear down each the benchmark and remove all listeners. + @override + void teardown() { + while (objects.isNotEmpty) { + var obj = objects.removeLast(); + if (obj.hasObservers || (obj is ObservableList && obj.hasListObservers)) { + window.alert('Observers leaked!'); + } + } + objects = null; + } + + /// Run the benchmark by creating a listener on each object. + @override + void run() { + for (var object in objects) { + var observer = newObserver(object); + + // **Note:** This is different than the JS implementation. Since run can + // be called an arbitrary number of times between [setup] and [teardown], + // we clean up all observers as we go. This means we are measuring both + // the setup and teardown of observers, versus the setup only in the + // JS benchmark. Not cleaning these up ends up giving `oh snap` errors. + if (observer is StreamSubscription) { + observer.cancel(); + } else if (observer is PathObserver) { + observer.close(); + } else { + throw 'Unknown observer type ${observer.runtimeType}. Only ' + '[PathObserver] and [StreamSubscription] are supported.'; + } + } + } +} diff --git a/benchmark/setup_path_benchmark.dart b/benchmark/setup_path_benchmark.dart new file mode 100644 index 0000000..72bba82 --- /dev/null +++ b/benchmark/setup_path_benchmark.dart @@ -0,0 +1,23 @@ +// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +library observe.test.benchmark.setup_path_benchmark; + +import 'package:observe/observe.dart'; +import 'setup_observation_benchmark_base.dart'; +import 'test_path_observable.dart'; + +class SetupPathBenchmark + extends SetupObservationBenchmarkBase { + final PropertyPath path = new PropertyPath('foo.bar.baz'); + + SetupPathBenchmark(int objectCount, String config) + : super('SetupPathBenchmark:$objectCount:$config', objectCount, config); + + @override + TestPathObservable newObject() => new TestPathObservable(1); + + @override + PathObserver newObserver(TestPathObservable obj) => + new PathObserver(obj, path)..open(() {}); +} diff --git a/benchmark/test_observable.dart b/benchmark/test_observable.dart new file mode 100644 index 0000000..15ef2d7 --- /dev/null +++ b/benchmark/test_observable.dart @@ -0,0 +1,39 @@ +// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +library observe.test.benchmark.test_observable; + +import 'package:observe/observe.dart'; + +class TestObservable extends AutoObservable { + @observable + int a = 0; + @observable + int b = 0; + @observable + int c = 0; + @observable + int d = 0; + @observable + int e = 0; + @observable + int f = 0; + @observable + int g = 0; + @observable + int h = 0; + @observable + int i = 0; + @observable + int j = 0; + @observable + int k = 0; + @observable + int l = 0; + @observable + int m = 0; + @observable + int n = 0; + @observable + int o = 0; +} diff --git a/benchmark/test_path_observable.dart b/benchmark/test_path_observable.dart new file mode 100644 index 0000000..6675822 --- /dev/null +++ b/benchmark/test_path_observable.dart @@ -0,0 +1,27 @@ +// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +library observe.test.benchmark.test_observable; + +import 'package:observe/observe.dart'; + +class Bar extends AutoObservable { + @observable + int baz; + + Bar(this.baz); +} + +class Foo extends AutoObservable { + @observable + Bar bar; + + Foo(int value) : bar = new Bar(value); +} + +class TestPathObservable extends AutoObservable { + @observable + Foo foo; + + TestPathObservable(int value) : foo = new Foo(value); +} diff --git a/lib/html.dart b/lib/html.dart index e29b0db..5000a78 100644 --- a/lib/html.dart +++ b/lib/html.dart @@ -19,7 +19,7 @@ import 'observe.dart'; /// An observable version of [window.location.hash]. final ObservableLocationHash windowLocation = new ObservableLocationHash._(); -class ObservableLocationHash extends PropertyChangeNotifier { +class ObservableLocationHash extends Observable { Object _currentHash; ObservableLocationHash._() { diff --git a/lib/src/auto_observable.dart b/lib/src/auto_observable.dart index 542bccc..bcb486a 100644 --- a/lib/src/auto_observable.dart +++ b/lib/src/auto_observable.dart @@ -13,7 +13,7 @@ import 'package:smoke/smoke.dart' as smoke; import 'dirty_check.dart' show dirtyCheckObservables, registerObservable; import 'metadata.dart' show ObservableProperty; -abstract class AutoObservable implements ChangeNotifier { +abstract class AutoObservable implements Observable { /// Performs dirty checking of objects that inherit from [AutoObservable]. /// This scans all observed objects using mirrors and determines if any fields /// have changed. If they have, it delivers the changes for the object. @@ -147,8 +147,8 @@ abstract class AutoObservable implements ChangeNotifier { /// - Unlike [Observable] this will not schedule [deliverChanges]; use /// [AutoObservable.dirtyCheck] instead. @override - void notifyChange([ChangeRecord record]) { - if (!hasObservers || record == null) return; + void notifyChange(ChangeRecord record) { + if (!hasObservers) return; if (_records == null) _records = []; _records.add(record); diff --git a/lib/src/list_path_observer.dart b/lib/src/list_path_observer.dart index c329103..09f1b6c 100644 --- a/lib/src/list_path_observer.dart +++ b/lib/src/list_path_observer.dart @@ -14,7 +14,7 @@ import 'package:observe/observe.dart'; /// Observes a path starting from each item in the list. @deprecated -class ListPathObserver extends PropertyChangeNotifier { +class ListPathObserver extends Observable { final ObservableList list; final String _itemPath; final List _observers = []; diff --git a/lib/src/metadata.dart b/lib/src/metadata.dart index 15e7905..6811332 100644 --- a/lib/src/metadata.dart +++ b/lib/src/metadata.dart @@ -37,7 +37,7 @@ const Reflectable reflectable = const Reflectable(); /// it available to `PathObserver` at runtime. For example: /// /// @reflectable -/// class Monster extends AutoObservable { +/// class Monster extends Observable { /// int _health; /// int get health => _health; /// ... diff --git a/lib/src/observable_box.dart b/lib/src/observable_box.dart index 5749890..51c584a 100644 --- a/lib/src/observable_box.dart +++ b/lib/src/observable_box.dart @@ -13,7 +13,7 @@ import 'package:observe/observe.dart'; /// value. For other cases, it is better to use [AutoObservableList], /// [AutoObservableMap], or a custom [AutoObservable] implementation based on /// [AutoObservable]. The property name for changes is "value". -class ObservableBox extends PropertyChangeNotifier { +class ObservableBox extends Observable { T _value; ObservableBox([T initialValue]) : _value = initialValue; diff --git a/pubspec.yaml b/pubspec.yaml index e9a4b36..e74974a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: observe -version: 0.15.0 +version: 0.14.0 author: Polymer.dart Authors description: > Observable properties and objects for use in template_binding. @@ -13,7 +13,7 @@ dependencies: barback: '>=0.14.2 <0.16.0' func: ^0.1.0 logging: '>=0.9.0 <0.12.0' - observable: '^0.17.0' + observable: '^0.14.0' path: '>=0.9.0 <2.0.0' smoke: '>=0.1.0 <0.4.0' source_maps: '>=0.9.4 <0.11.0' diff --git a/test/list_path_observer_test.dart b/test/list_path_observer_test.dart index f0e89b5..e3df022 100644 --- a/test/list_path_observer_test.dart +++ b/test/list_path_observer_test.dart @@ -85,7 +85,7 @@ _runTests() { _nextMicrotask(_) => new Future(() {}); @reflectable -class TestModel extends PropertyChangeNotifier { +class TestModel extends Observable { var _a, _b; TestModel(); diff --git a/test/path_observer_test.dart b/test/path_observer_test.dart index bc7731a..ba9cc22 100644 --- a/test/path_observer_test.dart +++ b/test/path_observer_test.dart @@ -175,8 +175,7 @@ observePathTests() { }); test('get value at path ObservableBox', () { - var obj = - new ObservableBox(new ObservableBox(new ObservableBox(1))); + var obj = new ObservableBox(new ObservableBox(new ObservableBox(1))); expect(new PathObserver(obj, '').value, obj); expect(new PathObserver(obj, 'value').value, obj.value); @@ -189,7 +188,7 @@ observePathTests() { obj.value.value = new ObservableBox(3); expect(new PathObserver(obj, 'value.value.value').value, 3); - obj.value = new ObservableBox(4); + obj.value = new ObservableBox(4); expect(() => new PathObserver(obj, 'value.value.value').value, _throwsNSM('value')); expect(new PathObserver(obj, 'value.value').value, 4); @@ -754,7 +753,7 @@ class IndexerModel implements Indexable { } @reflectable -class TestModel extends ChangeNotifier implements WatcherModel { +class TestModel extends Observable implements WatcherModel { var _a, _b, _c; TestModel([this._a, this._b, this._c]); @@ -776,15 +775,6 @@ class TestModel extends ChangeNotifier implements WatcherModel { void set c(newValue) { _c = notifyPropertyChange(#c, _c, newValue); } - - @override - /*=T*/ notifyPropertyChange/**/( - Symbol field, /*=T*/ oldValue, /*=T*/ newValue) { - if (hasObservers && oldValue != newValue) { - notifyChange(new PropertyChangeRecord(this, field, oldValue, newValue)); - } - return newValue; - } } class WatcherModel extends AutoObservable {