acyclic_steps 0.9.0

  • Readme
  • Changelog
  • Example
  • Installing
  • new50

Acyclic Steps for Dart #

Disclaimer: This is not an officially supported Google product.

Package acyclic_steps enables the definition of steps with acyclic dependencies on other steps and the evaluation of such steps. A step is a function (optionally async) which produces a result (or side-effect). A step may depend on other steps, but cyclic dependencies will produce a compile-time error.

When a step is evaluated, the dependencies for the step is evaluated first. To the extend permitted by dependency constraints the steps depended upon will run concurrently. Steps can also be overriden to inject an initial value, or a mock/fake object during testing. The result from a Step is cached in the Runner object that evaluated the Step, this ensures that steps will not be repeated.

package:acyclic_steps was written to facilitate complex projects with many components that depends on other components to be initialized. This is frequently the case for servers, where one step might be to setup a database connection, while other steps depend upon the database connection. This is also a frequent case where it is desirable to be able to override the database connection step during testing, to use a different database or even database driver.

The package is also intended to be useful for evaluation of complex task graphs, where tasks may depend on the result of previous tasks.

Example #

import 'package:acyclic_steps/acyclic_steps.dart';

/// A step that provides a message, this is a _virtual step_ because it
/// doesn't have an implementation instead it throws an error. Hence, to
/// evaluate a step that depends on [messageStep] it is necessary to
/// override this step, by injecting a value to replace it.
final Step<String> messageStep = Step.deps0(
  'message',
  () => throw UnimplementedError('message must be overriden with input'),
);

/// A step that provides date and time
final dateTimeStep = Step.deps0('date-time', () => DateTime.now().toString());

/// A step which has side effects.
final Step<void> printStep = Step.deps2(
    'print',
    // Dependencies:
    messageStep,
    dateTimeStep, (
  msg, // result from evaluation of messageStep
  time, // result from evaluation of dateTimeStep
) async {
  await Future.delayed(Duration(milliseconds: 100));
  print('$msg at $time');
});

Future<void> main() async {
  final r = Runner();
  // Override [messageStep] to provide an input value.
  r.override(messageStep, 'hello world');
  // Evaluate [printStep] which in turn evaluates [dateTimeStep], and re-uses
  // the overridden value for [messageStep].
  await r.run(printStep);

  // When testing it might be desirable to override the [dateTimeStep] to
  // produce the same output independent of time. To do this we must create a
  // new runner:
  final testRunner = Runner();
  testRunner.override(messageStep, 'hello world');
  testRunner.override(dateTimeStep, '2019-11-04 09:47:37.461795');
  // Now we can be use the [dateTimeStep] evaluates to something predictable
  assert(await testRunner.run(dateTimeStep) == '2019-11-04 09:47:37.461795');
  // This wil print a fixed time, useful when testing.
  await testRunner.run(printStep);
}

v0.9.0 #

  • Initial release.

example/main.dart

// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import 'dart:async' show Future;
import 'package:acyclic_steps/acyclic_steps.dart';

/// A step that provides a message, this is a _virtual step_ because it
/// doesn't have an implementation instead it throws an error. Hence, to
/// evaluate a step that depends on [messageStep] it is necessary to
/// override this step, by injecting a value to replace it.
final Step<String> messageStep = Step.deps0(
  'message',
  () => throw UnimplementedError('message must be overriden with input'),
);

/// A step that provides date and time
final dateTimeStep = Step.deps0('date-time', () => DateTime.now().toString());

/// A step which has side effects.
final Step<void> printStep = Step.deps2(
    'print',
    // Dependencies:
    messageStep,
    dateTimeStep, (
  msg, // result from evaluation of messageStep
  time, // result from evaluation of dateTimeStep
) async {
  await Future.delayed(Duration(milliseconds: 100));
  print('$msg at $time');
});

Future<void> main() async {
  final r = Runner();
  // Override [messageStep] to provide an input value.
  r.override(messageStep, 'hello world');
  // Evaluate [printStep] which in turn evaluates [dateTimeStep], and re-uses
  // the overridden value for [messageStep].
  await r.run(printStep);

  // When testing it might be desirable to override the [dateTimeStep] to
  // produce the same output independent of time. To do this we must create a
  // new runner:
  final testRunner = Runner();
  testRunner.override(messageStep, 'hello world');
  testRunner.override(dateTimeStep, '2019-11-04 09:47:37.461795');
  // Now we can be use the [dateTimeStep] evaluates to something predictable
  assert(await testRunner.run(dateTimeStep) == '2019-11-04 09:47:37.461795');
  // This wil print a fixed time, useful when testing.
  await testRunner.run(printStep);
}

Use this package as a library

1. Depend on it

Add this to your package's pubspec.yaml file:


dependencies:
  acyclic_steps: ^0.9.0

2. Install it

You can install packages from the command line:

with pub:


$ pub get

with Flutter:


$ flutter pub get

Alternatively, your editor might support pub get or flutter pub get. Check the docs for your editor to learn more.

3. Import it

Now in your Dart code, you can use:


import 'package:acyclic_steps/acyclic_steps.dart';
  
Popularity:
Describes how popular the package is relative to other packages. [more]
0
Health:
Code health derived from static analysis. [more]
100
Maintenance:
Reflects how tidy and up-to-date the package is. [more]
100
Overall:
Weighted score of the above. [more]
50
Learn more about scoring.

We analyzed this package on Nov 7, 2019, and provided a score, details, and suggestions below. Analysis was completed with status completed using:

  • Dart: 2.6.0
  • pana: 0.12.21

Platforms

Detected platforms: Flutter, web, other

No platform restriction found in primary library package:acyclic_steps/acyclic_steps.dart.

Dependencies

Package Constraint Resolved Available
Direct dependencies
Dart SDK >=2.4.0 <3.0.0
meta ^1.1.7 1.1.8
Dev dependencies
pedantic ^1.4.0
test ^1.5.1