Dartでのコード生成。 パート2.注釈、source_genおよびbuild_runner

最初の部分では、コード生成が必要な理由を見つけ、Dartでコード生成に必要なツールをリストしました。 第2部では、Dartで注釈を作成および使用する方法、およびsource_genbuild_runnerを使用してコード生成を開始する方法を学習します。













Dartアノテーション



注釈は、コードに追加できる構文メタデータです。 言い換えると、コードのコンポーネント(クラスやメソッドなど)に追加情報を追加する機会です。 注釈はDartコードで広く使用されています。 @required



を使用して名前付きパラメーターが必要であることを示し、注釈付きパラメーターが指定されていない場合、コードはコンパイルされません。 また、 @override



を使用して、親クラスで定義されたAPI @override



クラスで実装されていることを示します。 注釈は常に@



記号で始まります。







独自の注釈を作成する方法は?



コードにメタデータを追加するという考えは少しエキゾチックで複雑に聞こえますが、アノテーションはDart言語で最も単純なものの1つです。 以前に注釈が単に追加情報を運ぶと言われました。 これらはPODO (Plain Old Dart Objects)に似ています。 またconst



コンストラクターが定義されている場合、どのクラスも注釈として機能します







 class { final String name; final String todoUrl; const Todo(this.name, {this.todoUrl}) : assert(name != null); } @Todo('hello first annotation', todoUrl: 'https://www.google.com') class HelloAnnotations {}
      
      





ご覧のとおり、注釈は非常に簡単です。 そして主なことは、これらの注釈を使用して何をするかです。 これはsource_gen



build_runner



ます。







build_runnerの使用方法は?



build_runner



は、Dartコードを使用してファイルを生成するのに役立つDartパッケージです。 build.yaml



を使用してBuilder



ファイルを構成します。 設定されると、 Builder



はすべてのbuild



コマンドで、またはファイルが変更されたときに呼び出されます。 また、変更されたコードや特定の条件を満たすコードを解析する機会もあります。







Dartコードを理解するためのsource_gen



ある意味では、 build_runner



は「 いつコードを生成する必要があるのか​​」という質問に答えるメカニズムです。 同時に、 source_gen



は「 どのコードを生成する必要がありますか?」という質問に答えます。 source_gen



は、 source_gen



機能するBuilderを作成するためのフレームワークを提供します。 また、 source_gen



は、コードを解析および生成するための便利なAPIを提供します。







すべてをまとめる:TODOレポート



この記事の残りの部分では、 todo_reporter.dartプロジェクトを逆アセンブルします 。これは、 こちらにあります







コード生成を使用するすべてのプロジェクトが従う未記述のルールがあります。 アノテーションを含む パッケージと、これらのアノテーションを使用するジェネレーター用の個別のパッケージを作成する必要があります。 Dart / Flutterでパッケージライブラリを作成する方法については、 こちらをご覧ください







まず、 todo_reporter.dart



ディレクトリを作成する必要があります。 このディレクトリ内に、注釈を含むtodo_reporter_generator



ディレクトリ、注釈を処理するtodo_reporter_generator



ディレクトリ、および作成されるライブラリの機能のデモを含むexample



ディレクトリを作成する必要があります。







明確にするために、サフィックス.dart



ルートディレクトリ名に追加され.dart



います。 もちろん、これは必須ではありませんが、このルールに従って、このパッケージがどのDartプロジェクトでも使用できるという事実を正確に示したいと思います。 それどころか、このパッケージがFlutter( ozzie.flutterなど )専用であることを示したい場合は、別のサフィックスを使用します。 これは必ずしも必要ではありません。単なる命名規則であり、私はそれを順守しようとしています。







シンプルなアノテーションパッケージtodo_reporterの作成



todo_reporter



内にtodo_reporter.dart



を作成します。 これを行うには、 pubspec.yaml



ファイルとlib



ディレクトリを作成します。







pubspec.yaml



非常に簡単です。







 name: todo_reporter description: Keep track of all your TODOs. version: 1.0.0 author: Jorge Coca <jcocaramos@gmail.com> homepage: https://github.com/jorgecoca/todo_reporter.dart environment: sdk: ">=2.0.0 <3.0.0" dependencies: dev_dependencies: test: 1.3.4
      
      





開発プロセスで使用されるtest



パッケージを除き、依存関係はありません。







lib



ディレクトリで、次を実行する必要があります。









この場合、追加する必要があるのは注釈だけです。 注釈付きのtodo.dart



ファイルを作成しましょう。







 class Todo { final String name; final String todoUrl; const Todo(this.name, {this.todoUrl}) : assert(name != null); }
      
      





注釈を付けるのに必要なことはそれだけです。 簡単だと言った。 しかし、それだけではありません。 test



ディレクトリに単体テストを追加しましょう。







todo_test.dart
 import 'package:test/test.dart'; import 'package:todo_reporter/todo_reporter.dart'; void main() { group('Todo annotation', () { test('must have a non-null name', () { expect(() => Todo(null), throwsA(TypeMatcher<AssertionError>())); }); test('does not need to have a todoUrl', () { final todo = Todo('name'); expect(todo.todoUrl, null); }); test('if it is a given a todoUrl, it will be part of the model', () { final givenUrl = 'http://url.com'; final todo = Todo('name', todoUrl: givenUrl); expect(todo.todoUrl, givenUrl); }); }); }
      
      





これで注釈を作成することができます。 ここでコードを見つけることができます。 これでジェネレーターに行くことができます。







クールな仕事をする:todo_reporter_generator



パッケージの作成方法がわかったので、 todo_reporter_generator



パッケージを作成しましょう。 このパッケージ内には、 pubspec.yaml



およびbuild.yaml



lib



ディレクトリが必要です。 lib



ディレクトリには、 src



ディレクトリとbuilder.dart



ファイルがbuilder.dart



です。 todo_reporter_generator



は、 dev_dependency



として他のプロジェクトに追加される個別のパッケージと見なされます。 これは、コード生成が開発段階でのみ必要であり、完成したアプリケーションに追加する必要がないためです。







pubspec.yaml



次のとおりです。







 name: todo_reporter_generator description: An annotation processor for @Todo annotations. version: 1.0.0 author: Jorge Coca <jcocaramos@gmail.com> homepage: https://github.com/jorgecoca/todo_reporter.dart environment: sdk: ">=2.0.0 <3.0.0" dependencies: build: '>=0.12.0 <2.0.0' source_gen: ^0.9.0 todo_reporter: path: ../todo_reporter/ dev_dependencies: build_test: ^0.10.0 build_runner: '>=0.9.0 <0.11.0' test: ^1.0.0
      
      





ここでbuild.yaml



作成しましょう。 このファイルには、 Builderに必要な構成が含まれています。 詳細はこちらをご覧くださいbuild.yaml



次のとおりです。







 targets: $default: builders: todo_reporter_generator|todo_reporter: enabled: true builders: todo_reporter: target: ":todo_reporter_generator" import: "package:todo_reporter_generator/builder.dart" builder_factories: ["todoReporter"] build_extensions: {".dart": [".todo_reporter.g.part"]} auto_apply: dependents build_to: cache applies_builders: ["source_gen|combining_builder"]
      
      





import



プロパティは、 Builder



を含むファイルを指し、 builder_factories



プロパティは、コードを生成するメソッドを指します。







これで、 lib



ディレクトリにbuilder.dart



ファイルを作成できます。







 import 'package:build/build.dart'; import 'package:source_gen/source_gen.dart'; import 'package:todo_reporter_generator/src/todo_reporter_generator.dart'; Builder todoReporter(BuilderOptions options) => SharedPartBuilder([TodoReporterGenerator()], 'todo_reporter');
      
      





src



ディレクトリーのtodo_reporter_generator.dart



ファイル:







 import 'dart:async'; import 'package:analyzer/dart/element/element.dart'; import 'package:build/src/builder/build_step.dart'; import 'package:source_gen/source_gen.dart'; import 'package:todo_reporter/todo_reporter.dart'; class TodoReporterGenerator extends GeneratorForAnnotation<Todo> { @override FutureOr<String> generateForAnnotatedElement( Element element, ConstantReader annotation, BuildStep buildStep) { return "// Hey! Annotation found!"; } }
      
      





ご覧のとおり、 builder.dart



ファイルで、 Builder



が作成するtodoReporter



メソッドを定義しています。 Builder



は、 SharedPartBuilder



を使用するTodoReporterGenerator



を使用して作成されます。 したがって、 build_runner



source_gen



一緒に機能します。







TodoReporterGenerator



GeneratorForAnnotation



サブクラスGeneratorForAnnotation



。そのため、 generateForAnnotatedElement



メソッドは、この注釈(この場合は@Todo



)がコードで見つかった場合にのみ実行されます。







generateForAnnotatedElement



メソッドは、生成されたコードを含む文字列を返します。 生成されたコードがコンパイルされない場合、ビルドフェーズ全体が失敗します。 これは、将来の間違いを避けるのに役立つため、非常に便利です。







したがって、各コード生成で、 todo_repoter_generator



はコメント付きのpart



ファイルを作成します// Hey! Annotation found!



// Hey! Annotation found!



次の記事では、注釈を処理する方法を学びます。







すべてをまとめる:todo_reporterを使用する



これで、 todo_reporter.dart



を実演できます。 パッケージを使用する場合は、 example



プロジェクトを追加することをお勧めします。 そのため、他の開発者は、実際のプロジェクトでAPIを使用する方法を確認できます。







プロジェクトを作成し、必要な依存関係をpubspec.yaml



追加しましょう。 この例では、 example



ディレクトリ内にFlutterプロジェクトを作成し、依存関係を追加します。







 dependencies: flutter: sdk: flutter todo_reporter: path: ../todo_reporter/ dev_dependencies: build_runner: 1.0.0 flutter_test: sdk: flutter todo_reporter_generator: path: ../todo_reporter_generator/
      
      





パッケージを受け取った後( flutter packages get



)アノテーションを使用できます:







 import 'package:todo_reporter/todo_reporter.dart'; @Todo('Complete implementation of TestClass') class TestClass {}
      
      





すべてが整ったので、ジェネレーターを実行します:







 $ flutter packages pub run build_runner build
      
      





コマンドが完了すると、プロジェクトに新しいファイルtodo.g.dart



表示されます。 次のものが含まれます。







 // GENERATED CODE - DO NOT MODIFY BY HAND part of 'todo.dart'; // ***************************************************************** // TodoReporterGenerator // ******************************************************************** // Hey! Annotation found!
      
      





私たちは望んでいたものを達成しました! これで、コード内の各@Todo



アノテーションに対して正しいDartファイルを生成できます。 必要な数だけ試して作成してください。







次の記事で



これで、ファイルを生成するための正しい設定ができました。 次の記事では、生成されたコードが本当にクールなことを行えるように、注釈の使用方法を学習します。 実際、現在生成されているコードはあまり意味がありません。








All Articles