DEV Community

Joseph Mancuso for Masonite

Posted on • Edited on

Masonite Framework 2.0 Patch

The Patch and Challenge

Masonite is a pretty young framework and has a pretty young community. While building an awesome dashboard package for Masonite, I realized that a package I was working on was not working the same as it was with Masonite 1.6. More specifically, I was able to add static files to Whitenoise via a Service Provider but not I was not able to in 2.0.

Those familiar with Laravel and Service Providers, I present you a challenge. This challenge is to find what is wrong with this code snippet below.

The answer is below it so don't peak!

for provider in container.make('ProvidersConfig').PROVIDERS:
    located_provider = provider()
    located_provider.load_app(container).register()
    if located_provider.wsgi:
        container.make('WSGIProviders').append(located_provider)
    else:
        container.resolve(located_provider.boot)
        container.make('Providers').append(located_provider)

Not sure what it is? I can give you a hint.

for provider in container.make('ProvidersConfig').PROVIDERS:
    located_provider = provider()
    located_provider.load_app(container).register() # <-- this line
    if located_provider.wsgi:
        container.make('WSGIProviders').append(located_provider)
    else:
        container.resolve(located_provider.boot) # <-- and this line
        container.make('Providers').append(located_provider)

Find it yet? Ok I'll just tell you. This problem is that not all Service Providers have registered before all Service Providers have booted.

The Patch

The fix is to restructure the code to be like this:

for provider in container.make('ProvidersConfig').PROVIDERS:
    located_provider = provider()
    located_provider.load_app(container).register()
    if located_provider.wsgi:
        container.make('WSGIProviders').append(located_provider)
    else:
        container.make('Providers').append(located_provider)

for provider in container.make('Providers'):
    container.resolve(provider.boot)

You can patch this in your Masonite 2.0 applications.

My Thoughts and Possible Solutions

So the framework is young and we will have mistakes like this from time to time until the framework becomes extremely stable. There were a few solutions to this problem.

Solution 1 - New Major Release

This would entail everybody upgrading the framework from 2.0 to 2.1. It would require updating the documentation to 2.1 (without any documentation changes) and would mean a lot of confusion for people who didn't get the message

So this solution wasn't really a possibility.

Solution 2 - Patch All New Applications

We could simply make the fix in all future applications of Masonite but the problem will now arise that now there are technically 2 different versions of Masonite 2.0. 1 version with the patch and 1 without. This will make packaging hard.

The Winner

The solution we are going to be choosing for this problem is that we will release the next minor version of Masonite. In this new 2.0.x version will be a new function that looks like this:

def _check_patch(self):
    patched = False

    with open('wsgi.py', 'r') as file:
        # read a list of lines into data
        data = file.readlines()

    # change the line that starts with KEY=
    for line_number, line in enumerate(data):
        if line.startswith("for provider in container.make('Providers'):"):
            patched = True
            break

    if not patched:
        print('\033[93mWARNING: {}\033[0m'.format('Your Application Is Not Patched!'))

And we can put it inside the serve command:

def handle(self):
    self._check_patch() # <-- New Line Here

    if self.option('reload'):
        self._run_with_reloader(extra_files=[".env"])

    else:
        self._run_application()

What this will do is scan your wsgi.py file for the patch and if it does not detect it then it will show the error when you run craft serve.

Once you make the changes, Masonite will no longer show the warning message.


Again, the framework is young (but mature) and there may be some mistakes like this in the code. Very few parts of the framework are in the application itself so most fixes are made by upgrading Masonite itself. There are small parts of the framework that are inside the applications that are created and there is a small margin of error that can happen.

We'll need to work together as a community to find these issues and make Masonite a better framework we can all enjoy :)

Top comments (3)

Collapse
 
lloople profile image
David Llop

Congratulations with the framework! 🎉

There's a thing I don't understand, why you can simply use the first approach which is actually fixing the bug and releasing a minor version like 2.0.1? I think everyone using your framework should have it since I consider it a bug 🤔

Collapse
 
josephmancuso profile image
Joseph Mancuso

Really good question. So how all frameworks work is that at a macro level you have two parts of the application.

  1. Code that runs on the application and loads in the framework pieces
  2. Code that runs inside the package and supplies 99% of the functionality of the framework.

With number 1, it's hard to make changes here that are not breaking changes because it requires a manual adjustment to the code section. it's not something that upgrading the package can handle because its not inside the package.

With number 2, any code changes like this would simply be fixed with a minor release like you said.

This specific section of code is the category of number 1.


Also there is a small chance that packages were created with the notion that some providers are booting before all are registering. The framework is young so I'm currently aware of all packages being built but once it has a large scale repositories of packages then it would be a breaking change because now all providers are registered before booting which could possibly change how some packages function.

The framework is young enough that this solution works for now.

Does that answer the question?

Collapse
 
lloople profile image
David Llop

Absolutely! Thank you for clarifying this out 🙂