trough 1.0.0
trough: ^1.0.0 copied to clipboard
A middleware pipeline library for Dart, inspired by similar libraries in the JavaScript ecosystem. Create composable function pipelines for processing data in sequence.
Trough #
Trough is a middleware pipeline library for Dart, inspired by similar libraries in the JavaScript ecosystem. It allows you to create composable function pipelines for processing data in sequence.
Features #
- π Middleware Pipeline: Create composable function chains
- β‘ Sync & Async Support: Support for synchronous return values and asynchronous Futures
- π‘οΈ Error Handling: Built-in error propagation and handling mechanisms
- π Method Chaining: Fluent API design
- π Strong Typing: TypeScript-style type definitions
- π§ͺ Full Test Coverage: Comprehensive test suite included
Installation #
Add the dependency to your pubspec.yaml
file:
dependencies:
trough: ^1.0.0
Then run:
dart pub get
Quick Start #
Basic Usage #
import 'package:trough/trough.dart';
void main() {
// Create a new pipeline
final pipeline = trough();
// Add middleware - multiply input by 2
pipeline.use((List<dynamic> input, [Function? next]) {
final value = input.isNotEmpty ? input[0] as int : 0;
next?.call(null, [value * 2]);
return null;
});
// Add another middleware - add 10
pipeline.use((List<dynamic> input, [Function? next]) {
final value = input.isNotEmpty ? input[0] as int : 0;
next?.call(null, [value + 10]);
return null;
});
// Run the pipeline
pipeline.run([5], (Object? error, [List<dynamic>? output]) {
if (error != null) {
print('Error: $error');
} else {
print('Result: ${output?.first}'); // Output: Result: 20 (5 * 2 + 10)
}
});
}
Synchronous Middleware #
void main() {
final pipeline = trough();
// Synchronous middleware that returns a value directly
pipeline.use((List<dynamic> input, [Function? next]) {
final text = input.isNotEmpty ? input[0] as String : '';
return '$text World!';
});
pipeline.run(['Hello'], (Object? error, [List<dynamic>? output]) {
if (error != null) {
print('Error: $error');
} else {
print('Result: ${output?.first}'); // Output: Result: Hello World!
}
});
}
Asynchronous Middleware #
import 'dart:async';
void main() async {
final pipeline = trough();
// Asynchronous middleware
pipeline.use((List<dynamic> input, [Function? next]) {
return Future.delayed(Duration(milliseconds: 100), () {
final value = input.isNotEmpty ? input[0] as int : 0;
return value * 3;
});
});
pipeline.run([7], (Object? error, [List<dynamic>? output]) {
if (error != null) {
print('Error: $error');
} else {
print('Async result: ${output?.first}'); // Output: Async result: 21
}
});
}
Error Handling #
void main() {
final pipeline = trough();
// Middleware that throws an error
pipeline.use((List<dynamic> input, [Function? next]) {
if (input.isEmpty) {
throw Exception('Input cannot be empty');
}
next?.call(null, [input[0]]);
return null;
});
pipeline.run([], (Object? error, [List<dynamic>? output]) {
if (error != null) {
print('Caught error: $error'); // Output: Caught error: Exception: Input cannot be empty
} else {
print('Result: ${output?.first}');
}
});
}
API Documentation #
Type Definitions #
Callback
typedef Callback = void Function(Object? error, [List<dynamic>? output]);
Callback function called when the pipeline completes.
Middleware
typedef Middleware = dynamic Function(List<dynamic> input, [Function? next]);
Middleware function signature. Can accept input and an optional next callback function.
Main API #
trough()
Creates a new pipeline instance.
Returns: Pipeline
- A new pipeline instance
Pipeline.use(Middleware middleware)
Adds middleware to the pipeline.
Parameters:
middleware
: The middleware function to add
Returns: Pipeline
- Returns the pipeline instance for method chaining
Pipeline.run(List<dynamic> values, Callback callback)
Runs all middleware in the pipeline.
Parameters:
values
: Input values passed to the first middlewarecallback
: Callback function called when the pipeline completes
wrap(Middleware middleware, Callback callback)
Helper function to wrap middleware with a uniform interface.
Parameters:
middleware
: The middleware function to wrapcallback
: Callback function called on completion
Returns: Function
- The wrapped function
Middleware Patterns #
1. Using next
Callback #
pipeline.use((List<dynamic> input, [Function? next]) {
// Process input
final processed = processInput(input);
// Call next to continue the pipeline
next?.call(null, [processed]);
return null;
});
2. Direct Return Value (Synchronous) #
pipeline.use((List<dynamic> input, [Function? next]) {
final processed = processInput(input);
return processed; // Return processed value directly
});
3. Return Future (Asynchronous) #
pipeline.use((List<dynamic> input, [Function? next]) {
return processInputAsync(input); // Return Future
});
4. Error Handling #
pipeline.use((List<dynamic> input, [Function? next]) {
try {
final processed = processInput(input);
next?.call(null, [processed]);
} catch (error) {
next?.call(error); // Pass error along
}
return null;
});
Real-World Examples #
Text Processing Pipeline #
final textProcessor = trough()
..use((List<dynamic> input, [Function? next]) {
// Trim whitespace
final text = (input[0] as String).trim();
next?.call(null, [text]);
return null;
})
..use((List<dynamic> input, [Function? next]) {
// Convert to lowercase
final text = (input[0] as String).toLowerCase();
next?.call(null, [text]);
return null;
})
..use((List<dynamic> input, [Function? next]) {
// Replace special characters
final text = (input[0] as String).replaceAll(RegExp(r'[^\w\s]'), '');
next?.call(null, [text]);
return null;
});
textProcessor.run([' Hello, World! '], (error, output) {
print(output?.first); // Output: "hello world"
});
Data Validation Pipeline #
final validator = trough()
..use((List<dynamic> input, [Function? next]) {
// Check if input is empty
if (input.isEmpty || input[0] == null) {
throw Exception('Input cannot be empty');
}
next?.call(null, input);
return null;
})
..use((List<dynamic> input, [Function? next]) {
// Check type
if (input[0] is! String) {
throw Exception('Input must be a string');
}
next?.call(null, input);
return null;
})
..use((List<dynamic> input, [Function? next]) {
// Check length
final text = input[0] as String;
if (text.length < 3) {
throw Exception('Input must be at least 3 characters long');
}
next?.call(null, ['Validation passed: $text']);
return null;
});
Development #
Run Tests #
dart test
Run Example #
dart run example/trough_example.dart
Code Formatting #
dart format .
Static Analysis #
dart analyze
License #
This project is licensed under the MIT License. See the LICENSE file for details.
Contributing #
Contributions are welcome! Please feel free to submit Pull Requests or create Issues.
Related Projects #
- trough (JavaScript) - The original JavaScript version
- unified - Text processing ecosystem that uses trough
Note: This library's design is inspired by similar libraries in the JavaScript ecosystem, particularly wooorm/trough.