diff --git a/build.gradle b/build.gradle index 321f472..7f33b1e 100644 --- a/build.gradle +++ b/build.gradle @@ -18,7 +18,7 @@ startScripts { mainClassName = 'dev.alpas.alpasdev.StartKt' group 'dev.alpas.alpasdev' -version '0.9.6' +version '0.9.7' repositories { jcenter() diff --git a/src/main/resources/docs/alpas-console.md b/src/main/resources/docs/alpas-console.md index 8249028..9988a27 100644 --- a/src/main/resources/docs/alpas-console.md +++ b/src/main/resources/docs/alpas-console.md @@ -4,27 +4,29 @@ - [Registering Commands](#register) - [Writing Output](#writing-output) -As you may have noticed already, Alpas comes with a bunch of console commands—such as `make:controller`, `make:job`, -`route:list` etc.—to assist you performing some tasks from the command-line. You run an Alpas command by -prepending it with `alpas`. To see a list of all the Alpas commands as well as a short description for -each, you can use `alpas help` command. +As you may have already noticed, Alpas comes with a bunch of console commands—`make:controller`, `make:job`, +`route:list` etc. to name a few—to assist you in performing some tasks from a command-line. You run an +Alpas command by prepending it with `alpas`. + +To see a list of all the Alpas commands as well as a short description for each, you can use `alpas list` command. ### [Custom Commands](#custom-commands) If the core Alpas commands are not enough for you, it is easy to create your own. Alpas actually wraps [clikt][clikt] command library in its own thin wrapper. Clikt is very powerful library that makes -writing command line interfaces simple and intuitive. +writing command line interfaces simple and intuitive. It has pretty much everything you would +ever need to create powerful command-line interfaces. #### [Simple Commands](#simple-commands) -When writing a simple custom command, all you have to do is extend `dev.alpas.console.Command` class and override -the `run()` method. The `Command` constructor receives a number of optional parameters allowing you to -configure your commands the way you want it. This includes help text, summary text etc. +When writing a simple custom command, all you have to do is extend `dev.alpas.console.Command` class and +override the `run()` method. The `Command` constructor receives a number of optional parameters allowing +you to configure your commands the way you want it. This includes help text, summary text etc. -The easiest way to create a command is by using `make:command` Alpas command, which will generate a new -command under `console/commands` folder. +The easiest way to create a command is by using `make:command` Alpas command, which will +generate a new command under `console/commands` folder. ```bash @@ -32,11 +34,10 @@ $ alpas make:command GreetCommand ``` - + ```kotlin -// console/commands/GreetCommand.kt class GreetCommand : Command(name = "greet", help = "Say hello.") { private val name by argument() override fun run() { @@ -48,7 +49,7 @@ class GreetCommand : Command(name = "greet", help = "Say hello.") { -After [registering the above command](#register), you can call it like so: +After [registering this new command](#register), you can call it like so: ```bash @@ -61,22 +62,25 @@ $ alpas greet you #### [Generator Commands](#generator-commands) -Generator commands are the commands that generate some files when invoked. `make:command` is actually an example -of a generator command and so is `make:controller`. While you can use a simple command like `GreetCommand` -above to write a generator command, you have to wired few things to get it right. +Generator commands create some files and folders when invoked. `make:command` is actually an example of a +generator command and so is `make:controller`. + +While you can use a simple command like `GreetCommand` above to write a generator +command, you have to wired few things to get it right. + +Instead of extending `dev.alpas.console.Command` class, you can extend `dev.alpas.console.GeneratorCommand` +class to make your life much easier while writing generator commands. While it may not always satisfy +all your needs, but most of the times it does and even it doesn't, it's a good place to start. -Instead of extending `dev.alpas.console.Command` class, you can extend `dev.alpas.console.GeneratorCommand` class -to make your life much easier while writing such generator commands. While it may not always satisfy all your -needs for writing a generator command, but most of the times it does. You can pass `--generator` or `-g` -to `make:command` command to create a generator type command for you. Then all you have to do is -override one abstract method—`populateOutputFile()`. +You can pass `--generator` or `-g` to `make:command` command to create a generator type command +for you. Then all you have to do is override one abstract method—`populateOutputFile()`. Let's see an example of how we can write a `make:sandwich` generator command that creates a `NameOfSandwich.kt` -file under `sandwiches` folder, creating the folder if it already doesn't exist. +file under `sandwiches` folder, creating this folder if it already doesn't exist.
-1. Create a command +1. Create the command itself: ```bash @@ -86,12 +90,11 @@ $ alpas make:command MakeSandwich -g 2. Modify `console/commands/MakeSandwich.kt` file to: - + ```kotlin -// console/commands/MakeSandwich.kt class MakeSandwich(srcPackage: String) : GeneratorCommand(srcPackage, name = "make:sandwich", help = "Make a sandwich.") { @@ -103,6 +106,7 @@ class MakeSandwich(srcPackage: String) : val dir = "sandwiches" val file = File(sourceOutputPath(dir, *parentDirs), "${filename.toPascalCase()}.kt") + return OutputFile() .target(file) .packageName(makePackageName(dir, *parentDirs)) @@ -131,28 +135,29 @@ class MakeSandwich(srcPackage: String) : -Notice that we have a couple of placeholders in the code—`StubPackageName` and `StubClazzName` both of which will -be replaced with proper texts automatically when we invoke this command. +Notice that we have a couple of placeholders in the code—`StubPackageName` and `StubClazzName` both +of which will be replaced with proper texts automatically when we actually run the command. 3. Register this command in `ConsoleKernel` class: - + ```kotlin -// ConsoleKernel.kt // ... + override fun commands(app: Application): List { return listOf(MakeSandwich(app.srcPackage)) } + // ... ``` -4. Now we are ready to make a sandwich. +4. Now we are ready to make ourselves a sandwich or two: ```bash @@ -160,7 +165,9 @@ $ alpas make:sandwich club ``` -This command will generate a `sandwiches/Club.kt` file. You can actually create multiple sandwiches in one go: +This command will generate a `sandwiches/Club.kt` file. + +You can actually create multiple sandwiches in one go: ```bash @@ -177,31 +184,34 @@ After you have created your own commands, you must register them with the app ot to you. You can register a command in a number of ways—the easiest one is by overriding `commands()` method in `ConsoleKernel` class and returning a list of all your commands. - + ```kotlin -// ConsoleKernel.kt // ... + override fun commands(app: Application): List { return listOf(GreetCommand()) } + // ... ``` +An alternative way is by creating and registering a new [Service Provider](/docs/service-providers). + > /tip/ Clikt comes with many other powerful features such as `parameters`, `flags`, `choice options`, -`input prompts`, `password masking` and much more. The best part of it is that the features are properly -documented with lots of real-world examples. We highly recommend [consulting its documentation][clikt] -when creating your own commands. +>`input prompts`, `password masking` and much more. The best part of it is that the features are properly +>documented with lots of real-world examples. We highly recommend [consulting its documentation][clikt] +>when creating your own commands. ### [Writing Output](#writing-output) -When you need to write some output to the console, you can use `info`, `success`, `warning`, and `error` methods. -Most of these methods not only respect `--quiet` flag but also use proper ANSI colors for their purpose. +When you need to write some output to the console, you can use `info`, `success`, `warning`, and `error` +methods. Most of these methods respect `--quiet` flag and also use proper ANSI colors for their purpose. ```kotlin @@ -218,9 +228,10 @@ success("Yay! This is working.") ``` If you need more control over coloring the output, you can use `withColors()` method. Alpas uses the full-featured -text styling [Mordant](https://github.com/ajalt/mordant) library for coloring. This allows you to color the -output anyway you want. Keep in mind that `withColors()` **won't** print anything if `--quiet` flag is set. +text styling [Mordant][mordant] library for coloring console outputs. This allows you to color the output +anyway you want. Keep in mind that `withColors()` **won't** print anything if `--quiet` flag is set. -> /power/ Alpas Console is proudly powered by [Clikt][clikt]. +> /power/ Alpas Console is proudly powered by [Clikt][clikt] and [Mordant][mordant]. [clikt]: https://ajalt.github.io/clikt/ +[mordant]: https://github.com/ajalt/mordant diff --git a/src/main/resources/docs/controllers.md b/src/main/resources/docs/controllers.md index 2e96b64..a573a6f 100644 --- a/src/main/resources/docs/controllers.md +++ b/src/main/resources/docs/controllers.md @@ -22,10 +22,10 @@ Or you can use `alpas make:controller` Alpas command to create one or multiple c ```bash # creates AdminController.kt file under controllers/admin folder -alpas make:controller admin/AdminController +$ alpas make:controller admin/AdminController # creates DocsController.kt and HomeController.kt files under controllers folder -alpas make:controller DocsController HomeController +$ alpas make:controller DocsController HomeController ``` diff --git a/src/main/resources/docs/mail.md b/src/main/resources/docs/mail.md index 07ac189..23b9bde 100644 --- a/src/main/resources/docs/mail.md +++ b/src/main/resources/docs/mail.md @@ -5,9 +5,9 @@ - [Debugging Emails](#debugging-emails) - [Custom Mail Driver](#custom-mail-driver) -Alpas uses [Simple Java Mail](https://github.com/bbottema/simple-java-mail) library and sprinkles it with some of -its own APIs to make it easy for you to send emails from within your app. You can start with an SMTP driver or a -local file driver or write one of your own drivers. +Alpas uses [Simple Java Mail](https://github.com/bbottema/simple-java-mail) library and sprinkles it +with some of its own APIs to make it easy for you to send emails from within your app. You can +start with an SMTP driver or a local file driver or write one of your own drivers. ### [Available Drivers](#available-drivers) @@ -24,15 +24,15 @@ Currently, Alpas comes bundled with 2 mail drivers: ### [Getting Started](#getting-started) -If you open `configs/MailConfig.kt`, you'll notice that Alpas has lazily loaded two drivers for you `smpt` for SMTP -Driver and `local` for the Local Mail Driver. You can get an instance of one of these drivers during the runtime -by calling `driver()` method on `MailConfig` class and optionally passing the name of the driver. You can decide -to always use a specific driver by setting `MAIL_DRIVER` variable to one of the driver names. +If you open `configs/MailConfig.kt`, you'll notice that Alpas has lazily loaded two drivers for you—`smpt` for SMTP +Driver and `local` for the Local Mail Driver.You can get an instance of one of these drivers during the runtime +by calling `driver()` method on `MailConfig` class. You can pass the name of a driver or use the default driver. +You can decide to always use a specific driver by setting `MAIL_DRIVER` variable to one of the driver names. ### [Composing and Sending Emails](#composing-and-sending-emails) -To compose an email create an instance of `MailMessage` class and set the properties such as `to`, `subject`, +To compose an email, create an instance of `MailMessage` class and set the properties such as `to`,`subject`, `message` etc. Once the mail is composed, send it via one of the mail drivers' `send()` method. @@ -40,12 +40,16 @@ To compose an email create an instance of `MailMessage` class and set the proper ```kotlin fun send(call: HttpCall) { + //... + val mail = MailMessage().apply { to = "hello@example.com" subject = "Hello There!" message = "Just want to say hi!" } call.config().driver().send(mail) + + //... } ``` @@ -55,8 +59,8 @@ fun send(call: HttpCall) { ### [Using View Templates](#using-view-templates) -While composing a mail, instead of using plain text, you could also call `view()` method and pass the name of -the view template and an optional argument map to render the email's content. +While composing an email, instead of using plain text, you can also call `view()` method and pass +the name of the view template, and an optional argument map to render the email's content. @@ -82,32 +86,31 @@ fun send(call: HttpCall) { ### [Debugging Emails](#debugging-emails) -During development, it is very convenient to save email messages locally for debugging. Alpas supports saving all -your email messages to `storage/mails` folder by using `LocalMailDriver`. To use this driver, make sure the value of -`MAIL_DRIVER` is set to `local` in your `.env` file. +During development, it is very convenient to save email messages locally for debugging. Alpas supports +saving all your email messages to `storage/mails` folder by using `LocalMailDriver`. To use this +driver, make sure the value of `MAIL_DRIVER` is set to `local` in your `.env` file. -If you really want to send emails across the wire for real testing during development, you could use a service -like [Mailtrap](https://mailtrap.io/), which collects all your emails in a dummy demo inbox. +If you really wish to send emails across the wire for real testing during development, you can use a +service like [Mailtrap](https://mailtrap.io/), which collects all your emails in a dummy demo inbox. ### [Custom Mail Driver](#custom-mail-driver) It is easy to add a custom driver. All you have to do is create a class that implements -`dev.alpas.mailing.drivers.MailDriver` interface and override `send(mail: MailMessage)` method. Eventually, register -this new driver in `MailConfig` class with a name. +`dev.alpas.mailing.drivers.MailDriver` interface and override `send(mail: MailMessage)` +method. Eventually, register this new driver in `MailConfig` class under a name. -Let's see and example of how to write your own [SparkPost](https://sparkpost.com) driver by wrapping their -official [Java client API](https://github.com/SparkPost/java-sparkpost). +Let's see and example of how to write your own [SparkPost](https://sparkpost.com) driver by +wrapping their official [Java client API](https://github.com/SparkPost/java-sparkpost).
-1. Create the driver +1. Create the driver: - + ```kotlin -// SparkPostMailDriver.kt import dev.alpas.mailing.MailMessage import dev.alpas.mailing.drivers.MailDriver @@ -122,13 +125,12 @@ class SparkPostDriver(val apiKey: String, defaultFromAddress: String) : MailDriv -2. Register the driver +2. Register the driver: - + ```kotlin -// configs/MailConfig.kt class MailConfig(env: Environment) : BaseConfig(env) { init { // ... @@ -144,19 +146,23 @@ class MailConfig(env: Environment) : BaseConfig(env) { -3. Use the driver +3. Use the driver: ```kotlin fun send(call: HttpCall) { + //... + val mail = MailMessage().apply { to = "hello@example.com" subject = "Hello There!" message = "Just want to say hi!" } call.config().driver("sparkpost").send(mail) + + //... } ``` diff --git a/src/main/resources/docs/middleware.md b/src/main/resources/docs/middleware.md index 3bb387c..67cc7b6 100644 --- a/src/main/resources/docs/middleware.md +++ b/src/main/resources/docs/middleware.md @@ -21,7 +21,7 @@ it under `middleware` folder. ```bash -alpas make:middleware AdminOnlyMiddleware +$ alpas make:middleware AdminOnlyMiddleware ``` diff --git a/src/main/resources/docs/notifications.md b/src/main/resources/docs/notifications.md index 37501a6..58b483b 100644 --- a/src/main/resources/docs/notifications.md +++ b/src/main/resources/docs/notifications.md @@ -6,34 +6,37 @@ - [Mailable Notification](#mailable-notification) - [Custom Notification Channels](#custom-notification-channels) -Notification allows you to let a user know about an event via channels such as emails, SMS, Slack message etc. -While you could easily [send an email](/docs/mail) using one of the mail drivers, notifications gives you more -flexibility esp. when it comes to delivering a message via different channels - mail, Slack, SMS etc. +Notification allows you to let a user know about an event via channels such as emails, SMS, Slack message +etc. While you can easily [send an email](/docs/mail) using one of the mail drivers, notifications +gives you more flexibility esp. when it comes to delivering a message via different channels. -Let's say you are writing a social media web app that allows users to post a comment. When someone likes a user's -post, you may want to send the author a `PostLiked` email notification about. You may also want to add a -a notification in the database so that when they login to the app, they see like a blue badge just like the way -Twitter shows it next to a bell icon. +Let's say you are writing a social media web app that allows users to post a comment. When someone likes +a user's post, you may want to send the author a `PostLiked` email notification. You may also want to +add a notification in the database so that when they log-in to the app, they see like a red badge +notification just like the way Facebook shows it next to a bell icon. -Another example of a notification is when a user requests for a password reset. You can "notify" the actual email -author by sending an email notification. In fact this is what Alpas does when +Another example of a notification is when a user requests for a password reset. You can "notify" +the actual email author by sending an email notification. In fact this is what Alpas does when [sending reset emails](/docs/password-reset). ### [Getting Started](#getting-started) -A notification is any class implementing the `dev.alpas.notifications.Notification` interface. In -practice, unless you are writing a custom channel, you don't implement this interface directly but use one of -the out-of-the-box channels — `MailableNotification`, `SlackableNotification` etc. and override appropriate methods. +A notification is a class implementing `dev.alpas.notifications.Notification` interface. +In practice, however, unless you are writing a custom channel, you don't implement this interface +directly but use one of the out-of-the-box channels—`MailableNotification`, +`SlackableNotification` etc. and override appropriate methods. -The quickest way to creating a notification is to use `make:notification` Alpas command. It will generate a -notification that implements one or multiple notification types and put it under `notifications` folder. +The quickest way to create a notification is by using `make:notification` Alpas command. It will create a notification +that class implements [MailableNotification](#mailable-notification) type under `notifications` folder. ```bash -alpas make:notification PostLiked + +$ alpas make:notification PostLiked + ``` - + ```kotlin @@ -55,38 +58,46 @@ class PostLiked : MailableNotification { ### [Notifiable](#notifiable) -A notifiable object is any object that implements `dev.alpas.notifications.Notifiable` interface to whom -the notification is being sent. Since most of the times you send a notification to an "authenticatable user", the -`Authenticatable` interface already comes with `Notifiable` interface implemented. However, any other objects can -implement this interface to make them compatible with the notification system. +A notifiable object is an object that implements `dev.alpas.notifications.Notifiable` interface to whom a notification +is being sent. Since most of the times you would be sending a notification to an "authenticatable user", the +`Authenticatable` interface already comes with `Notifiable` interface implemented. However, any other +objects can implement this interface to make them compatible with Alpas's notification system. ### [Dispatching Notifications](#dispatching-notifications) -To dispatch a notification, you get an instance of `dev.alpas.notifications.NotificationDispatcher` from a -[DI container](/docs/ioc-container) and call `dispatch()` method passing the actual notification object and -the notifiable object. - +To dispatch a notification, you get an instance of `dev.alpas.notifications.NotificationDispatcher` +from a [DI container](/docs/ioc-container) and call `dispatch()` method passing the actual +notification object and the notifiable object. ```kotlin + // ... + val notification = PostLikedNotification(sender) container.make().dispatch(notification, receiver) + + // ... + ``` -For convenience, your controller classes already have a base `send()` method that you could use to send a notification -easily without having to *make* and call a `NotificationDispatcher` instance. +For convenience, your controller classes already have a base `send()` method that you can use to send a +notification easily without having to call the `NotificationDispatcher`'s `dispatch()` method. ```kotlin fun like(call: HttpCall){ + //... + send(PostLikedNotification(call.user), receiver) + + //... } ``` @@ -96,18 +107,22 @@ fun like(call: HttpCall){ ### [Notification Channels](#notification-channels) -Every notification must override `channels()` method and must return the class names of all the channels that -the notification will be delivered on. This method receives an instance of a notifiable object receiving the -notification. You could use this notifiable object to decide what channels to use for delivering the notification. +Every notification must override `channels()` method and must return the class names of all the +channels that the notification will be delivered on. This method receives an instance of a +notifiable object receiving the notification. You can use this notifiable object to +decide what channels to use for delivering the notification. - + ```kotlin - // notifications/PostLiked.kt - override fun channels(notifiable: Authenticatable): List> { - return listOf(MailChannel::class, SlackChannel::class) - } +//... + +override fun channels(notifiable: Authenticatable): List> { + return listOf(MailChannel::class, SlackChannel::class) +} + +//... ``` @@ -116,40 +131,45 @@ notification. You could use this notifiable object to decide what channels to us ### [Notifications Queueing](#notifications-queueing) -Most often than not, sending notifications take some time as they might have to make to an external API call to +More often than not, sending notifications takes some time as they might have to make to an external API call to deliver the notification. Instead of waiting for the notification to be delivered, you can put the notification -delivery in a queue and send it later. Not just for the performance reason, you could also decide to add notifications -to a queue to be processed by a different more efficient system that could well be written in a different programming -language. Queueing a notification can be easily done by overriding `shouldQueue()` method and returning `true`. By -default it returns `false`. +delivery in a queue and send it later. + +Not just for the performance reason, you could also decide to add notifications to a queue to be processed by +a different, probably more efficient system that could well have been written in a different programming +language. Queueing a notification can be easily done by overriding `shouldQueue()` method and +returning `true`. It returns `false` by default. -This method also receives an instance of the `Notifiable` object to help you make better decision to whether to -queue the notification or not. +`shouldQueue()` method also receives an instance of the `Notifiable` object to help you make +a better decision to whether to queue the notification or not. - + ```kotlin +//... + fun shouldQueue(notifiable: T): Boolean { return true } +//... + ``` -> /info/ It's upto a `NotificationChannel` to decide whether to respect the return value of `shouldQueue()` or -> not and how to queue it. It may not make sense for some `NotificationChannel` to queue anything. `DatabaseChannel` -> is one of such channels. +>/info/ It's upto a `NotificationChannel` to decide whether to respect the return value of `shouldQueue()` +>or not and how to queue it. It may not make sense for some `NotificationChannel` to queue anything. ### [Mailable Notification](#mailable-notification) -A mailable notification uses `MailChannel` to send a notification in an email form. Your notification must implement -`MailableNotification` interface, overrride `toMail()` method that returns an instance of a `MailMessage`, and lastly -return `MailChannel::class` as one of the channels from `channels()` method. +A mailable notification uses `MailChannel` to send a notification in an email form. Your notification must +implement `MailableNotification` interface, overrride `toMail()` method that returns an instance of a +`MailMessage`, and lastly return `MailChannel::class` as one of the channels from `channels()` method. - + ```kotlin @@ -174,17 +194,21 @@ class PostLiked : MailableNotification { ### [Custom Notification Channels](#custom-notification-channels) -If you want to write your own channels to deliver a notification, you can easily do so by extending a -`NotificationChannel` class and overriding `send()` method. +If you want to write your own channels to deliver a notification, you can easily do so by +extending a `NotificationChannel` class and overriding the `send()` method. + +Let's say we want to write a custom channel called `TextChannel` that delivers a notification to a +user via SMS text. Let's see a complete example of how we go can about writing such a custom +channel ourselves in few easy steps. -Let's say we want to write a custom channel called `TextChannel` that delivers a notification to a user via SMS text. -Let's see a complete example of how we go can about writing such a custom channel ourselves in few easy steps. +
- +1. Create a channel and related classes: + + ```kotlin -// TextChannel.kt class TextChannel : NotificationChannel { override fun send(notification: Notification, notifiable: T) { val textNotification = notification as TextableNotification @@ -194,25 +218,22 @@ class TextChannel : NotificationChannel { } } -// TextableNotification.kt interface TextableNotification : Notification { fun toText(notifiable: T): TextMessage } -// TextMessage.kt data class TextMessage(val number: Int, val message: String) ``` -Once we have our own channel, using it is very simple: +2. Once we have our own channel, using it is very simple: - + ```kotlin -// notifications/PostLiked.kt class PostLiked : TextableNotification { override fun channels(user: User): List> { return listOf(TextChannel::class) @@ -226,3 +247,5 @@ class PostLiked : TextableNotification { ``` + +
diff --git a/src/main/resources/docs/password-reset.md b/src/main/resources/docs/password-reset.md index ac04f03..57c3a21 100644 --- a/src/main/resources/docs/password-reset.md +++ b/src/main/resources/docs/password-reset.md @@ -34,7 +34,7 @@ in the table. If not, you need to migrate your tables using `db:migrate` command ```bash -alpas db:migrate +$ alpas db:migrate ``` diff --git a/src/main/resources/docs/queues.md b/src/main/resources/docs/queues.md index 9c977e8..91a72f8 100644 --- a/src/main/resources/docs/queues.md +++ b/src/main/resources/docs/queues.md @@ -13,10 +13,10 @@ - [Selecting Queues](#selecting-queues) - [Waiting for Jobs](#waiting-for-jobs) -Alpas makes it easy to defer time-consuming tasks to a queue for processing it later. You could choose one of the -many queues bundled with Alpas or create one of your own. No matter what you choose, it abstracted them away -behind a cohesive set of APIs. You can easily switch between different queue backends without having to -change your code. +Alpas makes it easy to defer time-consuming tasks to a queue for processing it later. You can choose +one of the many queues bundled with Alpas or create one of your own. No matter what you choose, +Alpas abstracts them away behind a cohesive set of APIs. You can easily switch between +different queue backends without having to change your code. The backends for queues are called `connections` in Alpas such as `DatabaseQueueConnection`. Each connection could have multiple queues that you could selectively use for queuing different tasks most possibly @@ -27,12 +27,13 @@ based on their priorities such as *high priority queue*, *low priority queue* et If you open `configs/Queue.kt`, you'll notice that Alpas has lazily registered three queue connections for you. Each connection is registered with a name. Alpas uses this key to pick one of these connections based on -the value of `QUEUE_CONNECTION` in your `.env` file. This is set to `passthrough` by default. Feel free -to register more queue connections or remove the ones that you know for sure you would't use. +the value of `QUEUE_CONNECTION` in your `.env` file. This is set to `passthrough` by default. + +Feel free to register more queue connections or remove the ones that you know for sure you would't use. Once you are happy with your queue connections, make sure to register `QueueServiceProvider::class` -by [adding it to the list of service providers](/docs/service-providers#registering) in both of -the kernel classes—`HttpKernel` and `ConsoleKernel`. +by [adding it to the list of service providers](/docs/service-providers#registering) in both +kernel classes—`HttpKernel` and `ConsoleKernel`. ### [Jobs](#jobs) @@ -40,8 +41,8 @@ the kernel classes—`HttpKernel` and `ConsoleKernel`. #### [Creating Jobs](#creating-jobs) -A job is just a serializable task with some metadata that you wish to process later. The metadata is used during -the processing of the task once it is dequeued from the backend. +A job is just a serializable task with some metadata that you want to process later. The +metadata is used during the processing of the task once it is de-queued from the backend. Here's a real example of a job that is used when [sending a mail](/docs/mail) such as when [resetting passwords](/docs/password-reset). @@ -65,8 +66,8 @@ class SendMailJob(val mail: MailMessage) : Job() {
You can create a job by extending `dev.alpas.queue.job.Job` class and overriding `invoke()` method, which will -be called after it is retrieved from the queue for actually processing the job. The easiest way to create a -job is by using `make:job` Alpas console command. The jobs will be placed under `jobs` folder. +be called after it is retrieved from the queue for actual processing of the job. The easiest way to create +a job is by using `make:job` Alpas console command. The jobs will be placed under `jobs` folder. ```bash @@ -74,11 +75,10 @@ $ alpas make:job SendInvoice ``` - + ```kotlin -// jobs/SendInvoice.kt class SendInvoice : Job() { override fun invoke(container: Container) { // make and send the actual invoice @@ -92,26 +92,26 @@ class SendInvoice : Job() { #### [Queuing Jobs](#queuing-jobs) -You can queue a job by asking a container for a `QueueDispatcher` instance and then calling `dispatch()` -method on it. +You can queue a job by asking a container for a `QueueDispatcher` instance and then calling its `dispatch()` method. ```kotlin // ... + // Enqueue MailInvoice job to be processed by the default // queue connection from the queue name called "high" container.make.dispatch(MailInvoice(), "high") + // ... ``` -Conveniently, since most of the times you'd want to queue a job from within a controller, you can call base -controller's `queue()` method. - +Conveniently, since most of the times you would be queueing a job from within a controller, +you can call base controller's `queue()` method. @@ -121,20 +121,20 @@ fun sendInvoice(call: HttpCall){ queue(MailInvoice(), "medium") } - ``` -If you want to queue a job on a non-default connection, you can pass the name of the connection as a third -parameter: +If you want to queue a job on a non-default connection, you can pass the name of the connection as a third parameter: ```kotlin // ... + container.make.dispatch(MailInvoice(), "high", "database") + // ... ``` @@ -164,15 +164,15 @@ means it will be available to be processed within approx. 1 second of adding it #### [Retrying Failed Jobs](#retrying-failed-jobs) Although undesirable, your job might fail during processing for one reason or the other. Usually when that -happens you would want to give few chances for that job to process again. You can do so by overriding -`tries` property of the job. It is set to **3 tries** by default. If the job keeps failing even -after the number of tries set in the job, it will be added to a failed job queue for you to -do further introspection. +happens you would want to give few chances for that job to be processed again. You can do so by +overriding `tries` property of the job. It is set to **3 tries** by default. If the job keeps +failing even after the number of tries set in the job, it will be added to a failed job +queue for you to do further introspection. -> /alert/ A job instance gets serialized as a JSON before putting it in a queue. This means a job must -be serializable along with all its properties and dependencies. Also, make sure that the values injected -via primary constructor are not private otherwise the job will fail to deserialize, and it won't be -processed. +> /alert/ A job instance gets serialized as a JSON before putting it in a queue. This means a job must +>be serializable along with all its properties and dependencies. Also, make sure that the values injected +>via primary constructor are not private otherwise the job will fail to deserialize, and it won't be +>processed. ### [Available Drivers](#available-drivers) @@ -186,8 +186,8 @@ Alpas comes bundled with 3 queue drivers: #### [Pass-through](#passthrough) -A `passthrough` driver simply invokes the job without holding it off. This is useful if you want to quickly debug -or test your jobs without having to go through them a queue as it needs some ceremonies to run a queue worker. +A `passthrough` driver simply invokes the job without holding it in queue. This is useful if you want to quickly debug +or test your jobs without having to have them go through a queue, as it needs some ceremonies to run a queue worker. > /alert/ Keep in mind that since the jobs are invoked right away with the `passthrough` driver, it completely ignores both `tries` and `delayInSeconds` properties. @@ -217,15 +217,18 @@ $ alpas migrate #### [Active MQ](#activemq) -For a more robust, flexible, and cross-platform queuing, you can use [ActiveMQ][activemq], which Alpas supports -out of the box. It requires a small ceremony to set up a messaging server, called a broker, but once that is -done, you get the one of the most popular messaging server for holding your jobs. Checkout the -[official ActiveMQ website][activemq] for more information of plethora of other features it -provides out-of-the-box. +For a more robust, flexible, and cross-platform queuing, you can use [ActiveMQ][activemq], which Alpas +supports out-of-the-box. It requires a small ceremony to set up a messaging server, called a broker, +but once that is done, you get the one of the most popular messaging servers for queueing your jobs. + +Checkout the [official ActiveMQ website][activemq] for more information of plethora +of other features it provides out-of-the-box. -Since Alpas abstracts away the intricacies of interacting with different queue drivers, nothing changes as far as -the process of queueing, de-queueing, and processing of jobs is concerned. You do need to set few values in -you `.env` file: +Since Alpas abstracts away the intricacies of interacting with different queue drivers, nothing +changes as far as queueing, de-queueing, and processing of jobs is concerned. You do need +to set few values in you `.env` file: + + ```toml @@ -246,6 +249,8 @@ ACTIVEMQ_FAILED_QUEUE_NAME=failed ``` + + Now all that remains is to set up an ActiveMQ broker either on your local machine or on a remote server. Let's do the minimum setup of the next version of ActiveMQ, [ActiveMQ Artemis][artemis], and play with it. @@ -277,7 +282,7 @@ using `artemis-service start` command. Let's run the broker before moving to the $ mybroker/bin/artemis run # You should now have access to a full management console dashboard at your -# disposal at, by default, # http://localhost:8161/. We were not kidding when +# disposal at, by default, http://localhost:8161/. We were not kidding when # we said ActiveMQ is an enterprise grade messaging queue! ``` @@ -286,17 +291,19 @@ $ mybroker/bin/artemis run to do that, you need to open `mybroker/etc/broker.xml` file and set `auto-delete-queues` to `false` and `default-address-routing-type` to `ANYCAST` under `` element: - + ```xml + false ANYCAST + ``` @@ -305,23 +312,23 @@ and `default-address-routing-type` to `ANYCAST` under ` ### [Running Queue](#running-queue) -One of the ways you can process a job is by using `queue:work` Alpas command. When you run it, Alpas -basically starts a new console app instance, dequeues and deserializes a job from a connection -one-by-one, and processes them. +One of the ways you can process a job is by using `queue:work` Alpas command. When you run it, +Alpas basically starts a new console app instance, de-queues and deserializes a job from +a connection one-by-one, and processes them. ```bash -alpas queue:work +$ alpas queue:work ``` @@ -332,42 +339,42 @@ must rebuild your app and then restart the process whenever your code changes. #### [Setting Connection](#setting-connection) -Queue worker selects the default queue connection specified in your `.env` file when dequeueing jobs from +Queue worker selects the default queue connection specified in your `.env` file when de-queueing jobs from a driver. You can specify which queue connection to pick by passing the name of the connection as an argument to `queue:work` command. ```bash # this command will process jobs from the 'database' connection -alpas queue:work database +$ alpas queue:work database ``` #### [Selecting Queues](#selecting-queues) -Not just the connection, you can also pick what queues you want to process by specifying `--queue` option -to `queue:work` command. +Not just the connection, you can also pick what queues you want to process by specifying +`--queue` option to `queue:work` command. ```bash # this command will process jobs from 'invoices' queue on 'database' connection -alpas queue:work database --queue=invoices +$ alpas queue:work database --queue=invoices ``` #### [Waiting for Jobs](#waiting-for-jobs) -When you have no jobs available on your queue, then you don't want to bombard the driver checking for jobs -availability. To be easy on your system, you might want the worker to sleep for some time before probing -for jobs again. You can do this by passing `--sleep` option and specifying the number of seconds -the worker will sleep if there are no new jobs available. +When you have no jobs available on your queue, then you don't want to bombard the driver checking for +jobs availability. To be easy on your system, you might want the worker to sleep for some time +before probing for jobs again. You can do this by passing `--sleep` option and specifying +the number of seconds the worker will sleep if there are no new jobs available. ```bash # The worker will sleep for 5 seconds if there are no new jobs available -alpas queue:work --sleep=5 +$ alpas queue:work --sleep=5 ``` @@ -375,10 +382,10 @@ If you don't pass a value for the `sleep` option, each driver uses its own timeo long to sleep. For ActiveMQ, it is 30 seconds and for a database queue it is 3 seconds. You can tweak the value for each connection individually while registering them in your `QueueConfig` class. -> /info/ Keep in mind that different drivers "sleep" differently. Database driver, for an example, just -makes the worker thread go to sleep by calling `Thread.sleep()` method. On the other hand, ActiveMQ -driver passes the sleep time as a parameter to `receive()` method as it is waiting for the next -job to arrive. While Passthrough driver ignores this and doesn't sleep at all! +>/info/Keep in mind that different drivers "sleep" differently. Database driver, for an example, just +>makes the worker thread go to sleep by calling `Thread.sleep()` method. On the other hand, ActiveMQ +>driver passes the sleep time as a parameter to `receive()` method as it is waiting for the next +>job to arrive. While the `passthrough` driver ignores this and doesn't sleep at all! - [activemq]: https://activemq.apache.org/ - [artemis]: https://activemq.apache.org/components/artemis/ diff --git a/src/main/resources/docs/redis.md b/src/main/resources/docs/redis.md index a456edd..e13f4a8 100644 --- a/src/main/resources/docs/redis.md +++ b/src/main/resources/docs/redis.md @@ -8,7 +8,9 @@ Let's see how we can easily integrate Jedis client in an app using a service pro 1. Crate a new service provider using `make:provider` Alpas command: ```bash -alpas make:provider JedisServiceProvider + +$ alpas make:provider JedisServiceProvider + ``` 2. Register an instance of `JedisPool` in the new created `JedisServiceProvider` class making sure that the pool diff --git a/src/main/resources/docs/validation.md b/src/main/resources/docs/validation.md index cc4f381..9e4b111 100644 --- a/src/main/resources/docs/validation.md +++ b/src/main/resources/docs/validation.md @@ -136,7 +136,7 @@ a new validation guard class under `guards` folder. ```bash -alpas make:guard CreatePageGuard +$ alpas make:guard CreatePageGuard ``` @@ -328,7 +328,7 @@ To start quickly, use `alpas make:rule` command to create a new rule under `rule ```bash -alpas make:rule above +$ alpas make:rule above ```