Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle named constructor parameters and ignore field order #245

Merged
merged 1 commit into from
Jan 25, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 27 additions & 17 deletions floor_generator/lib/processor/entity_processor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -222,28 +222,40 @@ class EntityProcessor extends Processor<Entity> {

@nonNull
String _getConstructor(final List<Field> fields) {
final columnNames = fields.map((field) => field.columnName).toList();
final constructorParameters = _classElement.constructors.first.parameters;
final parameterValues = constructorParameters
.map((parameterElement) => _getParameterValue(parameterElement, fields))
.where((parameterValue) => parameterValue != null)
.join(', ');

final parameterValues = <String>[];
return '${_classElement.displayName}($parameterValues)';
}

for (var i = 0; i < constructorParameters.length; i++) {
final parameterValue = "row['${columnNames[i]}']";
final constructorParameter = constructorParameters[i];
/// Returns `null` whenever field is @ignored
@nullable
String _getParameterValue(
final ParameterElement parameterElement,
final List<Field> fields,
) {
final parameterName = parameterElement.displayName;
final field = fields.firstWhere(
(field) => field.name == parameterName,
orElse: () => null, // whenever field is @ignored
);
if (field != null) {
final parameterValue = "row['${field.columnName}']";
final castedParameterValue =
_castParameterValue(constructorParameter.type, parameterValue);

if (castedParameterValue == null) {
throw _processorError.parameterTypeNotSupported(constructorParameter);
_castParameterValue(parameterElement.type, parameterValue);
if (parameterElement.isNamed) {
return '$parameterName: $castedParameterValue';
}

parameterValues.add(castedParameterValue);
return castedParameterValue; // also covers positional parameter
} else {
return null;
}

return '${_classElement.displayName}(${parameterValues.join(', ')})';
}

@nullable
@nonNull
String _castParameterValue(
final DartType parameterType,
final String parameterValue,
Expand All @@ -254,10 +266,8 @@ class EntityProcessor extends Processor<Entity> {
return '$parameterValue as String';
} else if (parameterType.isDartCoreInt) {
return '$parameterValue as int';
} else if (parameterType.isDartCoreDouble) {
return '$parameterValue as double';
} else {
return null;
return '$parameterValue as double'; // must be double
}
}
}
Expand Down
25 changes: 9 additions & 16 deletions floor_generator/lib/processor/error/entity_processor_error.dart
Original file line number Diff line number Diff line change
Expand Up @@ -40,16 +40,19 @@ class EntityProcessorError {
// ignore: non_constant_identifier_names
InvalidGenerationSourceError get FOREIGN_KEY_DOES_NOT_REFERENCE_ENTITY {
return InvalidGenerationSourceError(
"The foreign key doesn't reference an entity class.",
todo: 'Make sure to add an entity to the foreign key. ',
element: _classElement);
"The foreign key doesn't reference an entity class.",
todo: 'Make sure to add an entity to the foreign key. ',
element: _classElement,
);
}

// ignore: non_constant_identifier_names
InvalidGenerationSourceError get FOREIGN_KEY_NO_ENTITY {
return InvalidGenerationSourceError('No entity defined for foreign key',
todo: 'Make sure to add an entity to the foreign key. ',
element: _classElement);
return InvalidGenerationSourceError(
'No entity defined for foreign key',
todo: 'Make sure to add an entity to the foreign key. ',
element: _classElement,
);
}

// ignore: non_constant_identifier_names
Expand All @@ -62,16 +65,6 @@ class EntityProcessorError {
);
}

InvalidGenerationSourceError parameterTypeNotSupported(
final ParameterElement parameterElement,
) {
return InvalidGenerationSourceError(
'The given constrcutor parameter type is not supported.',
todo: 'Make sure to only use bool, String, int and double types.',
element: parameterElement,
);
}

InvalidGenerationSourceError noMatchingColumn(
final List<String> columnNames,
) {
Expand Down
109 changes: 109 additions & 0 deletions floor_generator/test/processor/entity_processor_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,115 @@ void main() {
expect(actual.fields.length, equals(2));
});

group('Constructors', () {
test('generate simple constructor', () async {
final classElement = await _createClassElement('''
@entity
class Person {
@primaryKey
final int id;

final String name;

Person(this.id, this.name);
}
''');

final actual = EntityProcessor(classElement).process().constructor;

const expected = "Person(row['id'] as int, row['name'] as String)";
expect(actual, equals(expected));
});

test('generate constructor with named argument', () async {
final classElement = await _createClassElement('''
@entity
class Person {
@primaryKey
final int id;

final String name;

final String bar;

Person(this.id, this.name, {this.bar});
}
''');

final actual = EntityProcessor(classElement).process().constructor;

const expected =
"Person(row['id'] as int, row['name'] as String, bar: row['bar'] as String)";
expect(actual, equals(expected));
});

test('generate constructor with named arguments', () async {
final classElement = await _createClassElement('''
@entity
class Person {
@primaryKey
final int id;

final String name;

final String bar;

Person({this.id, this.name, this.bar});
}
''');

final actual = EntityProcessor(classElement).process().constructor;

const expected =
"Person(id: row['id'] as int, name: row['name'] as String, bar: row['bar'] as String)";
expect(actual, equals(expected));
});

test('generate constructor with optional argument', () async {
final classElement = await _createClassElement('''
@entity
class Person {
@primaryKey
final int id;

final String name;

final String bar;

Person(this.id, this.name, [this.bar]);
}
''');

final actual = EntityProcessor(classElement).process().constructor;

const expected =
"Person(row['id'] as int, row['name'] as String, row['bar'] as String)";
expect(actual, equals(expected));
});

test('generate constructor with optional arguments', () async {
final classElement = await _createClassElement('''
@entity
class Person {
@primaryKey
final int id;

final String name;

final String bar;

Person([this.id, this.name, this.bar]);
}
''');

final actual = EntityProcessor(classElement).process().constructor;

const expected =
"Person(row['id'] as int, row['name'] as String, row['bar'] as String)";
expect(actual, equals(expected));
});
});

group('foreign keys', () {
test('foreign key holds correct values', () async {
final classElements = await _createClassElements('''
Expand Down