|
1 |
| -<!-- |
2 |
| -This README describes the package. If you publish this package to pub.dev, |
3 |
| -this README's contents appear on the landing page for your package. |
| 1 | +# Gate |
| 2 | +**Dart/Flutter Dependency injection generator** |
| 3 | + |
| 4 | +<p align="center"> |
| 5 | +<img src="https://github.com/Apparence-io/gate/raw/master/packages/gate_generator/doc/images/cover.png" alt="flutter anchored onboarding screen" /> |
| 6 | +</p> |
4 | 7 |
|
5 |
| -For information about how to write a good package README, see the guide for |
6 |
| -[writing package pages](https://dart.dev/guides/libraries/writing-package-pages). |
| 8 | +<p align="center"> |
| 9 | + <a href="https://pub.dev/packages/gate_generator"><img src="https://img.shields.io/pub/v/gate_generator" alt="pubdev" style="max-width: 100%;"></a> |
| 10 | + <a href="https://github.com/Apparence-io/gate/actions"><img src="https://github.com/Apparence-io/gate/workflows/Dart%20CI/badge.svg" alt="ci" style="max-width: 100%;"></a> |
| 11 | + <a href="https://opensource.org/licenses/MIT" rel="nofollow"><img src="https://camo.githubusercontent.com/83d3746e5881c1867665223424263d8e604df233d0a11aae0813e0414d433943/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d626c75652e737667" alt="License: MIT" data-canonical-src="https://img.shields.io/badge/license-MIT-blue.svg" style="max-width: 100%;"></a> |
| 12 | +</p> |
7 | 13 |
|
8 |
| -For general information about developing packages, see the Dart guide for |
9 |
| -[creating packages](https://dart.dev/guides/libraries/create-library-packages) |
10 |
| -and the Flutter guide for |
11 |
| -[developing packages and plugins](https://flutter.dev/developing-packages). |
12 |
| ---> |
13 |
| - |
14 |
| -TODO: Put a short description of the package here that helps potential users |
15 |
| -know whether this package might be useful for them. |
| 14 | +## Motivation |
| 15 | +Providing Service should be independant from your pages or other code. |
| 16 | +The DI pattern separates the responsibility of creating an object of the service class out of the client class. |
| 17 | +Also using code generation we can get rid of the boilerplate part. |
16 | 18 |
|
17 | 19 | ## Features
|
18 |
| - |
19 |
| -TODO: List what your package can do. Maybe include images, gifs, or videos. |
| 20 | +- provide Injectable as a singleton |
| 21 | +- provide Injectable as a dynamic service |
| 22 | +- Inject an Injectable class into another |
20 | 23 |
|
21 | 24 | ## Getting started
|
| 25 | +install gate with build_runner in you pubspec.yaml |
| 26 | +``` |
| 27 | +dependencies: |
| 28 | + build_runner: ^X.X.X |
| 29 | + gate: ^X.X.X |
| 30 | +
|
| 31 | +dev_dependencies: |
| 32 | + gate_generator: ^X.X.X |
| 33 | +``` |
| 34 | +> replace X.X.X with last available version on pub.dev. |
22 | 35 |
|
23 |
| -TODO: List prerequisites and provide or point to information on how to |
24 |
| -start using the package. |
25 | 36 |
|
26 | 37 | ## Usage
|
27 | 38 |
|
28 |
| -TODO: Include short and useful examples for package users. Add longer examples |
29 |
| -to `/example` folder. |
| 39 | +### 1 - Create a class you want to inject |
30 | 40 |
|
31 | 41 | ```dart
|
32 |
| -const like = 'sample'; |
| 42 | +import 'package:gate/gate.dart'; |
| 43 | +
|
| 44 | +@Injectable() |
| 45 | +class CoffeeService { |
| 46 | + final S1 s1; |
| 47 | +
|
| 48 | + CoffeeService._(this.s1); |
| 49 | +
|
| 50 | + @Singleton() |
| 51 | + factory CoffeeService.simple(S1 s1) => CoffeeService._(s1); |
| 52 | +
|
| 53 | + void pump() { |
| 54 | + print("CoffeeService it's working"); |
| 55 | + } |
| 56 | +} |
33 | 57 | ```
|
34 | 58 |
|
35 |
| -## Additional information |
| 59 | +You just have to |
| 60 | +- add @Injectable() above your class |
| 61 | +- add @Singleton() or @Provide() above a factory |
| 62 | + |
| 63 | +### 2 - inject dependency |
| 64 | + |
| 65 | +We can now inject our Injectables using @Inject annotation. |
| 66 | +InjectedChild use |
| 67 | +- Type you want to inject |
| 68 | +- the name of the factory you want to use (you can have multiple provided factories) |
| 69 | +- the attribute name (just in case you want to rename it or make it private...) |
| 70 | + |
| 71 | +```dart |
| 72 | +import 'package:gate_example/gate/gate_provider.dart'; |
| 73 | +import 'package:gate_example/gate/coffee_service.dart'; |
| 74 | +part 'coffee_page.gate_inject.g.part'; |
| 75 | +
|
| 76 | +@Inject(children: [ |
| 77 | + InjectedChild(S1, factoryName: "build"), |
| 78 | + InjectedChild(CoffeeService, factoryName: "simple", attrName: "coffeeService"), |
| 79 | +]) |
| 80 | +class CoffeePage extends StatelessWidget { |
| 81 | + const CoffeePage({Key? key}) : super(key: key); |
| 82 | +
|
| 83 | + @override |
| 84 | + Widget build(BuildContext context) { |
| 85 | + coffeeService.pump(); |
| 86 | + return Container(); |
| 87 | + } |
| 88 | +} |
| 89 | +``` |
| 90 | +You can inject as many dependency as you want into your class as long as their are Injectable. |
| 91 | +> ```part 'coffee_page.gate_inject.g.part';``` will be be added to your file |
| 92 | +> this file will be genererated on next step |
| 93 | +> don't forget to import "gate_provider.dart" that is generated next |
| 94 | +
|
| 95 | + |
| 96 | +### 3 - generate code |
| 97 | +Using build runner you can run the command in your shell |
| 98 | +```shell |
| 99 | +flutter packages pub run build_runner build --delete-conflicting-outputs |
| 100 | +``` |
| 101 | + |
| 102 | +Once build runner is done, you can run your flutter app and use all injected class. |
| 103 | + |
| 104 | +> If you change your factory or wants to inject other class... just rerun the build_runner :) |
| 105 | +
|
| 106 | +## Properties |
| 107 | +| Annotation | Description | |
| 108 | +|--------------|---------------------------------------------------------------------:| |
| 109 | +| Injectable | mark a class as containing Singleton or Provide factories | |
| 110 | +| Singleton | mark a factory as a Singleton Injectable | |
| 111 | +| Provide | mark a factory as a Dymamic Injectable. Each time you inject will create a new instance of that class | |
| 112 | +| Inject | mark a class to inject dependencies | |
| 113 | + |
| 114 | + |
| 115 | +### Inject annotation |
| 116 | +| Property | Description | |
| 117 | +|-----------------|---------------------------------------------------------------------:| |
| 118 | +| children | a list of InjectedChild | |
| 119 | + |
| 120 | + |
| 121 | +### InjectedChild annotation |
| 122 | +| Property | Description | |
| 123 | +|-----------------|---------------------------------------------------------------------:| |
| 124 | +| type | The type to inject | |
| 125 | +| factoryName | The factory name to use | |
| 126 | +| attrName | *(optionnal)* The attribute name to call it from your class | |
| 127 | + |
| 128 | + |
| 129 | +## Mock an injected dependency for testing |
| 130 | +To remplace your service by mocks, you have to set them, before testing. |
| 131 | +Gate will generate all methods for you. |
| 132 | + |
| 133 | +First, declare your mocked services with your prefered libraries. |
| 134 | + |
| 135 | +Then use the "appProvider" to set mocks. |
| 136 | + |
| 137 | +Finally, don't forget to reset mocks by setting them to null. |
| 138 | + |
| 139 | + |
| 140 | +```dart |
| 141 | +class CoffeeServiceMock extends Mock implements CoffeeService {} |
| 142 | +
|
| 143 | +class S1ServiceMock extends Mock implements S1 {} |
| 144 | +
|
| 145 | +class S2BServiceMock extends Mock implements S2B {} |
| 146 | +
|
| 147 | +class TodoServiceMock extends Mock implements TodoService {} |
| 148 | +
|
| 149 | +void main() { |
| 150 | + // Initialize mocks |
| 151 | + final CoffeeService coffeeServiceMock = CoffeeServiceMock(); |
| 152 | + final S1 s1ServiceMock = S1ServiceMock(); |
| 153 | + final S2B s2BServiceMock = S2BServiceMock(); |
| 154 | + final TodoService todoServiceMock = TodoServiceMock(); |
| 155 | +
|
| 156 | + group('Mock injection group', () { |
| 157 | + setUp(() { |
| 158 | + when(() => coffeeServiceMock.getMenu()).thenReturn("Best menu"); |
| 159 | + // Set injected services with mocks |
| 160 | + appProvider.setCoffeeServiceSimpleMock(coffeeServiceMock); |
| 161 | + appProvider.setS1BuildMock(s1ServiceMock); |
| 162 | + appProvider.setS2BBuildMock(s2BServiceMock); |
| 163 | + appProvider.setTodoServiceBeanMock(todoServiceMock); |
| 164 | + }); |
| 165 | +
|
| 166 | + tearDownAll(() { |
| 167 | + // Remove mocks from appProvider (real ones will be used) |
| 168 | + appProvider.setCoffeeServiceSimpleMock(null); |
| 169 | + appProvider.setS1BuildMock(null); |
| 170 | + appProvider.setS2BBuildMock(null); |
| 171 | + appProvider.setTodoServiceBeanMock(null); |
| 172 | + }); |
| 173 | +
|
| 174 | + test('Test mocks injection', () async { |
| 175 | + expect(appProvider.getCoffeeServiceSimple().getMenu(), |
| 176 | + equals('Best menu')); // Mocked returned value |
| 177 | +
|
| 178 | + appProvider.setCoffeeServiceSimpleMock(null); |
| 179 | +
|
| 180 | + expect(appProvider.getCoffeeServiceSimple().getMenu(), |
| 181 | + equals('No menu')); // True value (not mocked) |
| 182 | + }); |
| 183 | + }); |
| 184 | +} |
| 185 | +``` |
| 186 | + |
| 187 | +## Run tests |
| 188 | +```bash |
| 189 | +dart pub global activate coverage |
| 190 | +dart test --coverage="coverage" |
| 191 | +format_coverage --lcov --in=coverage --out=coverage.lcov --packages=.packages --report-on=lib |
| 192 | +``` |
| 193 | + |
| 194 | +<hr/> |
| 195 | + |
| 196 | +## FAQ |
| 197 | +*To be done* |
| 198 | + |
| 199 | +<hr/> |
| 200 | +<br><br> |
| 201 | +<a href="https://en.apparence.io"><img src="https://github.com/Apparence-io/bart/raw/master/.github/img/logo.png" alt="Apparence.io logo"></a> |
| 202 | +<p><small>Developed with 💙 by Apparence.io</small></p> |
36 | 203 |
|
37 |
| -TODO: Tell users more about the package: where to find more information, how to |
38 |
| -contribute to the package, how to file issues, what response they can expect |
39 |
| -from the package authors, and more. |
|
0 commit comments