Git Product home page Git Product logo

built_value.dart's Introduction

Build

Built Values for Dart - Introduction

Built Value provides:

  • Immutable value types;
  • EnumClass, classes that behave like enums;
  • JSON serialization.

Immutable collections are from built_collection.

See the API docs.

Articles

Tutorials

Tools

Examples

For an end to end example see the chat example, which was demoed at the Dart Summit 2016. The data model, used both client and server side, uses value types, enums and serialization from built_value.

Simple examples are here.

Since v5.2.0 codegen is triggered by running pub run build_runner build to do a one-off build or pub run build_runner watch to continuously watch your source and update the generated output when it changes. Note that you need a dev dependency on built_value_generator and build_runner. See the example pubspec.yaml.

If using Flutter, the equivalent command is flutter packages pub run build_runner build. Alternatively, put your built_value classes in a separate Dart package with no dependency on Flutter. You can then use built_value as normal.

If using a version before v5.2.0, codegen is triggered via either a build.dart to do a one-off build or a watch.dart to continuously watch your source and update generated output.

Value Types

Value types are, for our purposes, classes that are considered interchangeable if their fields have the same values.

Common examples include Date, Money and Url. Most code introduces its own value types. For example, every web app probably has some version of Account and User.

Value types are very commonly sent by RPC and/or stored for later retrieval.

The problems that led to the creation of the Built Value library have been discussed at great length in the context of AutoValue for Java.

In short: creating and maintaining value types by hand requires a lot of boilerplate. It's boring to write, and if you make a mistake, you very likely create a bug that's hard to track down.

Any solution for value types needs to allow them to participate in object oriented design. Date, for example, is the right place for code that does simple date manipulation.

AutoValue solves the problem for Java with code generation, and Built Values does the same for Dart. The boilerplate is generated for you, leaving you to specify which fields you need and to add code for the behaviour of the class.

Generating boilerplate for Value Types

Value types require a bit of boilerplate in order to connect it to generated code. Luckily, even this bit of boilerplate can be automated using code snippets support in your favourite text editor. For example, in IntelliJ you can use the following live template:

abstract class $CLASS_NAME$ implements Built<$CLASS_NAME$, $CLASS_NAME$Builder> {
  $CLASS_NAME$._();
  factory $CLASS_NAME$([void Function($CLASS_NAME$Builder) updates]) = _$$$CLASS_NAME$;
}

Using this template, you would only have to manually enter a name for your data class, which is something that can't be automated.

Enum Class

Enum Classes provide classes with enum features.

Enums are very helpful in modelling the real world: whenever there are a small fixed set of options, an enum is a natural choice. For an object oriented design, though, enums need to be classes. Dart falls short here, so Enum Classes provide what's missing!

Design:

  • Constants have name and toString, can be used in switch statements, and are real classes that can hold code and implement interfaces
  • Generated values method that returns all the enum values in a BuiltSet (immutable set)
  • Generated valueOf method that takes a String

Serialization

Built Values comes with JSON serialization support which allows you to serialize a complete data model of Built Values, Enum Classes and Built Collections. The chat example shows how easy this makes building a full application with Dart on the server and client.

Here are the major features of the serialization support:

It fully supports object oriented design: any object model that you can design can be serialized, including full use of generics and interfaces. Some other libraries require concrete types or do not fully support generics.

It allows different object oriented models over the same data. For example, in a client server application, it's likely that the client and server want different functionality from their data model. So, they are allowed to have different classes that map to the same data. Most other libraries enforce a 1:1 mapping between classes and types on the wire.

It requires well behaved types. They must be immutable, can use interface but not concrete inheritance, must have predictable nullability, hashCode, equals and toString. In fact, they must be Enum Classes, Built Collections or Built Values. Some other libraries allow badly behaved types to be serialized.

It supports changes to the data model. Optional fields can be added or removed, and fields can be switched from optional to required, allowing your data model to evolve without breaking compatbility. Some other libraries break compatibility on any change to any serializable class.

It's modular. Each endpoint can choose which classes to know about; for example, you can have multiple clients that each know about only a subset of the classes the server knows. Most other libraries are monolithic, requiring all endpoints to know all types.

It has first class support for validation via Built Values. An important part of a powerful data model is ensuring it's valid, so classes can make guarantees about what they can do. Other libraries also support validation but usually in a less prominent way.

It's pluggable. You can add serializers for your own types, and you can add plugins which run before and after all serializers. This could be used to interoperate with other tools or to add hand coded high performance serializers for specific classes. Some other libraries are not so extensible.

It was designed to be multi language, mapping to equivalent object models in Java and other languages. Currently only Dart is supported. The need for other languages didn't materialize as servers are typically either written in Dart or owned by third parties. Please open an issue if you'd like to explore support in more languages.

Common Usage

While full, compiled examples are available in example/lib, a common usage example is shown here. This example assumes that you are writing a client for a JSON API representing a person that looks like the following:

{
  "id": 12345,
  "age": 35,
  "first_name": "Jimmy",
  "hobbies": ["jumping", "basketball"]
}

The corresponding dart class employing built_value might look like this. Note that it is using the @nullable annotation to indicate that the field does not have to be present on the response, as well as the @BuiltValueField annotation to map between the property name on the response and the name of the member variable in the Person class.

import 'package:built_value/built_value.dart';
import 'package:built_value/serializer.dart';
import 'package:built_collection/built_collection.dart';

part 'person.g.dart';

abstract class Person implements Built<Person, PersonBuilder> {
  static Serializer<Person> get serializer => _$personSerializer;

  // Can never be null.
  int get id;

  @nullable
  int get age;

  @nullable
  @BuiltValueField(wireName: 'first_name')
  String get firstName;

  @nullable
  BuiltList<String> get hobbies;

  Person._();
  factory Person([void Function(PersonBuilder) updates]) = _$Person;
}

FAQ

How do I check a field is valid on instantiation?

The value class private constructor runs when all fields are initialized and can do arbitrary checks:

abstract class MyValue {
  MyValue._() {
    if (field < 0) {
      throw ArgumentError(field, 'field', 'Must not be negative.');
    }
  }

How do I process a field on instantiation?

Add a hook that runs immediately before a builder is built. For example, you could sort a list, so it's always sorted directly before the value is created:

abstract class MyValue {
  @BuiltValueHook(finalizeBuilder: true)
  static void _sortItems(MyValueBuilder b) =>
      b..items.sort();

How do I set a default for a field?

Add a hook that runs whenever a builder is created:

abstract class MyValue {
  @BuiltValueHook(initializeBuilder: true)
  static void _setDefaults(MyValueBuilder b) =>
      b
        ..name = 'defaultName'
        ..count = 0;

Should I check in and/or publish in the generated .g.dart files?

See the build_runner docs. You usually should not check in generated files, but you do need to publish them.

Features and bugs

Please file feature requests and bugs at the issue tracker.

built_value.dart's People

Contributors

abhijeethp avatar ahmednfwela avatar atonem avatar cbracken avatar daohoangson avatar davidmorgan avatar dependabot[bot] avatar devoncarew avatar dustyholmes-wf avatar fromlabs avatar greglittlefield-wf avatar har79 avatar heliostrike avatar iinozemtsev avatar jadengis avatar jakemac53 avatar kevmoo avatar knaeckekami avatar leptopoda avatar lexaknyazev avatar michaelrfairhurst avatar micimize avatar natebosch avatar praveenpenumaka avatar provokateurin avatar ralkan avatar scheglov avatar srawlins avatar srsudar avatar stereotype441 avatar

Stargazers

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

Watchers

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

built_value.dart's Issues

Throw `ArgumentError.notNull` instead

Code generated now...

  _$GitHubRepoData._({this.org, this.repo}) : super._() {
    if (org == null) throw new ArgumentError('null org');
    if (repo == null) throw new ArgumentError('null repo');
  }

use new ArgumentError.notNull – is nice

Add live templates for BuiltValue classes

I'm lazy. Here is a live template for adding new bare BuiltValue classes to our favorite IDE :)

It triggers from builtvalue to:

abstract class $NAME$ implements Built<$NAME$, $NAME$Builder> {
  $NAME$._();
  factory $NAME$([updates($NAME$Builder b)]) = _$$$NAME$;
}

I've attached the template. You can copy it to ~IntelliJ IDEA/config/templates. I might update it with additional templates for the part file and imports.

BuiltValue.xml.zip

cope with instance loops during build process

Consider a case like

class Foo implements Built<....> {
  Foo get anotherFoo;
}

class FooBuilder implements Builder<....> {
  FooBuilder anotherFoo;
}

doBuild() {
  final fooBuilder = new FooBuilder();

  fooBuilder.anotherFoo = fooBuilder;
}

The intention is to build an instance of Foo whose anotherFoo property points to itself. Obviously this is a trivial example and loops can be much more complex than that.

It would be awesome if the process of building could cope with this, although it would necessitate something like

class _$Foo ... {
  Foo _anotherFoo;
  Foo get anotherFoo => _anotherFoo;
}

Could introduce an annotation to mark fields (or a whole class) that should support this sort of initialisation

Version constraints: ">=0.3.0 <0.4.0"

In the pre-v1 world, 0.4.0 can introduce breaking changes from 0.3.x

If v1 isn't out yet, don't use >=0.3.0 <1.0.0 – you're likely to be broken!

Add an analyzer plugin

The generator can suggest fixes; it should be possible to have them applied to source for you.

For testing: toCode(): like toString() but valid Dart code

Assuming a BuiltValue only references other BuiltValues or BuiltCollections (or objects that implement toCode?), it would be really neat if there was a way to output mostly valid Dart code to recreate the BuiltValue.

Why? For testing. I write lots of expect(builtValue, CONSTANT) tests that work like golden tests. It would be very useful to have copy&paste-able output to update tests quickly. (Especially for complex objects.)

This is different than built_json etc. as this code lives in tests and doesn't get serialized/deserialized. Similar to repr in Python.

Thanks,
Andreas

Pick up properties from implemented interfaces

Unless I'm missing something you can't currently have a hierarchical model, where you inherit properties from parent models and similarly builders inherit from other builders.

Is this planned?

Let me define a constructor

Sometimes you just want a constructor to take in args. Building for two String properties is crazy heavy.

Clarify subclassing of BuiltValue

It's non-trivial (at least for me) to understand how BuiltValue plays with subclasses and polymorphism. Let me give an example:

I have an immutable WorldState. This WorldState contains a stack of Situations, so that the current most specific situation (like "answering question XYZ") is on top, and the most generic (like "life") is on bottom. It's a pushdown automaton.

Anywho, I need a BuiltList<Situation> for that stack. And the Situations in that list are all subclasses of Situation (because they are quite diverse).

So how do I do this?

Option A is to have Situation a full BuiltValue implementation. That means:

part 'situation.g.dart';

abstract class Situation implements Built<Situation, SituationBuilder> {
  // ...
  Situation._();
  factory Situation([updates(SituationBuilder b)]) = _$Situation;
}

But that gives me error about "Inconsistent declarations of 'rebuild'" when implementing the subclasses. And I have a feeling that would be just the beginning of my problems.

Note: I don't actually need Situation to be an implementation class. But I'm including it here because I assume for many applications of BuiltValue that will be the case.

Option B, which makes more sense to me, is to leave Situation abstract and not to unleash the BuiltValueGenerator on it.

abstract class Situation implements Built<Situation, SituationBuilder> {
  // ...
}

But I'm not sure how to do it since Built<Situation, SituationBuilder> requires some kind of SituationBuilder. Do I just add a class for that? Like this?

abstract class SituationBuilder
    implements Builder<Situation, SituationBuilder> {
  @virtual
  int id = _assignUniqueId();
  // .. other common fields ..
}

When I do that, the subclass (say, RandomEncounter) will need to both extend SituationBuilder and implement Builder<RandomEncounter, RandomEncounterBuilder>:

abstract class RandomEncounterBuilder extends SituationBuilder
    implements Builder<RandomEncounter, RandomEncounterBuilder> {
  // ...

  factory RandomEncounterBuilder() = _$RandomEncounterBuilder;
  RandomEncounterBuilder._();
}

Is that correct? Is built_value smart enough to pull the _assignUniqueId() initialization from SituationBuilder to RandomEncounterBuilder?

Is there Option C? Am I forcing BuiltValue to do something it wasn't meant to solve?

Adding a return type (`void`) to the factory constructor parameter breaks code generation

I changed

factory Component([updates(ComponentBuilder b)]) = _$Component;

to

factory Component([void updates(ComponentBuilder b)]) = _$Component;

to satisfy linter rule always_declare_return_types but that breaks code generation

// **************************************************************************
// Generator: BuiltValueGenerator
// Target: abstract class Component
// **************************************************************************

// Error: Please make the following changes to use BuiltValue:
//
//        1. Make class have factory: factory Component([updates(ComponentBuilder b)]) = _$Component;

Class 'DartObjectImpl' has no instance getter 'value' => in 1.14.0-dev.5.0

I copied the example including the build file. When I run the build.dart it creates a value.g.dart but it is empty except for the error

Dart VM version: 1.14.0-dev.5.0 (Fri Dec 18 02:14:17 2015) on "macos_x64"

The stack trace was

null
  LibraryGenerationResultKind.noop
lib/src/value.g.dart
  LibraryGenerationResultKind.created
    Class 'DartObjectImpl' has no instance getter 'value'.

    NoSuchMethodError: method not found: 'value'
    Receiver: Instance of 'DartObjectImpl'
    Arguments: []
    #0      Object._noSuchMethod (dart:core-patch/object_patch.dart:42)
    #1      Object.noSuchMethod (dart:core-patch/object_patch.dart:45)
    #2      BuiltValueGenerator.generateCode.<anonymous closure>.<anonymous closure> (package:built_value_generator/built_value_generator.dart:210:55)
    #3      Object&ListMixin.any (dart:collection/list.dart:115)
    #4      BuiltValueGenerator.generateCode.<anonymous closure> (package:built_value_generator/built_value_generator.dart:209:74)
    #5      WhereIterator.moveNext (dart:_internal/iterable.dart:436)
    #6      MappedIterator.moveNext (dart:_internal/iterable.dart:389)
    #7      Iterable.contains (dart:core/iterable.dart:206)
    #8      BuiltValueGenerator.generateCode (package:built_value_generator/built_value_generator.dart:233:31)
    #9      BuiltValueGenerator.generate.<generate_async_body> (package:built_value_generator/built_value_generator.dart:53:12)
    #10     Future.Future.microtask.<anonymous closure> (dart:async/future.dart:144)
    #11     _microtaskLoop (dart:async/schedule_microtask.dart:41)
    #12     _startMicrotaskLoop (dart:async/schedule_microtask.dart:50)
    #13     _runPendingImmediateCallback (dart:isolate-patch/isolate_patch.dart:96)
    #14     _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:149)

Allow custom toString, hashCode, equals

It looks like toString() is overridden by default. I'd like to have the option to provide my own toString(), and have built_value provide it only when it's not explicitly defined on the abstract class.

The strings generated by built_value contain a lot of newlines and get messy, especially when logging (the number one reason to call toString for me).

This provides an easy way to write deeply immutable classes for Dart that behave in a consistent, predicatable way.

Author of this software really believes that deeply immutable classes should means value types?

In computer science, the term value type is commonly used to refer to one of two kinds of data types: Types of values or Types of objects with deep copy semantics.

In object-oriented programming, object copying is creating a copy of an existing object, a unit of data in object-oriented programming. The resulting object is called an object copy or simply copy of the original object.

Immutability does not means a data of value types.

No problem but why this nonsences on the Google Github?
Is Google's programmers no longer have another job?

What usefull (as value types) means this monstruous code?

  String toString() {
    return 'Value {'
        'anInt=${anInt.toString()}\n'
        'aString=${aString.toString()}\n'
        'anObject=${anObject.toString()}\n'
        'aDefaultInt=${aDefaultInt.toString()}\n'
        'listOfInt=${listOfInt.toString()}\n'
        '}';
  }

A value (data of value types) is a sequence of bits. How it can contains String or even Object?

Sorry, but I cannot undestand the conclusions of the author.
It should read wikipedia before make such conclusions.

Value type

I am sorry again but my brain can not perceive such conclusions:

Built Values for Dart

Built values are value types for Dart.

Support "pretty" JSON output

A few people have asked about supporting "standard" JSON, meaning some kind of key->value maps with fieldnames.

It's not yet clearly exactly what would make sense here, because that isn't sufficient to serialize data models that are using interfaces rather than concrete types. Please expand on requirements here if this is what you'd like.

Non-nullable fields are null in rebuild() closure

[I may be doing something wrong, and it's too late for me to try and build a reduced test case, so take this with a grain of salt.]

I have a class like this:

abstract class Situation implements Built<Situation, SituationBuilder> {
  int get id;
  SituationState get state;
  // ...
}

And an associated builder like this:

abstract class SituationBuilder
    implements Builder<Situation, SituationBuilder> {
  @virtual
  int id = _random.nextInt(_largeInteger);
  @virtual
  SituationState state;

  SituationBuilder._();
  factory SituationBuilder() = _$SituationBuilder;
}

Note that state is not nullable. But when I run rebuild on the situation, the provided SituationBuilder instance's state field is set to null.

I'm on Dart 1.21.0. Problem happens both on Dart VM and in dart2js. The previous version didn't seem to have this issue (although I did recently refactor so I can't be sure).

Here's some debugging flow when rebuild is called on a Situation instance.


screen shot 2016-12-08 at 00 28 38


screen shot 2016-12-08 at 00 29 15


screen shot 2016-12-08 at 00 29 57


screen shot 2016-12-08 at 00 30 10


The last one is the state of the SituationBuilder when it's being passed to the provided updates() closure.

Null checks in generated builder setters not compatible w/strong mode analyzer

Strong mode doesn't allow overriding a field with a setter.

So either they must be declared as setter/getter pairs in the abstract class, or we omit the null check from the generated builder setters. (The build method can still do the null check).

Given the plan to use non nullable by default, it seems fine to move the null check to just the build method.

Support built collections of builders in builders

e.g.

class FooBuilder ... {
  SetBuilder<BarBuilder> bar;
}

I have a complex build process that makes it very handy to have builders in my collections. Calling fooBuilder.build() then would ideally first build all the BarBuilders then build the set

Generated code elides package imports

If your class imports like this:

`import 'foo.dart' as foo;

abstract class AppState implements Built<AppState, AppStateBuilder> {
foo.Bar get myBar;
// etc.
}
`

Then the generated code will fail because it will generate something like:

class _$AppState extends AppState { @override Bar myBar; // etc. }

Without re-importing 'foo.dart' or re-declaring the namespace.

Copy&paste friendly error messages from BuiltValueGenerator

It would be neat to have errors ala:

Please make the following changes to use BuiltValue:

1. Import generated part: part 'ast.g.dart';
2. Make class have exactly one constructor: Constant._();
3. Make class have factory: factory Constant([updates(ConstantBuilder b)]) = _$Constant;

formatted in a way that makes the suggestions easier to copy and paste by adding additional newlines:

Please make the following changes to use BuiltValue:

1. Import generated part: 
  
  part 'ast.g.dart';
 
2. Make class have exactly one constructor:
   
  Constant._();

3. Make class have factory:
  
   factory Constant([updates(ConstantBuilder b)]) = _$Constant;

It could also be an option just for people with bad mouse skills like me.


Or alternatively, could we apply patches right away after prompting the user? dart tool/build.dart is a somewhat manual process already, it could be nice to add the convenience of fixing stuff automatically :)

Thanks,
Andreas

Changes necessary when linter rules are enabled

Not everyone wants to enable all these rules but it would be great if the generated code would conform to most

My .analysis_options

analyzer:
  strong-mode:
    implicit-casts: false
    implicit-dynamic: false

#  language:
#    enableGenericMethods: true

  exclude:
#    - 'build/**'
#    - '**.g.dart'
    - 'bazel-*'

linter:
  rules:
# Error Rules
    - avoid_empty_else
    - cancel_subscriptions
    - close_sinks
    - comment_references
    - control_flow_in_finally
    - empty_statements
    - hash_and_equals
    - invariant_booleans
    - iterable_contains_unrelated_type
    - list_remove_unrelated_type
    - literal_only_boolean_expressions
    - test_types_in_equals
    - throw_in_finally
    - unrelated_type_equality_checks
    - valid_regexps

# Style Rules
    - always_declare_return_types
#    - always_specify_types
    - annotate_overrides
#    - avoid_as
    - avoid_init_to_null
    - avoid_return_types_on_setters
    - await_only_futures
    - camel_case_types
    - cascade_invocations
    - constant_identifier_names
    - empty_catches
    - empty_constructor_bodies
    - implementation_imports
    - library_names
    - library_prefixes
    - non_constant_identifier_names
    - one_member_abstracts
    - only_throw_errors
    - overridden_fields
    - package_api_docs
    - package_prefixed_library_names
    - prefer_is_not_empty
    - parameter_assignments
    - prefer_final_fields
    - prefer_final_locals
#    - public_member_api_docs
    - slash_for_doc_comments
#    - sort_constructor_first
    - sort_unnamed_constructors_first
    - super_goes_last
    - type_annotate_public_apis
    - type_init_formals
    - unawaited_futures
    - unnecessary_brace_in_string_interp
    - unnecessary_getters_setters

# Pub Rules
    - package_names

My input source files

library model2;

import 'package:built_value/built_value.dart';
import 'package:built_value/serializer.dart';
import 'component_target_type.dart';
import 'package:meta/meta.dart';

part 'component.g.dart';

abstract class Component implements Built<Component, ComponentBuilder> {
  String get name;
  String get importPath;
  String get className;
  String get tagName;
  ComponentTargetType get targetType;
  String get targetName;

  static Serializer<Component> get serializer => _$componentSerializer;

  factory Component([void updates(ComponentBuilder b)]) = _$Component;
  Component._();
}
library model2;

import 'package:built_collection/built_collection.dart';
import 'package:built_value/built_value.dart';
import 'package:built_value/serializer.dart';

part 'component_target_type.g.dart';

class ComponentTargetType extends EnumClass {
  static const ComponentTargetType custom = _$custom;
  static const ComponentTargetType fieldType = _$fieldType;
  static const ComponentTargetType fieldTypeList = _$fieldTypeList;
  static const ComponentTargetType backboneElement = _$backboneElement;
  static const ComponentTargetType extension = _$extentions;
  static const ComponentTargetType resource = _$resource;

  static final Serializer<ComponentTargetType> serializer =
      _$componentTargetTypeSerializer;

  const ComponentTargetType._(String name) : super(name);

  static BuiltSet<ComponentTargetType> get values => _$values;
  static ComponentTargetType valueOf(String name) => _$valueOf(name);
}

The changes I made to get rid of the linter hints/warnings

image
image

image

Support BuiltValue class hierarchies

Not sure how feasible this is, but it would be neat if one could inherit in one BuiltValue class from another BuiltValue class and it would do the right thing.

Given abstract class A implements Built<A, ABuilder> {...}:

abstract class B implements Built<B, BBuilder>, A {
  ...
}

BBuilder would have to implement ABuilder and the generator would need to look at the super class chain.


As a simpler alternative, it would suffice to support implementing pure abstract base classes:

abstract class Statement {
  Label get label;
}

abstract class Axiom implements Statement, Built<Axiom, AxiomBuilder> { ... }
abstract class Proof implements Statement, Built<Proof, ProofBuilder> { ... }

Right now, I have to copy&paste the getters into the sub-classes for the generator to pick them up and then keep them up-to-date. (Not a lot of work for small hierarchies but still duplicated code.)

Thanks,
Andreas

Generate crazy efficient hashcode

One-property class got

  int get hashCode {
    return hashObjects([packageName]);
  }

It would be much better if this just returned packageName.hashCode – no allocations, iteration, etc.

Help reduce boilerplate (best practices or further optional source_gen)

I find myself writing a lot of boilerplate code with custom built_values and I always wonder if it's possible to cut most of it. Looking at a typical class:

abstract class FightSituation extends Situation
    implements Built<FightSituation, FightSituationBuilder> {
  factory FightSituation.initialized(
          Iterable<int> playerTeamIds, Iterable<int> enemyTeamIds) =>
      new FightSituation((b) => b
        ..playerTeamIds = new BuiltList<int>(playerTeamIds)
        ..enemyTeamIds = new BuiltList<int>(enemyTeamIds));
  factory FightSituation([updates(FightSituationBuilder b)]) = _$FightSituation;
  FightSituation._();

  // ... actual logic of the class
}

And here's the builder:

abstract class FightSituationBuilder extends SituationBuilder
    implements Builder<FightSituation, FightSituationBuilder> {
  @virtual
  int id = getRandomId();
  @virtual
  int time = 0;
  @virtual
  BuiltList<int> playerTeamIds;
  @virtual
  BuiltList<int> enemyTeamIds;
  @virtual
  MapBuilder<int, TimedEventCallback> events =
      new MapBuilder<int, TimedEventCallback>();

  factory FightSituationBuilder() = _$FightSituationBuilder;
  FightSituationBuilder._();
}

Looking at these two:

  • Do you feel like some of this could be done better on my (the user's) part? Is some of it a downright anti-pattern? If so, how do we help people not to fall in that hole?
  • Could any of this be automated away by further source generation? Something like what AngularDart does with annotations? Or maybe half-way between current built_value and there?

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.