Deprecate Observable, add ChangeNotifier, setup travis (#11)

* Deprecate Observable, setup Travis

* Make changes suggested via review

* Update .travis.yml

Remove stable branch, as `collection` dependency won't resolve on it.
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..4fb40c9
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,8 @@
+language: dart
+
+dart:
+  - dev
+  # Currently requires a dev-branch in order to use properly.
+  # - stable
+
+script: ./tool/presubmit.sh
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 398c7c4..0c8e36d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,36 @@
+## 0.17.0
+
+This is a larger change with a goal of no runtime changes for current
+customers, but in the future `Observable` will [become][issue_10] a very
+lightweight interface, i.e.:
+
+```dart
+abstract class Observable<C extends ChangeRecord> {
+  Stream<List<C>> get changes;
+}
+```
+
+[issue_10]: https://github.com/dart-lang/observable/issues/10
+
+* Started deprecating the wide `Observable` interface
+    * `ChangeNotifier` should be used as a base class for these methods:
+        * `Observable.observed`
+        * `Observable.unobserved`
+        * `Observable.hasObservers`
+        * `Observable.deliverChanges`
+        * `Observable.notifyChange`
+    * `PropertyChangeNotifier` should be used for these methods:
+        * `Observable.notifyPropertyChange`
+    * Temporarily, `Observable` _uses_ `ChangeNotifier`
+        * Existing users of anything but `implements Observable` should
+          move to implementing or extending `ChangeNotifier`. In a
+          future release `Observable` will reduce API surface down to
+          an abstract `Stream<List<C>> get changes`.
+* Added the `ChangeNotifier` and `PropertyChangeNotifier` classes
+    * Can be used to implement `Observable` in a generic manner
+* Observable is now `Observable<C extends ChangeRecord>`
+    * When passing a generic type `C`, `notifyPropertyChange` is illegal
+
 ## 0.16.0
 
 * Refactored `MapChangeRecord`
diff --git a/lib/observable.dart b/lib/observable.dart
index 32a6560..516ff70 100644
--- a/lib/observable.dart
+++ b/lib/observable.dart
@@ -4,10 +4,11 @@
 
 library observable;
 
+export 'src/change_notifier.dart' show ChangeNotifier, PropertyChangeNotifier;
 export 'src/differs.dart' show Differ, EqualityDiffer, ListDiffer, MapDiffer;
-export 'src/records.dart' show ChangeRecord, ListChangeRecord, MapChangeRecord;
+export 'src/records.dart'
+    show ChangeRecord, ListChangeRecord, MapChangeRecord, PropertyChangeRecord;
 export 'src/observable.dart';
 export 'src/observable_list.dart';
 export 'src/observable_map.dart';
-export 'src/property_change_record.dart';
 export 'src/to_observable.dart';
diff --git a/lib/src/change_notifier.dart b/lib/src/change_notifier.dart
new file mode 100644
index 0000000..d86a4cd
--- /dev/null
+++ b/lib/src/change_notifier.dart
@@ -0,0 +1,144 @@
+import 'dart:async';
+
+import 'package:meta/meta.dart';
+
+import 'internal.dart';
+import 'observable.dart';
+import 'records.dart';
+
+/// Supplies [changes] and various hooks to implement [Observable].
+///
+/// May use [notifyChange] to queue a change record; they are asynchronously
+/// delivered at the end of the VM turn.
+///
+/// [ChangeNotifier] may be extended, mixed in, or used as a delegate.
+class ChangeNotifier<C extends ChangeRecord> implements Observable<C> {
+  StreamController<List<C>> _changes;
+
+  bool _scheduled = false;
+  List<C> _queue;
+
+  /// Emits a list of changes when the state of the object changes.
+  ///
+  /// Changes should produced in order, if significant.
+  @override
+  Stream<List<C>> get changes {
+    return (_changes ??= new StreamController<List<C>>.broadcast(
+      sync: true,
+      onListen: observed,
+      onCancel: unobserved,
+    ))
+        .stream;
+  }
+
+  /// May override to be notified when [changes] is first observed.
+  @override
+  @protected
+  @mustCallSuper
+  void observed() {}
+
+  /// May override to be notified when [changes] is no longer observed.
+  @override
+  @protected
+  @mustCallSuper
+  void unobserved() {
+    _changes = _queue = null;
+  }
+
+  /// If [hasObservers], synchronously emits [changes] that have been queued.
+  ///
+  /// Returns `true` if changes were emitted.
+  @override
+  @protected
+  @mustCallSuper
+  bool deliverChanges() {
+    List<ChangeRecord> changes;
+    if (_scheduled && hasObservers) {
+      if (_queue != null) {
+        changes = freezeInDevMode(_queue);
+        _queue = null;
+      } else {
+        changes = ChangeRecord.ANY;
+      }
+      _scheduled = false;
+      _changes.add(changes);
+    }
+    return changes != null;
+  }
+
+  /// Whether [changes] has at least one active listener.
+  ///
+  /// May be used to optimize whether to produce change records.
+  @override
+  bool get hasObservers => _changes?.hasListener == true;
+
+  /// Schedules [change] to be delivered.
+  ///
+  /// If [change] is omitted then [ChangeRecord.ANY] will be sent.
+  ///
+  /// If there are no listeners to [changes], this method does nothing.
+  @override
+  void notifyChange([C change]) {
+    if (!hasObservers) {
+      return;
+    }
+    if (change != null) {
+      (_queue ??= <C>[]).add(change);
+    }
+    if (!_scheduled) {
+      scheduleMicrotask(deliverChanges);
+      _scheduled = true;
+    }
+  }
+
+  @Deprecated('Exists to make migrations off Observable easier')
+  @override
+  @protected
+  /*=T*/ notifyPropertyChange/*<T>*/(
+    Symbol field,
+    /*=T*/
+    oldValue,
+    /*=T*/
+    newValue,
+  ) {
+    throw new UnsupportedError('Not supported by ChangeNotifier');
+  }
+}
+
+/// Implements [notifyPropertyChange] for classes that need support.
+///
+/// Will be folded entirely into [PropertyChangeNotifier] in the future.
+@Deprecated('Exists to make migrations off Observable easier')
+abstract class PropertyChangeMixin implements ChangeNotifier {
+  @override
+  /*=T*/ notifyPropertyChange/*<T>*/(
+    Symbol field,
+    /*=T*/
+    oldValue,
+    /*=T*/
+    newValue,
+  ) {
+    if (hasObservers && oldValue != newValue) {
+      notifyChange(
+        new PropertyChangeRecord/*<T>*/(
+          this,
+          field,
+          oldValue,
+          newValue,
+        ),
+      );
+    }
+    return newValue;
+  }
+}
+
+/// Supplies property `changes` and various hooks to implement [Observable].
+///
+/// May use `notifyChange` or `notifyPropertyChange` to queue a property change
+/// record; they are asynchronously delivered at the end of the VM turn.
+///
+/// [PropertyChangeNotifier] may be extended or used as a delegate. To use as
+/// a mixin, instead use with [PropertyChangeMixin]:
+///     with ChangeNotifier<PropertyChangeRecord>, PropertyChangeMixin
+class PropertyChangeNotifier = ChangeNotifier<PropertyChangeRecord>
+    with PropertyChangeMixin;
diff --git a/lib/src/differs.dart b/lib/src/differs.dart
index 8d155ed..40f005e 100644
--- a/lib/src/differs.dart
+++ b/lib/src/differs.dart
@@ -17,7 +17,7 @@
 
 /// Generic comparisons between two comparable objects.
 abstract class Differ<E> {
-  /// Returns a list of change records between [e1] and [e2].
+  /// Returns a list of change records between [oldValue] and [newValue].
   ///
   /// A return value of an empty [ChangeRecord.NONE] means no changes found.
   List<ChangeRecord> diff(E oldValue, E newValue);
diff --git a/lib/src/differs/list_differ.dart b/lib/src/differs/list_differ.dart
index f55ff4f..58004a7 100644
--- a/lib/src/differs/list_differ.dart
+++ b/lib/src/differs/list_differ.dart
@@ -210,7 +210,7 @@
   }
 
   if (currentStart == currentEnd) {
-    final spliceRemoved = old.sublist(oldStart, oldStart + (oldEnd - oldStart));
+    final spliceRemoved = old.sublist(oldStart, oldEnd);
     return [
       new ListChangeRecord/*<E>*/ .remove(
         current,
diff --git a/lib/src/observable.dart b/lib/src/observable.dart
index 8a90e09..2d2c49e 100644
--- a/lib/src/observable.dart
+++ b/lib/src/observable.dart
@@ -5,74 +5,57 @@
 library observable.src.observable;
 
 import 'dart:async';
-import 'dart:collection' show UnmodifiableListView;
 
 import 'package:meta/meta.dart';
 
-import 'property_change_record.dart' show PropertyChangeRecord;
-import 'records.dart' show ChangeRecord;
+import 'change_notifier.dart';
+import 'records.dart';
 
-/// Represents an object with observable properties. This is used by data in
-/// model-view architectures to notify interested parties of [changes] to the
-/// object's properties (fields or getter/setter pairs).
+/// Represents an object with observable state or properties.
 ///
 /// The interface does not require any specific technique to implement
-/// observability. You can implement it in the following ways:
-///
-/// - Deriving from this class via a mixin or base class. When a field,
-///   property, or indexable item is changed, the derived class should call
-///   [notifyPropertyChange]. See that method for an example.
-/// - Implementing this interface and providing your own implementation.
-abstract class Observable {
-  StreamController<List<ChangeRecord>> _changes;
+/// observability. You may implement it in the following ways:
+/// - Extend or mixin [ChangeNotifier]
+/// - Implement the interface yourself and provide your own implementation
+abstract class Observable<C extends ChangeRecord> {
+  // To be removed when https://github.com/dart-lang/observable/issues/10
+  final ChangeNotifier<C> _delegate = new ChangeNotifier<C>();
 
-  List<ChangeRecord> _records;
+  // Whether Observable was not given a type.
+  final bool _isNotGeneric = C == dynamic;
 
-  /// The stream of property change records to this object, delivered
-  /// asynchronously.
+  /// Emits a list of changes when the state of the object changes.
   ///
-  /// [deliverChanges] can be called to force synchronous delivery.
-  Stream<List<ChangeRecord>> get changes {
-    if (_changes == null) {
-      _changes = new StreamController.broadcast(
-          sync: true, onListen: observed, onCancel: unobserved);
-    }
-    return _changes.stream;
-  }
+  /// Changes should produced in order, if significant.
+  Stream<List<C>> get changes => _delegate.changes;
 
-  /// Derived classes may override this method to be called when the [changes]
-  /// are first observed.
-  // TODO(tvolkert): @mustCallSuper (github.com/dart-lang/sdk/issues/27275)
+  /// May override to be notified when [changes] is first observed.
   @protected
-  void observed() {}
+  @mustCallSuper
+  @Deprecated('Use ChangeNotifier instead to have this method available')
+  // REMOVE IGNORE when https://github.com/dart-lang/observable/issues/10
+  // ignore: invalid_use_of_protected_member
+  void observed() => _delegate.observed();
 
-  /// Derived classes may override this method to be called when the [changes]
-  /// are no longer being observed.
-  // TODO(tvolkert): @mustCallSuper (github.com/dart-lang/sdk/issues/27275)
+  /// May override to be notified when [changes] is no longer observed.
   @protected
-  void unobserved() {
-    // Free some memory
-    _changes = null;
-  }
+  @mustCallSuper
+  @Deprecated('Use ChangeNotifier instead to have this method available')
+  // REMOVE IGNORE when https://github.com/dart-lang/observable/issues/10
+  // ignore: invalid_use_of_protected_member
+  void unobserved() => _delegate.unobserved();
 
   /// True if this object has any observers.
-  bool get hasObservers => _changes != null && _changes.hasListener;
+  @Deprecated('Use ChangeNotifier instead to have this method available')
+  bool get hasObservers => _delegate.hasObservers;
 
-  /// Synchronously deliver pending [changes].
+  /// If [hasObservers], synchronously emits [changes] that have been queued.
   ///
-  /// Returns `true` if any records were delivered, otherwise `false`.
-  /// Pending records will be cleared regardless, to keep newly added
-  /// observers from being notified of changes that occurred before
-  /// they started observing.
-  bool deliverChanges() {
-    List<ChangeRecord> records = _records;
-    _records = null;
-    if (hasObservers && records != null) {
-      _changes.add(new UnmodifiableListView<ChangeRecord>(records));
-      return true;
-    }
-    return false;
-  }
+  /// Returns `true` if changes were emitted.
+  @Deprecated('Use ChangeNotifier instead to have this method available')
+  // REMOVE IGNORE when https://github.com/dart-lang/observable/issues/10
+  // ignore: invalid_use_of_protected_member
+  bool deliverChanges() => _delegate.deliverChanges();
 
   /// Notify that the [field] name of this object has been changed.
   ///
@@ -80,27 +63,45 @@
   /// equal, no change will be recorded.
   ///
   /// For convenience this returns [newValue].
+  ///
+  /// ## Deprecated
+  ///
+  /// All [Observable] objects will no longer be required to emit change records
+  /// when any property changes. For example, `ObservableList` will only emit
+  /// on `ObservableList.changes`, instead of on `ObservableList.listChanges`.
+  ///
+  /// If you are using a typed `implements/extends Observable<C>`, it is illegal
+  /// to call this method - will throw an [UnsupportedError] when called.
+  @Deprecated('Use PropertyChangeNotifier')
   /*=T*/ notifyPropertyChange/*<T>*/(
-      Symbol field, /*=T*/ oldValue, /*=T*/ newValue) {
+    Symbol field,
+    /*=T*/
+    oldValue,
+    /*=T*/
+    newValue,
+  ) {
     if (hasObservers && oldValue != newValue) {
-      notifyChange(new PropertyChangeRecord(this, field, oldValue, newValue));
+      if (_isNotGeneric) {
+        notifyChange(
+          new PropertyChangeRecord(
+            this,
+            field,
+            oldValue,
+            newValue,
+          ) as C,
+        );
+      } else {
+        throw new UnsupportedError('Generic typed Observable does not support');
+      }
     }
     return newValue;
   }
 
-  /// Notify observers of a change.
+  /// Schedules [change] to be delivered.
   ///
-  /// This will automatically schedule [deliverChanges].
+  /// If [change] is omitted then [ChangeRecord.ANY] will be sent.
   ///
-  /// For most objects [Observable.notifyPropertyChange] is more convenient, but
-  /// collections sometimes deliver other types of changes such as a
-  /// [MapChangeRecord].
-  void notifyChange(ChangeRecord record) {
-    if (!hasObservers) return;
-    if (_records == null) {
-      _records = [];
-      scheduleMicrotask(deliverChanges);
-    }
-    _records.add(record);
-  }
+  /// If there are no listeners to [changes], this method does nothing.
+  @Deprecated('Use ChangeNotifier instead to have this method available')
+  void notifyChange([C change]) => _delegate.notifyChange(change);
 }
diff --git a/lib/src/observable_map.dart b/lib/src/observable_map.dart
index 54e40e2..caebbda 100644
--- a/lib/src/observable_map.dart
+++ b/lib/src/observable_map.dart
@@ -6,9 +6,8 @@
 
 import 'dart:collection';
 
-import 'observable.dart' show Observable;
-import 'property_change_record.dart' show PropertyChangeRecord;
-import 'records.dart' show MapChangeRecord;
+import 'observable.dart';
+import 'records.dart';
 import 'to_observable.dart';
 
 // TODO(jmesserly): this needs to be faster. We currently require multiple
diff --git a/lib/src/property_change_record.dart b/lib/src/property_change_record.dart
deleted file mode 100644
index 153cb5d..0000000
--- a/lib/src/property_change_record.dart
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright (c) 2016, 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 observable.src.property_change_record;
-
-import 'observable.dart';
-import 'records.dart' show ChangeRecord;
-
-/// A change record to a field of an [Observable] object.
-class PropertyChangeRecord<T> extends ChangeRecord {
-  /// The object that changed.
-  final Object object;
-
-  /// The name of the property that changed.
-  final Symbol name;
-
-  /// The previous value of the property.
-  final T oldValue;
-
-  /// The new value of the property.
-  final T newValue;
-
-  PropertyChangeRecord(this.object, this.name, this.oldValue, this.newValue);
-
-  @override
-  String toString() =>
-      '#<PropertyChangeRecord $name from: $oldValue to: $newValue>';
-}
diff --git a/lib/src/records.dart b/lib/src/records.dart
index 07a9826..87d7d4f 100644
--- a/lib/src/records.dart
+++ b/lib/src/records.dart
@@ -11,6 +11,7 @@
 
 part 'records/list_change_record.dart';
 part 'records/map_change_record.dart';
+part 'records/property_change_record.dart';
 
 /// Result of a change to an observed object.
 class ChangeRecord {
diff --git a/lib/src/records/map_change_record.dart b/lib/src/records/map_change_record.dart
index 9150732..cd37fc9 100644
--- a/lib/src/records/map_change_record.dart
+++ b/lib/src/records/map_change_record.dart
@@ -55,10 +55,10 @@
   bool operator ==(Object o) {
     if (o is MapChangeRecord<K, V>) {
       return key == o.key &&
-             oldValue == o.oldValue &&
-             newValue == o.newValue &&
-             isInsert == o.isInsert &&
-             isRemove == o.isRemove;
+          oldValue == o.oldValue &&
+          newValue == o.newValue &&
+          isInsert == o.isInsert &&
+          isRemove == o.isRemove;
     }
     return false;
   }
diff --git a/lib/src/records/property_change_record.dart b/lib/src/records/property_change_record.dart
new file mode 100644
index 0000000..74566d5
--- /dev/null
+++ b/lib/src/records/property_change_record.dart
@@ -0,0 +1,31 @@
+// Copyright (c) 2016, 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.
+
+part of observable.src.records;
+
+/// A change record to a field of a generic observable object.
+class PropertyChangeRecord<T> implements ChangeRecord {
+  /// Object that changed.
+  final Object object;
+
+  /// Name of the property that changed.
+  final Symbol name;
+
+  /// Previous value of the property.
+  final T oldValue;
+
+  /// New value of the property.
+  final T newValue;
+
+  const PropertyChangeRecord(
+    this.object,
+    this.name,
+    this.oldValue,
+    this.newValue,
+  );
+
+  @override
+  String toString() => ''
+      '#<$PropertyChangeRecord $name from $oldValue to: $newValue';
+}
diff --git a/pubspec.yaml b/pubspec.yaml
index a5451f0..9d1baed 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,5 +1,5 @@
 name: observable
-version: 0.16.0
+version: 0.17.0
 author: Dart Team <misc@dartlang.org>
 description: Support for marking objects as observable
 homepage: https://github.com/dart-lang/observable
@@ -10,4 +10,5 @@
   meta: '^1.0.4'
   quiver: '^0.24.0'
 dev_dependencies:
+  dart_style: '^0.2.0'
   test: '^0.12.0'
diff --git a/test/observable_test.dart b/test/observable_test.dart
index eb824c7..af4d9c3 100644
--- a/test/observable_test.dart
+++ b/test/observable_test.dart
@@ -206,7 +206,7 @@
 
 createModel(int number) => new ObservableSubclass(number);
 
-class ObservableSubclass<T> extends Observable {
+class ObservableSubclass<T> extends PropertyChangeNotifier {
   ObservableSubclass([T initialValue]) : _value = initialValue;
 
   T get value => _value;
diff --git a/tool/presubmit.sh b/tool/presubmit.sh
new file mode 100755
index 0000000..364bee7
--- /dev/null
+++ b/tool/presubmit.sh
@@ -0,0 +1,29 @@
+#!/bin/bash
+
+# Make sure dartfmt is run on everything
+# This assumes you have dart_style as a dev_dependency
+echo "Checking dartfmt..."
+NEEDS_DARTFMT="$(find lib test -name "*.dart" | xargs pub run dart_style:format -n)"
+if [[ ${NEEDS_DARTFMT} != "" ]]
+then
+  echo "FAILED"
+  echo "${NEEDS_DARTFMT}"
+  exit 1
+fi
+echo "PASSED"
+
+# Make sure we pass the analyzer
+echo "Checking dartanalyzer..."
+FAILS_ANALYZER="$(find lib test -name "*.dart" | xargs dartanalyzer --options .analysis_options)"
+if [[ $FAILS_ANALYZER == *"[error]"* ]]
+then
+  echo "FAILED"
+  echo "${FAILS_ANALYZER}"
+  exit 1
+fi
+echo "PASSED"
+
+# Fail on anything that fails going forward.
+set -e
+
+pub run test