diff --git a/.idea/vcs.xml b/.idea/vcs.xml
deleted file mode 100644
index 94a25f7f..00000000
--- a/.idea/vcs.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 00000000..ac03088e
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,14 @@
+# 1.1.5
+Deprecated several commands, in favor of the `make`
+command:
+* `controller`
+* `plugin`
+* `service`
+* `test`
+
+The `rename` command will now replace *all* occurrences
+of the old project names with the new one in `config/`
+YAML files, and also operates on the glob `config/**/*.yaml`.
+
+Changed the call to run `angel start` to run `dart bin/server.dart` instead, after an
+`init` command.
\ No newline at end of file
diff --git a/bin/angel.dart b/bin/angel.dart
index 97f470fb..90aa8cd0 100644
--- a/bin/angel.dart
+++ b/bin/angel.dart
@@ -22,6 +22,7 @@ main(List args) async {
..addCommand(new StartCommand())
..addCommand(new RenameCommand())
..addCommand(new UpdateCommand())
+ ..addCommand(new MakeCommand())
..addCommand(new VersionCommand());
return await runner.run(args).then((_) {}).catchError((exc) {
diff --git a/lib/src/commands/commands.dart b/lib/src/commands/commands.dart
index 4264689c..d5360558 100644
--- a/lib/src/commands/commands.dart
+++ b/lib/src/commands/commands.dart
@@ -4,6 +4,7 @@ export 'controller.dart';
export "doctor.dart";
export "key.dart";
export "init.dart";
+export "make.dart";
export "plugin.dart";
export "rename.dart";
export "service.dart";
diff --git a/lib/src/commands/controller.dart b/lib/src/commands/controller.dart
index fbf5a06d..ce9458e6 100644
--- a/lib/src/commands/controller.dart
+++ b/lib/src/commands/controller.dart
@@ -4,6 +4,7 @@ import 'package:code_builder/code_builder.dart';
import "package:console/console.dart";
import 'package:pubspec/pubspec.dart';
import 'package:recase/recase.dart';
+import 'deprecated.dart';
class ControllerCommand extends Command {
final TextPen _pen = new TextPen();
@@ -17,6 +18,8 @@ class ControllerCommand extends Command {
@override
run() async {
+ warnDeprecated(this.name, _pen);
+
final name = await readInput("Name of Controller: "),
recase = new ReCase(name),
lower = recase.snakeCase;
diff --git a/lib/src/commands/deprecated.dart b/lib/src/commands/deprecated.dart
new file mode 100644
index 00000000..992da105
--- /dev/null
+++ b/lib/src/commands/deprecated.dart
@@ -0,0 +1,10 @@
+import 'package:console/console.dart';
+
+void warnDeprecated(String command, [TextPen pen]) {
+ pen ??= new TextPen();
+ pen
+ ..yellow()
+ ..call('The `$command` command is deprecated, and will be removed by v1.2.0.')
+ ..call()
+ ..reset();
+}
diff --git a/lib/src/commands/init.dart b/lib/src/commands/init.dart
index 57e1a8e3..c854f249 100644
--- a/lib/src/commands/init.dart
+++ b/lib/src/commands/init.dart
@@ -60,7 +60,7 @@ class InitCommand extends Command {
..text('\nCongratulations! You are ready to start developing with Angel!')
..text('\nTo start the server (with file watching), run ')
..magenta()
- ..text('`angel start`')
+ ..text('`dart bin/server.dart`')
..normal()
..text(' in your terminal.')
..text('\n\nFind more documentation about Angel:')
@@ -192,7 +192,8 @@ const BoilerplateInfo ormBoilerplate = const BoilerplateInfo(
const List allBoilerplates = const [
fullApplicationBoilerplate,
- lightBoilerplate
+ lightBoilerplate,
+ ormBoilerplate
];
class BoilerplateInfo {
diff --git a/lib/src/commands/make.dart b/lib/src/commands/make.dart
new file mode 100644
index 00000000..0a71c5be
--- /dev/null
+++ b/lib/src/commands/make.dart
@@ -0,0 +1,22 @@
+import 'package:args/command_runner.dart';
+import 'make/controller.dart';
+import 'make/model.dart';
+import 'make/plugin.dart';
+import 'make/service.dart';
+import 'make/test.dart';
+
+class MakeCommand extends Command {
+ @override
+ String get name => 'make';
+
+ @override
+ String get description => 'Generates common code for your project, such as projects and controllers.';
+
+ MakeCommand() {
+ addSubcommand(new ControllerCommand());
+ addSubcommand(new ModelCommand());
+ addSubcommand(new PluginCommand());
+ addSubcommand(new TestCommand());
+ addSubcommand(new ServiceCommand());
+ }
+}
\ No newline at end of file
diff --git a/lib/src/commands/make/controller.dart b/lib/src/commands/make/controller.dart
new file mode 100644
index 00000000..ae36bd80
--- /dev/null
+++ b/lib/src/commands/make/controller.dart
@@ -0,0 +1,105 @@
+import 'dart:io';
+import 'package:args/command_runner.dart';
+import 'package:code_builder/dart/core.dart';
+import 'package:code_builder/code_builder.dart';
+import 'package:console/console.dart';
+import 'package:pubspec/pubspec.dart';
+import 'package:recase/recase.dart';
+import 'maker.dart';
+
+class ControllerCommand extends Command {
+ final TextPen _pen = new TextPen();
+
+ @override
+ String get name => 'controller';
+
+ @override
+ String get description => 'Generates a controller class.';
+
+ ControllerCommand() {
+ argParser
+ ..addFlag('websocket',
+ abbr: 'w',
+ help:
+ 'Generates a WebSocketController, instead of an HTTP controller.',
+ negatable: false)
+ ..addOption('name',
+ abbr: 'n', help: 'Specifies a name for the model class.')
+ ..addOption('output-dir',
+ help: 'Specifies a directory to create the controller class in.',
+ defaultsTo: 'lib/src/controllers');
+ }
+
+ @override
+ run() async {
+ var pubspec = await PubSpec.load(Directory.current);
+ String name;
+ if (argResults.wasParsed('name')) name = argResults['name'];
+
+ if (name?.isNotEmpty != true) {
+ var p = new Prompter('Name of Controller class: ');
+ name = await p.prompt(checker: (s) => s.isNotEmpty);
+ }
+
+ List deps = [
+ const MakerDependency('angel_framework', '^1.0.0')
+ ];
+
+ var rc = new ReCase(name);
+ var controllerLib =
+ new LibraryBuilder('${pubspec.name}.src.controllers.${rc.snakeCase}');
+
+ if (argResults['websocket']) {
+ deps.add(const MakerDependency('angel_websocket', '^1.0.0'));
+ controllerLib.addDirective(
+ new ImportBuilder('package:angel_websocket/server.dart'));
+ } else
+ controllerLib.addDirective(
+ new ImportBuilder('package:angel_framework/angel_framework.dart'));
+
+ TypeBuilder parentType = new TypeBuilder(
+ argResults['websocket'] ? 'WebSocketController' : 'Controller');
+ ClassBuilder clazz =
+ new ClassBuilder('${rc.pascalCase}Controller', asExtends: parentType);
+ controllerLib.addMember(clazz);
+
+ if (argResults['websocket']) {
+ var meth = new MethodBuilder('hello', returnType: lib$core.$void);
+ meth.addAnnotation(new TypeBuilder('ExposeWs')
+ .constInstance([literal('get_${rc.snakeCase}')]));
+ meth.addPositional(
+ parameter('socket', [new TypeBuilder('WebSocketContext')]));
+ meth.addStatement(reference('socket').invoke('send', [
+ literal('got_${rc.snakeCase}'),
+ map({'message': literal('Hello, world!')})
+ ]));
+ clazz.addMethod(meth);
+ } else {
+ clazz.addAnnotation(new TypeBuilder('Expose')
+ .constInstance([literal('/${rc.snakeCase}')]));
+
+ var meth = new MethodBuilder('hello',
+ returnType: lib$core.String, returns: literal('Hello, world!'));
+ meth.addAnnotation(
+ new TypeBuilder('Expose').constInstance([literal('/')]));
+ clazz.addMethod(meth);
+ }
+
+ var outputDir = new Directory.fromUri(
+ Directory.current.uri.resolve(argResults['output-dir']));
+ var controllerFile =
+ new File.fromUri(outputDir.uri.resolve('${rc.snakeCase}.dart'));
+ if (!await controllerFile.exists())
+ await controllerFile.create(recursive: true);
+ await controllerFile
+ .writeAsString(prettyToSource(controllerLib.buildAst()));
+ _pen
+ ..green()
+ ..call(
+ '${Icon.CHECKMARK} Created controller file "${controllerFile.absolute.path}".')
+ ..call()
+ ..reset();
+
+ if (deps.isNotEmpty) await depend(deps);
+ }
+}
diff --git a/lib/src/commands/make/maker.dart b/lib/src/commands/make/maker.dart
new file mode 100644
index 00000000..bbee58b4
--- /dev/null
+++ b/lib/src/commands/make/maker.dart
@@ -0,0 +1,58 @@
+import 'dart:async';
+import 'dart:io';
+import 'package:pubspec/pubspec.dart';
+import 'package:pub_semver/pub_semver.dart';
+import '../pub.dart';
+
+class MakerDependency {
+ final String name, version;
+ final bool dev;
+ const MakerDependency(this.name, this.version, {this.dev: false});
+}
+
+Future depend(Iterable deps) async {
+ var pubspec = await PubSpec.load(Directory.current);
+ Map newDeps = {}, newDevDeps = {};
+
+ for (var dep in deps) {
+ var isPresent = false;
+ if (dep.dev)
+ isPresent = pubspec.devDependencies.containsKey(dep.name);
+ else
+ isPresent = pubspec.dependencies.containsKey(dep.name);
+
+ if (!isPresent) {
+ print('Installing ${dep.name}@${dep.version}...');
+
+ if (dep.dev)
+ newDevDeps[dep.name] =
+ new HostedReference(new VersionConstraint.parse(dep.version));
+ else
+ newDeps[dep.name] =
+ new HostedReference(new VersionConstraint.parse(dep.version));
+ }
+
+ if (newDeps.isNotEmpty || newDevDeps.isNotEmpty) {
+ var newPubspec = pubspec.copy(
+ dependencies:
+ new Map.from(pubspec.dependencies)
+ ..addAll(newDeps),
+ devDependencies:
+ new Map.from(pubspec.devDependencies)
+ ..addAll(newDevDeps));
+
+ await newPubspec.save(Directory.current);
+ var pubPath = resolvePub();
+
+ print('Now running `$pubPath get`...');
+
+ var pubGet = await Process.start(pubPath, ['get']);
+ pubGet.stdout.listen(stdout.add);
+ pubGet.stderr.listen(stderr.add);
+
+ var code = await pubGet.exitCode;
+
+ if (code != 0) throw 'pub get terminated with exit code $code';
+ }
+ }
+}
diff --git a/lib/src/commands/make/model.dart b/lib/src/commands/make/model.dart
new file mode 100644
index 00000000..208678af
--- /dev/null
+++ b/lib/src/commands/make/model.dart
@@ -0,0 +1,161 @@
+import 'dart:io';
+import 'package:args/command_runner.dart';
+import 'package:code_builder/dart/core.dart';
+import 'package:code_builder/code_builder.dart';
+import 'package:console/console.dart';
+import 'package:inflection/inflection.dart';
+import 'package:pubspec/pubspec.dart';
+import 'package:recase/recase.dart';
+import 'maker.dart';
+
+class ModelCommand extends Command {
+ final TextPen _pen = new TextPen();
+
+ @override
+ String get name => 'model';
+
+ @override
+ String get description => 'Generates a model class.';
+
+ ModelCommand() {
+ argParser
+ ..addFlag('migration',
+ abbr: 'm',
+ help: 'Generate an angel_orm migration file.',
+ negatable: false)
+ ..addFlag('orm', help: 'Generate angel_orm code.', negatable: false)
+ ..addFlag('serializable',
+ help: 'Generate angel_serialize annotations.', defaultsTo: true)
+ ..addOption('name',
+ abbr: 'n', help: 'Specifies a name for the model class.')
+ ..addOption('output-dir',
+ help: 'Specifies a directory to create the model class in.',
+ defaultsTo: 'lib/src/models')
+ ..addOption('migration-dir',
+ help: 'Specifies a directory to create the migration class in.',
+ defaultsTo: 'tool/migrations');
+ }
+
+ @override
+ run() async {
+ var pubspec = await PubSpec.load(Directory.current);
+ String name;
+ if (argResults.wasParsed('name')) name = argResults['name'];
+
+ if (name?.isNotEmpty != true) {
+ var p = new Prompter('Name of Model class: ');
+ name = await p.prompt(checker: (s) => s.isNotEmpty);
+ }
+
+ List deps = [
+ const MakerDependency('angel_framework', '^1.0.0'),
+ const MakerDependency('angel_model', '^1.0.0'),
+ ];
+
+ var rc = new ReCase(name);
+ var modelLib =
+ new LibraryBuilder('${pubspec.name}.src.models.${rc.snakeCase}');
+ modelLib.addDirective(
+ new ImportBuilder('package:angel_model/angel_model.dart'));
+
+ var needsSerialize = argResults['serializable'] || argResults['orm'];
+
+ if (needsSerialize) {
+ modelLib.addDirective(
+ new ImportBuilder('package:angel_serialize/angel_serialize.dart'));
+ deps.add(const MakerDependency('angel_serialize', '^1.0.0-alpha'));
+ }
+
+ if (argResults['orm']) {
+ modelLib
+ .addDirective(new ImportBuilder('package:angel_orm/angel_orm.dart'));
+ deps.add(const MakerDependency('angel_orm', '^1.0.0-alpha'));
+ }
+
+ var modelClazz = new ClassBuilder(
+ needsSerialize ? '_${rc.pascalCase}' : rc.pascalCase,
+ asExtends: new TypeBuilder('Model'));
+ modelLib.addMember(modelClazz);
+
+ if (needsSerialize) {
+ modelLib.addDirective(new PartBuilder('${rc.snakeCase}.g.dart'));
+ modelClazz.addAnnotation(reference('serializable'));
+ }
+
+ if (argResults['orm']) {
+ modelClazz.addAnnotation(reference('orm'));
+ }
+
+ // Save model file
+ var outputDir = new Directory.fromUri(
+ Directory.current.uri.resolve(argResults['output-dir']));
+ var modelFile =
+ new File.fromUri(outputDir.uri.resolve('${rc.snakeCase}.dart'));
+ if (!await modelFile.exists()) await modelFile.create(recursive: true);
+ await modelFile.writeAsString(prettyToSource(modelLib.buildAst()));
+ _pen
+ ..green()
+ ..call(
+ '${Icon.CHECKMARK} Created model file "${modelFile.absolute.path}".')
+ ..call()
+ ..reset();
+
+ if (argResults['migration']) {
+ deps.add(
+ const MakerDependency('angel_migration', '^1.0.0-alpha', dev: true));
+
+ var migrationLib = new LibraryBuilder()
+ ..addDirective(
+ new ImportBuilder('package:angel_migration/angel_migration.dart'));
+ var migrationClazz = new ClassBuilder('${rc.pascalCase}Migration',
+ asExtends: new TypeBuilder('Migration'));
+ migrationLib.addMember(migrationClazz);
+ var tableName = pluralize(rc.snakeCase);
+
+ // up()
+ var up = new MethodBuilder('up', returnType: lib$core.$void);
+ migrationClazz.addMethod(up);
+ up.addAnnotation(lib$core.override);
+ up.addPositional(parameter('schema', [new TypeBuilder('Schema')]));
+
+ // (table) { ... }
+ var callback = new MethodBuilder.closure();
+ callback.addPositional(parameter('table'));
+
+ var cascade = reference('table').cascade((table) => [
+ table.invoke('serial', [literal('id')]).invoke('primaryKey', []),
+ table.invoke('date', [literal('created_at')]),
+ table.invoke('date', [literal('updated_at')])
+ ]);
+ callback.addStatement(cascade);
+
+ up.addStatement(reference('schema').invoke('create', [callback]));
+
+ // down()
+ var down = new MethodBuilder('down', returnType: lib$core.$void);
+ migrationClazz.addMethod(down);
+ down.addAnnotation(lib$core.override);
+ down.addPositional(parameter('schema', [new TypeBuilder('Schema')]));
+ down.addStatement(
+ reference('schema').invoke('drop', [literal(tableName)]));
+
+ // Save migration file
+ var migrationDir = new Directory.fromUri(
+ Directory.current.uri.resolve(argResults['migration-dir']));
+ var migrationFile =
+ new File.fromUri(migrationDir.uri.resolve('${rc.snakeCase}.dart'));
+ if (!await migrationFile.exists())
+ await migrationFile.create(recursive: true);
+ await migrationFile
+ .writeAsString(prettyToSource(migrationLib.buildAst()));
+ _pen
+ ..green()
+ ..call(
+ '${Icon.CHECKMARK} Created migration file "${migrationFile.absolute.path}".')
+ ..call()
+ ..reset();
+ }
+
+ if (deps.isNotEmpty) await depend(deps);
+ }
+}
diff --git a/lib/src/commands/make/plugin.dart b/lib/src/commands/make/plugin.dart
new file mode 100644
index 00000000..b6c1183f
--- /dev/null
+++ b/lib/src/commands/make/plugin.dart
@@ -0,0 +1,74 @@
+import 'dart:io';
+import 'package:args/command_runner.dart';
+import "package:console/console.dart";
+import 'package:dart_style/dart_style.dart';
+import 'package:pubspec/pubspec.dart';
+import 'package:recase/recase.dart';
+import 'maker.dart';
+
+class PluginCommand extends Command {
+ final TextPen _pen = new TextPen();
+
+ @override
+ String get name => "plugin";
+
+ @override
+ String get description => "Creates a new plug-in within the given project.";
+
+ PluginCommand() {
+ argParser
+ ..addOption('name',
+ abbr: 'n', help: 'Specifies a name for the plug-in class.')
+ ..addOption('output-dir',
+ help: 'Specifies a directory to create the plug-in class in.',
+ defaultsTo: 'lib/src/config/plugins');
+ }
+
+ @override
+ run() async {
+ var pubspec = await PubSpec.load(Directory.current);
+ String name;
+ if (argResults.wasParsed('name')) name = argResults['name'];
+
+ if (name?.isNotEmpty != true) {
+ var p = new Prompter('Name of Controller class: ');
+ name = await p.prompt(checker: (s) => s.isNotEmpty);
+ }
+
+ List deps = [
+ const MakerDependency('angel_framework', '^1.0.0')
+ ];
+
+ var rc = new ReCase(name);
+ final pluginDir = new Directory.fromUri(
+ Directory.current.uri.resolve(argResults['output-dir']));
+ final pluginFile =
+ new File.fromUri(pluginDir.uri.resolve("${rc.snakeCase}.dart"));
+ if (!await pluginFile.exists()) await pluginFile.create(recursive: true);
+ await pluginFile.writeAsString(
+ new DartFormatter().format(_generatePlugin(pubspec, rc)));
+
+ if (deps.isNotEmpty) await depend(deps);
+
+ _pen.green();
+ _pen(
+ '${Icon.CHECKMARK} Successfully generated plug-in file "${pluginFile.absolute.path}".');
+ _pen();
+ }
+
+ String _generatePlugin(PubSpec pubspec, ReCase rc) {
+ return '''
+library ${pubspec.name}.src.config.plugins.${rc.snakeCase};
+
+import 'dart:async';
+import 'package:angel_framework/angel_framework.dart';
+
+class ${rc.pascalCase}Plugin extends AngelPlugin {
+ @override
+ Future call(Angel app) async {
+ // Work some magic...
+ }
+}
+ ''';
+ }
+}
diff --git a/lib/src/commands/make/service.dart b/lib/src/commands/make/service.dart
new file mode 100644
index 00000000..62707fd4
--- /dev/null
+++ b/lib/src/commands/make/service.dart
@@ -0,0 +1,126 @@
+import 'dart:io';
+import 'package:args/command_runner.dart';
+import 'package:code_builder/code_builder.dart';
+import 'package:console/console.dart';
+import 'package:inflection/inflection.dart';
+import 'package:pubspec/pubspec.dart';
+import 'package:recase/recase.dart';
+import '../service_generators/service_generators.dart';
+import 'maker.dart';
+
+class ServiceCommand extends Command {
+ final TextPen _pen = new TextPen();
+
+ @override
+ String get name => 'service';
+
+ @override
+ String get description => 'Generates an Angel service.';
+
+ ServiceCommand() {
+ argParser
+ ..addFlag('typed',
+ abbr: 't',
+ help: 'Wrap the generated service in a `TypedService` instance.',
+ negatable: false)
+ ..addOption('name',
+ abbr: 'n', help: 'Specifies a name for the service file.')
+ ..addOption('output-dir',
+ help: 'Specifies a directory to create the service file.',
+ defaultsTo: 'lib/src/services');
+ }
+
+ @override
+ run() async {
+ var pubspec = await PubSpec.load(Directory.current);
+ String name;
+ if (argResults.wasParsed('name')) name = argResults['name'];
+
+ if (name?.isNotEmpty != true) {
+ var p = new Prompter('Name of Service: ');
+ name = await p.prompt(checker: (s) => s.isNotEmpty);
+ }
+
+ List deps = [
+ const MakerDependency('angel_framework', '^1.0.0')
+ ];
+
+ var rc = new ReCase(name);
+ var serviceLib =
+ new LibraryBuilder('${pubspec.name}.src.services.${rc.snakeCase}');
+
+ ServiceGenerator generator;
+
+ var chooser = new Chooser(
+ serviceGenerators.map((g) => g.name).toList(),
+ message: 'What type of service would you like to create? ');
+ var type = await chooser.choose();
+
+ generator =
+ serviceGenerators.firstWhere((g) => g.name == type, orElse: () => null);
+
+ if (generator == null) {
+ _pen.red();
+ _pen('${Icon.BALLOT_X} \'$type\' services are not yet implemented. :(');
+ _pen();
+ throw 'Unrecognized service type: "$type".';
+ }
+
+ for (var dep in generator.dependencies) {
+ if (!deps.any((d) => d.name == dep.name)) deps.add(dep);
+ }
+
+ if (generator.goesFirst) {
+ generator.applyToLibrary(serviceLib, name, rc.snakeCase);
+ serviceLib.addMember(
+ new ImportBuilder('package:angel_framework/angel_framework.dart'));
+ } else {
+ serviceLib.addMember(
+ new ImportBuilder('package:angel_framework/angel_framework.dart'));
+ generator.applyToLibrary(serviceLib, name, rc.snakeCase);
+ }
+
+ if (argResults['typed']) {
+ serviceLib
+ ..addMember(new ImportBuilder('../models/${rc.snakeCase}.dart'));
+ }
+
+ // configureServer() {}
+ var configureServer = new MethodBuilder('configureServer',
+ returnType: new TypeBuilder('AngelConfigurer'));
+ generator.applyToConfigureServer(configureServer, name, rc.snakeCase);
+
+ // return (Angel app) async {}
+ var closure = new MethodBuilder.closure(modifier: MethodModifier.asAsync)
+ ..addPositional(parameter('app', [new TypeBuilder('Angel')]));
+ generator.beforeService(closure, name, rc.snakeCase);
+
+ // app.use('/api/todos', new MapService());
+ var service = generator.createInstance(closure, name, rc.snakeCase);
+
+ if (argResults['typed']) {
+ service = new TypeBuilder('TypedService',
+ genericTypes: [new TypeBuilder(rc.pascalCase)])
+ .newInstance([service]);
+ }
+
+ closure.addStatement(reference('app')
+ .invoke('use', [literal('/api/${pluralize(rc.snakeCase)}'), service]));
+ configureServer.addStatement(closure.asReturn());
+ serviceLib.addMember(configureServer);
+
+ final outputDir = new Directory.fromUri(
+ Directory.current.uri.resolve(argResults['output-dir']));
+ final serviceFile =
+ new File.fromUri(outputDir.uri.resolve("${rc.snakeCase}.dart"));
+ if (!await serviceFile.exists()) await serviceFile.create(recursive: true);
+ await serviceFile.writeAsString(prettyToSource(serviceLib.buildAst()));
+
+ _pen.green();
+ _pen(
+ '${Icon.CHECKMARK} Successfully generated service file "${serviceFile.absolute.path}".');
+ _pen();
+
+ if (deps.isNotEmpty) await depend(deps);
+ }
+}
diff --git a/lib/src/commands/make/test.dart b/lib/src/commands/make/test.dart
new file mode 100644
index 00000000..417e6eb5
--- /dev/null
+++ b/lib/src/commands/make/test.dart
@@ -0,0 +1,115 @@
+import 'dart:io';
+import 'package:args/command_runner.dart';
+import "package:console/console.dart";
+import 'package:dart_style/dart_style.dart';
+import 'package:pubspec/pubspec.dart';
+import 'package:recase/recase.dart';
+import 'maker.dart';
+
+class TestCommand extends Command {
+ final TextPen _pen = new TextPen();
+
+ @override
+ String get name => "test";
+
+ @override
+ String get description => "Creates a new test within the given project.";
+
+ TestCommand() {
+ argParser
+ ..addFlag('run-configuration',
+ help: 'Generate a run configuration for JetBrains IDE\'s.',
+ defaultsTo: true)
+ ..addOption('name',
+ abbr: 'n', help: 'Specifies a name for the plug-in class.')
+ ..addOption('output-dir',
+ help: 'Specifies a directory to create the plug-in class in.',
+ defaultsTo: 'test');
+ }
+
+ @override
+ run() async {
+ var pubspec = await PubSpec.load(Directory.current);
+ String name;
+ if (argResults.wasParsed('name')) name = argResults['name'];
+
+ if (name?.isNotEmpty != true) {
+ var p = new Prompter('Name of Test: ');
+ name = await p.prompt(checker: (s) => s.isNotEmpty);
+ }
+
+ List deps = [
+ const MakerDependency('angel_framework', '^1.0.0'),
+ const MakerDependency('angel_test', '^1.0.0', dev: true),
+ const MakerDependency('test', '^0.12.0', dev: true),
+ ];
+
+ var rc = new ReCase(name);
+ final testDir = new Directory.fromUri(
+ Directory.current.uri.resolve(argResults['output-dir']));
+ final testFile =
+ new File.fromUri(testDir.uri.resolve("${rc.snakeCase}_test.dart"));
+ if (!await testFile.exists()) await testFile.create(recursive: true);
+ await testFile
+ .writeAsString(new DartFormatter().format(_generateTest(pubspec, rc)));
+
+ if (deps.isNotEmpty) await depend(deps);
+
+ _pen.green();
+ _pen(
+ '${Icon.CHECKMARK} Successfully generated test file "${testFile.absolute.path}".');
+ _pen();
+
+ if (argResults['run-configuration']) {
+ final runConfig = new File.fromUri(Directory.current.uri
+ .resolve('.idea/runConfigurations/${name}_Tests.xml'));
+
+ if (!await runConfig.exists()) await runConfig.create(recursive: true);
+ await runConfig.writeAsString(_generateRunConfiguration(name, rc));
+
+ _pen.reset();
+ _pen.green();
+ _pen(
+ '${Icon.CHECKMARK} Successfully generated run configuration "$name Tests" at "${runConfig.absolute.path}".');
+ _pen();
+ }
+ }
+
+ _generateRunConfiguration(String name, ReCase rc) {
+ return '''
+
+
+
+
+
+
+'''
+ .trim();
+ }
+
+ String _generateTest(PubSpec pubspec, ReCase rc) {
+ return '''
+import 'dart:io';
+import 'package:${pubspec.name}/${pubspec.name}.dart';
+import 'package:angel_framework/angel_framework.dart';
+import 'package:angel_test/angel_test.dart';
+import 'package:test/test.dart';
+
+main() async {
+ TestClient client;
+
+ setUp(() async {
+ var app = await createServer();
+ client = await connectTo(app);
+ });
+
+ tearDown(() => client.close());
+
+ test('${rc.snakeCase}', () async {
+ final response = await client.get('/${rc.snakeCase}');
+ expect(response, hasStatus(HttpStatus.OK));
+ });
+}
+ ''';
+ }
+}
diff --git a/lib/src/commands/plugin.dart b/lib/src/commands/plugin.dart
index 2ded982f..49d5fc10 100644
--- a/lib/src/commands/plugin.dart
+++ b/lib/src/commands/plugin.dart
@@ -4,6 +4,7 @@ import "package:console/console.dart";
import 'package:dart_style/dart_style.dart';
import 'package:pubspec/pubspec.dart';
import 'package:recase/recase.dart';
+import 'deprecated.dart';
class PluginCommand extends Command {
final TextPen _pen = new TextPen();
@@ -16,6 +17,8 @@ class PluginCommand extends Command {
@override
run() async {
+ warnDeprecated(this.name, _pen);
+
var pubspec = await PubSpec.load(Directory.current);
final name = await readInput("Name of Plugin: "),
lower = new ReCase(name).snakeCase;
diff --git a/lib/src/commands/rename.dart b/lib/src/commands/rename.dart
index 2c90d961..f64c4047 100644
--- a/lib/src/commands/rename.dart
+++ b/lib/src/commands/rename.dart
@@ -3,6 +3,7 @@ import 'package:analyzer/analyzer.dart';
import 'package:args/command_runner.dart';
import 'package:console/console.dart';
import 'package:dart_style/dart_style.dart';
+import 'package:glob/glob.dart';
import 'package:pubspec/pubspec.dart';
import 'pub.dart';
@@ -63,14 +64,15 @@ renamePubspec(Directory dir, String oldName, String newName) async {
renameDartFiles(Directory dir, String oldName, String newName) async {
// Try to replace MongoDB URL
- var defaultYaml = new File.fromUri(dir.uri.resolve('config/default.yaml'));
+ var configGlob = new Glob('config/**/*.yaml');
- if (await defaultYaml.exists()) {
- print('Changing MongoDB URL in file "${defaultYaml.absolute.path}"...');
- var contents = await defaultYaml.readAsString();
- contents = contents.replaceAll('mongodb://localhost:27017/$oldName',
- 'mongodb://localhost:27017/$newName');
- await defaultYaml.writeAsString(contents);
+ await for (var yamlFile in configGlob.list(root: dir.absolute.path)) {
+ if (yamlFile is File) {
+ print('Replacing occurrences of "$oldName" with "$newName" in file "${yamlFile.absolute.path}"...');
+ var contents = await yamlFile.readAsString();
+ contents = contents.replaceAll(oldName, newName);
+ await yamlFile.writeAsString(contents);
+ }
}
var entry = new File.fromUri(dir.uri.resolve('lib/$oldName.dart'));
diff --git a/lib/src/commands/service.dart b/lib/src/commands/service.dart
index 1db296e7..9fd28165 100644
--- a/lib/src/commands/service.dart
+++ b/lib/src/commands/service.dart
@@ -7,16 +7,9 @@ import 'package:inflection/inflection.dart';
import 'package:pubspec/pubspec.dart';
import 'package:recase/recase.dart';
import 'service_generators/service_generators.dart';
+import 'deprecated.dart';
import 'init.dart' show preBuild;
-const List GENERATORS = const [
- const MapServiceGenerator(),
- const FileServiceGenerator(),
- const MongoServiceGenerator(),
- const RethinkServiceGenerator(),
- const CustomServiceGenerator()
-];
-
class ServiceCommand extends Command {
final TextPen _pen = new TextPen();
@@ -28,10 +21,12 @@ class ServiceCommand extends Command {
@override
run() async {
+ warnDeprecated(this.name, _pen);
+
var pubspec = await PubSpec.load(Directory.current);
var name = await readInput('Name of Service (not plural): ');
var chooser = new Chooser(
- GENERATORS.map((g) => g.name).toList(),
+ serviceGenerators.map((g) => g.name).toList(),
message: 'What type of service would you like to create? ');
var type = await chooser.choose();
@@ -40,7 +35,7 @@ class ServiceCommand extends Command {
var typed = (await chooser.choose()) == 'Yes';
var generator =
- GENERATORS.firstWhere((g) => g.name == type, orElse: () => null);
+ serviceGenerators.firstWhere((g) => g.name == type, orElse: () => null);
if (generator == null) {
_pen.blue();
diff --git a/lib/src/commands/service_generators/file_service.dart b/lib/src/commands/service_generators/file_service.dart
index 7778e2d9..d536cbc8 100644
--- a/lib/src/commands/service_generators/file_service.dart
+++ b/lib/src/commands/service_generators/file_service.dart
@@ -1,12 +1,17 @@
import 'generator.dart';
import 'package:code_builder/code_builder.dart';
import 'package:inflection/inflection.dart';
+import '../make/maker.dart';
class FileServiceGenerator extends ServiceGenerator {
const FileServiceGenerator() : super('Persistent JSON File');
@override
- bool get createsModel => false;
+ List get dependencies =>
+ const [const MakerDependency('angel_file_service', '^1.0.0')];
+
+ @override
+ bool get goesFirst => true;
@override
void applyToLibrary(LibraryBuilder library, String name, String lower) {
diff --git a/lib/src/commands/service_generators/generator.dart b/lib/src/commands/service_generators/generator.dart
index 79ccc4f8..970bb0fa 100644
--- a/lib/src/commands/service_generators/generator.dart
+++ b/lib/src/commands/service_generators/generator.dart
@@ -1,16 +1,30 @@
import 'package:code_builder/code_builder.dart';
+import '../make/maker.dart';
class ServiceGenerator {
final String name;
const ServiceGenerator(this.name);
+ List get dependencies => [];
+
+ @deprecated
bool get createsModel => true;
+
+ @deprecated
bool get createsValidator => true;
+
+ @deprecated
bool get exportedInServiceLibrary => true;
+
+ @deprecated
bool get injectsSingleton => false;
+
+ @deprecated
bool get shouldRunBuild => false;
+ bool get goesFirst => false;
+
void applyToLibrary(LibraryBuilder library, String name, String lower) {}
void beforeService(MethodBuilder methodBuilder, String name, String lower) {}
diff --git a/lib/src/commands/service_generators/mongo.dart b/lib/src/commands/service_generators/mongo.dart
index 16e7c363..c81153ef 100644
--- a/lib/src/commands/service_generators/mongo.dart
+++ b/lib/src/commands/service_generators/mongo.dart
@@ -1,10 +1,15 @@
import 'generator.dart';
import 'package:code_builder/code_builder.dart';
import 'package:inflection/inflection.dart';
+import '../make/maker.dart';
class MongoServiceGenerator extends ServiceGenerator {
const MongoServiceGenerator() : super('MongoDB');
+ @override
+ List get dependencies =>
+ const [const MakerDependency('angel_mongo', '^1.0.0')];
+
@override
bool get createsModel => false;
diff --git a/lib/src/commands/service_generators/rethink.dart b/lib/src/commands/service_generators/rethink.dart
index 22d0bd64..17c55b44 100644
--- a/lib/src/commands/service_generators/rethink.dart
+++ b/lib/src/commands/service_generators/rethink.dart
@@ -1,10 +1,15 @@
import 'generator.dart';
import 'package:code_builder/code_builder.dart';
import 'package:inflection/inflection.dart';
+import '../make/maker.dart';
class RethinkServiceGenerator extends ServiceGenerator {
const RethinkServiceGenerator() : super('RethinkDB');
+ @override
+ List get dependencies =>
+ const [const MakerDependency('angel_rethink', '^1.0.0')];
+
@override
bool get createsModel => false;
diff --git a/lib/src/commands/service_generators/service_generators.dart b/lib/src/commands/service_generators/service_generators.dart
index 658cc16b..0dfd685e 100644
--- a/lib/src/commands/service_generators/service_generators.dart
+++ b/lib/src/commands/service_generators/service_generators.dart
@@ -1,6 +1,15 @@
-export 'custom.dart';
-export 'file_service.dart';
+import 'custom.dart';
+import 'file_service.dart';
+import 'generator.dart';
+import 'map.dart';
+import 'mongo.dart';
+import 'rethink.dart';
export 'generator.dart';
-export 'map.dart';
-export 'mongo.dart';
-export 'rethink.dart';
\ No newline at end of file
+
+const List serviceGenerators = const [
+ const MapServiceGenerator(),
+ const FileServiceGenerator(),
+ const MongoServiceGenerator(),
+ const RethinkServiceGenerator(),
+ const CustomServiceGenerator()
+];
\ No newline at end of file
diff --git a/lib/src/commands/start.dart b/lib/src/commands/start.dart
index eae1456c..696ebf09 100644
--- a/lib/src/commands/start.dart
+++ b/lib/src/commands/start.dart
@@ -3,6 +3,7 @@ import 'dart:io';
import 'package:args/command_runner.dart';
import 'package:watcher/watcher.dart';
import 'package:yaml/yaml.dart';
+import 'deprecated.dart';
import 'pub.dart';
Process server;
@@ -34,6 +35,8 @@ class StartCommand extends Command {
@override
run() async {
+ warnDeprecated(this.name);
+
stderr
..writeln(
'WARNING: `angel start` is now deprecated, in favor of `package:angel_hot`.')
diff --git a/lib/src/commands/test.dart b/lib/src/commands/test.dart
index b74da8fd..b519b62e 100644
--- a/lib/src/commands/test.dart
+++ b/lib/src/commands/test.dart
@@ -4,6 +4,7 @@ import "package:console/console.dart";
import 'package:dart_style/dart_style.dart';
import 'package:pubspec/pubspec.dart';
import 'package:recase/recase.dart';
+import 'deprecated.dart';
class TestCommand extends Command {
final TextPen _pen = new TextPen();
@@ -16,6 +17,8 @@ class TestCommand extends Command {
@override
run() async {
+ warnDeprecated(this.name, _pen);
+
final name = await readInput("Name of Test: "),
lower = new ReCase(name).snakeCase;
final testDir = new Directory("test/services");