Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(646)

Unified Diff: pkg/barback/lib/src/transform_node.dart

Issue 21275003: Move barback to a more event-based model. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: pkg/barback/lib/src/transform_node.dart
diff --git a/pkg/barback/lib/src/transform_node.dart b/pkg/barback/lib/src/transform_node.dart
index 84e42c0d0a5664bf10f2189bff6578099b1ed210..c047b5cf474d83aec3cb93450962ba4ba73395fb 100644
--- a/pkg/barback/lib/src/transform_node.dart
+++ b/pkg/barback/lib/src/transform_node.dart
@@ -25,94 +25,151 @@ class TransformNode {
final Phase phase;
/// The [Transformer] to apply to this node's inputs.
- final Transformer _transformer;
+ final Transformer transformer;
/// The node for the primary asset this transform depends on.
final AssetNode primary;
+ /// The subscription to [primary]'s [AssetNode.onStateChange] stream.
+ StreamSubscription _primarySubscription;
+
/// True if an input has been modified since the last time this transform
- /// was run.
+ /// began running.
bool get isDirty => _isDirty;
var _isDirty = true;
/// The inputs read by this transform the last time it was run.
///
- /// Used to tell if an input was removed in a later run.
+ /// Used to tell if an input was added or removed in a later run.
var _inputs = new Set<AssetNode>();
- /// The outputs created by this transform the last time it was run.
- ///
- /// Used to tell if an output was removed in a later run.
- Set<AssetId> get outputs => _outputs;
- var _outputs = new Set<AssetId>();
+ /// The subscriptions to each input's [AssetNode.onStateChange] stream.
+ var _inputSubscriptions = new Map<AssetId, StreamSubscription>();
+
+ /// The controllers for the asset nodes emitted by this node.
+ var _outputControllers = new Map<AssetId, AssetNodeController>();
+
+ TransformNode(this.phase, this.transformer, this.primary) {
+ _primarySubscription = primary.onStateChange.listen((state) {
+ if (state.isRemoved) {
+ remove();
+ } else {
+ _dirty();
+ }
+ });
+ }
- TransformNode(this.phase, this._transformer, this.primary);
+ /// Marks this transform as removed.
+ ///
+ /// This causes all of the transform's outputs to be marked as removed as
+ /// well. Normally this will be automatically done internally based on events
+ /// from the primary input, but it's possible for a transform to no longer be
+ /// valid even if its primary input still exists.
+ void remove() {
+ _isDirty = true;
+ _primarySubscription.cancel();
+ for (var subscription in _inputSubscriptions.values) {
+ subscription.cancel();
+ }
+ for (var controller in _outputControllers.values) {
+ controller.setRemoved();
+ }
+ }
- /// Marks this transform as needing to be run.
- void dirty() {
+ /// Marks this transform as dirty.
+ ///
+ /// This causes all of the transform's outputs to be marked as dirty as well.
+ void _dirty() {
_isDirty = true;
+ for (var controller in _outputControllers.values) {
+ controller.setDirty();
+ }
}
/// Applies this transform.
///
- /// Returns a [TransformOutputs] describing the resulting outputs compared to
- /// previous runs.
- Future<TransformOutputs> apply() {
+ /// Returns a set of asset nodes representing the outputs from this transform
+ /// that weren't emitted last time it was run.
+ Future<Set<AssetNode>> apply() {
var newInputs = new Set<AssetNode>();
var newOutputs = new AssetSet();
var transform = createTransform(this, newInputs, newOutputs);
- return _transformer.apply(transform).catchError((error) {
- // Catch all transformer errors and pipe them to the results stream. This
- // is so a broken transformer doesn't take down the whole graph.
+ _isDirty = false;
+ return transformer.apply(transform).catchError((error) {
+ // If the transform became dirty while processing, ignore any errors from
+ // it.
+ if (_isDirty) return;
+
+ // Catch all transformer errors and pipe them to the results stream.
+ // This is so a broken transformer doesn't take down the whole graph.
phase.cascade.reportError(error);
// Don't allow partial results from a failed transform.
newOutputs.clear();
}).then((_) {
- _isDirty = false;
-
- // Stop watching any inputs that were removed.
- for (var oldInput in _inputs) {
- oldInput.consumers.remove(this);
- }
-
- // Watch any new inputs so this transform will be re-processed when an
- // input is modified.
- for (var newInput in newInputs) {
- newInput.consumers.add(this);
- }
-
- _inputs = newInputs;
-
- // See which outputs are missing from the last run.
- var outputIds = newOutputs.map((asset) => asset.id).toSet();
- var invalidIds = outputIds
- .where((id) => id.package != phase.cascade.package).toSet();
- outputIds.removeAll(invalidIds);
-
- for (var id in invalidIds) {
- // TODO(nweiz): report this as a warning rather than a failing error.
- phase.cascade.reportError(
- new InvalidOutputException(phase.cascade.package, id));
- }
-
- var removed = _outputs.difference(outputIds);
- _outputs = outputIds;
+ if (_isDirty) return [];
- return new TransformOutputs(newOutputs, removed);
+ _adjustInputs(newInputs);
+ return _adjustOutputs(newOutputs);
});
}
-}
-/// The result of running a [Transform], compared to the previous time it was
-/// applied.
-class TransformOutputs {
- /// The outputs that are new or were modified since the last run.
- final AssetSet updated;
+ /// Adjusts the inputs of the transform to reflect the inputs consumed on its
+ /// most recent run.
+ void _adjustInputs(Set<AssetNode> newInputs) {
+ // Stop watching any inputs that were removed.
+ for (var oldInput in _inputs.difference(newInputs)) {
+ _inputSubscriptions.remove(oldInput.id).cancel();
+ }
+
+ // Watch any new inputs so this transform will be re-processed when an
+ // input is modified.
+ for (var newInput in newInputs.difference(_inputs)) {
+ if (newInput.id == primary.id) continue;
Bob Nystrom 2013/07/31 20:05:41 How can this case get hit?
nweiz 2013/07/31 22:47:53 This case is hit whenever the transform loads the
+ // TODO(nweiz): support the case where a new secondary input changes
+ // after it's been loaded by the transform but before the transform has
+ // finished running.
+ _inputSubscriptions[newInput.id] = newInput.onStateChange
+ .listen((_) => _dirty());
+ }
+
+ _inputs = newInputs;
+ }
- /// The outputs that were created by the previous run but were not generated
- /// by the most recent run.
- final Set<AssetId> removed;
+ /// Adjusts the outputs of the transform to reflect the outputs emitted on its
+ /// most recent run.
+ Set<AssetNode> _adjustOutputs(AssetSet newOutputs) {
+ // Any ids that are for the wrong package are invalid.
Bob Nystrom 2013/07/31 20:05:41 "for the wrong" -> "to a different"
nweiz 2013/07/31 22:47:53 Done.
+ var invalidIds = newOutputs
+ .map((asset) => asset.id)
+ .where((id) => id.package != phase.cascade.package)
+ .toSet();
+ for (var id in invalidIds) {
+ newOutputs.removeId(id);
+ // TODO(nweiz): report this as a warning rather than a failing error.
+ phase.cascade.reportError(
+ new InvalidOutputException(phase.cascade.package, id));
+ }
+
+ // Remove outputs that used to exist but don't anymore.
+ for (var id in _outputControllers.keys.toList()) {
Bob Nystrom 2013/07/31 20:05:41 If we make AssetSet implement Set, you could do:
nweiz 2013/07/31 22:47:53 Eh, I think this is clear enough.
+ if (newOutputs.containsId(id)) continue;
+ _outputControllers.remove(id).setRemoved();
+ }
+
+ var brandNewOutputs = new Set<AssetNode>();
+ // Store any new outputs or new contents for existing outputs.
+ for (var asset in newOutputs) {
+ var controller = _outputControllers[asset.id];
Bob Nystrom 2013/07/31 20:05:41 Use putIfAbsent() here?
nweiz 2013/07/31 22:47:53 That doesn't work well when we want to call [setAv
Bob Nystrom 2013/07/31 23:20:15 You could just call that from within the pubIfAbse
+ if (controller != null) {
+ controller.setAvailable(asset);
+ } else {
+ var controller = new AssetNodeController.available(asset);
+ _outputControllers[asset.id] = controller;
+ brandNewOutputs.add(controller.node);
+ }
+ }
- TransformOutputs(this.updated, this.removed);
+ return brandNewOutputs;
+ }
}

Powered by Google App Engine
This is Rietveld 408576698