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

outdated

An unified data layer framework that enables serialization / persistence / state management for any Dart class (without code generation!). Contains common primitives such as GeoPoint and UUID.

Pub Package Github Actions CI

Overview #

An unified data layer framework that enables serialization / persistence / state management for any Dart class. This an early version and the APIs are not frozen. The package requires a null-safe version of Dart SDK (currently beta SDKs only).

What it gives you? #

  • Convert graphs to/from JSON.
  • Convert graphs to/from Protocol Buffers.
  • Use databases (upcoming).
    • Our sibling package database will use this framework in future.
  • Observe views / mutations in graphs
    • The package has ReactiveSystem for observing views and mutations of reactive states in the isolate. When you are deserializing JSON or Protocol Buffers, you get reactive objects by default without any further data wrangling.
  • Validate instances.
    • For instance, if you have StringKind(minLengthInUtf8:3, singleLine:true), you get a debugging-friendly error message when you try to validate instance "a".
  • Get meanings.
    • Developers can use kind.meaning and prop.meaning to describe kinds in terms of other vocabularies (such as schema.org schemas). The information may be utilized in user interfaces or data processing.
  • Get generated examples.
    • Kind has APIs for obtaining examples of instances.

Overview of APIs #

Built-in kinds #

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.3.0

2.Write data models #

In the following example, we use Field. Wrapping values inside Field simplifies state observation. If you want to use normal Dart getters / setters, see "Alternative approaches" section below.

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.

Mutable class, without reactive state management #

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();
  },
);

Mutable class, with reactive state management #

You can use ReactiveMixin for implementing getters and setters that send notifications to ReactiveSystem:

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.
// ...

Immutable class, without reactive state management #

// 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),
      );
    };
  },
);

Immutable class, with reactive state management #

You can use ReactiveMixin for implementing getters and setters that send notifications to ReactiveSystem:

// 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

An unified data layer framework that enables serialization / persistence / state management for any Dart class (without code generation!). Contains common 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