Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- class BuiltRef<T> {
- /// Holds the ref, can also be null
- final T value;
- /// Whether this was constructed using [BuiltRef.undefined]
- final bool isUndefined;
- /// Create a new reference to [value]
- const BuiltRef(this.value) : isUndefined = false;
- /// Used as the default value in parameter definition
- const BuiltRef.undefined()
- : value = null,
- isUndefined = true;
- @override
- int get hashCode => value.hashCode;
- @override
- operator ==(Object other) => other is BuiltRef
- ? value == other.value && isUndefined == other.isUndefined
- : false;
- @override
- String toString() => value.toString();
- }
- abstract class MyValue {
- /// String that can be null but also has a default
- String get nullableStringWithDefault => 'default value';
- /// Must have an int value
- int get mustHaveInt;
- MyValue._() {
- if ((nullableStringWithDefault?.length ?? 0) > 50) {
- throw Exception('String is too big');
- }
- print('validation checks pass!');
- }
- factory MyValue([Function(MyValueBuilder) updates]) = MyValueImpl;
- }
- class MyValueImpl extends MyValue {
- BuiltRef<String> _nullableStringWithDefault;
- String get nullableStringWithDefault =>
- (_nullableStringWithDefault ??= BuiltRef(super.nullableStringWithDefault))
- .value;
- BuiltRef<int> _mustHaveInt;
- int get mustHaveInt => _mustHaveInt.value;
- MyValueImpl._withBuiltRef({
- BuiltRef<String> nullableStringWithDefault = const BuiltRef.undefined(),
- BuiltRef<int> mustHaveInt,
- }) : _mustHaveInt = mustHaveInt,
- _nullableStringWithDefault =
- nullableStringWithDefault?.isUndefined ?? false
- ? null
- : nullableStringWithDefault,
- super._() {
- /// Null checks
- if (mustHaveInt?.value == null) {
- throw ArgumentError.notNull('mustHaveInt');
- }
- print('null checks pass!');
- }
- /// Existing code that uses this constructor should work as expected
- /// Here nullableStringWithDefault can be set to `null` explicitly
- MyValueImpl._({
- String nullableStringWithDefault,
- int mustHaveInt,
- }) : this._withBuiltRef(
- nullableStringWithDefault: BuiltRef(nullableStringWithDefault),
- mustHaveInt: BuiltRef(mustHaveInt),
- );
- /// Existing code using this factory will allow default values as well as setting to `null` explicitly
- factory MyValueImpl([Function(MyValueBuilder) updates]) =>
- (MyValueBuilder()..update(updates)).build();
- }
- class MyValueBuilder {
- MyValueImpl _$v;
- /// Initially, the builder doesn't initialize any fields so this will be null
- BuiltRef<String> _nullableStringWithDefault;
- String get nullableStringWithDefault =>
- _$this._nullableStringWithDefault.value;
- set nullableStringWithDefault(String nullableStringWithDefault) {
- /// This is a nullable field, we don't care what the value is
- _$this._nullableStringWithDefault = BuiltRef(nullableStringWithDefault);
- }
- BuiltRef<int> _mustHaveInt;
- int get mustHaveInt => _$this._mustHaveInt.value;
- set mustHaveInt(int mustHaveInt) {
- /// For non-nullable fields, throw optionally to provide a better stack trace
- _$this._mustHaveInt =
- BuiltRef(mustHaveInt ?? (throw ArgumentError.notNull('mustHaveInt')));
- /// Or just set the value and let the constructor of MyValueImpl handle null values
- // _$this._mustHaveInt = BuiltRef(mustHaveInt);
- }
- MyValueBuilder get _$this {
- if (_$v != null) {
- _nullableStringWithDefault =
- BuiltRef(_$v.nullableStringWithDefault) ?? BuiltRef.undefined();
- _mustHaveInt = BuiltRef(_$v.mustHaveInt);
- _$v = null;
- }
- return this;
- }
- void replace(MyValue value) => _$v = value;
- void update([Function(MyValueBuilder) updates]) => updates?.call(_$this);
- MyValue build() {
- return _$v ??
- MyValueImpl._withBuiltRef(
- nullableStringWithDefault:
- _nullableStringWithDefault ?? const BuiltRef.undefined(),
- mustHaveInt: _mustHaveInt,
- );
- }
- }
- void main([List<String> args]) {
- useCase("When default values are used", () {
- final defaultValue = MyValue((b) => b.mustHaveInt = 1);
- print(defaultValue.nullableStringWithDefault);
- });
- useCase("When default values are ignored (custom value is used)", () {
- final customValue = MyValue((b) => b
- ..nullableStringWithDefault = "custom value"
- ..mustHaveInt = 2);
- print(customValue.nullableStringWithDefault);
- });
- useCase("When default values are ignored and explicitly set to null", () {
- final explicitNullValue = MyValue((b) => b
- ..nullableStringWithDefault = null
- ..mustHaveInt = 3);
- print(explicitNullValue.nullableStringWithDefault);
- });
- useCase("When default values are ignored and validation fails", () {
- try {
- MyValue((b) => b
- ..nullableStringWithDefault =
- "custom value tooooooooooooooooooooooooooooooooooooooooooooooooooooo big"
- ..mustHaveInt = 4);
- } catch (e) {
- print('validation checks failed!');
- }
- });
- useCase("When default values are used but null checks fail", () {
- try {
- MyValue();
- } catch (e) {
- print('null checks failed');
- }
- });
- useCase("When non-nullables are explicitly set to null", () {
- try {
- MyValueBuilder()
- ..update((b) => b.mustHaveInt = null)
- ..build();
- } catch (e) {
- print('Builder fails to build');
- }
- });
- }
- void useCase(String name, Function function) {
- print("");
- print("".padRight(40, "#"));
- print(name);
- function();
- print("".padRight(40, "#"));
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement