kind 0.2.0 copy "kind: ^0.2.0" to clipboard
kind: ^0.2.0 copied to clipboard

outdated

A data modeling framework. Easy serialization and observation object graphs. The API is usable without code generation. Includes database primitives such as GeoPoint and UUID.

Pub Package Github Actions CI

Overview #

This is Kind framework for serialization / persistence / reactive state management.

This is an initial experimental version. The APIs are not frozen.

What it gives you? #

  • Convert graphs to/from JSON trees.
  • Convert graphs to/from Protocol Buffers (and GRPC) trees.
  • Use databases (upcoming).
    • Our sibling package database will use this framework in future.
  • Reactive programming
    • The package has ReactiveSystem for observing views and mutations of reactive states in the isolate. JSON / Protobuf deserialization methods construct reactive objects by default.
  • Various small helpers for data layer programming.
    • Use kind.instanceValidate to validate instances. For instance, if you have StringKind(minLengthInRunes:3, maxLines:1), you get a debugging-friendly error message when you validate instance "a".
    • Use kind.randomExample to generate random instances of the kind.
    • Frameworks may use kind.newList to construct a memory-efficient lists without knowing the type. For instance, Float32Kind, will give you a dart:typed_data Float32List rather than normal List<double> when you ask for a non-growable list.
    • Frameworks may use entityKind.meaning to understand kinds and properties in terms of other vocabularies (such as schema.org schemas).

Overview of APIs #

Built-in kinds #

Built-in string formats #

Data primitives #

  • Date (like DateTime, but does not define time)
  • DateTimeWithTimeZone (like DateTime, but allows any time zone)
  • GeoPoint (geographical latitude/longitude coordinates)
  • Uuid (128-bit object identifier)

Reactive collection classes #

Some alternatives #

For serialization #

  • built_value
    • Generates code for immutable values and JSON serialization.
  • json_serializable
    • Generates code for JSON serialization.
  • protobuf
    • Generates code for Protocol Buffers serialization.

For state management #

Getting started #

1.Adding dependency #

In pubspec.yaml, you should have something like:

environment:
  sdk: '>=2.12.0-0 <3.0.0'

dependencies:
  kind: ^0.2.0

2.Write data models #

In this example, we use Field helper to avoid writing reactive getters / setters:

class Person extends Entity {
  static final EntityKind<Person> kind = EntityKind<Person>(
    name: 'Person',
    build: (b) {
      b.optionalString(
        id: 1,
        name: 'fullName',
        minLength: 1,
        field: (e) => e.fullName,
      );
      b.requiredSet<Person>(
        id: 2,
        name: 'friends',
        itemsKind: Person.kind,
        field: (e) => e.friends,
      );
      b.constructor = () => Person();
    },
  );

  /// Full name.
  late final Field<String?> fullName = Field<String?>(this);

  /// Friends.
  late final SetField<Person> friends = SetField<Person>(this);

  @override
  EntityKind getKind() => kind;
}

Serialization #

JSON #

// Encode
final json = person.getKind().jsonTreeEncode(person);

// Decode
final person = Person.kind.jsonTreeDecode(json);

Protocol Buffers #

// Encode
final generatedMessage = person.getKind().protobufTreeEncode(person);

// Decode
final person = Person.kind.protobufTreeDecode(generatedMessage);

Alternative approaches to specifying data classes #

Why / why not? #

The alternative approaches:

  • Do not force you to deviate from the way you normally write classes.
  • Perform better when you have millions of objects.
  • Do not support reactive programming with ReactiveSystem unless you write a lot error-prone boilerplate code.
    • In future, we may release a code generator that generates boilerplate for you, but there will inevitably going to be some complexity unless Dart language designers decide to support something like decorator annotations.

For mutable classes #

...without reactive programming support #

You just define getter and setter in Prop for ordinary Dart fields:

class Person {
  /// Full name.
  String? fullName = '';

  /// Friends.
  final Set<Person> friends = {};
}

/// EntityKind for [Person].
final EntityKind<Person> personKind = EntityKind<Person>(
  name: 'Person',
  build: (builder) {
    builder.optionalString(
      id: 1,
      name: 'fullName',
      getter: (t) => t.fullName,
      setter: (t,v) => t.fullName = v,
    );
    builder.requiredSet<Person>(
      id: 2,
      name: 'friends',
      itemsKind: personKind,
      getter: (t) => t.friends,
    );
    builder.constructor = () => Person();
  },
);

...with reactive programming support #

You can use ReactiveMixin for implementing reactive getters and setters:

class Person extends Entity with ReactiveMixin {
  String? _fullName;
  final Set<Person> _friends = ReactiveSet<Person>();

  /// Full name of the person.
  String? get fullName => beforeGet(_fullName);
  set fullName(String? value) => _fullName = beforeSet(_fullName, value);

  /// Friends of the person.
  Set<Person> get friends => beforeGet(_friends);

  @override
  EntityKind<Person> getKind() => personKind;
}

// The `personKind` is identical to the previous example.
// ...

For immutable classes #

...without reactive programming support #

// Extending Entity is optional, but recommended.
class Person {
  /// Full name of the person.
  final String? name;

  /// Friends of the person.
  final Set<Person> friends;

  Person({
    this.fullName,
    this.friends = const {},
  });
}

/// EntityKind for [Person].
final EntityKind<Person> personKind = EntityKind<Person>(
  name: 'Person',
  build: (builder) {
    final fullName = builder.optionalString(
      id: 1,
      name: 'fullName',
      getter: (t) => t.fullName,
    );
    final friends = builder.requiredSet<Person>(
      id: 2,
      name: 'friends',
      itemsKind: personKind,
      getter: (t) => t.friends,
    );
    builder.constructorFromData = (data) {
      return Person(
        name: data.get(fullName),
        friends: data.get(friends),
      );
    };
  },
);

...with reactive programming support #

You can use ReactiveMixin for implementing reactive getters:

// Extending Entity is optional, but recommended.
class Person extends Entity with ReactiveMixin {
  final String? _fullName;
  final Set<Person> _friends;

  /// Full name of the person.
  String? get fullName => beforeGet(_fullName);

  /// Friends of the person.
  Set<Person> get friends => beforeGet(_friends);

  Person({
    required String? name,
    Set<Person> friends = const {},
  }) :
    _fullName = name,
    _friends = ReactiveSet<Person>.wrap(friends);

  @override
  EntityKind<Person> getKind() => personKind;
}

// The `personKind` is identical to the previous example.
// ...
8
likes
0
pub points
56%
popularity

Publisher

verified publisherdint.dev

A data modeling framework. Easy serialization and observation object graphs. The API is usable without code generation. Includes database primitives such as GeoPoint and UUID.

Repository (GitHub)
View/report issues

License

unknown (license)

Dependencies

collection, fixnum, meta, protobuf

More

Packages that depend on kind