copyable 0.0.1+2

  • Readme
  • Changelog
  • Example
  • Installing
  • 78

copyable #

A package for giving copy capabilities to classes, both local (i.e. in your source code), and foreign (i.e. defined in a third-party library).

Table of Contents #

Examples #

The best way to learn is with examples.

Usage #

The copyable package defines two interfaces for giving copy-like functionality to Dart classes: Copyable and Copier.

Once you define a class that implements either of the two interfaces, you can copy instances of the class like this:

Copyable

class Point implements Copyable<Point> {
  final int x;
  final int y;
  final Shape parent;
  
  Point(this.x, this.y, {this.parent});

  // Implement Copyable interface
}

Point origin = Point(0, 0);

// Copy
Point origin_copy = origin.copy();

// Copy, overriding some fields
Point x_intercept = origin.copyWith(x: 5);

Copier

class PointCopier implements Copier<Point> {
  // Implement Copier interface
}

PointCopier pointCopier = PointCopier();
Point origin = Point(0, 0);

// Copy
Point origin_copy = pointCopier.copy(origin);

// Copy, overriding some fields
Point x_intercept = pointCopier.copyWith(x: 5);

Whether to use Copyable or Copier is a matter of preference: the Copyable pattern adds copy functionality directly into a class, where as the Copier pattern adds copy functionality indirectly by creating an entirely new class.

Setup #

Add the following dependency to your pubspec.yaml

dependencies:
  copyable: ^0.0.1

Using the code generation tools requires additional setup.

Copyable #

Interface #

/// An interface for copying objects.
abstract class Copyable<T> {
  /// Copy the current object.
  @required T copy();

  /// Copy the current object, overriding with non-null properties of
  /// `master` when present.
  @required T copyWith(T master);

  /// Copy the current object, overriding with the given properties.
  @required T copyWithProperties(/* Add named properties here; typically
  these are object fields.*/);
}

Implementation #

import 'package:copyable/copyable.dart';

class Point implements Copyable<Point> {
  final int x;
  final int y;
  Point parent;

  Point({
    this.x,
    this.y,
    this.parent
  });

  // Copyable Implementation
  @override
  Point copy() => _copy(this);

  @override
  Point copyWith(Point master) => _copy(master);

  @override
  Point copyWithProperties({
    int x,
    int y,
    Point parent
  }) => _copy(this,
      x: x,
      y: y,
      parent: parent
  );

  static Point _copy(Point master, {
    int x,
    int y,
    Point parent
  }) {
    return Point(
        x : x ?? master?.x,
        y : y ?? master?.y,
        parent : parent ?? master?.parent
    );
  }
}

Code Generation #

If you don't want to manually implement the Copyable interface (I know, it's a lot of boilerplate :/ ), have no fear! Check out the code generation section.

Copier #

Interface #

abstract class Copier<T> {
  /// Necessary in order to support chaining of `copy()` calls.
  /// Basically a way to bootstrap a temporary master (from a previous `copy
  /// ()` call), it holds the result of the previous `.copy()` call so that
  /// the user doesn't have to re-wrap the result in a `Copier` instance.
  @required T master;

  /// The default template for copying. This will be the last
  /// fallback for any undefined properties of the object being copied.
  /// Typically just a bare-bones object (i.e. `T()`).
  @required T get defaultMaster;

  // Convenience Methods

  /// Copies the given object `master` and returns a new instance of `Copier<T>`
  /// whose master is the newly copied object.
  @required Copier<T> copy(T master);

  /// Copies the given object `master` and returns it.
  @required T copyAndResolve(T master);

  /// Copies `this.master` with the given parameters only. Returns a new
  /// instance of `Copier<T>` whose master is the newly copied object.
  /// **Note:** Concrete implementations of `Copier<T>` should add optional
  /// named parameters corresponding to the instance properties of `T`.
  @required Copier<T> copyWith(/* {{ Properties here! }} */);

  /// Copies `this.master` with the given parameters only. Returns the newly
  /// copied object.
  /// **Note:** Concrete implementations of `Copier<T>` should add optional
  /// named parameters corresponding to the instance properties of `T`.
  @required T copyWithAndResolve(/* {{ Properties here! }} */);

  /// Returns the result of the most recent copy (i.e. this.master).
  /// Literally the only code in this function should be `return this.master;`
  @required T resolve();
}

Implementation #

class CircleCopier implements Copier<Circle> {
  CircleCopier([this.master]);

  Circle master;

  Circle get defaultMaster {
    return Circle(radius: 1);
  }

  dynamic _copy(Circle master,
      {bool resolve = false, int radius, int centerX, int centerY}) {
    master = master ?? this.master;
    Circle newCircle = Circle(
        radius: radius ?? master?.radius ?? defaultMaster.radius,
        centerX: centerX ?? master?.centerX ?? defaultMaster.centerX,
        centerY: centerY ?? master?.centerY ?? defaultMaster.centerY);

    return resolve ? newCircle : CircleCopier(newCircle);
  }

  @override
  CircleCopier copy(Circle master) {
    return this._copy(
      master,
      resolve: false,
    ) as CircleCopier;
  }

  @override
  Circle copyAndResolve(Circle master) {
    return this._copy(
      master,
      resolve: true,
    ) as Circle;
  }

  @override
  CircleCopier copyWith({int radius, int centerX, int centerY}) {
    return this._copy(this.master,
        resolve: false,
        radius: radius,
        centerX: centerX,
        centerY: centerY) as CircleCopier;
  }

  @override
  Circle copyWithAndResolve({int radius, int centerX, int centerY}) {
    return this._copy(this.master,
        resolve: true,
        radius: radius,
        centerX: centerX,
        centerY: centerY) as Circle;
  }

  Circle resolve() {
    return this.master;
  }
}

Code Generation #

If you don't want to manually implement the Copier interface (I know, it's a lot of boilerplate :/ ), have no fear! Check out the code generation section.

Code Generation #

Most of the code needed to implement either Copyable or Copier is boilerplate and not fun. Instead, you can generate the respective Copyable or Copier code using build_runner.

Setup #

Add the following dev dependencies to your pubspec.yaml:

dev_dependencies:
  build_runner: ^1.0.0
  build_verify: ^1.1.0

Make sure to run pub get or flutter packages get.

If you don't already a build.yaml file, create one in your project root directory (wherever pubspec.yaml is). Then, add the following to your build.yaml:

targets:
  $default:
    builders:
      # TODO: Add builders
Builders #

There are four different builders you can use to generate copy-code. Each builder corresponds to one of the four use cases described earlier in [link here]:

-CopyableCopier
Localcopyablecopier
ForeignforeignCopyableLibforeignCopierLib

Once you know which builder(s) you want to use, add them your build.yaml with the pattern copyable|{BUILDER_NAME}:

targets:
  $default:
    builders:
      copyable|copyable:
        generate_for:
          - lib/point.dart
      copyable|foreignCopyableLib:
        generate_for:
          - lib/circle.dart
      # More builders if needed...

Usage #

Examples

  1. Import package:copyable/generator.dart.

copyable #

Example

  1. Annotate the class you want to generate copy code for with @generate_copyable.
  2. Add the following to the top of your file:
part '$FILE_NAME.g.dart';
  1. Run the builder (see below). This will generate a mixin with the necessary copy-code as a part of file
  2. Add the generated mixin to your original class.

copier #

Example

  1. Annotate the class you want to generate copy code for with @GenerateCopier(defaultObjectCode: $DEFAULT).

    In place of $DEFAULT, pass a string of the code to use to instantiate a "default" instance of the class; for example,

    @GenerateCopier(defaultObjectCode: 'Circle(radius: 1')   
    
  2. Add the following to the top of your file:

part '$FILE_NAME.g.dart';
  1. Run the builder (see below). This will generate a separate class with the necessary copy-code as a part of file.

foreignCopyableLib #

Example

  1. Create a CopyableMeta instance representing the class you want to generate a copyable version of for.
  2. Create a CopyMetaGenerator instance and pass in the meta object.
  3. Run the builder (see below). This will generate a separate library generated classes.

foreignCopyableLib #

Example

  1. Create a CopierMeta instance representing the class you want to generate a copyable version of for.
  2. Create a CopyMetaGenerator instance and pass in the meta object.
  3. Run the builder (see below). This will generate a separate library generated classes.

Running #

In the directory where your build.yaml is, run the following:

Flutter project

flutter packages pub run build_runner build

Non-flutter project

pub run build_runner build

[0.0.1+2] - June 20, 2019

Update dependencies and documentation.

[0.0.1] - June 20, 2019

Initial Release

example/README.md

Examples #

This directory contains examples for how to use the code generation features of the copyable package to avoid writing unnecessary code.

See usage instructions.

Copyable example: lib/point.dart

copyable example: lib/circle.dart

copier example: lib/rectangle.dart

foreignCopyableLib example: lib/metas.dart

foreignCopierLib example: lib/metas.dart

Use this package as a library

1. Depend on it

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


dependencies:
  copyable: ^0.0.1+2

2. Install it

You can install packages from the command line:

with Flutter:


$ flutter pub get

Alternatively, your editor might support 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:copyable/copyable.dart';
  
Popularity:
Describes how popular the package is relative to other packages. [more]
64
Health:
Code health derived from static analysis. [more]
100
Maintenance:
Reflects how tidy and up-to-date the package is. [more]
80
Overall:
Weighted score of the above. [more]
78
Learn more about scoring.

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

  • Dart: 2.6.1
  • pana: 0.12.21
  • Flutter: 1.9.1+hotfix.6

Platforms

Detected platforms: Flutter

References Flutter, and has no conflicting libraries.

Maintenance issues and suggestions

Support latest dependencies. (-10 points)

The version constraint in pubspec.yaml does not support the latest published versions for 1 dependency (analyzer).

Package is pre-v0.1 release. (-10 points)

While nothing is inherently wrong with versions of 0.0.*, it might mean that the author is still experimenting with the general direction of the API.

Dependencies

Package Constraint Resolved Available
Direct dependencies
Dart SDK >=2.1.0 <3.0.0
analyzer ^0.36.3 0.36.4 0.39.1
build ^1.1.4 1.2.2
code_builder ^3.2.0 3.2.1
flutter 0.0.0
meta ^1.1.6 1.1.7 1.1.8
source_gen ^0.9.4 0.9.4+4 0.9.4+6
Transitive dependencies
args 1.5.2
async 2.4.0
built_collection 4.2.2 4.3.0
built_value 7.0.0
charcode 1.1.2
collection 1.14.11 1.14.12
convert 2.1.1
crypto 2.1.3
csslib 0.16.1
dart_style 1.2.9 1.3.3
fixnum 0.10.11
front_end 0.1.19 0.1.29
glob 1.2.0
html 0.14.0+3
js 0.6.1+1
kernel 0.3.19 0.3.29
logging 0.11.3+2
matcher 0.12.6
node_interop 1.0.3
node_io 1.0.1+2
package_config 1.1.0
path 1.6.4
pedantic 1.8.0+1
pub_semver 1.4.2
quiver 2.1.2+1
sky_engine 0.0.99
source_span 1.5.5
stack_trace 1.9.3
string_scanner 1.0.5
term_glyph 1.1.0
typed_data 1.1.6
vector_math 2.0.8
watcher 0.9.7+13
yaml 2.2.0
Dev dependencies
flutter_test