platform/packages/pipeline/README.md

381 lines
8.5 KiB
Markdown
Raw Normal View History

<p align="center"><a href="https://protevus.com" target="_blank"><img src="https://git.protevus.com/protevus/branding/raw/branch/main/protevus-logo-bg.png"></a></p>
# Platform Pipeline
A Laravel-compatible pipeline implementation in Dart, providing a robust way to pass objects through a series of operations.
[![Pub Version](https://img.shields.io/pub/v/platform_pipeline)]()
[![Build Status](https://img.shields.io/github/workflow/status/platform/pipeline/tests)]()
## Table of Contents
- [Overview](#overview)
- [Features](#features)
- [Requirements](#requirements)
- [Installation](#installation)
- [Usage](#usage)
- [Basic Usage](#basic-usage)
- [Class-Based Pipes](#class-based-pipes)
- [Invokable Classes](#invokable-classes)
- [Using Different Method Names](#using-different-method-names)
- [Passing Parameters to Pipes](#passing-parameters-to-pipes)
- [Early Pipeline Termination](#early-pipeline-termination)
- [Conditional Pipeline Execution](#conditional-pipeline-execution)
- [Advanced Usage](#advanced-usage)
- [Working with Objects](#working-with-objects)
- [Async Operations](#async-operations)
- [Laravel API Compatibility](#laravel-api-compatibility)
- [Comparison with Laravel](#comparison-with-laravel)
- [Troubleshooting](#troubleshooting)
- [Testing](#testing)
- [Contributing](#contributing)
- [License](#license)
## Overview
Platform Pipeline is a 100% API-compatible port of Laravel's Pipeline to Dart. It allows you to pass an object through a series of operations (pipes) in a fluent, maintainable way. Each pipe can examine, modify, or replace the object before passing it to the next pipe in the sequence.
## Features
- 💯 100% Laravel Pipeline API compatibility
- 🔄 Support for class-based and callable pipes
- 🎯 Dependency injection through container integration
- ⚡ Async operation support
- 🔀 Conditional pipeline execution
- 🎭 Method name customization via `via()`
- 🎁 Parameter passing to pipes
- 🛑 Early pipeline termination
- 🧪 Comprehensive test coverage
## Requirements
- Dart SDK: >=2.17.0 <4.0.0
- platform_container: ^1.0.0
## Installation
Add this to your package's `pubspec.yaml` file:
```yaml
dependencies:
platform_pipeline: ^1.0.0
```
## Usage
### Basic Usage
```dart
import 'package:platform_pipeline/pipeline.dart';
import 'package:platform_container/container.dart';
void main() async {
// Create a container instance
var container = Container();
// Create a pipeline
var result = await Pipeline(container)
.send('Hello')
.through([
(String value, next) => next(value + ' World'),
(String value, next) => next(value + '!'),
])
.then((value) => value);
print(result); // Outputs: Hello World!
}
```
### Class-Based Pipes
```dart
class UppercasePipe {
Future<String> handle(String value, Function next) async {
return next(value.toUpperCase());
}
}
class AddExclamationPipe {
Future<String> handle(String value, Function next) async {
return next(value + '!');
}
}
void main() async {
var container = Container();
var result = await Pipeline(container)
.send('hello')
.through([
UppercasePipe(),
AddExclamationPipe(),
])
.then((value) => value);
print(result); // Outputs: HELLO!
}
```
### Invokable Classes
```dart
class TransformPipe {
Future<String> call(String value, Function next) async {
return next(value.toUpperCase());
}
}
void main() async {
var container = Container();
var result = await Pipeline(container)
.send('hello')
.through([TransformPipe()])
.then((value) => value);
print(result); // Outputs: HELLO
}
```
### Using Different Method Names
```dart
class CustomPipe {
Future<String> transform(String value, Function next) async {
return next(value.toUpperCase());
}
}
void main() async {
var container = Container();
var result = await Pipeline(container)
.send('hello')
.through([CustomPipe()])
.via('transform')
.then((value) => value);
print(result); // Outputs: HELLO
}
```
### Passing Parameters to Pipes
```dart
class PrefixPipe {
Future<String> handle(
String value,
Function next, [
String prefix = '',
]) async {
return next('$prefix$value');
}
}
void main() async {
var container = Container();
container.registerFactory<PrefixPipe>((c) => PrefixPipe());
var pipeline = Pipeline(container);
pipeline.registerPipeType('PrefixPipe', PrefixPipe);
var result = await pipeline
.send('World')
.through('PrefixPipe:Hello ')
.then((value) => value);
print(result); // Outputs: Hello World
}
```
### Early Pipeline Termination
```dart
void main() async {
var container = Container();
var result = await Pipeline(container)
.send('hello')
.through([
(value, next) => 'TERMINATED', // Pipeline stops here
(value, next) => next('NEVER REACHED'),
])
.then((value) => value);
print(result); // Outputs: TERMINATED
}
```
### Conditional Pipeline Execution
```dart
void main() async {
var container = Container();
var shouldTransform = true;
var result = await Pipeline(container)
.send('hello')
.when(() => shouldTransform, (Pipeline pipeline) {
pipeline.pipe([
(value, next) => next(value.toUpperCase()),
]);
})
.then((value) => value);
print(result); // Outputs: HELLO
}
```
## Advanced Usage
### Working with Objects
```dart
class User {
String name;
int age;
User(this.name, this.age);
}
class AgeValidationPipe {
Future<User> handle(User user, Function next) async {
if (user.age < 18) {
throw Exception('User must be 18 or older');
}
return next(user);
}
}
class NameFormattingPipe {
Future<User> handle(User user, Function next) async {
user.name = user.name.trim().toLowerCase();
return next(user);
}
}
void main() async {
var container = Container();
var user = User('John Doe ', 20);
try {
user = await Pipeline(container)
.send(user)
.through([
AgeValidationPipe(),
NameFormattingPipe(),
])
.then((value) => value);
print('${user.name} is ${user.age} years old');
// Outputs: john doe is 20 years old
} catch (e) {
print('Validation failed: $e');
}
}
```
### Async Operations
```dart
class AsyncTransformPipe {
Future<String> handle(String value, Function next) async {
// Simulate async operation
await Future.delayed(Duration(seconds: 1));
return next(value.toUpperCase());
}
}
void main() async {
var container = Container();
var result = await Pipeline(container)
.send('hello')
.through([AsyncTransformPipe()])
.then((value) => value);
print(result); // Outputs after 1 second: HELLO
}
```
## Laravel API Compatibility
This package maintains 100% API compatibility with Laravel's Pipeline implementation. All Laravel Pipeline features are supported:
- `send()` - Set the object being passed through the pipeline
- `through()` - Set the array of pipes
- `pipe()` - Push additional pipes onto the pipeline
- `via()` - Set the method to call on the pipes
- `then()` - Run the pipeline with a final destination callback
- `thenReturn()` - Run the pipeline and return the result
## Comparison with Laravel
| Feature | Laravel | Platform Pipeline |
|---------|---------|------------------|
| API Methods | ✓ | ✓ |
| Container Integration | ✓ | ✓ |
| Pipe Types | Class, Callable | Class, Callable |
| Async Support | ✗ | ✓ |
| Type Safety | ✗ | ✓ |
| Parameter Passing | ✓ | ✓ |
| Early Termination | ✓ | ✓ |
| Method Customization | ✓ | ✓ |
| Conditional Execution | ✓ | ✓ |
## Troubleshooting
### Common Issues
1. Container Not Provided
```dart
// ❌ Wrong
var pipeline = Pipeline(null);
// ✓ Correct
var container = Container();
var pipeline = Pipeline(container);
```
2. Missing Type Registration
```dart
// ❌ Wrong
pipeline.through('CustomPipe:param');
// ✓ Correct
pipeline.registerPipeType('CustomPipe', CustomPipe);
pipeline.through('CustomPipe:param');
```
3. Incorrect Method Name
```dart
// ❌ Wrong
class CustomPipe {
void process(value, next) {} // Wrong method name
}
// ✓ Correct
class CustomPipe {
void handle(value, next) {} // Default method name
}
// Or specify the method name:
pipeline.via('process').through([CustomPipe()]);
```
## Testing
Run the tests with:
```bash
dart test
```
## Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
## License
This package is open-sourced software licensed under the MIT license.