If it’s I/O, it’s asynchronous. Input/Output is any activity that occurs under your program: user input, printing on a terminal, reading a socket, write to disk. For example, you may want your user to keep browsing while your application is doing stuff like loading new content or sending emails.

Async with Masonite?

It is fully possible to do asynchronous jobs with Masonite with the Async Driver. This driver allows us to send jobs to the background using multi-threading. As it is the queue driver by default, we’ll use it by creating a simple app that sends an email when a sign in.

Setup environment

mkdir async-masonite
cd async-masonite
virtualenv --python=/usr/bin/python3.7 env
source env/bin/activate

Now, let’s install masonite and create the project.

pip3 install masonite
craft new async-masonite .
craft serve

Quickly set Project

We are going to quickly create the project. First, we must edit the .env file by adding some information and the mail driver we’re going to use.

MAIL_DRIVER=terminal
MAIL_FROM_ADDRESS=hellomasonite@gmail.com
MAIL_FROM_NAME=Masonite
MAIL_HOST=
MAIL_PORT=
MAIL_USERNAME=
MAIL_PASSWORD=

MAILGUN_SECRET=
MAILGUN_DOMAIN=

DB_CONNECTION=sqlite
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=test.db
DB_USERNAME=root
DB_PASSWORD=root
DB_LOG=True

Now, we can make migrations and use masonite auth system.

craft auth
craft controller Home
craft migrate

Now, the routes.

#routes/web.py
"""Web Routes."""

from masonite.routes import Get, Post

ROUTES = [
    #Get('/', 'WelcomeController@show').name('welcome'),
    Get('/','HomeController@show').name('home'),
]

from masonite.auth import Auth 
ROUTES += Auth.routes()

Now, the controller and the view.

#app/http/controllers/HomeController.py
"""A HomeController Module."""

from masonite.request import Request
from masonite.view import View
from masonite.controllers import Controller


class HomeController(Controller):
    """HomeController Controller Class."""

    def __init__(self, request: Request):
        """HomeController Initializer

        Arguments:
            request {masonite.request.Request} -- The Masonite Request class.
        """
        self.request = request

    def show(self, view: View):
        return view.render('home')

The View.

craft view home

Now, the home view.

#resources/templates/home.html
{% if auth() %}
	<h2>Hey!Look your console</h2>
{% else %}
	<a href="/login">Please Login</a>
{% endif %}

If you hit directly localhost:8000, it will ask you to login. If you don’t have login credentials, register at localhost:8000/register.

What is a Job?

Before diving into coding, we must clarify first what a job is. A “job” is the shell’s representation for a process group – a set of processes that can all be sent a signal – concretely a pipeline and its descendent processes. I know this definition can a little tricky and easily confused with tasks. If you want more information, read this detailed response in StackOverflow.

Create a job with Masonite

To create a job in Masonite, you can use the craft command.

craft job SendLoginEmail

As you can notice, the Job is a Queueable class with two methods: the constructor and the logic to handle the job.

#app/jobs/SendLoginEmail.py
"""A SendLoginEmail Queue Job."""

from masonite.queues import Queueable
from masonite.request import Request
from masonite import Mail

class SendLoginEmail(Queueable):
    """A SendLoginEmail Job."""

    def __init__(self, request: Request, mail: Mail):
        """A SendLoginEmail Constructor."""
        self.request = request
        self.mail = mail

    def handle(self):
        """Logic to handle the job."""
        self.mail.driver('terminal').to(self.request.user().email).template('mail/loginalert').send()

The line in the handle method, we precise the driver we want to use. You can use any driver you want, but here I’m going to show the email in the terminal, next we request the user email. After that, we use a mail template that we’ll create to send the mail. Let’s add this queue to the HomeController class.

#app/http/controllers/HomeController.py
...
from app.jobs.SendLoginEmail import SendLoginEmail
from masonite import Queue
...
    def show(self, view: View, queue: Queue):
        queue.push(SendLoginEmail)
        return view.render('home')

Mail template

Now, let’s create the mail template. By default, it will be a view.

craft view mail/loginalert
#resources/template/mail/loginalert
Hello. You just signed up here. Is it you?

Now login, and hit localhost:8000 . You may see something like this in your console. And Voilà.

Conclusion

In this tutorial, we learned how to use the async driver to send email when a user signs in and shows it in the terminal. There are other drivers available, so check the documentation about Queues and Jobs if you want to learn more.