Giter Site home page Giter Site logo

copyable's Introduction

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]:

- Copyable Copier
Local copyable copier
Foreign foreignCopyableLib foreignCopierLib

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

copyable's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

copyable's Issues

Error on every single .dart file in my project

Shouldn't the code generation just generate code for classes where the annotations exist? I'm getting errors for every single dart file in my project.

If someone else experiences this, they can be cleaned up (on mac, or on windows using the windows subsystem for linux) with these commands:

I was able to work around this by adding a build.yaml file, but now the copy code is not generated for the 1 class I'm trying to generate it for.

find . -type f -name '*.copiers.dart' -delete    
find . -type f -name '*.copyables.dart' -delete    
find . -type f -name '*.copyables.copiers.dart' -delete    

The error I happen to be getting is:
NoSuchMethodError: The method 'computeConstantValue' was called on null.

Errors for every single .dart file in my project.

Shouldn't the code generation just generate code for classes where the annotations exist? I'm getting errors for every single dart file in my project.

If someone else experiences this, they can be cleaned up (on mac, or on windows using the windows subsystem for linux) with these commands:

I was able to work around this by adding a build.yaml file, but now the copy code is not generated for the 1 class I'm trying to generate it for.

find . -type f -name '*.copiers.dart' -delete    
find . -type f -name '*.copyables.dart' -delete    
find . -type f -name '*.copyables.copiers.dart' -delete    

The error I happen to be getting is:
NoSuchMethodError: The method 'computeConstantValue' was called on null.

Getting `computeConstantValue` was called on `null` and `pub finished with exit code 1`

I'm not able to use this with latest stable flutter at least. This leads to be generating two new dart files fro every dart file in project with naming filename.copiers.dart and filename.copyables.dart and each of them contains this:

➜  copyable_test flutter --version
Flutter 1.12.13+hotfix.5 • channel stable • https://github.com/flutter/flutter.git
Framework • revision 27321ebbad (3 weeks ago) • 2019-12-10 18:15:01 -0800
Engine • revision 2994f7e1e6
Tools • Dart 2.7.0

Trying to run the generators..

➜  copyable_test flutter clean && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs
Deleting .dart_tool...                                              16ms
Running "flutter pub get" in copyable_test...                       0.7s
[INFO] Generating build script...
[INFO] Generating build script completed, took 402ms

[INFO] Creating build script snapshot......
[INFO] Creating build script snapshot... completed, took 12.5s

[INFO] Initializing inputs
[INFO] Building new asset graph...
[INFO] Building new asset graph completed, took 716ms

[INFO] Checking for unexpected pre-existing outputs....
[INFO] Deleting 6 declared outputs which already existed on disk.
[INFO] Checking for unexpected pre-existing outputs. completed, took 5ms

[INFO] Running build...
[INFO] build_resolvers:Generating SDK summary...
[INFO] 5.0s elapsed, 0/1 actions completed.
[INFO] build_resolvers:Generating SDK summary completed, took 4.9s

[SEVERE] copyable:copyable on lib/src/model/item.dart:

NoSuchMethodError: The getter 'length' was called on null.
Receiver: null
Tried calling: length
[INFO] 6.1s elapsed, 2/4 actions completed.
[INFO] 10.2s elapsed, 3/4 actions completed.
[INFO] 11.4s elapsed, 4/4 actions completed.
[SEVERE] copyable:foreignCopyableLib on test/widget_test.dart:
Error running ForeignCopyableLibraryGenerator
NoSuchMethodError: The method 'computeConstantValue' was called on null.
Receiver: null
Tried calling: computeConstantValue()
[SEVERE] copyable:foreignCopyableLib on lib/main.dart:
Error running ForeignCopyableLibraryGenerator
NoSuchMethodError: The method 'computeConstantValue' was called on null.
Receiver: null
Tried calling: computeConstantValue()
[SEVERE] copyable:foreignCopyableLib on lib/src/model/item.dart:
Error running ForeignCopyableLibraryGenerator
NoSuchMethodError: The method 'computeConstantValue' was called on null.
Receiver: null
Tried calling: computeConstantValue()
[SEVERE] copyable:foreignCopierLib on test/widget_test.dart:
Error running ForeignCopierLibraryGenerator
NoSuchMethodError: The method 'computeConstantValue' was called on null.
Receiver: null
Tried calling: computeConstantValue()
[SEVERE] copyable:foreignCopierLib on lib/main.dart:
Error running ForeignCopierLibraryGenerator
NoSuchMethodError: The method 'computeConstantValue' was called on null.
Receiver: null
Tried calling: computeConstantValue()
[SEVERE] copyable:foreignCopierLib on lib/src/model/item.dart:
Error running ForeignCopierLibraryGenerator
NoSuchMethodError: The method 'computeConstantValue' was called on null.
Receiver: null
Tried calling: computeConstantValue()
[INFO] Running build completed, took 11.7s

[INFO] Caching finalized dependency graph...
[INFO] Caching finalized dependency graph completed, took 40ms

[SEVERE] Failed after 11.8s
pub finished with exit code 1

And here's what each to the copier/copyable files contain:

// GENERATED CODE - DO NOT MODIFY BY HAND

// **************************************************************************
// ForeignCopyableLibraryGenerator
// **************************************************************************

// Error: NoSuchMethodError: The method 'computeConstantValue' was called on null.
//        Receiver: null
//        Tried calling: computeConstantValue()

Simple sample repro repo:
https://github.com/timolehto/copyable_test

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.