diff --git a/example/test/ex11_generic_subclass_manual_test.dart b/example/test/ex11_generic_subclass_manual_test.dart index 4fafdff..69b4c30 100644 --- a/example/test/ex11_generic_subclass_manual_test.dart +++ b/example/test/ex11_generic_subclass_manual_test.dart @@ -5,9 +5,10 @@ import 'package:test/test.dart'; abstract class A { T1 get x; + T2 get y; - A copyWith_A({ + A copyWith_A({ Opt? x, Opt? y, }); @@ -26,7 +27,7 @@ class B implements A { String toString() => "(B-x:$x|y:$y|z:$z)"; - B copyWith_A({ + A copyWith_A({ Opt? x, Opt? y, }) { @@ -34,7 +35,7 @@ class B implements A { x: x == null ? this.x as int : x.value as int, y: y == null ? this.y as String : y.value as String, z: (this as B).z, - ); + ) as A; } B copyWith_B({ diff --git a/example/test/ex29_copywith_subclasses_test.dart b/example/test/ex29_copywith_subclasses_test.dart index cdec31b..97fccc3 100644 --- a/example/test/ex29_copywith_subclasses_test.dart +++ b/example/test/ex29_copywith_subclasses_test.dart @@ -85,7 +85,7 @@ main() { test("db", () { D db = D(b: 5, a: "A"); - var db_copy = db.copyWith_B(a: () => "a", b: () => 6); + var db_copy = db.copyWith_B(a: () => "a", b: () => 6) as D; expect(db_copy.toString(), "(D-a:a|b:6)"); }); diff --git a/example/test/ex60_copywith_generic_test.dart b/example/test/ex60_copywith_generic_test.dart index da969c5..c74794f 100644 --- a/example/test/ex60_copywith_generic_test.dart +++ b/example/test/ex60_copywith_generic_test.dart @@ -7,15 +7,11 @@ part 'ex60_copywith_generic_test.morphy.dart'; main() { test("1", () { - // var a = A(x:1, y:2); - // - // var aAsInt = a.copyWith_A(x: () => 2); - // - // expect(aAsInt.runtimeType, A); + var a = A(x:1, y:2); - // var aAsDouble = a.copyWith_A(x: () => 2.1); - // - // expect(aAsDouble.runtimeType, 2.1); + var aAsInt = a.copyWith_A(x: () => 2); + + expect(aAsInt.runtimeType, A); }); } diff --git a/example/test/ex63_class_with_no_members_test.dart b/example/test/ex63_class_with_no_members_test.dart new file mode 100644 index 0000000..7b1aacd --- /dev/null +++ b/example/test/ex63_class_with_no_members_test.dart @@ -0,0 +1,30 @@ +import 'package:morphy_annotation/morphy_annotation.dart'; +import 'package:test/test.dart'; + +part 'ex63_class_with_no_members_test.morphy.dart'; + +@Morphy( + explicitSubTypes: [ + $AgreedEulaState, + ] +) +abstract class $EulaState +{ + // lack of members +} + +@Morphy() +abstract class $AgreedEulaState implements $EulaState +{ + bool get test; +} + +main() { + test("1", () { + var a = EulaState(); + var b = AgreedEulaState(test: true); + + expect(a == null, false); + expect(b.test, true); + }); +} diff --git a/morphy/CHANGELOG.md b/morphy/CHANGELOG.md index 6500d1e..ca3d187 100644 --- a/morphy/CHANGELOG.md +++ b/morphy/CHANGELOG.md @@ -1,3 +1,10 @@ +## 1.3.0 +- bug fixes, subclass with no members now works, generic copy with bug now fixed where type is incorrect +- in order to fix the above problem the type returned from the copywith is now the type of the named copywith function, eg b.copyWith_A now returns an A type +- this is a BREAKING change. +- if you specify D newD = d.copyWith_A(); then newD will be of type D not A and you'll receive an error +- the thing is that if you know it is a d type you'd more likely do a d.copyWith_D() so this should not be a problem + ## 1.2.0 - New functionality - private getters are now allowed! diff --git a/morphy/lib/src/helpers.dart b/morphy/lib/src/helpers.dart index c35993b..cfa6970 100644 --- a/morphy/lib/src/helpers.dart +++ b/morphy/lib/src/helpers.dart @@ -265,19 +265,43 @@ String getCopyWith({ var classNameTrimmed = className.replaceAll("\$", ""); var interfaceNameTrimmed = interfaceName.replaceAll("\$", ""); - isExplicitSubType // - ? sb.write("$interfaceNameTrimmed changeTo_${interfaceNameTrimmed}") - : sb.write("$classNameTrimmed copyWith_$interfaceNameTrimmed"); - - if (interfaceGenerics.isNotEmpty) { - var generic = interfaceGenerics // - .map((e) => e.type == null // - ? e.name - : "${e.name} extends ${e.type}") - .joinToString(separator: ", "); - sb.write("<$generic>"); + // var interfaceGenericString = interfaceGenerics // + // .map((e) => e.type == null // + // ? e.name + // : "${e.name} extends ${e.type}") + // .joinToString(separator: ", "); + + var interfaceGenericStringWithExtends = interfaceGenerics // + .map((e) => e.type == null // + ? e.name + : "${e.name} extends ${e.type}") + .joinToString(separator: ", "); + + if (interfaceGenericStringWithExtends.length > 0) { + interfaceGenericStringWithExtends = "<$interfaceGenericStringWithExtends>"; } + var interfaceGenericStringNoExtends = interfaceGenerics // + .map((e) => e.name) + .joinToString(separator: ", "); + + if (interfaceGenericStringNoExtends.length > 0) { + interfaceGenericStringNoExtends = "<$interfaceGenericStringNoExtends>"; + } + + isExplicitSubType // + ? sb.write("$interfaceNameTrimmed$interfaceGenericStringNoExtends changeTo_$interfaceNameTrimmed$interfaceGenericStringWithExtends") + : sb.write("$interfaceNameTrimmed$interfaceGenericStringNoExtends copyWith_$interfaceNameTrimmed$interfaceGenericStringWithExtends"); + + // if (interfaceGenerics.isNotEmpty) { + // var generic = interfaceGenerics // + // .map((e) => e.type == null // + // ? e.name + // : "${e.name} extends ${e.type}") + // .joinToString(separator: ", "); + // sb.write("<$generic>"); + // } + sb.write("("); //where property name of interface is the same as the one in the class @@ -315,7 +339,7 @@ String getCopyWith({ return "${getDataTypeWithoutDollars(interfaceType!)} Function()? $name,\n"; }).join()); - if (fieldsForSignature.isNotEmpty) // + if (fieldsForSignature.isNotEmpty|| requiredFields.isNotEmpty) // sb.write("}"); if (isClassAbstract && !isExplicitSubType) { @@ -350,10 +374,16 @@ String getCopyWith({ .where((element) => !interfaceFields.map((e) => e.name).contains(element.name)); sb.write(fieldsNotInSignature // - .map((e) => "${e.name.startsWith('_') ? e.name.substring(1) : e.name }: (this as $classNameTrimmed).${e.name},\n") + .map((e) => "${e.name.startsWith('_') ? e.name.substring(1) : e.name}: (this as $classNameTrimmed).${e.name},\n") .join()); - sb.writeln(");"); + sb.write(") as $interfaceNameTrimmed$interfaceGenericStringNoExtends;"); + + // if (isExplicitSubType) { + // sb.write(") as $interfaceNameTrimmed;"); + // } else { + // sb.write(") as $interfaceNameTrimmed$interfaceGenericStringNoExtends;"); + // } sb.write("}"); return sb.toString(); diff --git a/morphy/pubspec.yaml b/morphy/pubspec.yaml index 16ba19d..dbc5285 100644 --- a/morphy/pubspec.yaml +++ b/morphy/pubspec.yaml @@ -1,6 +1,6 @@ name: morphy description: Provides a clean class definition with extra functionality including; copy with, json serializable, tostring, equals that supports inheritance and polymorphism -version: 1.2.0 +version: 1.3.0 homepage: https://github.com/atreeon/morphy environment: diff --git a/morphy/test/helpers_test.dart b/morphy/test/helpers_test.dart index a5cecf6..ea69afd 100644 --- a/morphy/test/helpers_test.dart +++ b/morphy/test/helpers_test.dart @@ -582,7 +582,7 @@ String Function()? a, isClassAbstract: true, interfaceGenerics: [], ); - expectS(result, """B copyWith_A({ + expectS(result, """A copyWith_A({ int Function()? a, });"""); }); @@ -605,8 +605,7 @@ String Function()? a, }) { return A._( a: a == null ? this.a as String : a() as String, -); -}"""); +) as A;}"""); }); test("4p ba (see ex29_manual)", () { @@ -623,14 +622,13 @@ a: a == null ? this.a as String : a() as String, isClassAbstract: false, interfaceGenerics: [], ); - expectS(result, """B copyWith_A({ + expectS(result, """A copyWith_A({ String Function()? a, }) { return B._( a: a == null ? this.a as String : a() as String, b: (this as B).b, -); -}"""); +) as A;}"""); }); test("5p bb (see ex29_manual)", () { @@ -655,8 +653,7 @@ T1 Function()? b, return B._( a: a == null ? this.a as String : a() as String, b: b == null ? this.b as T1 : b() as T1, -); -}"""); +) as B;}"""); }); test("6p ca (see ex29_manual)", () { @@ -674,15 +671,14 @@ b: b == null ? this.b as T1 : b() as T1, isClassAbstract: false, interfaceGenerics: [], ); - expectS(result, """C copyWith_A({ + expectS(result, """A copyWith_A({ String Function()? a, }) { return C._( a: a == null ? this.a as String : a() as String, b: (this as C).b, c: (this as C).c, -); -}"""); +) as A;}"""); }); test("7p cb (see ex29_manual)", () { @@ -701,7 +697,7 @@ c: (this as C).c, isClassAbstract: false, interfaceGenerics: [], ); - expectS(result, """C copyWith_B({ + expectS(result, """B copyWith_B({ String Function()? a, T1 Function()? b, }) { @@ -709,8 +705,7 @@ return C._( a: a == null ? this.a as String : a() as String, b: b == null ? this.b as T1 : b() as T1, c: (this as C).c, -); -}"""); +) as B;}"""); }); test("8p cc (see ex29_manual)", () { @@ -739,8 +734,7 @@ return C._( a: a == null ? this.a as String : a() as String, b: b == null ? this.b as T1 : b() as T1, c: c == null ? this.c as bool : c() as bool, -); -}"""); +) as C;}"""); }); test("9p da (see ex29_manual) class with no fields", () { @@ -757,14 +751,13 @@ c: c == null ? this.c as bool : c() as bool, isClassAbstract: false, interfaceGenerics: [], ); - expectS(result, """D copyWith_A({ + expectS(result, """A copyWith_A({ String Function()? a, }) { return D._( a: a == null ? this.a as String : a() as String, b: (this as D).b, -); -}"""); +) as A;}"""); }); test("10p db (see ex29_manual) class with no fields", () { @@ -782,15 +775,14 @@ b: (this as D).b, isClassAbstract: false, interfaceGenerics: [], ); - expectS(result, """D copyWith_B({ + expectS(result, """B copyWith_B({ String Function()? a, T1 Function()? b, }) { return D._( a: a == null ? this.a as String : a() as String, b: b == null ? this.b as T1 : b() as T1, -); -}"""); +) as B;}"""); }); test("11p dd (see ex29_manual) class with no fields", () { @@ -815,8 +807,7 @@ T1 Function()? b, return D._( a: a == null ? this.a as String : a() as String, b: b == null ? this.b as T1 : b() as T1, -); -}"""); +) as D;}"""); }); test("12p x (see ex29_manual) interface with no fields", () { @@ -843,12 +834,11 @@ b: b == null ? this.b as T1 : b() as T1, isClassAbstract: false, interfaceGenerics: [], ); - expectS(result, """Y copyWith_X( + expectS(result, """X copyWith_X( ) { return Y._( a: (this as Y).a, -); -}"""); +) as X;}"""); }); test("14p yy (see ex29_manual) interface with no fields", () { @@ -869,8 +859,7 @@ String Function()? a, }) { return Y._( a: a == null ? this.a as String : a() as String, -); -}"""); +) as Y;}"""); }); test("15p aa (see ex7_manual) where subtypes are used", () { @@ -891,8 +880,7 @@ Person Function()? a, }) { return A._( a: a == null ? this.a as Person : a() as Person, -); -}"""); +) as A;}"""); }); test("16p ba (see ex7_manual) where subtypes are used", () { @@ -908,13 +896,12 @@ a: a == null ? this.a as Person : a() as Person, isClassAbstract: false, interfaceGenerics: [], ); - expectS(result, """B copyWith_A({ + expectS(result, """A copyWith_A({ Person Function()? a, }) { return B._( a: a == null ? this.a as Employee : a() as Employee, -); -}"""); +) as A;}"""); }); test("17p bb (see ex7_manual) where subtypes are used", () { @@ -935,8 +922,7 @@ Employee Function()? a, }) { return B._( a: a == null ? this.a as Employee : a() as Employee, -); -}"""); +) as B;}"""); }); test("18p ca (see ex7_manual) where subtypes are used", () { @@ -952,13 +938,12 @@ a: a == null ? this.a as Employee : a() as Employee, isClassAbstract: false, interfaceGenerics: [], ); - expectS(result, """C copyWith_A({ + expectS(result, """A copyWith_A({ Person Function()? a, }) { return C._( a: a == null ? this.a as Manager : a() as Manager, -); -}"""); +) as A;}"""); }); test("19p cb (see ex7_manual) where subtypes are used", () { @@ -974,13 +959,12 @@ a: a == null ? this.a as Manager : a() as Manager, isClassAbstract: false, interfaceGenerics: [], ); - expectS(result, """C copyWith_B({ + expectS(result, """B copyWith_B({ Employee Function()? a, }) { return C._( a: a == null ? this.a as Manager : a() as Manager, -); -}"""); +) as B;}"""); }); test("20p cc (see ex7_manual) where subtypes are used", () { @@ -1001,8 +985,7 @@ Manager Function()? a, }) { return C._( a: a == null ? this.a as Manager : a() as Manager, -); -}"""); +) as C;}"""); }); test("21p ba (see ex11_manual) where subtypes are used", () { @@ -1021,7 +1004,7 @@ a: a == null ? this.a as Manager : a() as Manager, className: "B", isClassAbstract: false, ); - expectS(result, """B copyWith_A({ + expectS(result, """A copyWith_A({ T1 Function()? x, T2 Function()? y, }) { @@ -1029,8 +1012,7 @@ return B._( x: x == null ? this.x as int : x() as int, y: y == null ? this.y as String : y() as String, z: (this as B).z, -); -}"""); +) as A;}"""); }); test("22p bb (see ex11_manual) where subtypes are used", () { @@ -1059,8 +1041,7 @@ return B._( x: x == null ? this.x as int : x() as int, y: y == null ? this.y as String : y() as String, z: z == null ? this.z as String : z() as String, -); -}"""); +) as B;}"""); }); test("23p a (see ex2_manual) where generics are used", () { @@ -1076,7 +1057,7 @@ z: z == null ? this.z as String : z() as String, className: "A", isClassAbstract: true, ); - expectS(result, """A copyWith_A({ + expectS(result, """A copyWith_A({ T Function()? x, });"""); }); @@ -1096,14 +1077,13 @@ T Function()? x, className: "B", isClassAbstract: false, ); - expectS(result, """B copyWith_A({ + expectS(result, """A copyWith_A({ T Function()? x, }) { return B._( x: x == null ? this.x as int : x() as int, y: (this as B).y, -); -}"""); +) as A;}"""); }); test("25p a (see ex21) no default constructor", () { @@ -1124,8 +1104,7 @@ String Function()? a, }) { return A._( a: a == null ? this.a as String : a() as String, -); -}"""); +) as A;}"""); }); test("26p function to leave in dollar", () { @@ -1146,8 +1125,7 @@ bool Function(\$X) Function()? fn, }) { return X._( fn: fn == null ? this.fn as bool Function(\$X) : fn() as bool Function(\$X), -); -}"""); +) as X;}"""); }); test("27p subtype from a supertype", () { @@ -1172,8 +1150,7 @@ String Function()? x, return B._( y: y as String, x: x == null ? this.x as String : x() as String, -); -}"""); +) as B;}"""); }); test("28p subtype from a supertype", () { @@ -1201,8 +1178,7 @@ return B._( y: y as String, z: z as Z, x: x == null ? this.x as String : x() as String, -); -}"""); +) as B;}"""); }); test("29p sub to sub sibling with abstract parent", () { @@ -1230,8 +1206,7 @@ return B._( y: y as String, z: z as Z, x: x == null ? this.x as String : x() as String, -); -}"""); +) as B;}"""); }); test("30p function to leave in dollar", () { @@ -1260,8 +1235,7 @@ return B._( x: x == null ? this.x as String : x() as String, cs: cs == null ? this.cs as List : cs() as List, z: z == null ? this.z as Z : z() as Z, -); -}"""); +) as B;}"""); }); test("31p FROM ABSTRACT SUPERCLASS TO SUB CLASS", () { @@ -1286,8 +1260,7 @@ String Function()? x, return B._( y: y as String, x: x == null ? this.x as String : x() as String, -); -}"""); +) as B;}"""); }); test("32p private constructor", () { @@ -1315,8 +1288,7 @@ return B._( y: y as String, z: z as Z, x: x == null ? this.x as String : x() as String, -); -}"""); +) as B;}"""); }); test("33p private property abstract class", () { @@ -1353,7 +1325,7 @@ String Function()? a, isClassAbstract: false, interfaceGenerics: [], ); - expectS(result, """C copyWith_B({ + expectS(result, """B copyWith_B({ String Function()? a, T1 Function()? b, }) { @@ -1361,8 +1333,7 @@ return C._( a: a == null ? this._a as String : a() as String, b: b == null ? this.b as T1 : b() as T1, c: (this as C).c, -); -}"""); +) as B;}"""); }); }); diff --git a/morphy_annotation/pubspec.yaml b/morphy_annotation/pubspec.yaml index cf0b44f..093f343 100644 --- a/morphy_annotation/pubspec.yaml +++ b/morphy_annotation/pubspec.yaml @@ -1,6 +1,6 @@ name: morphy_annotation description: annotation for morphy which provides a clean class definition with extra functionality including; copy with, json serializable, tostring, equals that supports inheritance and polymorphism -version: 1.2.0 +version: 1.3.0 homepage: https://github.com/atreeon/morphy environment: