From a271bb9aa88be40cd52c19cc97736567ec93e63e Mon Sep 17 00:00:00 2001 From: tanner0101 Date: Tue, 3 Apr 2018 17:47:02 -0400 Subject: [PATCH] add updated services docs --- 3.0/docs/crypto/getting-started.md | 2 +- 3.0/docs/service/getting-started.md | 51 ++++++++++++++ 3.0/docs/service/provider.md | 51 ++++++++++++++ 3.0/docs/service/services.md | 104 ++++++++++++++++++++++++++++ 3.0/mkdocs.yml | 58 ++++++++-------- 5 files changed, 238 insertions(+), 28 deletions(-) create mode 100644 3.0/docs/service/getting-started.md create mode 100644 3.0/docs/service/provider.md create mode 100644 3.0/docs/service/services.md diff --git a/3.0/docs/crypto/getting-started.md b/3.0/docs/crypto/getting-started.md index 938735b7..d67c3c0a 100644 --- a/3.0/docs/crypto/getting-started.md +++ b/3.0/docs/crypto/getting-started.md @@ -1,6 +1,6 @@ # Using Crypto -Crypto is a library containing common APIs related to cryptography and data generation. The [vapor/crypto](https://github.com/vapor/crypto) package contains two modules: +Crypto ([vapor/crypto](https://github.com/vapor/crypto)) is a library containing common APIs related to cryptography and data generation. The package contains two modules: - `Crypto` - `Random` diff --git a/3.0/docs/service/getting-started.md b/3.0/docs/service/getting-started.md new file mode 100644 index 00000000..1ccaf8aa --- /dev/null +++ b/3.0/docs/service/getting-started.md @@ -0,0 +1,51 @@ +# Getting Started with Service + +Service ([vapor/service](https://github.com/vapor/service)) is a dependency injection (inversion of control) framework. It allows you to register, configure, and create your application's dependencies in a maintainable way. + +```swift +/// register a service during boot +services.register(PrintLogger.self, as: Logger.self) + +/// you can then create that service later +let logger = try someContainer.make(Logger.self) +print(logger is PrintLogger) // true +``` + +You can read more about [dependency injection](https://en.wikipedia.org/wiki/Dependency_injection) on Wikipedia. Also be sure to check out the [Getting Started → Services](../getting-started/services.md) guide. + +## Vapor + +This package is included with Vapor and exported by default. You will have access to all `Service` APIs when you import `Vapor`. + +```swift +import Vapor +``` + +## Standalone + +The Service package is lightweight, pure-Swift, and has very few dependencies. This means it can be used as a dependency injection framework for any Swift project—even one not using Vapor. + +To include it in your package, add the following to your `Package.swift` file. + +```swift +// swift-tools-version:4.0 +import PackageDescription + +let package = Package( + name: "Project", + dependencies: [ + ... + .package(url: "https://github.com/vapor/service.git", from: "1.0.0"), + ], + targets: [ + .target(name: "Project", dependencies: ["Service", ... ]) + ] +) +``` + +Use `import Service` to access the APIs. + +!!! warning + Some of this guide may contain Vapor-specific APIs, however most of it should be applicable to the Services package in general. + Visit the [API Docs](https://api.vapor.codes/service/latest/Service/index.html) for Service-specific API info. + diff --git a/3.0/docs/service/provider.md b/3.0/docs/service/provider.md new file mode 100644 index 00000000..af6c8ea7 --- /dev/null +++ b/3.0/docs/service/provider.md @@ -0,0 +1,51 @@ +# Using Providers + +The [`Provider`](https://api.vapor.codes/service/latest/Service/Protocols/Provider.html) protocol make it easy to integrate external services into your application. All of Vapor's official packages, like [Fluent](../fluent/getting-started.md), use the provider system to expose their services. + +Providers can: + +- Register services to your [`Services`](https://api.vapor.codes/service/latest/Service/Structs/Services.html) struct. +- Hook into your [`Container`](https://api.vapor.codes/service/latest/Service/Protocols/Container.html)'s lifecycle. + +## Register + +Once you have added a Service-exposing [SPM dependency](../getting-started/spm/#dependencies) to your project, adding the provider is easy. + +```swift +import Foo + +try services.register(FooProvider()) +``` + +This is usually done in [`configure.swift`](../getting-started/structure/#configureswift). + +!!! note + You can search GitHub for the [`vapor-service`](https://github.com/topics/vapor-service) tag for a list of packages that expose services using this method. + + +## Create + +Creating a custom provider can be a great way to organize your code. You will also want to create a provider if you are working on a third-party package for Vapor. + +Here is what a simple provider would look like for the `Logger` examples from the [Services](services.md) section. + +```swift +public final class LoggerProvider: Provider { + /// See `Provider`. + public func register(_ services: inout Services) throws { + services.register(PrintLogger.self) + services.register(FileLogger.self) + } + + /// See `Provider`. + public func didBoot(_ container: Container) throws -> Future { + let logger = try container.make(Logger.self) + logger.log("Hello from LoggerProvider!") + return .done(on: container) + } +} +``` + +Now when someone registers the `LoggerProvider` to their `Services` struct, it will automatically register the print and file loggers. When the container boots, the success message will be printed to verify the provider was added. + +See the [`Provider`](https://api.vapor.codes/service/latest/Service/Protocols/Provider.html) protocol's API docs for more information. diff --git a/3.0/docs/service/services.md b/3.0/docs/service/services.md new file mode 100644 index 00000000..0a707d41 --- /dev/null +++ b/3.0/docs/service/services.md @@ -0,0 +1,104 @@ +# Using Services + +This guide will show you how to register, configure, and create your own service. In this example we will be assuming two different `Logger` implementations. + +- `PrintLogger`: Prints logs. +- `FileLogger`: Saves logs to a file. Already conforms to `ServiceType`. + +## Register + +Let's take a look at how we can register our `PrintLogger`. First you must conform your type to `Service`. The easiest way to do this is simply adding the conformance in an extension. + +```swift +extension PrintLogger: Service { } +``` + +It's an empty protocol so there should be no missing requirements. + +### Factory + +Now the service can be registered to the [`Services`](https://api.vapor.codes/service/latest/Service/Structs/Services.html) struct. This is usually done in [`configure.swift`](../getting-started/structure/#configureswift). + +```swift +services.register(Logger.self) { container in + return PrintLogger() +} +``` + +By registering the `PrintLogger` using a factory (closure) method, we allow the [`Container`](https://api.vapor.codes/service/latest/Service/Protocols/Container.html) to dynamically create the service once it is needed. Any [`SubContainer`](https://api.vapor.codes/service/latest/Service/Protocols/SubContainer.html)s created later can call this method again to create their own `PrintLogger`s. + + +### Service Type + +To make registering a service easier, you can conform it to [`ServiceType`](https://api.vapor.codes/service/latest/Service/Protocols/ServiceType.html). + +```swift +extension PrintLogger: ServiceType { + /// See `ServiceType`. + static var serviceSupports: [Any.Type] { + return [Logger.self] + } + + /// See `ServiceType`. + static func makeService(for worker: Container) throws -> PrintLogger { + return PrintLogger() + } +} +``` + +Services conforming to [`ServiceType`](https://api.vapor.codes/service/latest/Service/Protocols/ServiceType.html) can be registered using just the type name. This will automatically conform to `Service` as well. + +```swift +services.register(PrintLogger.self) +``` +### Instance + +You can also register pre-initialized instances to `Services`. + +```swift +services.register(PrintLogger(), as: Logger.self) +``` + +!!! warning + If using reference types (`class`) this method will share the _same_ object between all [`Container`](https://api.vapor.codes/service/latest/Service/Protocols/Container.html)s and [`SubContainer`](https://api.vapor.codes/service/latest/Service/Protocols/SubContainer.html)s. + Be careful to protect against race conditions. + +## Configure + +If more than one service is registered for a given interface, we will need to choose which service is used. + +```swift +services.register(PrintLogger.self) +services.register(FileLogger.self) +``` + +Assuming the above services are registered, we can use service [`Config`](https://api.vapor.codes/service/latest/Service/Structs/Config.html) to pick which one we want. + +```swift +switch env { +case .production: config.prefer(FileLogger.self, for: Logger.self) +default: config.prefer(PrintLogger.self, for: Logger.self) +} +``` + +Here we are using the [`Environment`](https://api.vapor.codes/service/latest/Service/Structs/Environment.html) to dynamically prefer a service. This is usually done in [`configure.swift`](../getting-started/structure/#configureswift). + +!!! note + You can also dynamically _register_ services based on environment instead of using service config. + However, service config is required for choosing services that come from the framework or a provider. + +## Create + +After you have registered your services, you can use a [`Container`](https://api.vapor.codes/service/latest/Service/Protocols/Container.html) to create them. + +```swift +let logger = try someContainer.make(Logger.self) +logger.log("Hello, world!") + +// PrintLogger or FileLogger depending on the container's environment +print(type(of: logger)) +``` + +!!! tip + Usually the framework will create any required containers for you. You can use [`BasicContainer`](https://api.vapor.codes/service/latest/Service/Classes/BasicContainer.html) if you want to create one for testing. + diff --git a/3.0/mkdocs.yml b/3.0/mkdocs.yml index 8334f636..4a5d0cfa 100644 --- a/3.0/mkdocs.yml +++ b/3.0/mkdocs.yml @@ -19,11 +19,14 @@ pages: - 'Async': 'getting-started/async.md' - 'Services': 'getting-started/services.md' - 'Deployment': 'getting-started/cloud.md' -- 'Routing': - - 'Getting Started': 'routing/getting-started.md' - - 'Parameters': 'routing/parameters.md' - - 'Route': 'routing/route.md' - - 'Router': 'routing/router.md' +- 'Crypto': + - 'Getting Started': 'crypto/getting-started.md' + - 'Digests': 'crypto/digests.md' + - 'Ciphers': 'crypto/ciphers.md' + - 'Asymmetric': 'crypto/asymmetric.md' + - 'Random': 'crypto/random.md' +- 'Deploy': + - 'Getting Started': 'deploy/getting-started.md' - 'Fluent': - 'Getting Started': 'fluent/getting-started.md' - 'Models': 'fluent/models.md' @@ -35,40 +38,41 @@ pages: - 'Pivot': 'fluent/pivot.md' - 'Transaction': 'fluent/transaction.md' - 'Database': 'fluent/database.md' -- 'PostgreSQL': - - 'Getting Started': 'postgresql/getting-started.md' - - 'Fluent PostgreSQL': 'postgresql/fluent.md' - - 'PostgreSQL Core': 'postgresql/core.md' -- 'MySQL': - - 'Getting Started': 'mysql/getting-started.md' - - 'Fluent MySQL': 'mysql/fluent.md' - - 'MySQL Core': 'mysql/core.md' -- 'SQLite': - - 'Getting Started': 'sqlite/getting-started.md' - - 'Fluent SQLite': 'sqlite/fluent.md' - - 'SQLite Core': 'sqlite/core.md' - 'Leaf': - 'Getting Started': 'leaf/getting-started.md' - 'Basics': 'leaf/basics.md' - 'Custom tags': 'leaf/custom-tags.md' +- 'MySQL': + - 'Getting Started': 'mysql/getting-started.md' + - 'Fluent MySQL': 'mysql/fluent.md' + - 'MySQL Core': 'mysql/core.md' +- 'PostgreSQL': + - 'Getting Started': 'postgresql/getting-started.md' + - 'Fluent PostgreSQL': 'postgresql/fluent.md' + - 'PostgreSQL Core': 'postgresql/core.md' - 'Redis': - 'Getting Started': 'redis/getting-started.md' - 'Basics': 'redis/basics.md' - 'Custom commands': 'redis/custom-commands.md' - 'Publish and Subscribe': 'redis/pub-sub.md' - 'Pipeline': 'redis/pipeline.md' -- 'WebSocket': - - 'Getting Started': 'websocket/websocket.md' -- 'Crypto': - - 'Getting Started': 'crypto/getting-started.md' - - 'Digests': 'crypto/digests.md' - - 'Ciphers': 'crypto/ciphers.md' - - 'Asymmetric': 'crypto/asymmetric.md' - - 'Random': 'crypto/random.md' +- 'Routing': + - 'Getting Started': 'routing/getting-started.md' + - 'Parameters': 'routing/parameters.md' + - 'Route': 'routing/route.md' + - 'Router': 'routing/router.md' +- 'Service': + - 'Getting Started': 'service/getting-started.md' + - 'Services': 'service/services.md' + - 'Provider': 'service/provider.md' +- 'SQLite': + - 'Getting Started': 'sqlite/getting-started.md' + - 'Fluent SQLite': 'sqlite/fluent.md' + - 'SQLite Core': 'sqlite/core.md' - 'Testing': - 'Getting Started': 'testing/getting-started.md' -- 'Deploy': - - 'Getting Started': 'deploy/getting-started.md' +- 'WebSocket': + - 'Getting Started': 'websocket/websocket.md' - 'Version (3.0-rc)': - '1.5': 'version/1_5.md' - '2.0': 'version/2_0.md'