Code Monkey home page Code Monkey logo

data_class's People

Contributors

brex900 avatar

Stargazers

 avatar  avatar  avatar

Watchers

 avatar  avatar

data_class's Issues

Changes class not generating while using sealed times

@immutable
@DataClass()
abstract base class Event with _$Event {
  const Event();
}

@immutable
@DataClass()
sealed class AuthenticationEvent extends Event with _$AuthenticationEvent {
  const AuthenticationEvent();
}

@immutable
@DataClass()
final class LoginEvent extends AuthenticationEvent with _$LoginEvent {
  const LoginEvent({required this.loginInfo});

  final LoginInfo loginInfo;
}

@immutable
@DataClass()
final class LogoutEvent extends AuthenticationEvent with _$LogoutEvent {
  const LogoutEvent();
}

On generated code: Classes and mixins can only implement other classes and mixins

class _LoginEventChanges implements _AuthenticationEventChanges {
...
}

That _AuthenticationEventChanges doesn't exist.

Make ClassToString print null values

Thank you really for this simple yet useful package, I love that is preserves dart class models compared to other data class generators. What is missing, is that I don't see class null values. I suppose void add(String field, Object? value) { if (value == null) return; is the one that causing it. Null is still a value, otherwise it seems like I don't have such parameter at all. You don't need to force it, just give an option to switch on or off for each generated ClassToString, idk in build.yaml file for example.
Thank you!

Incorrect code generated for data classes with positional parameters

Hi, thanks for this package!

When trying to use it I noticed that it doesn't support classes with positional parameters and expects all parameters to be named.

@DataClass()
class Test with _$Test {
  final int positional1;
  final String positional2;
  final bool named1;

  const Test(this.positional1, this.positional2, {this.named1 = true});
}

generates

mixin _$Test {
  Test get _self => this as Test;

  Iterable<Object?> get _props sync* {
    yield _self.positional1;
    yield _self.positional2;
    yield _self.named1;
  }

  bool operator ==(Object other) =>
      identical(this, other) ||
      other is Test &&
          runtimeType == other.runtimeType &&
          DataClass.$equals(_props, other._props);

  int get hashCode => Object.hashAll(_props);

  String toString() => (ClassToString('Test')
        ..add('positional1', _self.positional1)
        ..add('positional2', _self.positional2)
        ..add('named1', _self.named1))
      .toString();

  Test copyWith({
    int? positional1,
    String? positional2,
    bool? named1,
  }) {
    return Test(
      positional1: positional1 ?? _self.positional1,
      positional2: positional2 ?? _self.positional2,
      named1: named1 ?? _self.named1,
    );
  }

  Test change(void Function(_TestChanges c) updates) =>
      (_TestChanges._(_self)..update(updates)).build();

  _TestChanges toChanges() => _TestChanges._(_self);
}

class _TestChanges {
  int positional1;
  String positional2;
  bool named1;

  _TestChanges._(Test dc)
      : positional1 = dc.positional1,
        positional2 = dc.positional2,
        named1 = dc.named1;

  void update(void Function(_TestChanges c) updates) => updates(this);

  Test build() => Test(
        positional1: positional1,
        positional2: positional2,
        named1: named1,
      );
}

which contains a copyWith method and a _TestChanges class that assume all parameters are named.

I have a simple use case where the first 2 parameters are obvious and the rest of the parameters are named and optional, and I don't wish to make the first 2 parameters named.

Suggestion: add simple serialization methods

It would be nice if we could serialize and deserialize those classes to a Map<String, Object>.

Nothing fancy, only drop the values inside the map and vice-versa. No conversions, no renaming, nothing.

So,

@DataClass()
class Product with _$Product {
  final String title;
  @DataField(equality: DefaultEquality())
  final double price;

  const Product({
    required this.title,
    required this.price,
  });
  
  String get titlePrice => '$title$price';
}

outputs

// This is a Map, not a JSON string!
<String, Object>{
  "title": "some title",
  "price": 3.14,
}

And that's it! The dev would be responsible to make this map encodable by, let's say, JSON (then, there, it will handle incompatible types, such as DateTime, Enum, etc.)

Suggestion: Optimize generated code

Hi again,

I wanted to bring up a suggestion that this library could generate code that is more optimized. Currently it relies on iterating over props, which is slower than "hand written" equals or hashcode. It's not "slow" because it takes probably micro or nanoseconds, but its relatively slower than what you would write manually. Here's a sample benchmark:

test.dart

import 'package:mek_data_class/mek_data_class.dart';

part 'gen/test.g.dart';

@DataClass()
class TestDataClass with _$TestDataClass {
  final String name;
  final int age;
  TestDataClass(this.name, this.age);
}

class TestManualClass {
  final String name;
  final int age;
  TestManualClass(this.name, this.age);

  @override
  bool operator ==(Object other) =>
      identical(this, other) ||
      other is TestManualClass &&
          runtimeType == other.runtimeType &&
          name == other.name &&
          age == other.age;

  @override
  int get hashCode => name.hashCode ^ age.hashCode;
}

const warmupIterations = 10000;
const iterations = 10000000;

void main() {
  test('TestManualClass', TestManualClass('John', 30), TestManualClass('Jack', 31));
  test('TestDataClass', TestDataClass('John', 30), TestDataClass('Jack', 31));
}

void test(String name, Object a, Object b) {
  for (var i = 0; i < warmupIterations; i++) {
    a == b;
  }

  final stopwatch = Stopwatch()..start();
  for (var i = 0; i < iterations; i++) {
    a == b;
  }
  stopwatch.stop();
  print("$name equals: ${stopwatch.elapsedMilliseconds}");

  stopwatch.reset();

  for (var i = 0; i < warmupIterations; i++) {
    a.hashCode;
  }

  stopwatch.start();
  for (var i = 0; i < iterations; i++) {
    a.hashCode;
  }
  stopwatch.stop();
  print("$name hashCode: ${stopwatch.elapsedMilliseconds}");
}

On my machine, i9 9900K, I get the following:

TestManualClass equals: 65
TestManualClass hashCode: 48
TestDataClass equals: 1061
TestDataClass hashCode: 751

It also may have a slight impact on garbage collection as it needs to allocate iterators, but I didn't measure that.

So seeing as this is a codegen lib, I think this is exactly what a codegen lib should do - generate the boring boilerplate. I understand why it currently generates what it does - it's a simpler implementation. But since it already has access to all the fields, it can generate something that resembles the "manual" version more.

Curious about your thoughts on this. And thanks for this lib regardless :)

Fix mixin type

First off, very nice package.

I noticed this in the generated code:
image

Would it not be possible to remove this, if we create the mixin with the correct type?

mixin _$Comment on Comment {

then, this getter and the cast is unnecessary.

Unnecessary use of parentheses (linter warning)

On generated mixins toString():

@override
String toString() => (ClassToString('AuthenticationEvent')).toString();

Should be

@override
String toString() => ClassToString('AuthenticationEvent').toString();

Suggestion: Consider making field class static

In most cases there is no need for a path inside Fields class.

To use a field from a class currently you have to create a new class of fields ProfileFields().username, while you could use ProfileFields.username. I expect that when static meta programming is available I would personally use a static version or even an enum and the switch is going to be easier if that's the case here too.

The current implementation with a prefix seems a bit arbitrary.

Allow setting fields to `null` in `copyWith`

Right now, it is not possible to pass null to copyWith to set a nullable field to null, instead we have to use the Changes class.
However, it would be possible to include this functionality by generating a bit of extra code.

Here is an idea as to what this could look like:

final Object _undefined = Object();

/// not nullable property
final String a;

/// nullable property
final String? b;

MyClass copyWith({
  String? a,
  String? b,
}) => _copyWith(
    a: a,
    b: b,
  );

MyClass _copyWith({
  Object? a = _undefined,
  Object? b = _undefined,
}) => MyClass(
    /// because this property cant be [null], both [_undefined] and [null] count as absent
    a: a == _undefined || a == null ? this.a : a,
    /// because this property can be [null], only [_undefined] counts as passing nothing.  
    b: b == _undefined ? this.b : b,
  );

To demonstrate that this code actually works as intended, we can use this example:

Source Code
class MyClass {
  MyClass({
    required this.a,
    required this.b,
  });

  static const Object _undefined = Object();

  /// not nullable property
  final String a;

  /// nullable property
  final String? b;

  MyClass copyWith({
    String? a,
    String? b,
  }) =>
      _copyWith(
        a: a,
        b: b,
      );

  MyClass _copyWith({
    Object? a = _undefined,
    Object? b = _undefined,
  }) =>
      MyClass(
        /// because this property cant be [null], both [_undefined] and [null] count as absent
        a: a == _undefined || a == null ? this.a : a as String,

        /// because this property can be [null], only [_undefined] counts as passing nothing.
        b: b == _undefined ? this.b : b as String?,
      );

  @override
  String toString() {
    return 'MyClass(a: $a, b: $b)';
  }
}

void main() {
  MyClass c = MyClass(
    a: 'a',
    b: 'b',
  );
  print(c);
  c = c.copyWith(b: null);
  print(c);
}

typedef, namespace support

Generator not respects namespaces nor aliases. The latter is not so important, but missing namespace support causes compile error.

import 'package:some_package/api.dart' as api;

typedef MyCustomDate = DateTime

@DataClass()
class Product with _$Product {
    final api.Product apiProduct;
    final MyCustomDate date;
}

generates:

  Product copyWith({
    Product? apiProduct,
    DateTime? date,
  }) 

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.