diff --git a/lib/src/commands/service.dart b/lib/src/commands/service.dart index ad970320..73090211 100644 --- a/lib/src/commands/service.dart +++ b/lib/src/commands/service.dart @@ -1,19 +1,20 @@ import 'dart:io'; import 'package:args/command_runner.dart'; +import 'package:code_builder/code_builder.dart'; import 'package:console/console.dart'; -import 'package:id/id.dart'; +import 'package:inflection/inflection.dart'; import 'package:recase/recase.dart'; +import 'service_generators/service_generators.dart'; import 'init.dart' show preBuild; +const List GENERATORS = const [ + const MapServiceGenerator(), + const MongoServiceGenerator(), + const RethinkServiceGenerator(), + const CustomServiceGenerator() +]; + class ServiceCommand extends Command { - final String CUSTOM = 'Custom'; - final String MEMORY = 'In-Memory'; - final String MEMORY_JSON = 'In-Memory (serialized via `source_gen`)'; - final String MONGO = 'MongoDB'; - final String MONGO_TYPED = 'MongoDB (typed)'; - final String MONGO_TYPED_JSON = - 'MongoDB (typed, serialized via `source_gen`)'; - final String TRESTLE = 'Trestle'; final TextPen _pen = new TextPen(); @override @@ -22,130 +23,146 @@ class ServiceCommand extends Command { @override String get description => 'Creates a new service within the given project.'; - String _snake(name) => idFromString(name).snake; - @override run() async { var name = await readInput('Name of Service (not plural): '); - var chooser = new Chooser([MONGO, MONGO_TYPED, MEMORY, CUSTOM], + var chooser = new Chooser( + GENERATORS.map((g) => g.name).toList(), message: 'What type of service would you like to create? '); var type = await chooser.choose(); - fail() { - _pen.red(); - _pen('Could not successfully create service $name.'); - _pen(); - } + print('Wrap this service in a TypedService? (slight performance cost)'); + chooser = new Chooser(['Yes', 'No']); + var typed = (await chooser.choose()) == 'Yes'; - String serviceSource = ''; + var generator = + GENERATORS.firstWhere((g) => g.name == type, orElse: () => null); - if (type == MONGO) { - serviceSource = _generateMongoService(name); - } else if (type == MONGO_TYPED) { - serviceSource = _generateMongoTypedService(name); - await _generateMongoModel(name); - await _generateValidator(name); - } else if (type == MONGO_TYPED_JSON) { - serviceSource = _generateMongoTypedService(name); - await _generateMongoModelJson(name); - await _generateValidator(name); - } else if (type == MEMORY) { - serviceSource = _generateMemoryService(name); - await _generateMemoryModel(name); - await _generateValidator(name); - } else if (type == MEMORY_JSON) { - serviceSource = _generateMemoryService(name); - await _generateMemoryModelJson(name); - await _generateValidator(name); - } else if (type == CUSTOM) { - serviceSource = _generateCustomService(name); - } else if (type == TRESTLE) { + if (generator == null) { _pen.blue(); - _pen('${Icon.STAR} Trestle services are not yet implemented. :('); + _pen('${Icon.STAR} \'$type\' services are not yet implemented. :('); _pen(); } else { - print('Code to generate a $type service is not yet written.'); + var rc = new ReCase(name); + name = rc.pascalCase; + var lower = rc.snakeCase; + var servicesDir = new Directory('lib/src/services'); + var serviceFile = + new File.fromUri(servicesDir.uri.resolve('$lower.dart')); + var testDir = new Directory('test/services'); + var testFile = + new File.fromUri(testDir.uri.resolve('${lower}_test.dart')); + + if (!await servicesDir.exists()) + await servicesDir.create(recursive: true); + if (!await testDir.exists()) await testDir.create(recursive: true); + + await serviceFile + .writeAsString(_generateService(generator, name, lower, typed)); + await testFile.writeAsString(_generateTests(lower, type)); + + var runConfig = new File('./.idea/runConfigurations/${name}_Tests.xml'); + + if (!await runConfig.exists()) { + await runConfig.create(recursive: true); + await runConfig.writeAsString(_generateRunConfiguration(name, lower)); + } + + if (generator.createsModel == true) { + await _generateModel(name, lower); + } + + if (generator.createsValidator == true) { + await _generateValidator(lower, rc.constantCase); + } + + if (generator.exportedInServiceLibrary == true) { + var serviceLibrary = new File('lib/src/models/models.dart'); + await serviceLibrary.writeAsString("\nexport '$lower.dart';", + mode: FileMode.APPEND); + } + + if (generator.shouldRunBuild == true) { + await preBuild(Directory.current); + } + + _pen.green(); + _pen('${Icon.CHECKMARK} Successfully generated service $name.'); + _pen(); } - - if (serviceSource.isEmpty) { - fail(); - throw new Exception('Empty generated service code.'); - } - - var lower = _snake(name); - var servicesDir = new Directory('lib/src/services'); - var serviceFile = new File.fromUri(servicesDir.uri.resolve('$lower.dart')); - var testDir = new Directory('test/services'); - var testFile = new File.fromUri(testDir.uri.resolve('${lower}_test.dart')); - - if (!await servicesDir.exists()) await servicesDir.create(recursive: true); - - if (!await testDir.exists()) await testDir.create(recursive: true); - - await serviceFile.writeAsString(serviceSource); - - if (type == MONGO_TYPED || type == MEMORY) { - var serviceLibrary = new File('lib/src/models/models.dart'); - await serviceLibrary.writeAsString("\nexport '$lower.dart';", - mode: FileMode.APPEND); - await preBuild(Directory.current); - } - - await testFile.writeAsString(_generateTests(name, type)); - - var runConfig = new File('./.idea/runConfigurations/${name}_Tests.xml'); - - if (!await runConfig.exists()) { - await runConfig.create(recursive: true); - await runConfig.writeAsString(_generateRunConfiguration(name)); - } - - _pen.green(); - _pen('${Icon.CHECKMARK} Successfully generated service $name.'); - _pen(); } - _generateValidator(String name) async { - var rc = new ReCase(name); - var file = new File('lib/src/validators/${rc.snakeCase}.dart'); + String _generateService( + ServiceGenerator generator, String name, String lower, bool typed) { + var lib = new LibraryBuilder(); - if (!await file.exists()) await file.createSync(recursive: true); + /* + import 'package:angel_framework/angel_framework.dart'; + import '../models/$lower.dart'; + export '../models/$lower.dart'; + */ + lib.addMember( + new ImportBuilder('package:angel_framework/angel_framework.dart')); + generator.applyToLibrary(lib, name, lower); - await file.writeAsString(''' -import 'package:angel_validate/angel_validate.dart'; + if (generator.createsModel == true) { + lib + ..addMember(new ImportBuilder('../models/$lower.dart')) + ..addMember(new ExportBuilder('../models/$lower.dart')); + } -final Validator CREATE_${rc.constantCase} = - new Validator({'name*': isString, 'desc*': isString}); - ''' - .trim()); + // configureServer() {} + var configureServer = new MethodBuilder('configureServer', + returnType: new TypeBuilder('AngelConfigurer')); + generator.applyToConfigureServer(configureServer, name, lower); + + // return (Angel app) async {} + var closure = new MethodBuilder.closure(modifier: MethodModifier.asAsync) + ..addPositional(parameter('app', [new TypeBuilder('Angel')])); + generator.beforeService(closure, name, lower); + + // app.use('/api/todos', new MapService()); + var service = generator.createInstance(closure, name, lower); + + if (typed == true) { + service = + new TypeBuilder('TypedService', genericTypes: [new TypeBuilder(name)]) + .newInstance([service]); + } + + closure.addStatement(reference('app') + .invoke('use', [literal('/api/${pluralize(lower)}'), service])); + + if (generator.injectsSingleton == true) { + closure.addStatement(varField('service', + value: reference('app') + .invoke('service', [literal('/api/${pluralize(lower)}')]).castAs( + new TypeBuilder('HookedService')))); + closure.addStatement(reference('app') + .property('container') + .invoke('singleton', [reference('service').property('inner')])); + } + + configureServer.addStatement(closure.asReturn()); + + lib.addMember(configureServer); + + return prettyToSource(lib.buildAst()); } - _generateCustomService(String name) { - return ''' -import 'package:angel_framework/angel_framework.dart'; - -class ${name}Service extends Service { - ${name}Service():super() { - // Your logic here! - } -} - ''' - .trim(); - } - - _generateMemoryModel(String name) async { - var lower = _snake(name); + _generateModel(String name, String lower) async { var file = new File('lib/src/models/$lower.dart'); if (!await file.exists()) await file.createSync(recursive: true); await file.writeAsString(''' -library angel.models.$lower; import 'package:angel_framework/common.dart'; class $name extends Model { - String name, desc; + String name; + + String desc; $name({String id, this.name, this.desc}) { this.id = id; @@ -155,197 +172,21 @@ class $name extends Model { .trim()); } - _generateMemoryModelJson(String name) async { - var lower = _snake(name); - var file = new File('lib/src/models/$lower.dart'); + _generateValidator(String lower, String constantCase) async { + var file = new File('lib/src/validators/$lower.dart'); if (!await file.exists()) await file.createSync(recursive: true); await file.writeAsString(''' -library angel.models.$lower; - -import 'package:angel_framework/common.dart'; -import 'package:source_gen/generators/json_serializable.dart'; - -part '$lower.g.dart'; - -@JsonSerializable() -class $name extends Model with _\$${name}SerializerMixin { - @JsonKey('id') - @override - String id; - - @JsonKey('name') - String name; - - @JsonKey('desc') - String desc; - - factory $name.fromJson(Map json) => _\$${name}FromJson(json); - - $name({this.id, this.name, this.desc}); -} - ''' - .trim()); - } - - _generateMemoryService(String name) { - var rc = new ReCase(name); - var lower = rc.snakeCase; - - return ''' -import 'package:angel_framework/angel_framework.dart'; -import '../models/$lower.dart'; -export '../models/$lower.dart'; - -/// Manages [$name] in-memory. -class ${name}Service extends MemoryService<$name> { - ${name}Service():super() { - // Your logic here! - } -} - ''' - .trim(); - } - - _generateMongoModel(String name) async { - var lower = _snake(name); - var file = new File('lib/src/models/$lower.dart'); - - if (!await file.exists()) await file.createSync(recursive: true); - - await file.writeAsString(''' -library angel.models.$lower; - -import 'package:angel_mongo/model.dart'; -import 'package:source_gen/generators/json_serializable.dart'; - -part '$lower.g.dart'; - -@JsonSerializable() -class $name extends Model with _\$${name}SerializerMixin { - @JsonKey('id') - @override - String id; - - @JsonKey('name') - String name; - - @JsonKey('desc') - String desc; - - factory $name.fromJson(Map json) => _\$${name}FromJson(json); - - $name({this.id, this.name, this.desc}); -} - ''' - .trim()); - } - - _generateMongoModelJson(String name) async { - var lower = _snake(name); - var file = new File('lib/src/models/$lower.dart'); - - if (!await file.exists()) await file.createSync(recursive: true); - - await file.writeAsString(''' -library angel.models.$lower; - -import 'package:angel_framework/common.dart'; -import 'package:source_gen/generators/json_serializable.dart'; - -part '$lower.g.dart'; - -@JsonSerializable() -class $name extends Model with _\$${name}SerializerMixin { - @JsonKey('id') - @override - String id; - - @JsonKey('name') - String name; - - @JsonKey('desc') - String desc; - - factory $name.fromJson(Map json) => _\$${name}FromJson(json); - - $name({this.id, this.name, this.desc}); -} - ''' - .trim()); - } - - _generateMongoService(String name) { - var lower = _snake(name); - - return ''' -import 'package:angel_framework/angel_framework.dart'; -import 'package:angel_mongo/angel_mongo.dart'; import 'package:angel_validate/angel_validate.dart'; -import 'package:angel_validate/server.dart'; -import 'package:mongo_dart/mongo_dart.dart'; -final Validator ${lower}Schema = new Validator({ - 'name*': [isString, isNotEmpty], - 'desc*': [isString, isNotEmpty] -}); - -configureServer(Db db) { - return (Angel app) async { - app.use('/api/${lower}s', new ${name}Service(db.collection('${lower}s'))); - - HookedService service = app.service('api/${lower}s'); - app.container.singleton(service.inner); - - service - ..beforeCreate.listen(validateEvent(${lower}Schema)) - ..beforeUpdate.listen(validateEvent(${lower}Schema)); - }; -} - -/// Manages [$name] in the database. -class ${name}Service extends MongoService { - ${name}Service(collection):super(collection) { - // Your logic here! - } -} +final Validator CREATE_$constantCase = + new Validator({'name*': isString, 'desc*': isString}); ''' - .trim(); + .trim()); } - _generateMongoTypedService(String name) { - var lower = _snake(name); - - return ''' -import 'package:angel_framework/angel_framework.dart'; -import 'package:angel_mongo/angel_mongo.dart'; -import 'package:mongo_dart/mongo_dart.dart'; -import '../models/$lower.dart'; -export '../models/$lower.dart'; - -configureServer(Db db) { - return (Angel app) async { - app.use('/api/${lower}s', new ${name}Service(db.collection('${lower}s'))); - - HookedService service = app.service('api/${lower}s'); - app.container.singleton(service.inner); - }; -} - -/// Manages [$name] in the database. -class ${name}Service extends MongoTypedService<$name> { - ${name}Service(collection):super(collection) { - // Your logic here! - } -} - ''' - .trim(); - } - - _generateRunConfiguration(String name) { - var lower = _snake(name); - + _generateRunConfiguration(String name, String lower) { return ''' @@ -357,9 +198,7 @@ class ${name}Service extends MongoTypedService<$name> { .trim(); } - _generateTests(String name, String type) { - var lower = _snake(name); - + _generateTests(String lower, String type) { return ''' import 'dart:io'; import 'package:angel/angel.dart'; @@ -382,13 +221,13 @@ main() async { }); test('index via REST', () async { - var response = await client.get('/api/${lower}s'); + var response = await client.get('/api/${pluralize(lower)}'); expect(response, hasStatus(HttpStatus.OK)); }); - test('Index ${lower}s', () async { - var ${lower}s = await client.service('api/${lower}s').index(); - print(${lower}s); + test('Index ${pluralize(lower)}', () async { + var ${pluralize(lower)} = await client.service('api/${pluralize(lower)}').index(); + print(${pluralize(lower)}); }); } diff --git a/lib/src/commands/service_generators/custom.dart b/lib/src/commands/service_generators/custom.dart new file mode 100644 index 00000000..7d1f7f90 --- /dev/null +++ b/lib/src/commands/service_generators/custom.dart @@ -0,0 +1,24 @@ +import 'package:code_builder/code_builder.dart'; +import 'generator.dart'; + +class CustomServiceGenerator extends ServiceGenerator { + @override + bool get createsModel => false; + + @override + bool get createsValidator => false; + + const CustomServiceGenerator() : super('Custom'); + + @override + void applyToLibrary(LibraryBuilder library, String name, String lower) { + var clazz = new ClassBuilder('${name}Service', asExtends: new TypeBuilder('Service')); + library.addMember(clazz); + } + + @override + ExpressionBuilder createInstance( + MethodBuilder methodBuilder, String name, String lower) { + return new TypeBuilder('${name}Service').newInstance([]); + } +} diff --git a/lib/src/commands/service_generators/generator.dart b/lib/src/commands/service_generators/generator.dart new file mode 100644 index 00000000..280962eb --- /dev/null +++ b/lib/src/commands/service_generators/generator.dart @@ -0,0 +1,24 @@ +import 'package:code_builder/code_builder.dart'; + +class ServiceGenerator { + final String name; + + const ServiceGenerator(this.name); + + bool get createsModel => true; + bool get createsValidator => true; + bool get exportedInServiceLibrary => true; + bool get injectsSingleton => true; + bool get shouldRunBuild => false; + + void applyToLibrary(LibraryBuilder library, String name, String lower) {} + + void beforeService(MethodBuilder methodBuilder, String name, String lower) {} + + void applyToConfigureServer( + MethodBuilder configureServer, String name, String lower) {} + + ExpressionBuilder createInstance( + MethodBuilder methodBuilder, String name, String lower) => + literal(null); +} diff --git a/lib/src/commands/service_generators/map.dart b/lib/src/commands/service_generators/map.dart new file mode 100644 index 00000000..d0f7eb44 --- /dev/null +++ b/lib/src/commands/service_generators/map.dart @@ -0,0 +1,15 @@ +import 'generator.dart'; +import 'package:code_builder/code_builder.dart'; + +class MapServiceGenerator extends ServiceGenerator { + @override + bool get createsModel => false; + + const MapServiceGenerator() : super('In-Memory'); + + @override + ExpressionBuilder createInstance( + MethodBuilder methodBuilder, String name, String lower) { + return new TypeBuilder('MapService').newInstance([]); + } +} diff --git a/lib/src/commands/service_generators/mongo.dart b/lib/src/commands/service_generators/mongo.dart new file mode 100644 index 00000000..1e9d1fd5 --- /dev/null +++ b/lib/src/commands/service_generators/mongo.dart @@ -0,0 +1,29 @@ +import 'generator.dart'; +import 'package:code_builder/code_builder.dart'; +import 'package:inflection/inflection.dart'; + +class MongoServiceGenerator extends ServiceGenerator { + const MongoServiceGenerator() : super('MongoDB'); + + @override + void applyToConfigureServer( + MethodBuilder configureServer, String name, String lower) { + configureServer.addPositional(parameter('db', [new TypeBuilder('Db')])); + } + + @override + void applyToLibrary(LibraryBuilder library, String name, String lower) { + library.addMembers([ + 'package:angel_mongo/angel_mongo.dart', + 'package:mongo_dart/mongo_dart.dart' + ].map((str) => new ImportBuilder(str))); + } + + @override + ExpressionBuilder createInstance( + MethodBuilder methodBuilder, String name, String lower) { + return new TypeBuilder('MongoService').newInstance([ + reference('db').invoke('collection', [literal(pluralize(lower))]) + ]); + } +} diff --git a/lib/src/commands/service_generators/rethink.dart b/lib/src/commands/service_generators/rethink.dart new file mode 100644 index 00000000..80c8646c --- /dev/null +++ b/lib/src/commands/service_generators/rethink.dart @@ -0,0 +1,31 @@ +import 'generator.dart'; +import 'package:code_builder/code_builder.dart'; +import 'package:inflection/inflection.dart'; + +class RethinkServiceGenerator extends ServiceGenerator { + const RethinkServiceGenerator() : super('RethinkDB'); + + @override + void applyToConfigureServer( + MethodBuilder configureServer, String name, String lower) { + configureServer + ..addPositional(parameter('connection', [new TypeBuilder('Connection')])) + ..addPositional(parameter('r', [new TypeBuilder('Rethinkdb')])); + } + + @override + void applyToLibrary(LibraryBuilder library, String name, String lower) { + library.addMembers([ + 'package:angel_rethink/angel_rethink.dart', + 'package:rethinkdb_driver/rethinkdb_driver.dart' + ].map((str) => new ImportBuilder(str))); + } + + @override + ExpressionBuilder createInstance( + MethodBuilder methodBuilder, String name, String lower) { + return new TypeBuilder('RethinkService').newInstance([ + reference('r').invoke('table', [literal(pluralize(lower))]) + ]); + } +} diff --git a/lib/src/commands/service_generators/service_generators.dart b/lib/src/commands/service_generators/service_generators.dart new file mode 100644 index 00000000..760cd927 --- /dev/null +++ b/lib/src/commands/service_generators/service_generators.dart @@ -0,0 +1,5 @@ +export 'custom.dart'; +export 'generator.dart'; +export 'map.dart'; +export 'mongo.dart'; +export 'rethink.dart'; \ No newline at end of file diff --git a/lib/src/commands/service_old.dart b/lib/src/commands/service_old.dart new file mode 100644 index 00000000..ad970320 --- /dev/null +++ b/lib/src/commands/service_old.dart @@ -0,0 +1,398 @@ +import 'dart:io'; +import 'package:args/command_runner.dart'; +import 'package:console/console.dart'; +import 'package:id/id.dart'; +import 'package:recase/recase.dart'; +import 'init.dart' show preBuild; + +class ServiceCommand extends Command { + final String CUSTOM = 'Custom'; + final String MEMORY = 'In-Memory'; + final String MEMORY_JSON = 'In-Memory (serialized via `source_gen`)'; + final String MONGO = 'MongoDB'; + final String MONGO_TYPED = 'MongoDB (typed)'; + final String MONGO_TYPED_JSON = + 'MongoDB (typed, serialized via `source_gen`)'; + final String TRESTLE = 'Trestle'; + final TextPen _pen = new TextPen(); + + @override + String get name => 'service'; + + @override + String get description => 'Creates a new service within the given project.'; + + String _snake(name) => idFromString(name).snake; + + @override + run() async { + var name = await readInput('Name of Service (not plural): '); + var chooser = new Chooser([MONGO, MONGO_TYPED, MEMORY, CUSTOM], + message: 'What type of service would you like to create? '); + var type = await chooser.choose(); + + fail() { + _pen.red(); + _pen('Could not successfully create service $name.'); + _pen(); + } + + String serviceSource = ''; + + if (type == MONGO) { + serviceSource = _generateMongoService(name); + } else if (type == MONGO_TYPED) { + serviceSource = _generateMongoTypedService(name); + await _generateMongoModel(name); + await _generateValidator(name); + } else if (type == MONGO_TYPED_JSON) { + serviceSource = _generateMongoTypedService(name); + await _generateMongoModelJson(name); + await _generateValidator(name); + } else if (type == MEMORY) { + serviceSource = _generateMemoryService(name); + await _generateMemoryModel(name); + await _generateValidator(name); + } else if (type == MEMORY_JSON) { + serviceSource = _generateMemoryService(name); + await _generateMemoryModelJson(name); + await _generateValidator(name); + } else if (type == CUSTOM) { + serviceSource = _generateCustomService(name); + } else if (type == TRESTLE) { + _pen.blue(); + _pen('${Icon.STAR} Trestle services are not yet implemented. :('); + _pen(); + } else { + print('Code to generate a $type service is not yet written.'); + } + + if (serviceSource.isEmpty) { + fail(); + throw new Exception('Empty generated service code.'); + } + + var lower = _snake(name); + var servicesDir = new Directory('lib/src/services'); + var serviceFile = new File.fromUri(servicesDir.uri.resolve('$lower.dart')); + var testDir = new Directory('test/services'); + var testFile = new File.fromUri(testDir.uri.resolve('${lower}_test.dart')); + + if (!await servicesDir.exists()) await servicesDir.create(recursive: true); + + if (!await testDir.exists()) await testDir.create(recursive: true); + + await serviceFile.writeAsString(serviceSource); + + if (type == MONGO_TYPED || type == MEMORY) { + var serviceLibrary = new File('lib/src/models/models.dart'); + await serviceLibrary.writeAsString("\nexport '$lower.dart';", + mode: FileMode.APPEND); + await preBuild(Directory.current); + } + + await testFile.writeAsString(_generateTests(name, type)); + + var runConfig = new File('./.idea/runConfigurations/${name}_Tests.xml'); + + if (!await runConfig.exists()) { + await runConfig.create(recursive: true); + await runConfig.writeAsString(_generateRunConfiguration(name)); + } + + _pen.green(); + _pen('${Icon.CHECKMARK} Successfully generated service $name.'); + _pen(); + } + + _generateValidator(String name) async { + var rc = new ReCase(name); + var file = new File('lib/src/validators/${rc.snakeCase}.dart'); + + if (!await file.exists()) await file.createSync(recursive: true); + + await file.writeAsString(''' +import 'package:angel_validate/angel_validate.dart'; + +final Validator CREATE_${rc.constantCase} = + new Validator({'name*': isString, 'desc*': isString}); + ''' + .trim()); + } + + _generateCustomService(String name) { + return ''' +import 'package:angel_framework/angel_framework.dart'; + +class ${name}Service extends Service { + ${name}Service():super() { + // Your logic here! + } +} + ''' + .trim(); + } + + _generateMemoryModel(String name) async { + var lower = _snake(name); + var file = new File('lib/src/models/$lower.dart'); + + if (!await file.exists()) await file.createSync(recursive: true); + + await file.writeAsString(''' +library angel.models.$lower; + +import 'package:angel_framework/common.dart'; + +class $name extends Model { + String name, desc; + + $name({String id, this.name, this.desc}) { + this.id = id; + } +} + ''' + .trim()); + } + + _generateMemoryModelJson(String name) async { + var lower = _snake(name); + var file = new File('lib/src/models/$lower.dart'); + + if (!await file.exists()) await file.createSync(recursive: true); + + await file.writeAsString(''' +library angel.models.$lower; + +import 'package:angel_framework/common.dart'; +import 'package:source_gen/generators/json_serializable.dart'; + +part '$lower.g.dart'; + +@JsonSerializable() +class $name extends Model with _\$${name}SerializerMixin { + @JsonKey('id') + @override + String id; + + @JsonKey('name') + String name; + + @JsonKey('desc') + String desc; + + factory $name.fromJson(Map json) => _\$${name}FromJson(json); + + $name({this.id, this.name, this.desc}); +} + ''' + .trim()); + } + + _generateMemoryService(String name) { + var rc = new ReCase(name); + var lower = rc.snakeCase; + + return ''' +import 'package:angel_framework/angel_framework.dart'; +import '../models/$lower.dart'; +export '../models/$lower.dart'; + +/// Manages [$name] in-memory. +class ${name}Service extends MemoryService<$name> { + ${name}Service():super() { + // Your logic here! + } +} + ''' + .trim(); + } + + _generateMongoModel(String name) async { + var lower = _snake(name); + var file = new File('lib/src/models/$lower.dart'); + + if (!await file.exists()) await file.createSync(recursive: true); + + await file.writeAsString(''' +library angel.models.$lower; + +import 'package:angel_mongo/model.dart'; +import 'package:source_gen/generators/json_serializable.dart'; + +part '$lower.g.dart'; + +@JsonSerializable() +class $name extends Model with _\$${name}SerializerMixin { + @JsonKey('id') + @override + String id; + + @JsonKey('name') + String name; + + @JsonKey('desc') + String desc; + + factory $name.fromJson(Map json) => _\$${name}FromJson(json); + + $name({this.id, this.name, this.desc}); +} + ''' + .trim()); + } + + _generateMongoModelJson(String name) async { + var lower = _snake(name); + var file = new File('lib/src/models/$lower.dart'); + + if (!await file.exists()) await file.createSync(recursive: true); + + await file.writeAsString(''' +library angel.models.$lower; + +import 'package:angel_framework/common.dart'; +import 'package:source_gen/generators/json_serializable.dart'; + +part '$lower.g.dart'; + +@JsonSerializable() +class $name extends Model with _\$${name}SerializerMixin { + @JsonKey('id') + @override + String id; + + @JsonKey('name') + String name; + + @JsonKey('desc') + String desc; + + factory $name.fromJson(Map json) => _\$${name}FromJson(json); + + $name({this.id, this.name, this.desc}); +} + ''' + .trim()); + } + + _generateMongoService(String name) { + var lower = _snake(name); + + return ''' +import 'package:angel_framework/angel_framework.dart'; +import 'package:angel_mongo/angel_mongo.dart'; +import 'package:angel_validate/angel_validate.dart'; +import 'package:angel_validate/server.dart'; +import 'package:mongo_dart/mongo_dart.dart'; + +final Validator ${lower}Schema = new Validator({ + 'name*': [isString, isNotEmpty], + 'desc*': [isString, isNotEmpty] +}); + +configureServer(Db db) { + return (Angel app) async { + app.use('/api/${lower}s', new ${name}Service(db.collection('${lower}s'))); + + HookedService service = app.service('api/${lower}s'); + app.container.singleton(service.inner); + + service + ..beforeCreate.listen(validateEvent(${lower}Schema)) + ..beforeUpdate.listen(validateEvent(${lower}Schema)); + }; +} + +/// Manages [$name] in the database. +class ${name}Service extends MongoService { + ${name}Service(collection):super(collection) { + // Your logic here! + } +} + ''' + .trim(); + } + + _generateMongoTypedService(String name) { + var lower = _snake(name); + + return ''' +import 'package:angel_framework/angel_framework.dart'; +import 'package:angel_mongo/angel_mongo.dart'; +import 'package:mongo_dart/mongo_dart.dart'; +import '../models/$lower.dart'; +export '../models/$lower.dart'; + +configureServer(Db db) { + return (Angel app) async { + app.use('/api/${lower}s', new ${name}Service(db.collection('${lower}s'))); + + HookedService service = app.service('api/${lower}s'); + app.container.singleton(service.inner); + }; +} + +/// Manages [$name] in the database. +class ${name}Service extends MongoTypedService<$name> { + ${name}Service(collection):super(collection) { + // Your logic here! + } +} + ''' + .trim(); + } + + _generateRunConfiguration(String name) { + var lower = _snake(name); + + return ''' + + + + +''' + .trim(); + } + + _generateTests(String name, String type) { + var lower = _snake(name); + + return ''' +import 'dart:io'; +import 'package:angel/angel.dart'; +import 'package:angel_framework/angel_framework.dart'; +import 'package:angel_test/angel_test.dart'; +import 'package:test/test.dart'; + +main() async { + Angel app; + TestClient client; + + setUp(() async { + app = await createServer(); + client = await connectTo(app); + }); + + tearDown(() async { + await client.close(); + app = null; + }); + + test('index via REST', () async { + var response = await client.get('/api/${lower}s'); + expect(response, hasStatus(HttpStatus.OK)); + }); + + test('Index ${lower}s', () async { + var ${lower}s = await client.service('api/${lower}s').index(); + print(${lower}s); + }); +} + + ''' + .trim(); + } +} diff --git a/pubspec.yaml b/pubspec.yaml index 3d466f53..87f1192a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,13 +2,15 @@ author: "Tobe O " description: "Command-line tools for the Angel framework." homepage: "https://github.com/angel-dart/angel_cli" name: "angel_cli" -version: "1.0.0" +version: "1.0.1" dependencies: analyzer: "^0.29.0" args: "^0.13.7" + code_builder: "^1.0.0-alpha" console: "^2.2.3" glob: "^1.1.0" id: "^1.0.0" + inflection: "^0.4.1" pubspec: "^0.0.14" random_string: "^0.0.1" recase: "^1.0.0"