-
Notifications
You must be signed in to change notification settings - Fork 135
0.2 Release #16
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
0.2 Release #16
Changes from all commits
0424db5
6233dab
147d9fc
a234548
f75e23e
e29a49e
f2a16d1
5f54b18
864749d
b558ff3
6097bd6
b4b6d71
0699069
491b7f1
4846d9f
f094be3
06f5da2
38dee1b
819b8e7
f1e85ca
e9500c0
c6253df
6e4a891
e472eba
174e620
67622a0
d53350e
7a759c1
10a3b70
9f8f456
7efb031
ab13e8a
3c99394
7a77c86
6e585f8
d45932b
bd6cebe
13f1938
13281f5
fcaf2a2
327905b
11361e1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,7 @@ | ||
.dart_tool/ | ||
.packages | ||
build/ | ||
pubspec.lock | ||
objectbox/ | ||
**/.dart_tool/ | ||
**/.packages | ||
**/build/ | ||
**/pubspec.lock | ||
misc/ | ||
.idea/ | ||
.idea/ | ||
**/*.g.dart |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,14 @@ | ||
0.2.0 (2019-09-11) | ||
------------------ | ||
* UTF-8 support for Store and Box (thanks to [Buggaboo](https://github.com/Buggaboo) for [#14](https://github.com/objectbox/objectbox-dart/pull/14)!) | ||
* Bulk put and get functions (getMany, getAll, putMany) | ||
* Updated to objectbox-c 0.7 | ||
* Basic Store options | ||
* Minimal unit tests | ||
* Removed reflection code, switched to model code generation instead | ||
* Minimal Flutter Desktop example for Dart 2.5.0 | ||
|
||
0.1.0 (2019-09-03) | ||
------------------ | ||
* Minimal Store setup | ||
* Minimal Box with put and get | ||
* Minimal Box with put and get |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -36,10 +36,10 @@ Getting started | |
--------------- | ||
To try out the demo code in this repository, follow these steps: | ||
|
||
1. Install [objectbox-c](https://github.com/objectbox/objectbox-c) system-wide: `bash <(curl -s https://github.com/raw/objectbox/objectbox-c/master/download.sh)` (answer Y when it asks about installing to /usr/lib). | ||
2. Back in this repository, run `pub get` to download all Dart dependencies. | ||
3. Finally run `dart test/test.dart` to start the demo script. | ||
Note that, as fairly recent language features are used, the minimal required Dart version is 2.2.2. | ||
1. Install [objectbox-c](https://github.com/objectbox/objectbox-c) system-wide: `bash <(curl -s https://github.com/raw/objectbox/objectbox-c/master/download.sh) 0.7` (answer Y when it asks about installing to /usr/lib). | ||
2. Back in this repository, run `pub get` in each of the following directories: `objectbox`, `objectbox_model_generator`, `objectbox_test`. If you just want to try out the tests, running it in `objectbox_test` is enough. | ||
3. Move into the `objectbox_test` directory and execute `pub run build_runner build`. This regenerates the ObjectBox model to make it usable in Dart (i.e. the file `test/test.g.dart`) and is necessary each time you add or change a class annotated with `@Entity(...)`. | ||
4. Finally run `pub run test test/test.dart` to run the unit tests. | ||
|
||
Dart integration | ||
---------------- | ||
|
@@ -59,37 +59,37 @@ New (not yet persisted) objects typically have _Id_ value of `0` or `null`: call | |
|
||
*Note:* specifying the (meta model) IDs in annotations manually is a temporary quick solution. | ||
In a later version, you won't have to do this the and e.g. `@Property(id: 2, uid: 1002)` can be dropped completely. | ||
Technically, we need to setup [build time tools for Dart](https://github.com/objectbox/objectbox-dart/issues/3) for automatic management of the meta model IDs. | ||
As specified in step 3 of the _Getting started_ section, Dart's _build\_runner_ and _source\_gen_ are currently used and the generator will be extended to automatically manage the meta model IDs in the future. | ||
|
||
```dart | ||
import "../lib/objectbox.dart"; | ||
part "test.g.dart"; | ||
|
||
@Entity(id: 1, uid: 1) | ||
class Note { | ||
@Id(id: 1, uid: 1001, type: Type.Long) | ||
@Id(id: 1, uid: 1001) // automatically always 'int' in Dart code and 'Long' in ObjectBox | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. IDs should be uint64 (in objectbox), preferrable similar in the sample code as well There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you mean I should change the comment to "...and 'uint64' in ObjectBox"? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Generated code should pass flags: unsigned & ID Originally I also meant the entity class should use unsigned integer type but I've found out there's none in dart. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. FYI: "int" in Dart is the closest type to ObjectBox IDs. The JS limitations are funny though, btw... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
PropertyFlags_ID (1) is enough; haven't seen PropertyFlags_UNSIGNED (8192) with IDs, better not try this? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Right, I haven't really seen |
||
int id; | ||
|
||
@Property(id: 2, uid: 1002) | ||
String text; | ||
|
||
Note(); // empty default constructor needed | ||
Note.construct(this.text); | ||
|
||
toString() => "Note{id: $id, text: $text}"; | ||
} | ||
``` | ||
|
||
In your main function, you can then create a _store_ which needs an array of your entity classes to be constructed. | ||
In your main function, you can then create a _store_ which needs an array of your entity classes and definitions to be constructed. If you have several entities, construct your store like `Store([[Entity1, Entity1_OBXDefs], [Entity2, Entity2_OBXDefs]])` etc. | ||
Finally, you need a _box_, representing the interface for objects of one specific entity type. | ||
|
||
```dart | ||
var store = Store([Note]); | ||
var store = Store([[Note, Note_OBXDefs]]); | ||
var box = Box<Note>(store); | ||
|
||
var note = Note("Hello"); | ||
box.put(note); | ||
var note = Note.construct("Hello"); | ||
note.id = box.put(note); | ||
print("new note got id ${note.id}"); | ||
print("refetched note: ${box.getById(note.id)}"); | ||
print("refetched note: ${box.get(note.id)}"); | ||
|
||
store.close(); | ||
``` | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
targets: | ||
$default: | ||
builders: | ||
objectbox_model_generator|objectbox: | ||
enabled: true | ||
|
||
builders: | ||
objectbox: | ||
target: ":objectbox_model_generator" | ||
import: "package:objectbox_model_generator/builder.dart" | ||
builder_factories: ["objectboxModelFactory"] | ||
build_extensions: {".dart": [".objectbox_model.g.part"]} | ||
auto_apply: dependents | ||
build_to: cache | ||
applies_builders: ["source_gen|combining_builder"] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import "package:build/build.dart"; | ||
import "package:source_gen/source_gen.dart"; | ||
import "package:objectbox_model_generator/src/generator.dart"; | ||
|
||
Builder objectboxModelFactory(BuilderOptions options) => | ||
SharedPartBuilder([EntityGenerator()], "objectbox_model"); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
import "dart:async"; | ||
import "package:analyzer/dart/element/element.dart"; | ||
import "package:build/src/builder/build_step.dart"; | ||
import "package:source_gen/source_gen.dart"; | ||
|
||
import "package:objectbox/objectbox.dart"; | ||
import "package:objectbox/src/bindings/constants.dart"; | ||
|
||
class EntityGenerator extends GeneratorForAnnotation<Entity> { | ||
@override | ||
FutureOr<String> generateForAnnotatedElement(Element elementBare, ConstantReader annotation, BuildStep buildStep) { | ||
if(elementBare is! ClassElement) | ||
throw InvalidGenerationSourceError("in target ${elementBare.name}: annotated element isn't a class"); | ||
|
||
// get basic entity info | ||
var entity = Entity(id: annotation.read('id').intValue, uid: annotation.read('uid').intValue); | ||
var element = elementBare as ClassElement; | ||
var ret = """ | ||
const _${element.name}_OBXModel = { | ||
"entity": { | ||
"name": "${element.name}", | ||
"id": ${entity.id}, | ||
"uid": ${entity.uid} | ||
}, | ||
"properties": [ | ||
"""; | ||
|
||
// read all suitable annotated properties | ||
var props = []; | ||
String idPropertyName; | ||
for(var f in element.fields) { | ||
if(f.metadata == null || f.metadata.length != 1) // skip unannotated fields | ||
continue; | ||
var annotElmt = f.metadata[0].element as ConstructorElement; | ||
var annotType = annotElmt.returnType.toString(); | ||
var annotVal = f.metadata[0].computeConstantValue(); | ||
var fieldTypeObj = annotVal.getField("type"); | ||
int fieldType = fieldTypeObj == null ? null : fieldTypeObj.toIntValue(); | ||
|
||
var prop = { | ||
"name": f.name, | ||
"id": annotVal.getField("id").toIntValue(), | ||
"uid": annotVal.getField("uid").toIntValue(), | ||
"flags": 0, | ||
}; | ||
|
||
if(annotType == "Id") { | ||
if(idPropertyName != null) | ||
throw InvalidGenerationSourceError("in target ${elementBare.name}: has more than one properties annotated with @Id"); | ||
if(fieldType != null) | ||
throw InvalidGenerationSourceError("in target ${elementBare.name}: programming error: @Id property may not specify a type"); | ||
if(f.type.toString() != "int") | ||
throw InvalidGenerationSourceError("in target ${elementBare.name}: field with @Id property has type '${f.type.toString()}', but it must be 'int'"); | ||
|
||
fieldType = OBXPropertyType.Long; | ||
prop["flags"] = OBXPropertyFlag.ID; | ||
idPropertyName = f.name; | ||
} else if(annotType == "Property") { | ||
// nothing special here | ||
} else { | ||
// skip unknown annotations | ||
continue; | ||
} | ||
|
||
if(fieldType == null) { | ||
var fieldTypeStr = f.type.toString(); | ||
if(fieldTypeStr == "int") | ||
fieldType = OBXPropertyType.Int; | ||
else if(fieldTypeStr == "String") | ||
fieldType = OBXPropertyType.String; | ||
else { | ||
print("warning: skipping field '${f.name}' in entity '${element.name}', as it has the unsupported type '$fieldTypeStr'"); | ||
continue; | ||
} | ||
} | ||
|
||
prop["type"] = fieldType; | ||
props.add(prop); | ||
ret += """ | ||
{ | ||
"name": "${prop['name']}", | ||
"id": ${prop['id']}, | ||
"uid": ${prop['uid']}, | ||
"type": ${prop['type']}, | ||
"flags": ${prop['flags']}, | ||
"flatbuffers_id": ${(prop['id'] as int) - 1}, | ||
}, | ||
"""; | ||
} | ||
|
||
// some checks on the entity's integrity | ||
if(idPropertyName == null) | ||
throw InvalidGenerationSourceError("in target ${elementBare.name}: has no properties annotated with @Id"); | ||
|
||
// main code for instance builders and readers | ||
ret += """ | ||
], | ||
"idPropertyName": "${idPropertyName}", | ||
}; | ||
|
||
${element.name} _${element.name}_OBXBuilder(Map<String, dynamic> members) { | ||
${element.name} r = new ${element.name}(); | ||
${props.map((p) => "r.${p['name']} = members[\"${p['name']}\"];").join()} | ||
return r; | ||
} | ||
|
||
Map<String, dynamic> _${element.name}_OBXReader(${element.name} inst) { | ||
Map<String, dynamic> r = {}; | ||
${props.map((p) => "r[\"${p['name']}\"] = inst.${p['name']};").join()} | ||
return r; | ||
} | ||
|
||
const ${element.name}_OBXDefs = { | ||
"model": _${element.name}_OBXModel, | ||
"builder": _${element.name}_OBXBuilder, | ||
"reader": _${element.name}_OBXReader, | ||
}; | ||
"""; | ||
|
||
return ret; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
name: objectbox_model_generator | ||
version: 0.2.0 | ||
description: >- | ||
A preprocessor for Dart source files containing ObjectBox entity definitions. | ||
environment: | ||
sdk: '>=2.5.0 <3.0.0' | ||
dependencies: | ||
build: '>=0.12.0 <2.0.0' | ||
source_gen: ^0.9.0 | ||
objectbox: | ||
path: ../objectbox | ||
dev_dependencies: | ||
build_runner: '>=0.9.0 <0.11.0' |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
.atom/ | ||
.dart_tool/ | ||
.idea | ||
.vscode/ | ||
.packages | ||
.pub/ | ||
android/ | ||
build/ | ||
ios/ | ||
packages | ||
.flutter-plugins | ||
# Since the current expectation is that clients are using Flutter head, | ||
# and Dart SDK changes unlock dependencies, pubspec.lock isn't useful. | ||
# Ignore it since it just creates noise. When there's a system in place | ||
# for versioning this project relative to Flutter, remove this. | ||
pubspec.lock | ||
objectbox |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
# This file tracks properties of this Flutter project. | ||
# Used by Flutter tool to assess capabilities and perform upgrades etc. | ||
# | ||
# This file should be version controlled and should not be manually edited. | ||
|
||
version: | ||
revision: eaa9b47a4ac278a9439468911d2c361a472b114b | ||
channel: master |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
# objectbox_demo_desktop | ||
|
||
## Getting Started | ||
|
||
In this early demo version, only desktop environments are supported. Mobile platform support, i.e. Android and iOS, will be added later. The current code is based on Google's [flutter-desktop-embedding](https://github.com/google/flutter-desktop-embedding) repository. | ||
|
||
If you have never run Flutter on desktop before, execute the following commands (these are for Linux, adjust accordingly for your OS, see [here](https://github.com/flutter/flutter/wiki/Desktop-shells#tooling)): | ||
|
||
flutter channel master | ||
flutter upgrade | ||
flutter config --enable-linux-desktop | ||
|
||
When trying out this demo for the first time, you definitely need to run: | ||
|
||
flutter packages get | ||
|
||
And on first run and whenever you added or changed any classes annotated with ObjectBox's `@Entity(...)`, execute: | ||
|
||
flutter pub run build_runner build | ||
flutter run |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new branch doesn't follow conventions https://dart.dev/tools/pub/package-layout or rather it creates three packages.
What is the reason it must be split in such a way instead having a single package?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For this, I generally stuck to how Dart's json_serializable organizes its files. It uses source_gen as well (just like objectbox_model_generator), so it seemed like quite a good reference for some first orientation. Also, it subdivides the repository into three subprojects as well, each with its own
pubspec.yaml
:example
(ourobjectbox_test
),json_annotation
(ourobjectbox
) andjson_serializable
(ourobjectbox_model_generator
).Unfortunately, source_gen and build_runner are rather poorly documented for the use case of generating source code for annotations, so it's hard to find good resources on how to better organize the directory structure. Apart from that, I also think that the model generator needs to have its own Dart package in order for
build.yaml
to work properly.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Core packages are not always a best source of organization since they use to have long history, before most conventions even emerged.
The page I've linked (https://dart.dev/tools/pub/package-layout) is an official recommendation how a package should be structured and as such it would be strongly preferable to follow there are serious reasons we need to diverge. If you say building the generator is (I don't know enough of dart to help you there at the moment), then let's keep the generator as a separate project for now but at least leave the rest as a single one. What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great, I definitely agree; I'll also keep the generator as a separate package for now. Please check out my last commit, I reorganized the file structure similar to how you suggested.