Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/migration #1

Merged
merged 14 commits into from
Apr 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions .github/workflows/pr.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
name: PR Workflow

on:
pull_request:
branches: [main, develop]

jobs:
pr_workflow:
runs-on: ubuntu-latest

env:
PROJECT_NAME: zef_di_core

steps:
- name: Checkout Code
uses: actions/checkout@v2

- name: Setup Dart
uses: dart-lang/[email protected]
with:
sdk: stable

- name: Install dependencies
run: dart pub get

- name: Check if version is published on pub.dev
run: |
PACKAGE_NAME="${{ env.PROJECT_NAME }}"
EXPECTED_VERSION=$(grep 'version:' pubspec.yaml | cut -d ' ' -f 2)
echo "Expected package version: $EXPECTED_VERSION"
LATEST_PUBLISHED_VERSION=$(curl -s https://pub.dev/packages/$PACKAGE_NAME | grep 'data-version="' | head -1 | sed -n 's/.*data-version="\([^"]*\)".*/\1/p')
echo "Latest published version on pub.dev: $LATEST_PUBLISHED_VERSION"
if [ "$EXPECTED_VERSION" = "$LATEST_PUBLISHED_VERSION" ]; then
echo "::error::Version $EXPECTED_VERSION is already published on pub.dev."
exit 1
else
echo "Version $EXPECTED_VERSION is not published on pub.dev. Proceeding..."
fi

- name: Run dart analyze
run: dart analyze

- name: Run tests if test files exist
run: |
if compgen -G "test/*_test.dart" > /dev/null; then
dart test
else
echo "No test files found in test/"
fi
shell: bash
12 changes: 12 additions & 0 deletions .github/workflows/publish.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
name: Publish to pub.dev

on:
push:
tags:
- "v**"

jobs:
publish:
permissions:
id-token: write # Required for authentication using OIDC
uses: dart-lang/setup-dart/.github/workflows/publish.yml@v1
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# https://dart.dev/guides/libraries/private-files
# Created by `dart pub`
.dart_tool/

# Avoid committing pubspec.lock for library packages; see
# https://dart.dev/guides/libraries/private-files#pubspeclock.
pubspec.lock
14 changes: 14 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "ZEF_DI_Core",
"request": "launch",
"type": "dart",
"program": "example/zef_di_core_example.dart",
}
]
}
88 changes: 88 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
## 4.0.0

- Renamed project
- Merged `ZEF_DI_Abstractions` and `ZEF_DI_Inglue` together into this project
- Added async version to all functions
- Split tests in multiple files and reorganized them
- Implemented `registerSingletonFactory` function

## 3.0.0

- Renamed method `overrideInstance` with `overrideWithSingleton`
- Renamed method `overrideFactory` with `overrideWithTransient`
- Added method `overrideWithLazy`

## 2.0.1

- Just a version change because of a retracted package on pub.dev

## 2.0.0

- Changed the names of the annotations to better reflect it's registration type

## 1.3.0

- Added annotation `DependencyModule`

## 1.2.1

- Changed the interfaces from `List` to `Set`
- Changed the return type from `List` to `Set`

## 1.2.0

- Added methods for lazy registration
- Added new Annotation `RegisterLazy`
- Improved many dart doc comments

## 1.1.1

- Renamed class `Registration` to `BaseRegistration` because of a conflict

## 1.1.0

- Added annotations for the generator

## 1.0.0

- Possibility to pass arguments to factory resolutions
- replaced `getFirst()` with `resolve()`
- replaced `getFirstOrNull()` with `resolveOrNull()`
- replaced `getAll()` with `resolveAll()`

## 0.0.9

- Implemented usage of zef_log_core
- Implemented property to set if errors should be thrown

## 0.0.8

- Github workflow tests #7

## 0.0.7

- Github workflow tests #6

## 0.0.6

- Github workflow tests #5

## 0.0.5

- Github workflow tests #4

## 0.0.4

- Github workflow tests #3

## 0.0.3

- Github workflow tests #2

## 0.0.2

- Github workflow tests

## 0.0.1

- Initial version.
194 changes: 194 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
# zef_di_core

A dart library which provides abstractions for dependency injection and a default implementation.

This project is the continuation of [zef_di_abstractions](https://pub.dev/packages/zef_di_abstractions) and [zef_di_inglue](https://pub.dev/packages/zef_di_inglue). If you are still using those, you should upgrade to this successor.

## Features

- **Framework Agnostic**: Designed to be a flexible wrapper, this package can be used with any Dependency Injection (DI) framework, offering a unified interface for service registration and resolution.
- **Multiple Service Resolution**: Supports resolving multiple services registered under the same interface, enhancing the flexibility of service retrieval in complex applications.
- **Custom Adapter Integration**: Enables users to integrate any external DI framework by writing custom adapters, ensuring compatibility and extending functionality according to project needs.
- **Code generation**: Automatic dependency registration and resolution.

## Definitions

### **Singleton**

Singletons are instances which are held in memory and being reused everytime the dependency is resolved. This makes them very fast, but memory consuming.
Keep in mind that when you destroy the object by hand (or via an external package) the reference still exists inside the DI framework, but will throw an Exception.

#### **Transient**

A Transient registration is like registering a function which gets called every time we request an object. This way the memory usage is minimal as no instance is cached, but we instantiate the object every time we request it, which can result in performance issues.

#### **Lazy**

Lazies are a combination of Singleton and Transients. You will register a factory which will be called the first time you resolve the type and then the instance will be stored in memory. This way you will always get the same instance.

## Getting Started

### Initialization and Usage

We are fans of the "Builder" pattern, so that's the way how you initialize the ServiceLocator. Inside your `main()` function, call it like so:

```dart
void main() {
ServiceLocatorBuilder()
.build();

// Your application logic here
}
```

And you are good to go. Since the ServiceLocator is a Singleton, you are able to access it all over your application with `ServiceLocator.instance`, or shorthand `ServiceLocator.I`.

### Singletons

#### Simple registration

To register a `Singleton` you directly pass an instance of the object you want to have registered:

```dart
ServiceLocator.I.registerSingleton(MyService());
```

And to resolve that instance you call the `resolve()` method:

```dart
final MyService myService = ServiceLocator.I.resolve<MyService>();
```

---

**NOTE**

You can register the same instance multiple times if you have set this in `ServiceLocatorConfig`. This is turned on by default.
The method `resolve()` will then return the first registered instance by default, but you can also get the last registered with:

```dart
final MyService myService = ServiceLocator.I.resolve<MyService>(resolveFirst: false);
```

The same principle applies to the following registration option

---

#### Registering with a factory

You can also register a `Singleton` with a factory:

```dart
ServiceLocator.I.registerSingletonFactory<MyService>(
(args) => MyService(),
);
```

This way you have more control over the instance creation.
Note that the factory will only be called once, and directly after the registration.

#### Named registration

You can pass a name with your registration.

```dart
ServiceLocator.I.registerSingleton(MyService(), name: 'One');
ServiceLocator.I.registerSingleton(MyService(), name: 'Two');
```

This way you can resolve different instances with ease:

```dart
final MyService myService = ServiceLocator.I.resolve<MyService>(name: 'one'); // Will return the instance with name `one`
final MyService myService = ServiceLocator.I.resolve<MyService>(name: 'two'); // Will return the instance with name `two`
```

#### Keyed registration

The same principle as named registrations, but with a different property

#### Environmental registration

The same principle as named registrations, but with a different property. Mostly used to define your instances under different environments like "dev", "test", "prod", ...

### Transient registration

#### Simple registration

```dart
ServiceLocator.I.registerTransient<MyService>(
(args) => MyService(),
);
```

And to resolve, you do the same as with the `Singleton` resolution:

```dart
final MyService myService = ServiceLocator.I.resolve<MyService>();
```

#### Resolving with parameters

One feature for `Transient` factories is, that you can pass arguments to resolve the instance.
First you need to tell the framework how to resolve the factory:

```dart
ServiceLocator.I.registerTransient<UserService>(
(Map<String, dynamic> args) => UserService(
id: args['theUserId'] as UserId, // This is how your parameter will be provided
username: args['theUsername'], // You dont need to tell the type
password: args['thePassword'] as String, // But you must pass all the needed parameters
),
);
```

The `args` parameter is a Map of arguments you will pass when trying to resolve a factory:

```dart
final UserService userService =
ServiceLocator.I.resolve<UserService>(
args: {
'theUserId': UserId('1'),
'theUsername': 'HansZimmer123',
'thePassword': 'blafoo1!',
},
);
```

If you don't pass a required argument, a `TypeError` will be thrown.

### Lazy Registration

```dart
ServiceLocator.I.registerLazy<MyLazyService>(
Lazy<MyLazyService>(() => MyLazyService()),
);
```

To resolve a `Lazy` registered service, you use the same resolve method:

```dart
final MyLazyService myLazyService = ServiceLocator.I.resolve<MyLazyService>();
```

## Implementing a Custom Adapter

This package comes with a built-in adapter which should mostly work for your needs, but you can still develop you own one to have the full control. Here's a conceptual example to guide you:

```dart
class MyDIAdapter extends ServiceLocatorAdapter {
// Implement the adapter methods using your chosen DI framework
}
```

## Code generation

If you want to use the code generator, please refer to [this package here](https://pub.dev/zef_di_abstractions_generator).

## Customization and Extensibility

Our package's design encourages customization and extensibility. By creating adapters for your chosen DI frameworks, you can leverage our wrapper's features while utilizing the specific functionalities and optimizations of those frameworks.

## Contributing

Contributions are welcome! Please read our contributing guidelines and code of conduct before submitting pull requests or issues. Also every annotation or idea to improve is warmly appreciated.
1 change: 1 addition & 0 deletions analysis_options.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
include: package:lints/recommended.yaml
23 changes: 23 additions & 0 deletions example/test_classes/implementations/chicken.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import '../interfaces/bird.dart';

class Chicken implements Bird {
@override
void fly() {
print('I am a chicken, I cannot fly');
}

@override
void eat() {
print('I am a chicken, I eat grains');
}

@override
void move() {
print('I am a chicken, I walk');
}

@override
void sleep() {
print('I am a chicken, I sleep');
}
}
Loading
Loading