]> gitweb.hamatoma.de Git - exhibition.git/commitdiff
Generator, helper, module_meta_data PageMetaData.
authorHamatoma <author.hamatoma.de>
Sun, 3 Oct 2021 22:20:21 +0000 (00:20 +0200)
committerHamatoma <author.hamatoma.de>
Sun, 3 Oct 2021 22:20:53 +0000 (00:20 +0200)
* Generator:
** new: page_generator, sql_generator generator_base
* helper: new toCamelCase()
* module_meta_data: Fix: no flutter dependencies.
* PageMetaData: minimalistic constructor

35 files changed:
Meta
ReCreateMetaTool
bin/generator.dart [deleted file]
bin/meta_tool.dart [deleted file]
lib/base/defines.dart
lib/base/helper.dart
lib/meta/module_meta_data.dart
lib/meta/roles_meta.dart
lib/meta/users_meta.dart
lib/page/page_collection.dart
lib/page/page_collection_custom.dart [new file with mode: 0644]
lib/page/roles/create_role_custom.dart [new file with mode: 0644]
lib/page/roles/create_role_page.dart [new file with mode: 0644]
lib/page/roles/edit_role_custom.dart [new file with mode: 0644]
lib/page/roles/edit_role_page.dart [new file with mode: 0644]
lib/page/roles/list_role_custom.dart [new file with mode: 0644]
lib/page/roles/list_role_page.dart [new file with mode: 0644]
lib/page/users/create_user_custom.dart [new file with mode: 0644]
lib/page/users/create_user_page.dart [new file with mode: 0644]
lib/page/users/delete_user_custom.dart [new file with mode: 0644]
lib/page/users/delete_user_page.dart [new file with mode: 0644]
lib/page/users/edit_user_custom.dart [new file with mode: 0644]
lib/page/users/edit_user_page.dart [new file with mode: 0644]
lib/page/users/list_user_custom.dart [new file with mode: 0644]
lib/page/users/list_user_page.dart [new file with mode: 0644]
lib/page/users/users_edit_page.dart [deleted file]
lib/widget/attended_widget.dart
metatool/Compile [new file with mode: 0755]
metatool/bin/generator.dart [new file with mode: 0644]
metatool/bin/generator_base.dart [new file with mode: 0644]
metatool/bin/meta_tool.dart [new file with mode: 0644]
metatool/bin/page_generator.dart [new file with mode: 0644]
metatool/bin/sql_generator.dart [new file with mode: 0644]
metatool/pubspec.yaml [new file with mode: 0644]
pubspec.yaml

diff --git a/Meta b/Meta
index 6668928d20c774133a26ea9862ca465138f829f0..4dcab93afe9e8ce0c1a084e43128b7c7429b04c7 100755 (executable)
--- a/Meta
+++ b/Meta
@@ -1,5 +1,6 @@
 #! /bin/sh
 EXE=tools/meta_tool
 test ! -x $EXE && ./ReCreateMetaTool
-$EXE $*
+cd metatool
+../$EXE $*
 
index bbfbb6b4fd46cfea687a628e9f5dfd76a7b74059..ece33819ead7c771deaf7d82d401707acec33988 100755 (executable)
@@ -1,3 +1,4 @@
 #! /bin/bash
-APP=meta_tool
-dart compile exe bin/$APP.dart -o tools/$APP
+cd metatool
+./Compile
+
diff --git a/bin/generator.dart b/bin/generator.dart
deleted file mode 100644 (file)
index 89b8b7f..0000000
+++ /dev/null
@@ -1,388 +0,0 @@
-import 'dart:io';
-
-import 'package:dart_bones/dart_bones.dart';
-import 'package:exhibition/meta/module_meta_data.dart';
-import 'package:exhibition/meta/modules.dart';
-import 'package:path/path.dart';
-
-/// Converts a [className] into a file name using Dart conventions.
-/// Example: "UserData" is converted to "user_data"
-String classToFilename(String className) {
-  String upperCase = className.toUpperCase();
-  String rc = '';
-  for (var ix = 0; ix < className.length; ix++) {
-    if (className[ix] == upperCase[ix] && ix > 0) {
-      rc += '_';
-    }
-    rc += className[ix];
-  }
-  return rc.toLowerCase();
-}
-
-/// Converts a [filename] into a class name using Dart conventions.
-/// Example: "user_data.dart" is converted to "UserData"
-String filenameToClass(String filename) {
-  final parts = basenameWithoutExtension(filename).split('_');
-  String rc = '';
-  for (var part in parts) {
-    if (part.isNotEmpty) {
-      rc += part[0].toUpperCase() + part.substring(1);
-    }
-  }
-  return rc;
-}
-
-class Generator {
-  BaseLogger logger = globalLogger;
-  Generator(this.logger);
-
-  /// Appends a [string] at the end of a [buffer].
-  /// If [buffer] is null a new instance is created.
-  /// If the last line in [buffer] has a length greater than [maxLength]
-  /// a newline is inserted and [indent] spaces.
-  /// If [separator] is not null that is inserted above the [string].
-  /// Returns the buffer (for chaining).
-  StringBuffer addToBuffer(String string,
-      {required int maxLength,
-      String? separator,
-      int indent = 0,
-      StringBuffer? buffer}) {
-    buffer ??= StringBuffer();
-    if (separator != null) {
-      buffer.write(separator);
-    }
-    final last = buffer.toString().lastIndexOf('\n');
-    if (buffer.length - (last < 0 ? 0 : last) + string.length > maxLength) {
-      buffer.write('\n' + (' ' * indent));
-    }
-    buffer.write(string);
-    return buffer;
-  }
-
-  ModuleMetaData? checkModule(List<String> args, int index) {
-    ModuleMetaData? rc;
-    if (index >= args.length) {
-      logger.error('+++ too few arguments. Missing MODULE.');
-    } else if ((rc = moduleByName(args[index])) == null) {
-      logger.error('+++ unknown module: ${args[index]}');
-      rc = null;
-    }
-    return rc;
-  }
-
-  /// Returns a DDL statement creating the database table of the [module].
-  String createDbTable(ModuleMetaData module) {
-    final tableName = module.tableName;
-    final list = module.propertyList;
-    final buffer = StringBuffer();
-    buffer.write('-- DO NOT CHANGE. This file is created by the meta_tool\n');
-    buffer.write('CREATE TABLE $tableName (\n');
-    String? primary;
-    for (var item in list) {
-      if (item.options.contains('primary')) {
-        primary = item.columnName;
-      }
-      buffer.write(' ${item.columnName}');
-      buffer.write(' ' + module.mySqlType(item.dataType, item.size));
-      buffer.write(module.dbOptions(item) + ',\n');
-    }
-    buffer.write('  PRIMARY KEY ($primary)\n');
-    buffer.write(');\n');
-    return buffer.toString();
-  }
-
-  /// Returns a Dart class definition of the [module].
-  String createModuleData(ModuleMetaData module) {
-    final buffer = StringBuffer();
-    PropertyMetaData? primary = module.primaryOf();
-    buffer.writeln('// DO NOT CHANGE. This file is created by the meta_tool');
-    buffer.writeln("import '../../base/defines.dart';");
-    buffer.writeln("import '../../base/helper.dart';");
-    buffer.writeln("import '../../persistence/data_record.dart';");
-    final type = 'int';
-    buffer.writeln(
-        'class ${module.moduleNameSingular}Data extends DataRecord<$type>{');
-    for (var item in module.propertyList) {
-      buffer.writeln('  ' + module.dartType(item.dataType) + '? ${item.name};');
-    }
-    buffer.writeln('  ${module.moduleNameSingular}Data({');
-    String separator = '';
-    for (var item in module.propertyList) {
-      buffer.write('$separator this.${item.name}');
-      separator = ',';
-    }
-    buffer.writeln('});');
-    buffer.writeln('  ${module.moduleNameSingular}Data.createFromMap(DataMap map) {');
-    buffer.writeln('    fromMap(map);');
-    buffer.writeln('  }');
-    buffer.writeln('  @override');
-    buffer.writeln('  void fromMap(DataMap map) {');
-    for (var item in module.propertyList) {
-      final name = item.columnName;
-      final type = item.dataType.toString();
-      //id = data.containsKey('id') ? int.tryParse(data['id']) : null;
-      buffer.writeln(
-          "    ${item.name} = map.containsKey('$name') ? valueOf($type, "
-          "map['$name']) : null;");
-    }
-    buffer.writeln('  }');
-    final name = primary == null ? 'id' : primary.name;
-    final name2 = primary == null ? 'id' : primary.columnName;
-    buffer.write('''  @override
-  int keyOf() {
-    return $name ?? 0;
-  }
-  @override
-  String nameOfKey(){
-    return '$name2';
-  }
-''');
-    buffer.writeln('  static DataType? dataTypeOf(String name) {');
-    buffer.writeln('    DataType? rc;');
-    buffer.writeln('    switch(name){');
-    for (var item in module.propertyList) {
-      buffer.writeln("    case '${item.name}':");
-      buffer.writeln('      rc = ${item.dataType};');
-      buffer.writeln('      break;');
-    }
-    buffer.write('''    default:
-      break;
-    }
-    return rc;
-  }
-''');
-    buffer.write('''  @override
-  DataMap toMap({DataMap? map, bool clear = true}) {
-    map ??= DataMap();
-    if (clear) {
-      map.clear();
-    }
-''');
-    for (var item in module.propertyList) {
-      final name1 = item.columnName;
-      final name2 = item.name;
-      buffer.writeln("    map['$name1'] = $name2;");
-    }
-  buffer.write('''    return map;
-  }
-}
-''');
-    return buffer.toString();
-  }
-
-  /// Creates the file modules.dart.
-  String createModules() {
-    final buffer = StringBuffer();
-    buffer.write('// DO NOT CHANGE. This file is created by the meta_tool\n');
-    buffer.write("import 'module_meta_data.dart';\n");
-    final modules = <String>[];
-    final files = <String>[];
-    final fileOfModule = <String, String>{};
-
-    for (var item in Directory('lib/meta').listSync()) {
-      final name = basename(item.path);
-      if (name.endsWith('_meta.dart')) {
-        final moduleName = filenameToClass(name.replaceFirst('_meta.dart', ''));
-        fileOfModule[moduleName] = name;
-        modules.add(moduleName);
-        files.add(name);
-      }
-    }
-    modules.sort();
-    files.sort();
-    for (var file in files) {
-      buffer.write("import '$file';\n");
-    }
-    buffer.write('''
-/// Returns the meta data of the module given by [name].
-/// Returns null if not found.
-ModuleMetaData? moduleByName(String name) {
-  ModuleMetaData? rc;
-  switch (name) {
-''');
-    for (var module in modules) {
-      buffer.write("    case '$module':\n");
-      final className = findClass(fileOfModule[module]!);
-      buffer.write("      rc = $className();\n");
-      buffer.write("      break;\n");
-    }
-    buffer.write('''    default:
-      break;
-  }
-  return rc;
-}
-/// Returns the module names as string list.
-List<String> moduleNames(){
-  return [
-''');
-    for (var module in modules) {
-      buffer.write("    '$module',\n");
-    }
-    buffer.write('''  ];
-}
-''');
-    return buffer.toString();
-  }
-
-  /// Returns the SQL statements for insert, update, delete...
-  /// for a given [module].
-  /// This yaml file is a configuration for the rest_server.
-  String createSqlStatements(ModuleMetaData module) {
-    final moduleName = module.moduleName;
-    final tableName = module.tableName;
-    final list = module.propertyList;
-    final buffer = StringBuffer();
-    buffer.write('---\n');
-    buffer.write('# DO NOT CHANGE. This file is created by the meta_tool\n');
-    buffer.write('''# SQL statements of the module "$moduleName":\n
-module: $moduleName
-list:
-  type: list
-  parameters: []
-  sql: "select * from $tableName;"
-byId:
-  type: record
-  parameters: [ "${list[0].name}" ]
-  sql: "select * from $tableName where ${list[0].columnName}=:${list[0].name};"
-delete:
-  type: delete
-  parameters: [ "${list[0].name}" ]
-  sql: "delete * from $tableName where ${list[0].columnName}=:${list[0].name};"
-update:
-  type: update
-''');
-    var items = module.standardColumns('changedBy');
-    var parameters = addToBuffer('  parameters: [', maxLength: 80);
-    var assignments = addToBuffer('    ', maxLength: 80);
-    var first = true;
-    for (var item in items) {
-      addToBuffer('":${item.name}"',
-          maxLength: 80,
-          separator: first ? null : ',',
-          indent: 4,
-          buffer: parameters);
-      addToBuffer('${item.columnName}=:${item.name}',
-          maxLength: 80,
-          separator: first ? null : ',',
-          indent: 4,
-          buffer: assignments);
-      first = false;
-    }
-    parameters.write(']');
-    var item = module.properties['changed']!;
-    addToBuffer('${item.columnName}=NOW()',
-        maxLength: 80, separator: ',', indent: 4, buffer: assignments);
-    // parameters: [":id", ":name", ":displayname", ":email", ":changedby"]
-    buffer.writeln(parameters);
-    buffer.write('  sql: "UPDATE $tableName SET\n');
-    buffer.writeln(assignments);
-    //user_name=:name, user_displayname=:displayname, user_email=:email, user_changed=NOW(), user_changedby=:changedby
-    buffer.write('    WHERE ${list[0].columnName}=:${list[0].name};"\n');
-
-    items = module.standardColumns('createdBy');
-    parameters = addToBuffer('  parameters: [', maxLength: 80);
-    final sql1 = addToBuffer('  sql: "INSERT INTO $tableName(', maxLength: 80);
-    final sql2 = addToBuffer('    VALUES(', maxLength: 80);
-    first = true;
-    for (var item in items) {
-      addToBuffer('":${item.name}"',
-          maxLength: 80,
-          separator: first ? null : ',',
-          indent: 4,
-          buffer: parameters);
-      addToBuffer('${item.columnName}',
-          maxLength: 80,
-          separator: first ? null : ',',
-          indent: 6,
-          buffer: sql1);
-      addToBuffer(':${item.name}',
-          maxLength: 80,
-          separator: first ? null : ',',
-          indent: 6,
-          buffer: sql2);
-      first = false;
-    }
-    parameters.write(']');
-    item = module.properties['created']!;
-    addToBuffer('${item.columnName})',
-        maxLength: 80, separator: ',', indent: 4, buffer: sql1);
-    addToBuffer('NOW());"',
-        maxLength: 80, separator: ',', indent: 4, buffer: sql2);
-    // parameters: [":id", ":name", ":displayname", ":email", ":changedby"]
-    buffer.write('''insert:
-  type: insert
-''');
-    buffer.writeln(parameters);
-    buffer.writeln(sql1);
-    buffer.writeln(sql2);
-    return buffer.toString();
-  }
-
-  /// Finds the class name of a module meta class given by the file [node].
-  String findClass(String node) {
-    final contents = File('lib/meta/$node').readAsStringSync();
-    RegExpMatch? match;
-    String rc;
-    if ((match = RegExp(r'class\s+(\w+)\s').firstMatch(contents)) != null) {
-      rc = match!.group(1)!;
-    } else {
-      logger.error('+++ missing class in $node');
-      rc = filenameToClass(node.replaceFirst('.dart', ''));
-    }
-    return rc;
-  }
-
-  /// Replaces print().
-  void out(String line) {
-    logger.log(line);
-  }
-
-  /// Generates all modules defined in the meta data.
-  void updateModules(Generator generator) {
-    writeFile('lib/meta/modules.dart', createModules());
-    final modules = moduleNames();
-    for (var name in modules) {
-      ModuleMetaData? module = moduleByName(name);
-      if (module == null) {
-        logger.error('+++ unknown module: $name');
-      } else {
-        String filename =
-            classToFilename(module.moduleNameSingular) + '_data.dart';
-        final directory = name.toLowerCase();
-        writeFile('lib/page/$directory/$filename',
-            generator.createModuleData(module));
-      }
-    }
-  }
-
-  /// Generates the modules.dart.
-  void updateModulesList(Generator generator) {
-    writeFile('lib/meta/modules.dart', createModules());
-  }
-
-  /// Generates the Sql statement file for each module defined in the meta data.
-  void updateSql(Generator generator) {
-    final modules = moduleNames();
-    for (var name in modules) {
-      ModuleMetaData? module = moduleByName(name);
-      if (module == null) {
-        logger.error('+++ unknown module: $name');
-      } else {
-        logger.log('current directory: ${Directory.current.path}');
-        String filename = 'rest_server/data/sql/${name.toLowerCase()}.sql.yaml';
-        writeFile(filename, generator.createSqlStatements(module));
-      }
-    }
-  }
-
-  /// Writes a given [contents] into a file named [filename];
-  void writeFile(String filename, String contents) {
-    logger.log('creating $filename ...');
-    final file = File(filename);
-    final parent = Directory(dirname(filename));
-    if (!parent.existsSync()) {
-      parent.createSync(recursive: true);
-    }
-    file.writeAsStringSync(contents);
-  }
-}
diff --git a/bin/meta_tool.dart b/bin/meta_tool.dart
deleted file mode 100644 (file)
index 0c30a98..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-import 'package:dart_bones/dart_bones.dart';
-import 'package:exhibition/base/i18n_io.dart';
-import 'package:exhibition/meta/module_meta_data.dart';
-import 'package:exhibition/meta/modules.dart';
-
-import 'generator.dart';
-
-void main(List<String> args) {
-  final logger = MemoryLogger(LEVEL_DETAIL);
-  final generator = Generator(logger);
-  I18nIo.internal('data/i18n', logger);
-  if (args.isEmpty) {
-    usage(generator);
-    generator.out('+++ missing arguments.');
-  } else {
-    ModuleMetaData? metaData;
-    switch (args[0]) {
-      case 'all-modules':
-        generator.out(moduleNames().join('\n'));
-        break;
-      case 'print-modules-list':
-        generator.out(generator.createModules());
-        break;
-      case 'print-table':
-        if ((metaData = generator.checkModule(args, 1)) != null) {
-          generator.out(generator.createDbTable(metaData!));
-        }
-        break;
-      case 'print-sql':
-        if ((metaData = generator.checkModule(args, 1)) != null) {
-          generator.out(generator.createSqlStatements(metaData!));
-        }
-        break;
-      case 'print-data':
-        if ((metaData = generator.checkModule(args, 1)) != null) {
-          generator.out(generator.createModuleData(metaData!));
-        }
-        break;
-      case 'update-modules':
-        generator.updateModules(generator);
-        break;
-      case 'update-modules-list':
-        generator.updateModulesList(generator);
-        break;
-      case 'update-sql':
-        generator.updateSql(generator);
-        break;
-      default:
-        usage(generator);
-        generator.out('+++ unknown MODE: ${args[0]}.');
-        break;
-    }
-  }
-}
-
-void usage(Generator generator) {
-  generator.out('''Usage: meta_tool MODE MODULE
-  Generate code specified by MODE for the module MODULE.
-MODE:
-  all-modules
-    Prints the module names.
-  print-modules-list
-    Prints the file lib/meta/modules.dart.
-    That works without of meta-data: It can be called before
-    creating a the meta_tool.
-  print-table MODULE
-    Prints the SQL table definition of MODULE.
-  print-data MODULE
-    Creates the data storage class of MODULE
-  print-sql MODULE
-    Creates the SQL statements (for the rest_server) of MODULE
-  update-modules-list
-    Generates the modules.dart file in the correct directory.
-    That works without of meta-data: It can be called before
-    creating a the meta_tool.
-  update-modules
-    Generates all module files depending on the meta data.
-  update-sql
-    Generates all Sql statement files depending on the meta data.
-MODULE: 
-  The name of the module, e.g. "Users"
-Example:
-meta_tool print-table Users
-''');
-}
index 88bbea2f187af11d01e34637dade6feb7ed5fa6e..1496a8018d6b65e8e897b6e6812f3d50a149c666 100644 (file)
@@ -8,6 +8,7 @@ typedef JsonData = Object;
 enum EventSource { undef, localData, remoteData }
 enum EventCardinality { undef, record, list }
 enum DataType {
+  undefined,
   bool,
   currency,
   date,
index 1ee83b3362325f1666a5b4e6ef88cba9ca1f8633..23a13af58f1ff3a2a606b57d56c629d9d65891f6 100644 (file)
@@ -1,5 +1,10 @@
 import 'defines.dart';
 
+String toCamelCase(String name) {
+  final rc = name.isEmpty ? '' : (name[0].toUpperCase() + name.substring(1));
+  return rc;
+}
+
 /// Returns the [dataType] specific value of [data].
 dynamic valueOf(DataType dataType, String data) {
   dynamic rc;
@@ -23,6 +28,8 @@ dynamic valueOf(DataType dataType, String data) {
     case DataType.string:
       rc = data;
       break;
+    case DataType.undefined:
+      throw FormatException('valueOf(): data type is undefined');
   }
   return rc;
 }
index 71943ce86876ab612e656c051bb679475daa5033..6c4f4f867936104f8e4f7b3597c91b991738e830 100644 (file)
@@ -1,8 +1,8 @@
-import 'package:flutter/material.dart';
+import 'package:dart_bones/dart_bones.dart';
 
 import '../base/defines.dart';
 
-typedef MenuItemBuilder = List<DropdownMenuItem<T>> Function<T>(
+typedef MetaMenuItemBuilder = List<dynamic> Function<T>(
     PropertyMetaData propertyMetaData);
 
 /// Describes a button of a page.
@@ -131,6 +131,8 @@ class ModuleMetaData {
       case DataType.string:
         rc = 'String';
         break;
+      case DataType.undefined:
+        throw FormatException('dartType(): data type is undefined');
     }
     return rc;
   }
@@ -195,10 +197,26 @@ class ModuleMetaData {
           rc = 'LARGE TEXT';
         }
         break;
+      case DataType.undefined:
+        throw FormatException('mySqlType(): data type is undefined');
     }
     return rc;
   }
 
+  /// Will be called after the constructor.
+  ///
+  /// Override it if needed.
+  void onInitialized() {
+    // do nothing
+  }
+
+  /// Returns the meta data of a page given by its [name] or null if missing.
+  PageMetaData? pageByName(String name) {
+    final rc =
+        pageList.firstWhere((element) => element.name == name, orElse: null);
+    return rc;
+  }
+
   /// Returns the primary key of the relation.
   PropertyMetaData? primaryOf() {
     final rc = propertyList.firstWhere(
@@ -207,6 +225,13 @@ class ModuleMetaData {
     return rc;
   }
 
+  /// Returns the meta data of a property given by its [name] or null if missing.
+  PropertyMetaData? propertyByName(String name) {
+    final rc = propertyList.singleWhere((element) => element.name == name,
+        orElse: null);
+    return rc;
+  }
+
   /// Returns the properties that are not in [metaColumns].
   /// : if the name of the property is [included] the property is always part of
   /// the result.
@@ -218,15 +243,17 @@ class ModuleMetaData {
 }
 
 class PageMetaData {
-  final String name;
+  String name = '';
+  final String label;
   final PageType pageType;
   ModuleMetaData module = DummyModule();
-  List<FieldMetaData>? fields;
-  PageMetaData(
-    this.name,
-    this.pageType,
-    this.fields,
-  );
+  final List<FieldMetaData>? field = [];
+  PageMetaData(this.label, this.pageType, {String name = ''}) {
+    if (name.isEmpty) {
+      name = enumToString(pageType);
+    }
+    this.name = name;
+  }
 }
 
 enum PageType { create, custom, delete, edit, list }
@@ -245,7 +272,7 @@ class PropertyMetaData {
   final String options;
   final DisplayType displayType;
   final DataType dataType;
-  MenuItemBuilder? menuItemBuilder;
+  MetaMenuItemBuilder? menuItemBuilder;
 
   /// The size if dataType is DataType.string.
   final int size;
index e45c3472a8df6c46ea2bad24492297b74321ed75..7e1225abd6674cf97486d4bfe7619cca8670b4de 100644 (file)
@@ -28,8 +28,10 @@ class RoleMeta extends ModuleMetaData {
               'changedBy', i18n.tr('Changed by'), DataType.string, ':hidden:',
               size: 32),
         ], [
-          PageMetaData('New Role', PageType.create, []),
-          PageMetaData('Change Role', PageType.edit, []),
-          PageMetaData('Roles Overview', PageType.list, []),
+          PageMetaData('New Role', PageType.create),
+          PageMetaData('Change Role', PageType.edit),
+          PageMetaData('Roles Overview', PageType.list),
         ]);
+  @override
+  void onInitialized() {}
 }
index c627751554363bfc4c0fe764f10832f97fb099e7..af9ba008fd2eb2e198136301f88bf835f1ab750a 100644 (file)
@@ -37,9 +37,9 @@ class UserMeta extends ModuleMetaData {
               'changedBy', i18n.tr('Changed by'), DataType.string, ':hidden:',
               size: 32),
         ], [
-          PageMetaData('New User', PageType.create, []),
-          PageMetaData('Change User', PageType.edit, []),
-          PageMetaData('Delete User', PageType.delete, []),
-          PageMetaData('Users Overview', PageType.list, []),
+          PageMetaData('New User', PageType.create),
+          PageMetaData('Change User', PageType.edit),
+          PageMetaData('Delete User', PageType.delete),
+          PageMetaData('Users Overview', PageType.list),
         ]);
 }
index 2187508cff16f2ec65b31ab191034461f507b9b0..675ce07a42ba16d21520ee7353b2f9e597396b0b 100644 (file)
@@ -1,7 +1,15 @@
+// DO NOT CHANGE. This file is created by the meta_tool!
 import 'package:flutter/material.dart';
 import '../setting/global_data.dart';
+import 'page_collection_custom.dart';
 
-import 'users/users_edit_page.dart';
+import 'roles/create_role_page.dart';
+import 'roles/edit_role_page.dart';
+import 'roles/list_role_page.dart';
+import 'users/create_user_page.dart';
+import 'users/edit_user_page.dart';
+import 'users/delete_user_page.dart';
+import 'users/list_user_page.dart';
 
 /// Manages all meta data driven pages of the program.
 class PageCollection {
@@ -18,8 +26,29 @@ class PageCollection {
     StatefulWidget? rc;
     final globalData = GlobalData();
     switch (route) {
+      case '/roles/create':
+        rc = CreateRolePage(globalData);
+        break;
+      case '/roles/edit':
+        rc = EditRolePage(globalData);
+        break;
+      case '/roles/list':
+        rc = ListRolePage(globalData);
+        break;
+      case '/users/create':
+        rc = CreateUserPage(globalData);
+        break;
       case '/users/edit':
-        rc = UsersEditPage(globalData);
+        rc = EditUserPage(globalData);
+        break;
+      case '/users/delete':
+        rc = DeleteUserPage(globalData);
+        break;
+      case '/users/list':
+        rc = ListUserPage(globalData);
+        break;
+      default:
+        rc = customPageByRoute(route, globalData);
         break;
     }
     if (rc != null) {
diff --git a/lib/page/page_collection_custom.dart b/lib/page/page_collection_custom.dart
new file mode 100644 (file)
index 0000000..c61ba97
--- /dev/null
@@ -0,0 +1,24 @@
+// This file is created by the meta_tool. But it can be customized.
+// It will never overridden by the meta_tool.
+import 'package:flutter/material.dart';
+import '../setting/global_data.dart';
+import 'info_page.dart';
+import 'log_page.dart';
+
+/// Creates a page defined by a [route].
+///
+/// Note: only pages without meta data definitions should be handled here.
+StatefulWidget? customPageByRoute(String route, GlobalData globalData) {
+  StatefulWidget? rc;
+  switch (route) {
+    case '/info':
+      rc = InfoPage(globalData);
+      break;
+    case '/log':
+      rc = LogPage(globalData);
+      break;
+    default:
+      break;
+  }
+  return rc;
+}
diff --git a/lib/page/roles/create_role_custom.dart b/lib/page/roles/create_role_custom.dart
new file mode 100644 (file)
index 0000000..7d338e8
--- /dev/null
@@ -0,0 +1,34 @@
+// This file is created by the meta_tool. But it can be customized.
+// It will never overridden by the meta_tool.
+import 'package:flutter/material.dart';
+
+import '../../base/i18n.dart';
+import '../../setting/global_data.dart';
+import '../../widget/attended_page.dart';
+import '../../widget/widget_form.dart';
+import 'create_role_page.dart';
+
+final i18n = I18N();
+
+class CreateRoleCustom extends State<CreateRolePage> {
+  final globalData = GlobalData();
+  AttendedPage? attendedPage;
+
+  @override
+  Widget build(BuildContext context) {
+    final padding = 16.0;
+    final rc = Scaffold(
+        appBar: globalData.appBarBuilder(i18n.tr('Change Role data')),
+        drawer: globalData.drawerBuilder(context),
+        body: SafeArea(
+            child: WidgetForm.flexibleGrid(attendedPage!.attendedWidgets(),
+                screenWidth: attendedPage!.pageStates.screenWidth,
+                padding: padding)));
+    return rc;
+  }
+
+  @override
+  void initState() {
+    super.initState();
+  }
+}
diff --git a/lib/page/roles/create_role_page.dart b/lib/page/roles/create_role_page.dart
new file mode 100644 (file)
index 0000000..6018fb8
--- /dev/null
@@ -0,0 +1,31 @@
+// DO NOT CHANGE. This file is created by the meta_tool!
+import 'package:flutter/material.dart';
+
+import '../../meta/roles_meta.dart';
+import '../../setting/global_data.dart';
+import '../../widget/attended_page.dart';
+import 'create_role_custom.dart';
+
+class CreateRolePage extends StatefulWidget {
+  final GlobalData globalData;
+  final PageStates pageStates = PageStates();
+  CreateRolePage(this.globalData) : super();
+  @override
+  _CreateRolePageState createState() {
+    final rc = _CreateRolePageState();
+    rc.attendedPage =
+        AttendedPage(this, rc, globalData, RoleMeta.instance, pageStates);
+    pageStates.attendedPage = rc.attendedPage;
+    return rc;
+  }
+}
+
+class _CreateRolePageState extends CreateRoleCustom {
+  @override
+  void didChangeDependencies() {
+    final size = MediaQuery.of(context).size;
+    attendedPage!.pageStates.screenWidth = size.width;
+    attendedPage!.pageStates.screenHeight = size.height;
+    super.didChangeDependencies();
+  }
+}
diff --git a/lib/page/roles/edit_role_custom.dart b/lib/page/roles/edit_role_custom.dart
new file mode 100644 (file)
index 0000000..b66d65b
--- /dev/null
@@ -0,0 +1,34 @@
+// This file is created by the meta_tool. But it can be customized.
+// It will never overridden by the meta_tool.
+import 'package:flutter/material.dart';
+
+import '../../base/i18n.dart';
+import '../../setting/global_data.dart';
+import '../../widget/attended_page.dart';
+import '../../widget/widget_form.dart';
+import 'edit_role_page.dart';
+
+final i18n = I18N();
+
+class EditRoleCustom extends State<EditRolePage> {
+  final globalData = GlobalData();
+  AttendedPage? attendedPage;
+
+  @override
+  Widget build(BuildContext context) {
+    final padding = 16.0;
+    final rc = Scaffold(
+        appBar: globalData.appBarBuilder(i18n.tr('Change Role data')),
+        drawer: globalData.drawerBuilder(context),
+        body: SafeArea(
+            child: WidgetForm.flexibleGrid(attendedPage!.attendedWidgets(),
+                screenWidth: attendedPage!.pageStates.screenWidth,
+                padding: padding)));
+    return rc;
+  }
+
+  @override
+  void initState() {
+    super.initState();
+  }
+}
diff --git a/lib/page/roles/edit_role_page.dart b/lib/page/roles/edit_role_page.dart
new file mode 100644 (file)
index 0000000..309976c
--- /dev/null
@@ -0,0 +1,31 @@
+// DO NOT CHANGE. This file is created by the meta_tool!
+import 'package:flutter/material.dart';
+
+import '../../meta/roles_meta.dart';
+import '../../setting/global_data.dart';
+import '../../widget/attended_page.dart';
+import 'edit_role_custom.dart';
+
+class EditRolePage extends StatefulWidget {
+  final GlobalData globalData;
+  final PageStates pageStates = PageStates();
+  EditRolePage(this.globalData) : super();
+  @override
+  _EditRolePageState createState() {
+    final rc = _EditRolePageState();
+    rc.attendedPage =
+        AttendedPage(this, rc, globalData, RoleMeta.instance, pageStates);
+    pageStates.attendedPage = rc.attendedPage;
+    return rc;
+  }
+}
+
+class _EditRolePageState extends EditRoleCustom {
+  @override
+  void didChangeDependencies() {
+    final size = MediaQuery.of(context).size;
+    attendedPage!.pageStates.screenWidth = size.width;
+    attendedPage!.pageStates.screenHeight = size.height;
+    super.didChangeDependencies();
+  }
+}
diff --git a/lib/page/roles/list_role_custom.dart b/lib/page/roles/list_role_custom.dart
new file mode 100644 (file)
index 0000000..f367ee2
--- /dev/null
@@ -0,0 +1,34 @@
+// This file is created by the meta_tool. But it can be customized.
+// It will never overridden by the meta_tool.
+import 'package:flutter/material.dart';
+
+import '../../base/i18n.dart';
+import '../../setting/global_data.dart';
+import '../../widget/attended_page.dart';
+import '../../widget/widget_form.dart';
+import 'list_role_page.dart';
+
+final i18n = I18N();
+
+class ListRoleCustom extends State<ListRolePage> {
+  final globalData = GlobalData();
+  AttendedPage? attendedPage;
+
+  @override
+  Widget build(BuildContext context) {
+    final padding = 16.0;
+    final rc = Scaffold(
+        appBar: globalData.appBarBuilder(i18n.tr('Change Role data')),
+        drawer: globalData.drawerBuilder(context),
+        body: SafeArea(
+            child: WidgetForm.flexibleGrid(attendedPage!.attendedWidgets(),
+                screenWidth: attendedPage!.pageStates.screenWidth,
+                padding: padding)));
+    return rc;
+  }
+
+  @override
+  void initState() {
+    super.initState();
+  }
+}
diff --git a/lib/page/roles/list_role_page.dart b/lib/page/roles/list_role_page.dart
new file mode 100644 (file)
index 0000000..b2f01d4
--- /dev/null
@@ -0,0 +1,31 @@
+// DO NOT CHANGE. This file is created by the meta_tool!
+import 'package:flutter/material.dart';
+
+import '../../meta/roles_meta.dart';
+import '../../setting/global_data.dart';
+import '../../widget/attended_page.dart';
+import 'list_role_custom.dart';
+
+class ListRolePage extends StatefulWidget {
+  final GlobalData globalData;
+  final PageStates pageStates = PageStates();
+  ListRolePage(this.globalData) : super();
+  @override
+  _ListRolePageState createState() {
+    final rc = _ListRolePageState();
+    rc.attendedPage =
+        AttendedPage(this, rc, globalData, RoleMeta.instance, pageStates);
+    pageStates.attendedPage = rc.attendedPage;
+    return rc;
+  }
+}
+
+class _ListRolePageState extends ListRoleCustom {
+  @override
+  void didChangeDependencies() {
+    final size = MediaQuery.of(context).size;
+    attendedPage!.pageStates.screenWidth = size.width;
+    attendedPage!.pageStates.screenHeight = size.height;
+    super.didChangeDependencies();
+  }
+}
diff --git a/lib/page/users/create_user_custom.dart b/lib/page/users/create_user_custom.dart
new file mode 100644 (file)
index 0000000..309f2e0
--- /dev/null
@@ -0,0 +1,34 @@
+// This file is created by the meta_tool. But it can be customized.
+// It will never overridden by the meta_tool.
+import 'package:flutter/material.dart';
+
+import '../../base/i18n.dart';
+import '../../setting/global_data.dart';
+import '../../widget/attended_page.dart';
+import '../../widget/widget_form.dart';
+import 'create_user_page.dart';
+
+final i18n = I18N();
+
+class CreateUserCustom extends State<CreateUserPage> {
+  final globalData = GlobalData();
+  AttendedPage? attendedPage;
+
+  @override
+  Widget build(BuildContext context) {
+    final padding = 16.0;
+    final rc = Scaffold(
+        appBar: globalData.appBarBuilder(i18n.tr('Change User data')),
+        drawer: globalData.drawerBuilder(context),
+        body: SafeArea(
+            child: WidgetForm.flexibleGrid(attendedPage!.attendedWidgets(),
+                screenWidth: attendedPage!.pageStates.screenWidth,
+                padding: padding)));
+    return rc;
+  }
+
+  @override
+  void initState() {
+    super.initState();
+  }
+}
diff --git a/lib/page/users/create_user_page.dart b/lib/page/users/create_user_page.dart
new file mode 100644 (file)
index 0000000..dec4186
--- /dev/null
@@ -0,0 +1,31 @@
+// DO NOT CHANGE. This file is created by the meta_tool!
+import 'package:flutter/material.dart';
+
+import '../../meta/users_meta.dart';
+import '../../setting/global_data.dart';
+import '../../widget/attended_page.dart';
+import 'create_user_custom.dart';
+
+class CreateUserPage extends StatefulWidget {
+  final GlobalData globalData;
+  final PageStates pageStates = PageStates();
+  CreateUserPage(this.globalData) : super();
+  @override
+  _CreateUserPageState createState() {
+    final rc = _CreateUserPageState();
+    rc.attendedPage =
+        AttendedPage(this, rc, globalData, UserMeta.instance, pageStates);
+    pageStates.attendedPage = rc.attendedPage;
+    return rc;
+  }
+}
+
+class _CreateUserPageState extends CreateUserCustom {
+  @override
+  void didChangeDependencies() {
+    final size = MediaQuery.of(context).size;
+    attendedPage!.pageStates.screenWidth = size.width;
+    attendedPage!.pageStates.screenHeight = size.height;
+    super.didChangeDependencies();
+  }
+}
diff --git a/lib/page/users/delete_user_custom.dart b/lib/page/users/delete_user_custom.dart
new file mode 100644 (file)
index 0000000..6a3dfd1
--- /dev/null
@@ -0,0 +1,34 @@
+// This file is created by the meta_tool. But it can be customized.
+// It will never overridden by the meta_tool.
+import 'package:flutter/material.dart';
+
+import '../../base/i18n.dart';
+import '../../setting/global_data.dart';
+import '../../widget/attended_page.dart';
+import '../../widget/widget_form.dart';
+import 'delete_user_page.dart';
+
+final i18n = I18N();
+
+class DeleteUserCustom extends State<DeleteUserPage> {
+  final globalData = GlobalData();
+  AttendedPage? attendedPage;
+
+  @override
+  Widget build(BuildContext context) {
+    final padding = 16.0;
+    final rc = Scaffold(
+        appBar: globalData.appBarBuilder(i18n.tr('Change User data')),
+        drawer: globalData.drawerBuilder(context),
+        body: SafeArea(
+            child: WidgetForm.flexibleGrid(attendedPage!.attendedWidgets(),
+                screenWidth: attendedPage!.pageStates.screenWidth,
+                padding: padding)));
+    return rc;
+  }
+
+  @override
+  void initState() {
+    super.initState();
+  }
+}
diff --git a/lib/page/users/delete_user_page.dart b/lib/page/users/delete_user_page.dart
new file mode 100644 (file)
index 0000000..e5ac100
--- /dev/null
@@ -0,0 +1,31 @@
+// DO NOT CHANGE. This file is created by the meta_tool!
+import 'package:flutter/material.dart';
+
+import '../../meta/users_meta.dart';
+import '../../setting/global_data.dart';
+import '../../widget/attended_page.dart';
+import 'delete_user_custom.dart';
+
+class DeleteUserPage extends StatefulWidget {
+  final GlobalData globalData;
+  final PageStates pageStates = PageStates();
+  DeleteUserPage(this.globalData) : super();
+  @override
+  _DeleteUserPageState createState() {
+    final rc = _DeleteUserPageState();
+    rc.attendedPage =
+        AttendedPage(this, rc, globalData, UserMeta.instance, pageStates);
+    pageStates.attendedPage = rc.attendedPage;
+    return rc;
+  }
+}
+
+class _DeleteUserPageState extends DeleteUserCustom {
+  @override
+  void didChangeDependencies() {
+    final size = MediaQuery.of(context).size;
+    attendedPage!.pageStates.screenWidth = size.width;
+    attendedPage!.pageStates.screenHeight = size.height;
+    super.didChangeDependencies();
+  }
+}
diff --git a/lib/page/users/edit_user_custom.dart b/lib/page/users/edit_user_custom.dart
new file mode 100644 (file)
index 0000000..3c20745
--- /dev/null
@@ -0,0 +1,34 @@
+// This file is created by the meta_tool. But it can be customized.
+// It will never overridden by the meta_tool.
+import 'package:flutter/material.dart';
+
+import '../../base/i18n.dart';
+import '../../setting/global_data.dart';
+import '../../widget/attended_page.dart';
+import '../../widget/widget_form.dart';
+import 'edit_user_page.dart';
+
+final i18n = I18N();
+
+class EditUserCustom extends State<EditUserPage> {
+  final globalData = GlobalData();
+  AttendedPage? attendedPage;
+
+  @override
+  Widget build(BuildContext context) {
+    final padding = 16.0;
+    final rc = Scaffold(
+        appBar: globalData.appBarBuilder(i18n.tr('Change User data')),
+        drawer: globalData.drawerBuilder(context),
+        body: SafeArea(
+            child: WidgetForm.flexibleGrid(attendedPage!.attendedWidgets(),
+                screenWidth: attendedPage!.pageStates.screenWidth,
+                padding: padding)));
+    return rc;
+  }
+
+  @override
+  void initState() {
+    super.initState();
+  }
+}
diff --git a/lib/page/users/edit_user_page.dart b/lib/page/users/edit_user_page.dart
new file mode 100644 (file)
index 0000000..4c6bcf3
--- /dev/null
@@ -0,0 +1,31 @@
+// DO NOT CHANGE. This file is created by the meta_tool!
+import 'package:flutter/material.dart';
+
+import '../../meta/users_meta.dart';
+import '../../setting/global_data.dart';
+import '../../widget/attended_page.dart';
+import 'edit_user_custom.dart';
+
+class EditUserPage extends StatefulWidget {
+  final GlobalData globalData;
+  final PageStates pageStates = PageStates();
+  EditUserPage(this.globalData) : super();
+  @override
+  _EditUserPageState createState() {
+    final rc = _EditUserPageState();
+    rc.attendedPage =
+        AttendedPage(this, rc, globalData, UserMeta.instance, pageStates);
+    pageStates.attendedPage = rc.attendedPage;
+    return rc;
+  }
+}
+
+class _EditUserPageState extends EditUserCustom {
+  @override
+  void didChangeDependencies() {
+    final size = MediaQuery.of(context).size;
+    attendedPage!.pageStates.screenWidth = size.width;
+    attendedPage!.pageStates.screenHeight = size.height;
+    super.didChangeDependencies();
+  }
+}
diff --git a/lib/page/users/list_user_custom.dart b/lib/page/users/list_user_custom.dart
new file mode 100644 (file)
index 0000000..e2b1db5
--- /dev/null
@@ -0,0 +1,34 @@
+// This file is created by the meta_tool. But it can be customized.
+// It will never overridden by the meta_tool.
+import 'package:flutter/material.dart';
+
+import '../../base/i18n.dart';
+import '../../setting/global_data.dart';
+import '../../widget/attended_page.dart';
+import '../../widget/widget_form.dart';
+import 'list_user_page.dart';
+
+final i18n = I18N();
+
+class ListUserCustom extends State<ListUserPage> {
+  final globalData = GlobalData();
+  AttendedPage? attendedPage;
+
+  @override
+  Widget build(BuildContext context) {
+    final padding = 16.0;
+    final rc = Scaffold(
+        appBar: globalData.appBarBuilder(i18n.tr('Change User data')),
+        drawer: globalData.drawerBuilder(context),
+        body: SafeArea(
+            child: WidgetForm.flexibleGrid(attendedPage!.attendedWidgets(),
+                screenWidth: attendedPage!.pageStates.screenWidth,
+                padding: padding)));
+    return rc;
+  }
+
+  @override
+  void initState() {
+    super.initState();
+  }
+}
diff --git a/lib/page/users/list_user_page.dart b/lib/page/users/list_user_page.dart
new file mode 100644 (file)
index 0000000..b05bbf3
--- /dev/null
@@ -0,0 +1,31 @@
+// DO NOT CHANGE. This file is created by the meta_tool!
+import 'package:flutter/material.dart';
+
+import '../../meta/users_meta.dart';
+import '../../setting/global_data.dart';
+import '../../widget/attended_page.dart';
+import 'list_user_custom.dart';
+
+class ListUserPage extends StatefulWidget {
+  final GlobalData globalData;
+  final PageStates pageStates = PageStates();
+  ListUserPage(this.globalData) : super();
+  @override
+  _ListUserPageState createState() {
+    final rc = _ListUserPageState();
+    rc.attendedPage =
+        AttendedPage(this, rc, globalData, UserMeta.instance, pageStates);
+    pageStates.attendedPage = rc.attendedPage;
+    return rc;
+  }
+}
+
+class _ListUserPageState extends ListUserCustom {
+  @override
+  void didChangeDependencies() {
+    final size = MediaQuery.of(context).size;
+    attendedPage!.pageStates.screenWidth = size.width;
+    attendedPage!.pageStates.screenHeight = size.height;
+    super.didChangeDependencies();
+  }
+}
diff --git a/lib/page/users/users_edit_page.dart b/lib/page/users/users_edit_page.dart
deleted file mode 100644 (file)
index 4f66ce6..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-import 'package:flutter/material.dart';
-
-import '../../base/i18n.dart';
-import '../../meta/users_meta.dart';
-import '../../setting/global_data.dart';
-import '../../widget/attended_page.dart';
-import '../../widget/widget_form.dart';
-
-final i18n = I18N();
-
-class UsersEditPage extends StatefulWidget {
-  final GlobalData globalData;
-  final PageStates pageStates = PageStates();
-  UsersEditPage(this.globalData) : super();
-  @override
-  _UsersEditPageState createState() {
-    final rc = _UsersEditPageState();
-    rc.attendedPage =
-        AttendedPage(this, rc, globalData, UserMeta.instance, pageStates);
-    pageStates.attendedPage = rc.attendedPage;
-    return rc;
-  }
-}
-
-class _UsersEditPageState extends State<UsersEditPage> {
-  AttendedPage? attendedPage;
-
-  final nameController = TextEditingController();
-  final globalData = GlobalData();
-  @override
-  Widget build(BuildContext context) {
-    final padding = 16.0;
-    final rc = Scaffold(
-        appBar: globalData.appBarBuilder(i18n.tr('Change User data')),
-        drawer: globalData.drawerBuilder(context),
-        body: SafeArea(
-            child: WidgetForm.flexibleGrid(attendedPage!.attendedWidgets(),
-                screenWidth: attendedPage!.pageStates.screenWidth,
-                padding: padding)));
-    return rc;
-  }
-
-  @override
-  void didChangeDependencies() {
-    final size = MediaQuery.of(context).size;
-    attendedPage!.pageStates.screenWidth = size.width;
-    attendedPage!.pageStates.screenHeight = size.height;
-    super.didChangeDependencies();
-  }
-
-  @override
-  void initState() {
-    super.initState();
-  }
-}
index e88c5b8620cbd05d4af2996955cef0036caec1a2..cf9cebbfad1d5f589baecae9da0be1722083f8e0 100644 (file)
@@ -3,6 +3,9 @@ import 'package:flutter/material.dart';
 import '../meta/module_meta_data.dart';
 import '../widget/attended_page.dart';
 
+typedef MenuItemBuilder = List<DropdownMenuItem<T>> Function<T>(
+    PropertyMetaData propertyMetaData);
+
 /// Manages a widget controlled by the meta data.
 abstract class AttendedWidget {
   final AttendedPage attendedPage;
diff --git a/metatool/Compile b/metatool/Compile
new file mode 100755 (executable)
index 0000000..4fb1079
--- /dev/null
@@ -0,0 +1,5 @@
+#! /bin/bash
+APP=meta_tool
+/usr/bin/dart compile exe bin/$APP.dart -o ../tools/$APP
+
+
diff --git a/metatool/bin/generator.dart b/metatool/bin/generator.dart
new file mode 100644 (file)
index 0000000..fa0bb51
--- /dev/null
@@ -0,0 +1,197 @@
+import 'dart:io';
+
+import 'package:dart_bones/dart_bones.dart';
+import 'package:path/path.dart';
+
+import '../lib/meta/module_meta_data.dart';
+import '../lib/meta/modules.dart';
+import 'generator_base.dart';
+import 'page_generator.dart';
+
+class Generator extends PageGenerator {
+  Generator(BaseLogger logger) : super(logger);
+
+  ModuleMetaData? checkModule(List<String> args, int index) {
+    ModuleMetaData? rc;
+    if (index >= args.length) {
+      logger.error('+++ too few arguments. Missing MODULE.');
+    } else if ((rc = moduleByName(args[index])) == null) {
+      logger.error('+++ unknown module: ${args[index]}');
+      rc = null;
+    }
+    return rc;
+  }
+
+  /// Returns a Dart class definition of the [module].
+  String createModuleData(ModuleMetaData module) {
+    final buffer = StringBuffer();
+    PropertyMetaData? primary = module.primaryOf();
+    buffer.writeln('// DO NOT CHANGE. This file is created by the meta_tool');
+    buffer.writeln("import '../../base/defines.dart';");
+    buffer.writeln("import '../../base/helper.dart';");
+    buffer.writeln("import '../../persistence/data_record.dart';");
+    final type = 'int';
+    buffer.writeln(
+        'class ${module.moduleNameSingular}Data extends DataRecord<$type>{');
+    for (var item in module.propertyList) {
+      buffer.writeln('  ' + module.dartType(item.dataType) + '? ${item.name};');
+    }
+    buffer.writeln('  ${module.moduleNameSingular}Data({');
+    String separator = '';
+    for (var item in module.propertyList) {
+      buffer.write('$separator this.${item.name}');
+      separator = ',';
+    }
+    buffer.writeln('});');
+    buffer.writeln(
+        '  ${module.moduleNameSingular}Data.createFromMap(DataMap map) {');
+    buffer.writeln('    fromMap(map);');
+    buffer.writeln('  }');
+    buffer.writeln('  @override');
+    buffer.writeln('  void fromMap(DataMap map) {');
+    for (var item in module.propertyList) {
+      final name = item.columnName;
+      final type = item.dataType.toString();
+      //id = data.containsKey('id') ? int.tryParse(data['id']) : null;
+      buffer.writeln(
+          "    ${item.name} = map.containsKey('$name') ? valueOf($type, "
+          "map['$name']) : null;");
+    }
+    buffer.writeln('  }');
+    final name = primary == null ? 'id' : primary.name;
+    final name2 = primary == null ? 'id' : primary.columnName;
+    buffer.write('''  @override
+  int keyOf() {
+    return $name ?? 0;
+  }
+  @override
+  String nameOfKey(){
+    return '$name2';
+  }
+''');
+    buffer.writeln('  static DataType? dataTypeOf(String name) {');
+    buffer.writeln('    DataType? rc;');
+    buffer.writeln('    switch(name){');
+    for (var item in module.propertyList) {
+      buffer.writeln("    case '${item.name}':");
+      buffer.writeln('      rc = ${item.dataType};');
+      buffer.writeln('      break;');
+    }
+    buffer.write('''    default:
+      break;
+    }
+    return rc;
+  }
+''');
+    buffer.write('''  @override
+  DataMap toMap({DataMap? map, bool clear = true}) {
+    map ??= DataMap();
+    if (clear) {
+      map.clear();
+    }
+''');
+    for (var item in module.propertyList) {
+      final name1 = item.columnName;
+      final name2 = item.name;
+      buffer.writeln("    map['$name1'] = $name2;");
+    }
+    buffer.write('''    return map;
+  }
+}
+''');
+    return buffer.toString();
+  }
+
+  /// Creates the file modules.dart.
+  String createModules() {
+    final buffer = StringBuffer();
+    buffer.write('// DO NOT CHANGE. This file is created by the meta_tool\n');
+    buffer.write("import 'module_meta_data.dart';\n");
+    final modules = <String>[];
+    final files = <String>[];
+    final fileOfModule = <String, String>{};
+
+    for (var item in Directory('lib/meta').listSync()) {
+      final name = basename(item.path);
+      if (name.endsWith('_meta.dart')) {
+        final moduleName =
+            GeneratorBase.filenameToClass(name.replaceFirst('_meta.dart', ''));
+        fileOfModule[moduleName] = name;
+        modules.add(moduleName);
+        files.add(name);
+      }
+    }
+    modules.sort();
+    files.sort();
+    for (var file in files) {
+      buffer.write("import '$file';\n");
+    }
+    buffer.write('''
+/// Returns the meta data of the module given by [name].
+/// Returns null if not found.
+ModuleMetaData? moduleByName(String name) {
+  ModuleMetaData? rc;
+  switch (name) {
+''');
+    for (var module in modules) {
+      buffer.write("    case '$module':\n");
+      final className = findClass(fileOfModule[module]!);
+      buffer.write("      rc = $className();\n");
+      buffer.write("      break;\n");
+    }
+    buffer.write('''    default:
+      break;
+  }
+  return rc;
+}
+/// Returns the module names as string list.
+List<String> moduleNames(){
+  return [
+''');
+    for (var module in modules) {
+      buffer.write("    '$module',\n");
+    }
+    buffer.write('''  ];
+}
+''');
+    return buffer.toString();
+  }
+
+  /// Finds the class name of a module meta class given by the file [node].
+  String findClass(String node) {
+    final contents = File('lib/meta/$node').readAsStringSync();
+    RegExpMatch? match;
+    String rc;
+    if ((match = RegExp(r'class\s+(\w+)\s').firstMatch(contents)) != null) {
+      rc = match!.group(1)!;
+    } else {
+      logger.error('+++ missing class in $node');
+      rc = GeneratorBase.filenameToClass(node.replaceFirst('.dart', ''));
+    }
+    return rc;
+  }
+
+  /// Generates all modules defined in the meta data.
+  void updateModules(Generator generator) {
+    writeFile('lib/meta/modules.dart', createModules());
+    final modules = moduleNames();
+    for (var name in modules) {
+      ModuleMetaData? module = moduleByName(name);
+      if (module == null) {
+        logger.error('+++ unknown module: $name');
+      } else {
+        String filename =
+            GeneratorBase.classToFilename(module.moduleNameSingular) +
+                '_data.dart';
+        final directory = name.toLowerCase();
+        writeFile('../../lib/page/$directory/$filename',
+            generator.createModuleData(module));
+      }
+    }
+  }
+
+  /// Generates the modules.dart.
+  void updateModulesList(Generator generator) {
+    writeFile('lib/meta/modules.dart', createModules());
+  }
+}
diff --git a/metatool/bin/generator_base.dart b/metatool/bin/generator_base.dart
new file mode 100644 (file)
index 0000000..fc5fb42
--- /dev/null
@@ -0,0 +1,81 @@
+import 'dart:io';
+
+import 'package:dart_bones/dart_bones.dart';
+import 'package:path/path.dart';
+
+/// The base class of all [Generator]s.
+class GeneratorBase {
+  final BaseLogger logger;
+  GeneratorBase(this.logger);
+
+  /// Appends a [string] at the end of a [buffer].
+  /// If [buffer] is null a new instance is created.
+  /// If the last line in [buffer] has a length greater than [maxLength]
+  /// a newline is inserted and [indent] spaces.
+  /// If [separator] is not null that is inserted above the [string].
+  /// Returns the buffer (for chaining).
+  StringBuffer addToBuffer(String string,
+      {required int maxLength,
+      String? separator,
+      int indent = 0,
+      StringBuffer? buffer}) {
+    buffer ??= StringBuffer();
+    if (separator != null) {
+      buffer.write(separator);
+    }
+    final last = buffer.toString().lastIndexOf('\n');
+    if (buffer.length - (last < 0 ? 0 : last) + string.length > maxLength) {
+      buffer.write('\n' + (' ' * indent));
+    }
+    buffer.write(string);
+    return buffer;
+  }
+
+  /// Replaces print().
+  void out(String line) {
+    logger.log(line);
+  }
+
+  String toCamelCase(String name) {
+    final rc = name.isEmpty ? '' : (name[0].toUpperCase() + name.substring(1));
+    return rc;
+  }
+
+  /// Writes a given [contents] into a file named [filename];
+  void writeFile(String filename, String contents) {
+    logger.log('creating $filename ...');
+    final file = File(filename);
+    final parent = Directory(dirname(filename));
+    if (!parent.existsSync()) {
+      parent.createSync(recursive: true);
+    }
+    file.writeAsStringSync(contents);
+  }
+
+  /// Converts a [className] into a file name using Dart conventions.
+  /// Example: "UserData" is converted to "user_data"
+  static String classToFilename(String className) {
+    String upperCase = className.toUpperCase();
+    String rc = '';
+    for (var ix = 0; ix < className.length; ix++) {
+      if (className[ix] == upperCase[ix] && ix > 0) {
+        rc += '_';
+      }
+      rc += className[ix];
+    }
+    return rc.toLowerCase();
+  }
+
+  /// Converts a [filename] into a class name using Dart conventions.
+  /// Example: "user_data.dart" is converted to "UserData"
+  static String filenameToClass(String filename) {
+    final parts = basenameWithoutExtension(filename).split('_');
+    String rc = '';
+    for (var part in parts) {
+      if (part.isNotEmpty) {
+        rc += part[0].toUpperCase() + part.substring(1);
+      }
+    }
+    return rc;
+  }
+}
diff --git a/metatool/bin/meta_tool.dart b/metatool/bin/meta_tool.dart
new file mode 100644 (file)
index 0000000..603757a
--- /dev/null
@@ -0,0 +1,114 @@
+import 'package:dart_bones/dart_bones.dart';
+import '../lib/base/i18n_io.dart';
+import '../lib/meta/module_meta_data.dart';
+import '../lib/meta/modules.dart';
+
+import 'generator.dart';
+
+void main(List<String> args) {
+  final logger = MemoryLogger(LEVEL_DETAIL);
+  final generator = Generator(logger);
+  I18nIo.internal('data/i18n', logger);
+  if (args.isEmpty) {
+    usage(generator);
+    generator.out('+++ missing arguments.');
+  } else {
+    ModuleMetaData? metaData;
+    switch (args[0]) {
+      case 'all-modules':
+        generator.out(moduleNames().join('\n'));
+        break;
+      case 'show-modules-list':
+        generator.out(generator.createModules());
+        break;
+      case 'show-page':
+        if (args.length < 3) {
+          generator.out('+++ missing PAGE');
+        } else if ((metaData = generator.checkModule(args, 1)) != null) {
+          final pageName = args[2];
+          final page = metaData!.pageByName(pageName);
+          if (page == null) {
+            generator.out('+++ unknown page: $pageName');
+          } else {
+            generator.out((generator.createPage(page)));
+          }
+        }
+        break;
+      case 'show-custom-page':
+        if (args.length < 3) {
+          generator.out('+++ missing PAGE');
+        } else if ((metaData = generator.checkModule(args, 1)) != null) {
+          final pageName = args[2];
+          final page = metaData!.pageByName(pageName);
+          if (page == null) {
+            generator.out('+++ unknown page: $pageName');
+          } else {
+            generator.out((generator.createCustomized(page)));
+          }
+        }
+        break;
+      case 'show-table':
+        if ((metaData = generator.checkModule(args, 1)) != null) {
+          generator.out(generator.createDbTable(metaData!));
+        }
+        break;
+      case 'show-sql':
+        if ((metaData = generator.checkModule(args, 1)) != null) {
+          generator.out(generator.createSqlStatements(metaData!));
+        }
+        break;
+      case 'show-data':
+        if ((metaData = generator.checkModule(args, 1)) != null) {
+          generator.out(generator.createModuleData(metaData!));
+        }
+        break;
+      case 'update-modules-files':
+        generator.updateModules(generator);
+        generator.updatePages(generator);
+        break;
+      case 'update-modules-names':
+        generator.updateModulesList(generator);
+        break;
+      case 'update-sql':
+        generator.updateSql(generator);
+        break;
+      default:
+        usage(generator);
+        generator.out('+++ unknown MODE: ${args[0]}.');
+        break;
+    }
+  }
+}
+
+void usage(Generator generator) {
+  generator.out('''Usage: meta_tool MODE MODULE
+  Generate code specified by MODE for the module MODULE.
+MODE:
+  all-modules
+    Shows the module names.
+  show-modules-list
+    Shows the file lib/meta/modules.dart.
+    That works without of meta-data: It can be called before
+    creating a the meta_tool.
+  show-page MODULE PAGE
+    Shows the page PAGE from the module MODULE.
+  show-table MODULE
+    Shows the SQL table definition of MODULE.
+  show-data MODULE
+    Creates the data storage class of MODULE
+  show-sql MODULE
+    Creates the SQL statements (for the rest_server) of MODULE
+  update-modules-names
+    Generates the modules.dart file in the correct directory.
+    That works without of meta-data: It can be called before
+    creating a the meta_tool.
+  update-modules-files
+    Generates all module files depending on the meta data.
+  update-sql
+    Generates all Sql statement files depending on the meta data.
+MODULE: 
+  The name of the module, e.g. "Users"
+Example:
+meta_tool print-table Users
+''');
+}
diff --git a/metatool/bin/page_generator.dart b/metatool/bin/page_generator.dart
new file mode 100644 (file)
index 0000000..6c13ce6
--- /dev/null
@@ -0,0 +1,236 @@
+import 'dart:io';
+
+import 'package:dart_bones/dart_bones.dart';
+
+import '../lib/meta/module_meta_data.dart';
+import '../lib/meta/modules.dart';
+import 'generator.dart';
+import 'generator_base.dart';
+import 'sql_generator.dart';
+
+/// Handles the page generation. Is a base class of [Generator].
+class PageGenerator extends SqlGenerator {
+  static final templatePage = '''
+// DO NOT CHANGE. This file is created by the meta_tool!
+import 'package:flutter/material.dart';
+
+import '../../meta/users_meta.dart';
+import '../../setting/global_data.dart';
+import '../../widget/attended_page.dart';
+import 'edit_user_custom.dart';
+
+class EditUserPage extends StatefulWidget {
+  final GlobalData globalData;
+  final PageStates pageStates = PageStates();
+  EditUserPage(this.globalData) : super();
+  @override
+  _EditUserPageState createState() {
+    final rc = _EditUserPageState();
+    rc.attendedPage =
+        AttendedPage(this, rc, globalData, UserMeta.instance, pageStates);
+    pageStates.attendedPage = rc.attendedPage;
+    return rc;
+  }
+}
+
+class _EditUserPageState extends EditUserCustom {
+  @override
+  void didChangeDependencies() {
+    final size = MediaQuery.of(context).size;
+    attendedPage!.pageStates.screenWidth = size.width;
+    attendedPage!.pageStates.screenHeight = size.height;
+    super.didChangeDependencies();
+  }
+}
+''';
+
+  static final templateCustom = '''// This file is created by the meta_tool. But it can be customized.
+// It will never overridden by the meta_tool.
+import 'package:flutter/material.dart';
+
+import '../../base/i18n.dart';
+import '../../setting/global_data.dart';
+import '../../widget/attended_page.dart';
+import '../../widget/widget_form.dart';
+import 'edit_user_page.dart';
+
+final i18n = I18N();
+
+class EditUserCustom extends State<EditUserPage> {
+  final globalData = GlobalData();
+  AttendedPage? attendedPage;
+
+
+  @override
+  Widget build(BuildContext context) {
+    final padding = 16.0;
+    final rc = Scaffold(
+        appBar: globalData.appBarBuilder(i18n.tr('Change User data')),
+        drawer: globalData.drawerBuilder(context),
+        body: SafeArea(
+            child: WidgetForm.flexibleGrid(attendedPage!.attendedWidgets(),
+                screenWidth: attendedPage!.pageStates.screenWidth,
+                padding: padding)));
+    return rc;
+  }
+  @override
+  void initState() {
+    super.initState();
+  }
+}
+''';
+  static final templateCollection = '''// DO NOT CHANGE. This file is created by the meta_tool!
+import 'package:flutter/material.dart';
+import '../setting/global_data.dart';
+import 'page_collection_custom.dart';
+
+#IMPORTS#
+
+/// Manages all meta data driven pages of the program.
+class PageCollection {
+  static PageCollection? _instance;
+  PageCollection.internal();
+  factory PageCollection() {
+    _instance ??= PageCollection.internal();
+    return _instance!;
+  }
+  final map = <String, StatefulWidget>{};
+
+  /// Creates a page defined by a [route].
+  StatefulWidget? newPageByRoute(String route) {
+    StatefulWidget? rc;
+    final globalData = GlobalData();
+    switch (route) {
+#CASES#      default:
+        rc = customPageByRoute(route, globalData);
+        break;
+    }
+    if (rc != null) {
+      map[route] = rc;
+    }
+    return rc;
+  }
+
+  /// Returns the last generated page of a given [route].
+  StatefulWidget? existingPageByRoute(String route) {
+    return map[route];
+  }
+}
+''';
+  static final templateCollectionCustom = '''// This file is created by the meta_tool. But it can be customized.
+// It will never overridden by the meta_tool.
+import 'package:flutter/material.dart';
+import '../setting/global_data.dart';
+import 'info_page.dart';
+import 'log_page.dart';
+
+/// Creates a page defined by a [route].
+///
+/// Note: only pages without meta data definitions should be handled here.
+StatefulWidget? customPageByRoute(String route, GlobalData globalData) {
+  StatefulWidget? rc;
+  switch (route) {
+    case '/info':
+      rc = InfoPage(globalData);
+      break;
+    case '/log':
+      rc = LogPage(globalData);
+      break;
+    default:
+      break;
+  }
+  return rc;
+}
+''';
+  static final templateCase = '''      case '/users/edit':
+        rc = EditUserPage(globalData);
+        break;
+''';
+  PageGenerator(BaseLogger logger) : super(logger);
+
+  String replaceVariables(String template, PageMetaData page) {
+    final camelModules = toCamelCase(page.module.moduleName);
+    final camelModule = toCamelCase(page.module.moduleNameSingular);
+    final camelPageName = toCamelCase(page.name);
+    final rc = template
+        .replaceAll('Users', camelModules)
+        .replaceAll('User', camelModule)
+        .replaceAll('Edit', camelPageName)
+        .replaceAll('edit', page.name)
+        .replaceAll('users', page.module.moduleName.toLowerCase())
+        .replaceAll('user', page.module.moduleNameSingular.toLowerCase());
+        return rc;
+  }
+  /// Returns a Dart class definition of the [page] that should not be modified.
+  String createCustomized(PageMetaData page) {
+    var rc = replaceVariables(templateCustom, page);
+    return rc;
+  }
+
+  /// Returns a Dart class definition of the [page] that should not be modified.
+  String createPage(PageMetaData page) {
+    var rc = replaceVariables(templatePage, page);
+    return rc;
+  }
+
+  /// Creates the file page_collection.dart and (if it does not exist) the file
+  /// page_collection_custom.dart.
+  void updateCollection(){
+    final modules = moduleNames();
+    final imports = StringBuffer();
+    final cases = StringBuffer();
+    for (var name in modules){
+      ModuleMetaData? module = moduleByName(name);
+      for (var page in module!.pageList) {
+        imports.writeln("import '${name.toLowerCase()}/${page.name}_"
+        "${module.moduleNameSingular.toLowerCase()}_page.dart';");
+        cases.write(replaceVariables(templateCase, page));
+      }
+    }
+    final content = templateCollection.replaceFirst('#IMPORTS#', imports.toString())
+    .replaceFirst('#CASES#', cases.toString());
+    writeFile('../lib/page/page_collection.dart', content);
+    final full = '../lib/page/page_collection_custom.dart';
+    if (File(full).existsSync()){
+      logger.log('$full already exists. We do not override');
+    } else {
+      writeFile(full, templateCollectionCustom);
+    }
+  }
+  /// Generates all modules defined in the meta data.
+  void updatePages(Generator generator) {
+    if (!Directory('bin').existsSync() ||
+        !Directory('../lib/page').existsSync()) {
+      logger.error('missing ../lib/page: The program must be started '
+          'in the directory "metatool". cwd: ${Directory.current.path}');
+    } else {
+      final modules = moduleNames();
+      for (var name in modules) {
+        ModuleMetaData? module = moduleByName(name);
+        if (module == null) {
+          logger.error('+++ unknown module: $name');
+        } else {
+          for (var page in module.pageList) {
+            String filename = page.name +
+                '_' +
+                module.moduleNameSingular.toLowerCase() +
+                '_page.dart';
+            var full = '../lib/page/${name.toLowerCase()}/$filename';
+            writeFile(full, generator.createPage(page));
+            filename = page.name +
+                '_' +
+                module.moduleNameSingular.toLowerCase() +
+                '_custom.dart';
+            full = '../lib/page/${name.toLowerCase()}/$filename';
+            if (File(full).existsSync()) {
+              out('$filename already exists. We do not override.');
+            } else {
+              writeFile(full, generator.createCustomized(page));
+            }
+          }
+        }
+      }
+      updateCollection();
+    }
+  }
+}
diff --git a/metatool/bin/sql_generator.dart b/metatool/bin/sql_generator.dart
new file mode 100644 (file)
index 0000000..8efbb1d
--- /dev/null
@@ -0,0 +1,143 @@
+import 'dart:io';
+
+import 'package:dart_bones/dart_bones.dart';
+import '../lib/meta/module_meta_data.dart';
+import '../lib/meta/modules.dart';
+
+import 'generator.dart';
+import 'generator_base.dart';
+
+/// Handles the SQL generation part of the generator.
+class SqlGenerator extends GeneratorBase {
+  SqlGenerator(BaseLogger logger) : super(logger);
+
+  /// Returns a DDL statement creating the database table of the [module].
+  String createDbTable(ModuleMetaData module) {
+    final tableName = module.tableName;
+    final list = module.propertyList;
+    final buffer = StringBuffer();
+    buffer.write('-- DO NOT CHANGE. This file is created by the meta_tool\n');
+    buffer.write('CREATE TABLE $tableName (\n');
+    String? primary;
+    for (var item in list) {
+      if (item.options.contains('primary')) {
+        primary = item.columnName;
+      }
+      buffer.write(' ${item.columnName}');
+      buffer.write(' ' + module.mySqlType(item.dataType, item.size));
+      buffer.write(module.dbOptions(item) + ',\n');
+    }
+    buffer.write('  PRIMARY KEY ($primary)\n');
+    buffer.write(');\n');
+    return buffer.toString();
+  }
+
+  /// Returns the SQL statements for insert, update, delete...
+  /// for a given [module].
+  /// This yaml file is a configuration for the rest_server.
+  String createSqlStatements(ModuleMetaData module) {
+    final moduleName = module.moduleName;
+    final tableName = module.tableName;
+    final list = module.propertyList;
+    final buffer = StringBuffer();
+    buffer.write('---\n');
+    buffer.write('# DO NOT CHANGE. This file is created by the meta_tool\n');
+    buffer.write('''# SQL statements of the module "$moduleName":\n
+module: $moduleName
+list:
+  type: list
+  parameters: []
+  sql: "select * from $tableName;"
+byId:
+  type: record
+  parameters: [ "${list[0].name}" ]
+  sql: "select * from $tableName where ${list[0].columnName}=:${list[0].name};"
+delete:
+  type: delete
+  parameters: [ "${list[0].name}" ]
+  sql: "delete * from $tableName where ${list[0].columnName}=:${list[0].name};"
+update:
+  type: update
+''');
+    var items = module.standardColumns('changedBy');
+    var parameters = addToBuffer('  parameters: [', maxLength: 80);
+    var assignments = addToBuffer('    ', maxLength: 80);
+    var first = true;
+    for (var item in items) {
+      addToBuffer('":${item.name}"',
+          maxLength: 80,
+          separator: first ? null : ',',
+          indent: 4,
+          buffer: parameters);
+      addToBuffer('${item.columnName}=:${item.name}',
+          maxLength: 80,
+          separator: first ? null : ',',
+          indent: 4,
+          buffer: assignments);
+      first = false;
+    }
+    parameters.write(']');
+    var item = module.properties['changed']!;
+    addToBuffer('${item.columnName}=NOW()',
+        maxLength: 80, separator: ',', indent: 4, buffer: assignments);
+    // parameters: [":id", ":name", ":displayname", ":email", ":changedby"]
+    buffer.writeln(parameters);
+    buffer.write('  sql: "UPDATE $tableName SET\n');
+    buffer.writeln(assignments);
+    //user_name=:name, user_displayname=:displayname, user_email=:email, user_changed=NOW(), user_changedby=:changedby
+    buffer.write('    WHERE ${list[0].columnName}=:${list[0].name};"\n');
+
+    items = module.standardColumns('createdBy');
+    parameters = addToBuffer('  parameters: [', maxLength: 80);
+    final sql1 = addToBuffer('  sql: "INSERT INTO $tableName(', maxLength: 80);
+    final sql2 = addToBuffer('    VALUES(', maxLength: 80);
+    first = true;
+    for (var item in items) {
+      addToBuffer('":${item.name}"',
+          maxLength: 80,
+          separator: first ? null : ',',
+          indent: 4,
+          buffer: parameters);
+      addToBuffer('${item.columnName}',
+          maxLength: 80,
+          separator: first ? null : ',',
+          indent: 6,
+          buffer: sql1);
+      addToBuffer(':${item.name}',
+          maxLength: 80,
+          separator: first ? null : ',',
+          indent: 6,
+          buffer: sql2);
+      first = false;
+    }
+    parameters.write(']');
+    item = module.properties['created']!;
+    addToBuffer('${item.columnName})',
+        maxLength: 80, separator: ',', indent: 4, buffer: sql1);
+    addToBuffer('NOW());"',
+        maxLength: 80, separator: ',', indent: 4, buffer: sql2);
+    // parameters: [":id", ":name", ":displayname", ":email", ":changedby"]
+    buffer.write('''insert:
+  type: insert
+''');
+    buffer.writeln(parameters);
+    buffer.writeln(sql1);
+    buffer.writeln(sql2);
+    return buffer.toString();
+  }
+
+  /// Generates the Sql statement file for each module defined in the meta data.
+  void updateSql(Generator generator) {
+    final modules = moduleNames();
+    for (var name in modules) {
+      ModuleMetaData? module = moduleByName(name);
+      if (module == null) {
+        logger.error('+++ unknown module: $name');
+      } else {
+        logger.log('current directory: ${Directory.current.path}');
+        String filename = 'rest_server/data/sql/${name.toLowerCase()}.sql.yaml';
+        writeFile(filename, generator.createSqlStatements(module));
+      }
+    }
+  }
+}
diff --git a/metatool/pubspec.yaml b/metatool/pubspec.yaml
new file mode 100644 (file)
index 0000000..224883d
--- /dev/null
@@ -0,0 +1,18 @@
+name: meta-tool
+description: A generator of programming code for building Flutter apps.
+version: 1.0.0
+homepage: https://github.com/hamatoma/exhibition
+
+environment:
+  sdk: ">=2.12.0 <3.0.0"
+
+dependencies:
+  meta: ^1.7.0
+  sprintf: ^6.0.0
+  yaml: ^3.1.0
+  path: ^1.8.0
+
+dev_dependencies:
+  pedantic: ^1.11.1
+  test: ^1.17.10
+
index 1de4d8344bfdbb4f923ff6e1cec44f5a60ef4e37..ed0fb863c975e8585a3de96270fcfa5af510cb31 100644 (file)
@@ -30,12 +30,12 @@ dependencies:
   flutter:
     sdk: flutter
   path: ^1.8.0
-  dart_bones: "^1.1.1"
-  url_launcher: ^6.0.3
+  dart_bones: ^1.2.2
+  url_launcher: ^6.0.9
   flutter_markdown: ^0.6.1
   flutter_bloc: ^7.1.0
   equatable: ^2.0.3
-  permission_handler: ^7.0.0
+  permission_handler: ^8.1.6
   # The following adds the Cupertino Icons font to your application.
   # Use with the CupertinoIcons class for iOS style icons.
   cupertino_icons: ^1.0.2