Skip to content

Commit

Permalink
- Add support for nested embedded objects.
Browse files Browse the repository at this point in the history
- Add boolean extension for parsing nullable fields.
- Add Fieldable abstract class for both (Field, Embedded).
- Passing null in constructor when field is ignore.
- Fix nullable boolean field test case.
- Some improvement and code enhancement.
  • Loading branch information
hsul4n committed May 28, 2020
1 parent 9848bbd commit 446187b
Show file tree
Hide file tree
Showing 21 changed files with 338 additions and 189 deletions.
20 changes: 2 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -211,13 +211,6 @@ With the annotation, it's possible to give columns a custom name and define if t
- Floor automatically uses the **first** constructor defined in the entity class for creating in-memory objects from database rows.
- There needs to be a constructor.

`@Embedded` includes annotationed class fields to be embedded to SQL query as if they are fields but from another class.
You can specifies a `prefix` to prepend the column names of the fields in the embedded fields.

#### Limitations
- Floor automatically uses the **first** constructor defined in the entity class for creating in-memory objects from database rows.
- There needs to be a constructor.

```dart
@Entity(tableName: 'person')
class Person {
Expand All @@ -227,16 +220,7 @@ class Person {
@ColumnInfo(name: 'custom_name', nullable: false)
final String name;
@Embedded(prefix: 'custom_prefix')
final Address address;
Person(this.id, this.name, this.address);
}
class Address {
final String street;
Address(this.street);
Person(this.id, this.name);
}
```

Expand Down Expand Up @@ -679,4 +663,4 @@ For general communication use [floor's Slack](https://join.slack.com/t/floor-flu
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
limitations under the License.
18 changes: 11 additions & 7 deletions example/lib/database.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 9 additions & 1 deletion example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,15 @@ class TasksTextField extends StatelessWidget {
if (message.trim().isEmpty) {
_textEditingController.clear();
} else {
final task = Task(null, message, Timestamp.now());
final task = Task(
null,
message,
Timestamp(
createdAt: DateTime.now().toString(),
updatedAt: DateTime.now().toString(),
),
);

await dao.insertTask(task);
_textEditingController.clear();
}
Expand Down
4 changes: 2 additions & 2 deletions example/lib/task.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@ class Task {
message == other.message;

@override
int get hashCode => id.hashCode ^ message.hashCode;
int get hashCode => id.hashCode ^ message.hashCode ^ timestamp.hashCode;

@override
String toString() {
return 'Task{id: $id, message: $message}';
return 'Task{id: $id, message: $message, timestamp: $timestamp}';
}
}
22 changes: 17 additions & 5 deletions example/lib/timestamp.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,26 @@ import 'package:floor/floor.dart';

class Timestamp {
@ColumnInfo(name: 'created_at')
String createdAt;
final String createdAt;

@ColumnInfo(name: 'updated_at')
String updatedAt;
final String updatedAt;

Timestamp({this.createdAt, this.updatedAt});

Timestamp.now()
: createdAt = DateTime.now().toString(),
updatedAt = DateTime.now().toString();
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is Timestamp &&
runtimeType == other.runtimeType &&
createdAt == other.createdAt &&
updatedAt == other.updatedAt;

@override
int get hashCode => createdAt.hashCode ^ updatedAt.hashCode;

@override
String toString() {
return 'Task{createdAt: $createdAt, updatedAt: $updatedAt}';
}
}
14 changes: 1 addition & 13 deletions floor/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -207,9 +207,6 @@ For more information about primary keys and especially compound primary keys, re
`@ColumnInfo` enables custom mapping of single table columns.
With the annotation, it's possible to give columns a custom name and define if the column is able to store `null`.

`@Embedded` includes annotationed class fields to be embedded to SQL query as if they are fields but from another class.
You can specifies a `prefix` to prepend the column names of the fields in the embedded fields.

#### Limitations
- Floor automatically uses the **first** constructor defined in the entity class for creating in-memory objects from database rows.
- There needs to be a constructor.
Expand All @@ -223,16 +220,7 @@ class Person {
@ColumnInfo(name: 'custom_name', nullable: false)
final String name;
@Embedded(prefix: 'custom_prefix')
final Address address;
Person(this.id, this.name, this.address);
}
class Address {
final String street;
Address(this.street);
Person(this.id, this.name);
}
```

Expand Down
14 changes: 14 additions & 0 deletions floor_generator/lib/extension/field_element_extension.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import 'package:analyzer/dart/element/element.dart';
import 'package:floor_annotation/floor_annotation.dart' as annotations;
import 'package:floor_generator/misc/type_utils.dart';

extension FieldElementExtension on FieldElement {
bool shouldBeIncluded() {
final isIgnored = hasAnnotation(annotations.ignore.runtimeType);
return !(isStatic || isSynthetic || isIgnored || isEmbedded);
}

bool get isEmbedded {
return hasAnnotation(annotations.Embedded) && type.element is ClassElement;
}
}
4 changes: 3 additions & 1 deletion floor_generator/lib/generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import 'package:floor_generator/value_object/database.dart';
import 'package:floor_generator/writer/dao_writer.dart';
import 'package:floor_generator/writer/database_builder_writer.dart';
import 'package:floor_generator/writer/database_writer.dart';
import 'package:floor_generator/writer/extension_writer.dart';
import 'package:floor_generator/writer/floor_writer.dart';
import 'package:source_gen/source_gen.dart';

Expand All @@ -34,7 +35,8 @@ class FloorGenerator extends GeneratorForAnnotation<annotations.Database> {
..body.add(FloorWriter(database.name).write())
..body.add(DatabaseBuilderWriter(database.name).write())
..body.add(databaseClass)
..body.addAll(daoClasses));
..body.addAll(daoClasses)
..body.add(ExtensionWriter().write()));

return library.accept(DartEmitter()).toString();
}
Expand Down
33 changes: 25 additions & 8 deletions floor_generator/lib/processor/embedded_processor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,39 +8,56 @@ import 'package:floor_generator/processor/field_processor.dart';
import 'package:floor_generator/processor/processor.dart';
import 'package:floor_generator/value_object/embedded.dart';
import 'package:floor_generator/value_object/field.dart';
import 'package:floor_generator/extension/field_element_extension.dart';

class EmbeddedProcessor extends Processor<Embedded> {
final ClassElement _classElement;
final FieldElement _fieldElement;
final String _prefix;

EmbeddedProcessor(final FieldElement fieldElement)
EmbeddedProcessor(final FieldElement fieldElement, [final String prefix = ''])
: assert(fieldElement != null),
assert(prefix != null),
_fieldElement = fieldElement,
_classElement = fieldElement.type.element as ClassElement;
_classElement = fieldElement.type.element as ClassElement,
_prefix = prefix;

@nonNull
@override
Embedded process() {
return Embedded(
_fieldElement,
_getFields(),
_getChildren(),
);
}

@nonNull
String _getPrefix() {
return _fieldElement
.getAnnotation(annotations.Embedded)
.getField(AnnotationField.embeddedPrefix)
?.toStringValue() ??
return _prefix +
_fieldElement
.getAnnotation(annotations.Embedded)
.getField(AnnotationField.embeddedPrefix)
?.toStringValue() ??
'';
}

@nonNull
List<Field> _getFields() {
final fields = _classElement.fields
.where((fieldElement) => fieldElement.shouldBeIncluded())
.map((field) => FieldProcessor(field, _getPrefix()).process())
.toList();

return fields;
}

@nonNull
List<Embedded> _getChildren() {
return _classElement.fields
.map((fieldElement) =>
FieldProcessor(fieldElement, _getPrefix()).process())
.where((fieldElement) => fieldElement.isEmbedded)
// pass the previous prefix so we can prepend it with the old ones
.map((embedded) => EmbeddedProcessor(embedded, _getPrefix()).process())
.toList();
}
}
6 changes: 3 additions & 3 deletions floor_generator/lib/processor/entity_processor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,17 @@ class EntityProcessor extends QueryableProcessor<Entity> {
Entity process() {
final name = _getName();
final fields = getFields();
final embedded = getEmbeddeds();
final embeddeds = getEmbeddeds();

return Entity(
classElement,
name,
fields,
embedded,
embeddeds,
_getPrimaryKey(fields),
_getForeignKeys(),
_getIndices(fields, name),
getConstructor(fields, embedded),
getConstructor([...fields, ...embeddeds]),
);
}

Expand Down
Loading

0 comments on commit 446187b

Please sign in to comment.