mirror of https://github.com/AMT-Cheif/drift.git
API and parser for custom types
This commit is contained in:
parent
dcc7f29492
commit
c2bff3ae42
|
@ -1,3 +1,6 @@
|
|||
## unreleased
|
||||
- Support custom columns
|
||||
|
||||
## 1.6.0
|
||||
- Experimental web support! See [the documentation](https://moor.simonbinder.eu/web) for details.
|
||||
- Make transactions easier to use: Thanks to some Dart async magic, you no longer need to run
|
||||
|
|
|
@ -107,6 +107,10 @@ class ColumnBuilder<
|
|||
/// store the current date/time as a default value.
|
||||
Builder withDefault(Expression<ResultDartType, ResultSqlType> e) => null;
|
||||
|
||||
/// Uses a custom [converter] to store custom Dart objects in a single column
|
||||
/// and automatically mapping them from and to sql.
|
||||
Builder map<T>(TypeConverter<T, ResultDartType> converter) => null;
|
||||
|
||||
/// Turns this column builder into a column. This method won't actually be
|
||||
/// called in your code. Instead, moor_generator will take a look at your
|
||||
/// source code to figure out your table structure.
|
||||
|
|
|
@ -93,7 +93,6 @@ abstract class Table {
|
|||
/// ```
|
||||
/// RealColumn get averageSpeed => real()();
|
||||
/// ```
|
||||
/// Note
|
||||
@protected
|
||||
RealColumnBuilder real() => null;
|
||||
}
|
||||
|
|
|
@ -25,7 +25,9 @@ class InsertStatement<D extends DataClass> {
|
|||
/// Otherwise, an exception will be thrown.
|
||||
///
|
||||
/// If the table contains an auto-increment column, the generated value will
|
||||
/// be returned.
|
||||
/// be returned. If there is no auto-increment column, you can't rely on the
|
||||
/// return value, but the future will resolve to an error when the insert
|
||||
/// fails.
|
||||
Future<int> insert(Insertable<D> entity, {bool orReplace = false}) async {
|
||||
_validateIntegrity(entity);
|
||||
final ctx = _createContext(entity, orReplace);
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
part of 'sql_types.dart';
|
||||
|
||||
/// Maps a custom dart object of type [D] into a primitive type [S] understood
|
||||
/// by the sqlite backend.
|
||||
///
|
||||
/// Moor currently supports [DateTime], [double], [int], [Uint8List], [bool]
|
||||
/// and [String] for [S].
|
||||
abstract class TypeConverter<D, S> {
|
||||
/// Empty constant constructor so that subclasses can have a constant
|
||||
/// constructor.
|
||||
const TypeConverter();
|
||||
|
||||
/// Map a value from an object in Dart into something that will be understood
|
||||
/// by the database. Be aware that [value] is nullable.
|
||||
S mapToSql(D value);
|
||||
|
||||
/// Maps a column from the database back to Dart. Be aware that [fromDb] is
|
||||
/// nullable.
|
||||
D mapToDart(S fromDb);
|
||||
}
|
|
@ -1,5 +1,7 @@
|
|||
import 'dart:typed_data';
|
||||
|
||||
part 'custom_type.dart';
|
||||
|
||||
/// A type that can be mapped from Dart to sql. The generic type parameter here
|
||||
/// denotes the resolved dart type.
|
||||
abstract class SqlType<T> {
|
||||
|
|
|
@ -50,6 +50,8 @@ class SharedTodos extends Table {
|
|||
class TableWithoutPK extends Table {
|
||||
IntColumn get notReallyAnId => integer()();
|
||||
RealColumn get someFloat => real()();
|
||||
|
||||
TextColumn get custom => text().map(const CustomConverter())();
|
||||
}
|
||||
|
||||
class PureDefaults extends Table {
|
||||
|
@ -57,6 +59,25 @@ class PureDefaults extends Table {
|
|||
TextColumn get txt => text().nullable()();
|
||||
}
|
||||
|
||||
// example object used for custom mapping
|
||||
class MyCustomObject {
|
||||
final String data;
|
||||
MyCustomObject(this.data);
|
||||
}
|
||||
|
||||
class CustomConverter extends TypeConverter<MyCustomObject, String> {
|
||||
const CustomConverter();
|
||||
@override
|
||||
MyCustomObject mapToDart(String fromDb) {
|
||||
return fromDb == null ? null : MyCustomObject(fromDb);
|
||||
}
|
||||
|
||||
@override
|
||||
String mapToSql(MyCustomObject value) {
|
||||
return value?.data;
|
||||
}
|
||||
}
|
||||
|
||||
@UseMoor(
|
||||
tables: [
|
||||
TodosTable,
|
||||
|
|
|
@ -842,18 +842,25 @@ class TableWithoutPKData extends DataClass
|
|||
implements Insertable<TableWithoutPKData> {
|
||||
final int notReallyAnId;
|
||||
final double someFloat;
|
||||
TableWithoutPKData({@required this.notReallyAnId, @required this.someFloat});
|
||||
final MyCustomObject custom;
|
||||
TableWithoutPKData(
|
||||
{@required this.notReallyAnId,
|
||||
@required this.someFloat,
|
||||
@required this.custom});
|
||||
factory TableWithoutPKData.fromData(
|
||||
Map<String, dynamic> data, GeneratedDatabase db,
|
||||
{String prefix}) {
|
||||
final effectivePrefix = prefix ?? '';
|
||||
final intType = db.typeSystem.forDartType<int>();
|
||||
final doubleType = db.typeSystem.forDartType<double>();
|
||||
final myCustomObjectType = db.typeSystem.forDartType<MyCustomObject>();
|
||||
return TableWithoutPKData(
|
||||
notReallyAnId: intType
|
||||
.mapFromDatabaseResponse(data['${effectivePrefix}not_really_an_id']),
|
||||
someFloat: doubleType
|
||||
.mapFromDatabaseResponse(data['${effectivePrefix}some_float']),
|
||||
custom: myCustomObjectType
|
||||
.mapFromDatabaseResponse(data['${effectivePrefix}custom']),
|
||||
);
|
||||
}
|
||||
factory TableWithoutPKData.fromJson(Map<String, dynamic> json,
|
||||
|
@ -861,6 +868,7 @@ class TableWithoutPKData extends DataClass
|
|||
return TableWithoutPKData(
|
||||
notReallyAnId: serializer.fromJson<int>(json['notReallyAnId']),
|
||||
someFloat: serializer.fromJson<double>(json['someFloat']),
|
||||
custom: serializer.fromJson<MyCustomObject>(json['custom']),
|
||||
);
|
||||
}
|
||||
@override
|
||||
|
@ -869,6 +877,7 @@ class TableWithoutPKData extends DataClass
|
|||
return {
|
||||
'notReallyAnId': serializer.toJson<int>(notReallyAnId),
|
||||
'someFloat': serializer.toJson<double>(someFloat),
|
||||
'custom': serializer.toJson<MyCustomObject>(custom),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -882,40 +891,49 @@ class TableWithoutPKData extends DataClass
|
|||
someFloat: someFloat == null && nullToAbsent
|
||||
? const Value.absent()
|
||||
: Value(someFloat),
|
||||
custom:
|
||||
custom == null && nullToAbsent ? const Value.absent() : Value(custom),
|
||||
) as T;
|
||||
}
|
||||
|
||||
TableWithoutPKData copyWith({int notReallyAnId, double someFloat}) =>
|
||||
TableWithoutPKData copyWith(
|
||||
{int notReallyAnId, double someFloat, MyCustomObject custom}) =>
|
||||
TableWithoutPKData(
|
||||
notReallyAnId: notReallyAnId ?? this.notReallyAnId,
|
||||
someFloat: someFloat ?? this.someFloat,
|
||||
custom: custom ?? this.custom,
|
||||
);
|
||||
@override
|
||||
String toString() {
|
||||
return (StringBuffer('TableWithoutPKData(')
|
||||
..write('notReallyAnId: $notReallyAnId, ')
|
||||
..write('someFloat: $someFloat')
|
||||
..write('someFloat: $someFloat, ')
|
||||
..write('custom: $custom')
|
||||
..write(')'))
|
||||
.toString();
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
$mrjf($mrjc($mrjc(0, notReallyAnId.hashCode), someFloat.hashCode));
|
||||
int get hashCode => $mrjf($mrjc(
|
||||
$mrjc($mrjc(0, notReallyAnId.hashCode), someFloat.hashCode),
|
||||
custom.hashCode));
|
||||
@override
|
||||
bool operator ==(other) =>
|
||||
identical(this, other) ||
|
||||
(other is TableWithoutPKData &&
|
||||
other.notReallyAnId == notReallyAnId &&
|
||||
other.someFloat == someFloat);
|
||||
other.someFloat == someFloat &&
|
||||
other.custom == custom);
|
||||
}
|
||||
|
||||
class TableWithoutPKCompanion extends UpdateCompanion<TableWithoutPKData> {
|
||||
final Value<int> notReallyAnId;
|
||||
final Value<double> someFloat;
|
||||
final Value<MyCustomObject> custom;
|
||||
const TableWithoutPKCompanion({
|
||||
this.notReallyAnId = const Value.absent(),
|
||||
this.someFloat = const Value.absent(),
|
||||
this.custom = const Value.absent(),
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -950,8 +968,20 @@ class $TableWithoutPKTable extends TableWithoutPK
|
|||
);
|
||||
}
|
||||
|
||||
final VerificationMeta _customMeta = const VerificationMeta('custom');
|
||||
GeneratedTextColumn _custom;
|
||||
@override
|
||||
List<GeneratedColumn> get $columns => [notReallyAnId, someFloat];
|
||||
GeneratedTextColumn get custom => _custom ??= _constructCustom();
|
||||
GeneratedTextColumn _constructCustom() {
|
||||
return GeneratedTextColumn(
|
||||
'custom',
|
||||
$tableName,
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
List<GeneratedColumn> get $columns => [notReallyAnId, someFloat, custom];
|
||||
@override
|
||||
$TableWithoutPKTable get asDslTable => this;
|
||||
@override
|
||||
|
@ -976,6 +1006,12 @@ class $TableWithoutPKTable extends TableWithoutPK
|
|||
} else if (someFloat.isRequired && isInserting) {
|
||||
context.missing(_someFloatMeta);
|
||||
}
|
||||
if (d.custom.present) {
|
||||
context.handle(
|
||||
_customMeta, custom.isAcceptableValue(d.custom.value, _customMeta));
|
||||
} else if (custom.isRequired && isInserting) {
|
||||
context.missing(_customMeta);
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
|
@ -996,6 +1032,9 @@ class $TableWithoutPKTable extends TableWithoutPK
|
|||
if (d.someFloat.present) {
|
||||
map['some_float'] = Variable<double, RealType>(d.someFloat.value);
|
||||
}
|
||||
if (d.custom.present) {
|
||||
map['custom'] = Variable<MyCustomObject, StringType>(d.custom.value);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
|
|
|
@ -26,9 +26,8 @@ void main() {
|
|||
|
||||
test('can insert floating point values', () async {
|
||||
// regression test for https://github.com/simolus3/moor/issues/30
|
||||
await db
|
||||
.into(db.tableWithoutPK)
|
||||
.insert(TableWithoutPKData(notReallyAnId: 42, someFloat: 3.1415));
|
||||
await db.into(db.tableWithoutPK).insert(
|
||||
TableWithoutPKData(notReallyAnId: 42, someFloat: 3.1415, custom: null));
|
||||
|
||||
verify(executor.runInsert(
|
||||
'INSERT INTO table_without_p_k '
|
||||
|
|
|
@ -16,11 +16,12 @@ class DaoGenerator extends GeneratorForAnnotation<UseDao> {
|
|||
|
||||
@override
|
||||
generateForAnnotatedElement(
|
||||
Element element, ConstantReader annotation, BuildStep buildStep) {
|
||||
Element element, ConstantReader annotation, BuildStep buildStep) async {
|
||||
final tableTypes =
|
||||
annotation.peek('tables').listValue.map((obj) => obj.toTypeValue());
|
||||
final parsedTables =
|
||||
tableTypes.map((type) => state.parseType(type, element)).toList();
|
||||
final parsedTables = await Stream.fromIterable(tableTypes)
|
||||
.asyncMap((type) => state.parseType(type, element))
|
||||
.toList();
|
||||
final queries = annotation.peek('queries')?.mapValue ?? {};
|
||||
|
||||
if (element is! ClassElement) {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import 'package:analyzer/dart/ast/ast.dart';
|
||||
import 'package:analyzer/dart/element/type.dart';
|
||||
import 'package:built_value/built_value.dart';
|
||||
|
||||
part 'specified_column.g.dart';
|
||||
|
@ -99,9 +100,27 @@ class SpecifiedColumn {
|
|||
/// expression.
|
||||
final Expression defaultArgument;
|
||||
|
||||
/// If a type converter has been specified as the argument of
|
||||
/// ColumnBuilder.map, this contains the Dart code that references that type
|
||||
/// converter.
|
||||
final Expression typeConverter;
|
||||
|
||||
/// If the type of this column has been overridden, contains the actual Dart
|
||||
/// type. Otherwise null.
|
||||
///
|
||||
/// Column types can be overridden with type converters. For instance, if
|
||||
/// `C` was a type converter that converts `D` to `num`s, the column generated
|
||||
/// by `real().map(const C())()` would have type `D` instead of `num`.
|
||||
final DartType overriddenDartType;
|
||||
|
||||
/// The dart type that matches the values of this column. For instance, if a
|
||||
/// table has declared an `IntColumn`, the matching dart type name would be [int].
|
||||
String get dartTypeName => dartTypeNames[type];
|
||||
String get dartTypeName {
|
||||
if (overriddenDartType != null) {
|
||||
return overriddenDartType.name;
|
||||
}
|
||||
return dartTypeNames[type];
|
||||
}
|
||||
|
||||
/// The column type from the dsl library. For instance, if a table has
|
||||
/// declared an `IntColumn`, the matching dsl column name would also be an
|
||||
|
@ -148,6 +167,8 @@ class SpecifiedColumn {
|
|||
this.nullable = false,
|
||||
this.features = const [],
|
||||
this.defaultArgument,
|
||||
this.typeConverter,
|
||||
this.overriddenDartType,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ class MoorGenerator extends GeneratorForAnnotation<UseMoor> {
|
|||
|
||||
@override
|
||||
generateForAnnotatedElement(
|
||||
Element element, ConstantReader annotation, BuildStep buildStep) {
|
||||
Element element, ConstantReader annotation, BuildStep buildStep) async {
|
||||
final tableTypes =
|
||||
annotation.peek('tables').listValue.map((obj) => obj.toTypeValue());
|
||||
final daoTypes = annotation
|
||||
|
@ -33,7 +33,7 @@ class MoorGenerator extends GeneratorForAnnotation<UseMoor> {
|
|||
var resolvedQueries = <SqlQuery>[];
|
||||
|
||||
for (var table in tableTypes) {
|
||||
tablesForThisDb.add(state.parseType(table, element));
|
||||
tablesForThisDb.add(await state.parseType(table, element));
|
||||
}
|
||||
|
||||
if (queries.isNotEmpty) {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import 'package:analyzer/dart/ast/ast.dart';
|
||||
import 'package:analyzer/dart/element/element.dart';
|
||||
import 'package:analyzer/dart/element/type.dart';
|
||||
import 'package:moor_generator/src/errors.dart';
|
||||
import 'package:moor_generator/src/model/specified_column.dart';
|
||||
import 'package:moor_generator/src/parser/parser.dart';
|
||||
|
@ -20,7 +21,7 @@ final Set<String> starters = {
|
|||
startBool,
|
||||
startDateTime,
|
||||
startBlob,
|
||||
startReal
|
||||
startReal,
|
||||
};
|
||||
|
||||
const String _methodNamed = 'named';
|
||||
|
@ -31,6 +32,7 @@ const String _methodWithLength = 'withLength';
|
|||
const String _methodNullable = 'nullable';
|
||||
const String _methodCustomConstraint = 'customConstraint';
|
||||
const String _methodDefault = 'withDefault';
|
||||
const String _methodMap = 'map';
|
||||
|
||||
const String _errorMessage = 'This getter does not create a valid column that '
|
||||
'can be parsed by moor. Please refer to the readme from moor to see how '
|
||||
|
@ -49,6 +51,7 @@ class ColumnParser extends ParserBase {
|
|||
we can extract what it means for the column (name, auto increment, PK,
|
||||
constraints...).
|
||||
*/
|
||||
|
||||
final expr = returnExpressionOfMethod(getter);
|
||||
|
||||
if (!(expr is FunctionExpressionInvocation)) {
|
||||
|
@ -68,6 +71,8 @@ class ColumnParser extends ParserBase {
|
|||
String foundExplicitName;
|
||||
String foundCustomConstraint;
|
||||
Expression foundDefaultExpression;
|
||||
Expression foundTypeConverter;
|
||||
DartType overrideDartType;
|
||||
var wasDeclaredAsPrimaryKey = false;
|
||||
var nullable = false;
|
||||
|
||||
|
@ -148,6 +153,18 @@ class ColumnParser extends ParserBase {
|
|||
final args = remainingExpr.argumentList;
|
||||
final expression = args.arguments.single;
|
||||
foundDefaultExpression = expression;
|
||||
break;
|
||||
case _methodMap:
|
||||
final args = remainingExpr.argumentList;
|
||||
final expression = args.arguments.single;
|
||||
|
||||
// the map method has a parameter type that resolved to the runtime
|
||||
// type of the custom object
|
||||
final type = remainingExpr.typeArgumentTypes.single;
|
||||
|
||||
foundTypeConverter = expression;
|
||||
overrideDartType = type;
|
||||
break;
|
||||
}
|
||||
|
||||
// We're not at a starting method yet, so we need to go deeper!
|
||||
|
@ -172,6 +189,8 @@ class ColumnParser extends ParserBase {
|
|||
nullable: nullable,
|
||||
features: foundFeatures,
|
||||
defaultArgument: foundDefaultExpression,
|
||||
typeConverter: foundTypeConverter,
|
||||
overriddenDartType: overrideDartType,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -13,18 +13,18 @@ import 'package:moor/sqlite_keywords.dart';
|
|||
class TableParser extends ParserBase {
|
||||
TableParser(SharedState state) : super(state);
|
||||
|
||||
SpecifiedTable parse(ClassElement element) {
|
||||
final sqlName = _parseTableName(element);
|
||||
Future<SpecifiedTable> parse(ClassElement element) async {
|
||||
final sqlName = await _parseTableName(element);
|
||||
if (sqlName == null) return null;
|
||||
|
||||
final columns = _parseColumns(element);
|
||||
final columns = await _parseColumns(element);
|
||||
|
||||
return SpecifiedTable(
|
||||
fromClass: element,
|
||||
columns: columns,
|
||||
sqlName: escapeIfNeeded(sqlName),
|
||||
dartTypeName: _readDartTypeName(element),
|
||||
primaryKey: _readPrimaryKey(element, columns),
|
||||
primaryKey: await _readPrimaryKey(element, columns),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -40,7 +40,7 @@ class TableParser extends ParserBase {
|
|||
}
|
||||
}
|
||||
|
||||
String _parseTableName(ClassElement element) {
|
||||
Future<String> _parseTableName(ClassElement element) async {
|
||||
// todo allow override via a field (final String tableName = '') as well
|
||||
|
||||
final tableNameGetter = element.getGetter('tableName');
|
||||
|
@ -52,7 +52,8 @@ class TableParser extends ParserBase {
|
|||
|
||||
// we expect something like get tableName => "myTableName", the getter
|
||||
// must do nothing more complicated
|
||||
final tableNameDeclaration = state.loadElementDeclaration(tableNameGetter);
|
||||
final tableNameDeclaration =
|
||||
await state.loadElementDeclaration(tableNameGetter);
|
||||
final returnExpr = returnExpressionOfMethod(
|
||||
tableNameDeclaration.node as MethodDeclaration);
|
||||
|
||||
|
@ -67,15 +68,15 @@ class TableParser extends ParserBase {
|
|||
return tableName;
|
||||
}
|
||||
|
||||
Set<SpecifiedColumn> _readPrimaryKey(
|
||||
ClassElement element, List<SpecifiedColumn> columns) {
|
||||
Future<Set<SpecifiedColumn>> _readPrimaryKey(
|
||||
ClassElement element, List<SpecifiedColumn> columns) async {
|
||||
final primaryKeyGetter = element.getGetter('primaryKey');
|
||||
if (primaryKeyGetter == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final ast = state.loadElementDeclaration(primaryKeyGetter).node
|
||||
as MethodDeclaration;
|
||||
final resolved = await state.loadElementDeclaration(primaryKeyGetter);
|
||||
final ast = resolved.node as MethodDeclaration;
|
||||
final body = ast.body;
|
||||
if (body is! ExpressionFunctionBody) {
|
||||
state.errors.add(MoorError(
|
||||
|
@ -110,12 +111,12 @@ class TableParser extends ParserBase {
|
|||
return parsedPrimaryKey;
|
||||
}
|
||||
|
||||
List<SpecifiedColumn> _parseColumns(ClassElement element) {
|
||||
return element.fields
|
||||
Future<List<SpecifiedColumn>> _parseColumns(ClassElement element) {
|
||||
return Stream.fromIterable(element.fields)
|
||||
.where((field) => isColumn(field.type) && field.getter != null)
|
||||
.map((field) {
|
||||
final node =
|
||||
state.loadElementDeclaration(field.getter).node as MethodDeclaration;
|
||||
.asyncMap((field) async {
|
||||
final resolved = await state.loadElementDeclaration(field.getter);
|
||||
final node = resolved.node as MethodDeclaration;
|
||||
|
||||
return state.columnParser.parse(node, field.getter);
|
||||
}).toList();
|
||||
|
|
|
@ -21,21 +21,23 @@ class SharedState {
|
|||
|
||||
final tableTypeChecker = const TypeChecker.fromRuntime(Table);
|
||||
|
||||
final Map<DartType, SpecifiedTable> foundTables = {};
|
||||
final Map<DartType, Future<SpecifiedTable>> _foundTables = {};
|
||||
|
||||
SharedState(this.options) {
|
||||
tableParser = TableParser(this);
|
||||
columnParser = ColumnParser(this);
|
||||
}
|
||||
|
||||
ElementDeclarationResult loadElementDeclaration(Element element) {
|
||||
final result =
|
||||
element.library.session.getParsedLibraryByElement(element.library);
|
||||
return result.getElementDeclaration(element);
|
||||
Future<ElementDeclarationResult> loadElementDeclaration(
|
||||
Element element) async {
|
||||
final resolvedLibrary = await element.library.session
|
||||
.getResolvedLibraryByElement(element.library);
|
||||
|
||||
return resolvedLibrary.getElementDeclaration(element);
|
||||
}
|
||||
|
||||
SpecifiedTable parseType(DartType type, Element initializedBy) {
|
||||
return foundTables.putIfAbsent(type, () {
|
||||
Future<SpecifiedTable> parseType(DartType type, Element initializedBy) {
|
||||
return _foundTables.putIfAbsent(type, () {
|
||||
if (!tableTypeChecker.isAssignableFrom(type.element)) {
|
||||
errors.add(MoorError(
|
||||
critical: true,
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import 'package:analyzer/dart/element/element.dart';
|
||||
import 'package:moor_generator/src/model/specified_column.dart';
|
||||
import 'package:moor_generator/src/model/specified_table.dart';
|
||||
import 'package:moor_generator/src/options.dart';
|
||||
import 'package:moor_generator/src/parser/column_parser.dart';
|
||||
import 'package:moor_generator/src/parser/table_parser.dart';
|
||||
|
@ -56,46 +57,47 @@ void main() async {
|
|||
..tableParser = TableParser(state);
|
||||
});
|
||||
|
||||
Future<SpecifiedTable> parse(String name) {
|
||||
return TableParser(state).parse(testLib.getType(name));
|
||||
}
|
||||
|
||||
group('SQL table name', () {
|
||||
test('should parse correctly when valid', () {
|
||||
expect(
|
||||
TableParser(state)
|
||||
.parse(testLib.getType('TableWithCustomName'))
|
||||
.sqlName,
|
||||
equals('my-fancy-table'));
|
||||
test('should parse correctly when valid', () async {
|
||||
final parsed = await parse('TableWithCustomName');
|
||||
expect(parsed.sqlName, equals('my-fancy-table'));
|
||||
});
|
||||
|
||||
test('should use class name if table name is not specified', () {
|
||||
expect(TableParser(state).parse(testLib.getType('Users')).sqlName,
|
||||
equals('users'));
|
||||
test('should use class name if table name is not specified', () async {
|
||||
final parsed = await parse('Users');
|
||||
expect(parsed.sqlName, equals('users'));
|
||||
});
|
||||
|
||||
test('should not parse for complex methods', () async {
|
||||
TableParser(state).parse(testLib.getType('WrongName'));
|
||||
await TableParser(state).parse(testLib.getType('WrongName'));
|
||||
|
||||
expect(state.errors.errors, isNotEmpty);
|
||||
});
|
||||
});
|
||||
|
||||
group('Columns', () {
|
||||
test('should use field name if no name has been set explicitely', () {
|
||||
final table = TableParser(state).parse(testLib.getType('Users'));
|
||||
test('should use field name if no name has been set explicitely', () async {
|
||||
final table = await parse('Users');
|
||||
final idColumn =
|
||||
table.columns.singleWhere((col) => col.name.name == 'id');
|
||||
|
||||
expect(idColumn.name, equals(ColumnName.implicitly('id')));
|
||||
});
|
||||
|
||||
test('should use explicit name, if it exists', () {
|
||||
final table = TableParser(state).parse(testLib.getType('Users'));
|
||||
test('should use explicit name, if it exists', () async {
|
||||
final table = await parse('Users');
|
||||
final idColumn =
|
||||
table.columns.singleWhere((col) => col.name.name == 'user_name');
|
||||
|
||||
expect(idColumn.name, equals(ColumnName.explicitly('user_name')));
|
||||
});
|
||||
|
||||
test('should parse min and max length for text columns', () {
|
||||
final table = TableParser(state).parse(testLib.getType('Users'));
|
||||
test('should parse min and max length for text columns', () async {
|
||||
final table = await parse('Users');
|
||||
final idColumn =
|
||||
table.columns.singleWhere((col) => col.name.name == 'user_name');
|
||||
|
||||
|
@ -103,8 +105,8 @@ void main() async {
|
|||
contains(LimitingTextLength.withLength(min: 6, max: 32)));
|
||||
});
|
||||
|
||||
test('should only parse max length when relevant', () {
|
||||
final table = TableParser(state).parse(testLib.getType('Users'));
|
||||
test('should only parse max length when relevant', () async {
|
||||
final table = await parse('Users');
|
||||
final idColumn =
|
||||
table.columns.singleWhere((col) => col.dartGetterName == 'onlyMax');
|
||||
|
||||
|
@ -112,9 +114,8 @@ void main() async {
|
|||
idColumn.features, contains(LimitingTextLength.withLength(max: 100)));
|
||||
});
|
||||
|
||||
test('parses custom constraints', () {
|
||||
final table =
|
||||
TableParser(state).parse(testLib.getType('CustomPrimaryKey'));
|
||||
test('parses custom constraints', () async {
|
||||
final table = await parse('CustomPrimaryKey');
|
||||
|
||||
final partA =
|
||||
table.columns.singleWhere((c) => c.dartGetterName == 'partA');
|
||||
|
@ -125,8 +126,8 @@ void main() async {
|
|||
expect(partA.customConstraints, isNull);
|
||||
});
|
||||
|
||||
test('parsed default values', () {
|
||||
final table = TableParser(state).parse(testLib.getType('Users'));
|
||||
test('parsed default values', () async {
|
||||
final table = await parse('Users');
|
||||
final defaultsColumn =
|
||||
table.columns.singleWhere((c) => c.name.name == 'defaults');
|
||||
|
||||
|
@ -134,8 +135,8 @@ void main() async {
|
|||
});
|
||||
});
|
||||
|
||||
test('parses custom primary keys', () {
|
||||
final table = TableParser(state).parse(testLib.getType('CustomPrimaryKey'));
|
||||
test('parses custom primary keys', () async {
|
||||
final table = await parse('CustomPrimaryKey');
|
||||
|
||||
expect(table.primaryKey, containsAll(table.columns));
|
||||
expect(table.columns.any((column) => column.hasAI), isFalse);
|
||||
|
|
Loading…
Reference in New Issue