Add a document-by-document JSON transformer. This makes it easy to send JSON over WebSocket connections, for example. R=rnystrom@google.com Review URL: https://codereview.chromium.org//1643843002 .
diff --git a/pkgs/stream_channel/lib/src/json_document_transformer.dart b/pkgs/stream_channel/lib/src/json_document_transformer.dart new file mode 100644 index 0000000..8d8dcce --- /dev/null +++ b/pkgs/stream_channel/lib/src/json_document_transformer.dart
@@ -0,0 +1,41 @@ +// 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. + +import 'dart:convert'; + +import 'package:async/async.dart'; + +import '../stream_channel.dart'; +import 'stream_channel_transformer.dart'; + +/// The canonical instance of [JsonDocumentTransformer]. +final jsonDocument = new JsonDocumentTransformer(); + +/// A [StreamChannelTransformer] that transforms JSON documents—strings that +/// contain individual objects encoded as JSON—into decoded Dart objects. +/// +/// This decodes JSON that's emitted by the transformed channel's stream, and +/// encodes objects so that JSON is passed to the transformed channel's sink. +class JsonDocumentTransformer + implements StreamChannelTransformer<String, Object> { + /// The underlying codec that implements the encoding and decoding logic. + final JsonCodec _codec; + + /// Creates a new transformer. + /// + /// The [reviver] and [toEncodable] arguments work the same way as the + /// corresponding arguments to [new JsonCodec]. + JsonDocumentTransformer({reviver(key, value), toEncodable(object)}) + : _codec = new JsonCodec(reviver: reviver, toEncodable: toEncodable); + + JsonDocumentTransformer._(this._codec); + + StreamChannel bind(StreamChannel<String> channel) { + var stream = channel.stream.map(_codec.decode); + var sink = new StreamSinkTransformer.fromHandlers(handleData: (data, sink) { + sink.add(_codec.encode(data)); + }).bind(channel.sink); + return new StreamChannel(stream, sink); + } +}
diff --git a/pkgs/stream_channel/lib/stream_channel.dart b/pkgs/stream_channel/lib/stream_channel.dart index e7fb055..f914188 100644 --- a/pkgs/stream_channel/lib/stream_channel.dart +++ b/pkgs/stream_channel/lib/stream_channel.dart
@@ -8,6 +8,7 @@ export 'src/delegating_stream_channel.dart'; export 'src/isolate_channel.dart'; +export 'src/json_document_transformer.dart'; export 'src/multi_channel.dart'; export 'src/stream_channel_completer.dart'; export 'src/stream_channel_transformer.dart';
diff --git a/pkgs/stream_channel/test/json_document_transformer_test.dart b/pkgs/stream_channel/test/json_document_transformer_test.dart new file mode 100644 index 0000000..fec3d2a --- /dev/null +++ b/pkgs/stream_channel/test/json_document_transformer_test.dart
@@ -0,0 +1,50 @@ +// 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. + +import 'dart:async'; +import 'dart:convert'; + +import 'package:stream_channel/stream_channel.dart'; +import 'package:test/test.dart'; + +import 'utils.dart'; + +void main() { + var streamController; + var sinkController; + var channel; + setUp(() { + streamController = new StreamController(); + sinkController = new StreamController(); + channel = new StreamChannel( + streamController.stream, sinkController.sink); + }); + + test("decodes JSON emitted by the channel", () { + var transformed = channel.transform(jsonDocument); + streamController.add('{"foo": "bar"}'); + expect(transformed.stream.first, completion(equals({"foo": "bar"}))); + }); + + test("encodes objects added to the channel", () { + var transformed = channel.transform(jsonDocument); + transformed.sink.add({"foo": "bar"}); + expect(sinkController.stream.first, + completion(equals(JSON.encode({"foo": "bar"})))); + }); + + test("supports the reviver function", () { + var transformed = channel.transform( + new JsonDocumentTransformer(reviver: (key, value) => "decoded")); + streamController.add('{"foo": "bar"}'); + expect(transformed.stream.first, completion(equals("decoded"))); + }); + + test("supports the toEncodable function", () { + var transformed = channel.transform( + new JsonDocumentTransformer(toEncodable: (object) => "encoded")); + transformed.sink.add(new Object()); + expect(sinkController.stream.first, completion(equals('"encoded"'))); + }); +}