To make it possible to read and write ObjectBox entity instances as easily as possible, wrapper code needs to be
generated. Such code is only generated for Dart classes which have been annotated to indicate that they represent
an ObjectBox entity (i.e. using @Entity
). The code is generated by executing pub run build_runner build
.
See docs:
- https://github.com/dart-lang/build/blob/master/docs/writing_a_builder.md
- https://github.com/dart-lang/build/blob/master/docs/writing_an_aggregate_builder.md
- https://pub.dev/packages/build_config#configuring-builders-applied-to-your-package
In order to set up code generation, a new package needs to be created exclusively for this task.
Here, it it called objectbox_generator
. This package needs to contain a build.yaml
as well as an entry
point for the builder, objectbox_generator.dart
which defines code-generator factories
for two generators used in build.yaml.
After all entities have been found in the project, they're compared to an existing model definition in
an objectbox-model.json and merged. Also note that in this step, IDs and UIDs are generated automatically for new
instances. UIDs are always random, IDs are assigned in ascending order. UIDs may never be reused, e.g. after a property
has been removed, and previously used UIDs of remove instances (entities, properties, indexes, ...) are stored in
the JSON file.
For accomplishing actually automated testing capabilities for objectbox_generator
, various wrapper classes are needed, as the build
package is only designed to generate output files; yet, during testing, it is necessary to dump generated code to string variables, so they can be compared easily by Dart's test
framework.
The entry function for generator testing is the main function of generator_test.dart
. It makes sure that any existing file called objectbox-model.json
is removed before every test, because we want a fresh start each time.
The build
package internally uses designated classes for reading from and writing to files or, to be more general, any kind of assets. In this case, we do not want to involve any kind of files as output and only very specific files as input, so it is necessary to create our own versions of the so-called AssetReader
and AssetWriter
.
In helpers.dart
, _InMemoryAssetWriter
is supposed to receive a single output string and store it in memory. Eventually, the string it stores will be the output of EntityGenerator
.
On the other hand, _SingleFileAssetReader
shall read a single input Dart source file from the test/cases
directory. Note that currently, test cases have the rather ugly file extension .dart_testcase
, such as single_entity.dart_testcase
. This is a workaround, because otherwise, running pub run build_runner build
in the repository's root directory would generate .g.dart
files from all .dart
files in the repository. An option to exclude certain directories from build_runner
is yet to be found.
Eventually, the function runBuilder
can be executed, which is part of the build
package. It encapsulates everything related to generating the final output. Thus, after it is finished and in case generation was successful, the _InMemoryAssetWriter
instance contains the generated code, which can then be compared against the expected code.