├── MANIFEST.in ├── src ├── __init__.py └── masonite_modules │ ├── stubs │ ├── __init__.py │ ├── route.skeleton │ ├── index.skeleton │ ├── model.skeleton │ ├── controller.skeleton │ └── layout.skeleton │ ├── templates │ └── index.html │ ├── __init__.py │ ├── providers │ ├── __init__.py │ └── module_provider.py │ ├── commands │ ├── __init__.py │ ├── module_install.py │ └── module_create.py │ ├── routes │ └── route.py │ ├── config │ └── modules.py │ └── masonite_module.py ├── tests ├── __init__.py ├── unit │ ├── __init__.py │ └── test_package.py └── integrations │ ├── app │ ├── __init__.py │ ├── controllers │ │ ├── __init__.py │ │ └── WelcomeController.py │ ├── middlewares │ │ ├── __init__.py │ │ ├── VerifyCsrfToken.py │ │ └── AuthenticationMiddleware.py │ └── models │ │ └── User.py │ ├── config │ ├── __init__.py │ ├── exceptions.py │ ├── session.py │ ├── auth.py │ ├── modules.py │ ├── application.py │ ├── broadcast.py │ ├── notification.py │ ├── cache.py │ ├── mail.py │ ├── filesystem.py │ ├── queue.py │ ├── providers.py │ └── database.py │ ├── storage │ ├── .gitignore │ └── public │ │ ├── robots.txt │ │ ├── logo.png │ │ └── favicon.ico │ ├── templates │ ├── __init__.py │ ├── errors │ │ ├── 500.html │ │ ├── 404.html │ │ └── 403.html │ ├── maintenance.html │ ├── base.html │ └── welcome.html │ ├── databases │ ├── seeds │ │ ├── __init__.py │ │ ├── database_seeder.py │ │ └── user_table_seeder.py │ └── migrations │ │ ├── 2021_01_09_033202_create_password_reset_table.py │ │ └── 2021_01_09_043202_create_users_table.py │ ├── resources │ ├── css │ │ └── app.css │ └── js │ │ ├── app.js │ │ └── bootstrap.js │ ├── routes │ └── web.py │ └── Kernel.py ├── requirements.txt ├── pytest.ini ├── modules └── blogs │ ├── routes │ ├── __pycache__ │ │ └── route.cpython-39.pyc │ └── route.py │ ├── models │ └── Blog.py │ ├── controllers │ ├── __pycache__ │ │ └── BlogController.cpython-39.pyc │ └── BlogController.py │ └── templates │ ├── index.html │ └── layout.html ├── .env-example ├── .gitignore ├── setup.cfg ├── pyproject.toml ├── craft ├── codecov.yml ├── .github ├── dependabot.yml ├── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md └── workflows │ ├── pythonpublish.yml │ └── pythonapp.yml ├── wsgi.py ├── makefile ├── LICENSE ├── setup.py ├── README.md └── CONTRIBUTING.md /MANIFEST.in: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/unit/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/masonite_modules/stubs/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/integrations/app/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/integrations/config/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/integrations/storage/.gitignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/integrations/templates/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/integrations/app/controllers/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/integrations/databases/seeds/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/masonite_modules/templates/index.html: -------------------------------------------------------------------------------- 1 | Hello World! -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | masonite>=4,<5 2 | masonite-orm>=2,<3 3 | -------------------------------------------------------------------------------- /tests/integrations/resources/css/app.css: -------------------------------------------------------------------------------- 1 | /* Put your CSS here */ 2 | -------------------------------------------------------------------------------- /tests/integrations/resources/js/app.js: -------------------------------------------------------------------------------- 1 | 2 | require("./bootstrap.js") 3 | -------------------------------------------------------------------------------- /tests/integrations/storage/public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: -------------------------------------------------------------------------------- /pytest.ini: -------------------------------------------------------------------------------- 1 | [pytest] 2 | filterwarnings = 3 | ignore::DeprecationWarning 4 | -------------------------------------------------------------------------------- /tests/integrations/config/exceptions.py: -------------------------------------------------------------------------------- 1 | HANDLERS = {"stack_overflow": True, "solutions": True} 2 | -------------------------------------------------------------------------------- /src/masonite_modules/__init__.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa F401 2 | from .providers.module_provider import ModuleProvider 3 | -------------------------------------------------------------------------------- /src/masonite_modules/providers/__init__.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa: E501 2 | from .module_provider import ModuleProvider 3 | -------------------------------------------------------------------------------- /tests/integrations/routes/web.py: -------------------------------------------------------------------------------- 1 | from masonite.routes import Route 2 | 3 | ROUTES = [Route.get("/", "WelcomeController@show")] 4 | -------------------------------------------------------------------------------- /tests/integrations/storage/public/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/py-package/masonite-modules/HEAD/tests/integrations/storage/public/logo.png -------------------------------------------------------------------------------- /tests/integrations/storage/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/py-package/masonite-modules/HEAD/tests/integrations/storage/public/favicon.ico -------------------------------------------------------------------------------- /tests/integrations/config/session.py: -------------------------------------------------------------------------------- 1 | # from masonite.environment import env 2 | 3 | 4 | DRIVERS = { 5 | "default": "cookie", 6 | "cookie": {}, 7 | } 8 | -------------------------------------------------------------------------------- /modules/blogs/routes/__pycache__/route.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/py-package/masonite-modules/HEAD/modules/blogs/routes/__pycache__/route.cpython-39.pyc -------------------------------------------------------------------------------- /src/masonite_modules/commands/__init__.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa F401 2 | 3 | from .module_install import ModuleInstallCommand 4 | from .module_create import ModuleCreateCommand 5 | -------------------------------------------------------------------------------- /modules/blogs/models/Blog.py: -------------------------------------------------------------------------------- 1 | """Blog Model.""" 2 | from masoniteorm.models import Model 3 | 4 | 5 | class Blog(Model): 6 | """Blog Model.""" 7 | 8 | __fillable__ = [] 9 | -------------------------------------------------------------------------------- /tests/integrations/app/middlewares/__init__.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa: F401 2 | from .VerifyCsrfToken import VerifyCsrfToken 3 | from .AuthenticationMiddleware import AuthenticationMiddleware 4 | -------------------------------------------------------------------------------- /.env-example: -------------------------------------------------------------------------------- 1 | APP_DEBUG=True 2 | APP_ENV=development 3 | APP_KEY=plyUWY8iZnEH9_8WrVjl-LS3B8aRtHK9UAB35fGAq0M= 4 | DB_CONFIG_PATH=tests/integrations/config/database 5 | DB_CONNECTION=sqlite 6 | -------------------------------------------------------------------------------- /tests/integrations/app/middlewares/VerifyCsrfToken.py: -------------------------------------------------------------------------------- 1 | from masonite.middleware import VerifyCsrfToken as Middleware 2 | 3 | 4 | class VerifyCsrfToken(Middleware): 5 | 6 | exempt = [] 7 | -------------------------------------------------------------------------------- /modules/blogs/controllers/__pycache__/BlogController.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/py-package/masonite-modules/HEAD/modules/blogs/controllers/__pycache__/BlogController.cpython-39.pyc -------------------------------------------------------------------------------- /modules/blogs/templates/index.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | 3 | {% block content %} 4 |
Great, you have successfully created your view for "Blog" Module.
5 | {% endblock %} -------------------------------------------------------------------------------- /modules/blogs/routes/route.py: -------------------------------------------------------------------------------- 1 | from masonite.routes import Route 2 | 3 | from modules.blogs.controllers.BlogController import BlogController 4 | 5 | ROUTES = [ 6 | Route.get("/blogs", BlogController.show), 7 | ] 8 | -------------------------------------------------------------------------------- /tests/unit/test_package.py: -------------------------------------------------------------------------------- 1 | from masonite.tests import TestCase 2 | 3 | 4 | class TestMasoniteModules(TestCase): 5 | def test_module_route_exists(self): 6 | return self.get('/blogs').assertIsStatus(200) 7 | -------------------------------------------------------------------------------- /src/masonite_modules/routes/route.py: -------------------------------------------------------------------------------- 1 | from ..masonite_module import MasoniteModule 2 | 3 | ROUTES = [] 4 | 5 | modules = MasoniteModule.get_modules() 6 | 7 | for module in modules: 8 | ROUTES += getattr(module, "ROUTES") 9 | -------------------------------------------------------------------------------- /src/masonite_modules/stubs/route.skeleton: -------------------------------------------------------------------------------- 1 | from masonite.routes import Route 2 | 3 | from __module__.__pluralize__.controllers.__class__ import __class__ 4 | 5 | ROUTES = [ 6 | Route.get("/__pluralize__", __class__.show), 7 | ] -------------------------------------------------------------------------------- /src/masonite_modules/stubs/index.skeleton: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | 3 | {% block content %} 4 |
Great, you have successfully created your view for "__singularize_cap__" Module.
5 | {% endblock %} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | venv 2 | venv2 3 | .vscode 4 | .idea/ 5 | build/ 6 | .pypirc 7 | .coverage 8 | coverage.xml 9 | .pytest_* 10 | **/*__pycache__* 11 | **/*.DS_Store* 12 | **.pyc 13 | dist 14 | .env 15 | *.db 16 | src/masonite_modules.egg-info -------------------------------------------------------------------------------- /src/masonite_modules/stubs/model.skeleton: -------------------------------------------------------------------------------- 1 | """__singularize_cap__ Model.""" 2 | from masoniteorm.models import Model 3 | 4 | 5 | class __singularize_cap__(Model): 6 | """__singularize_cap__ Model.""" 7 | 8 | __fillable__ = [] 9 | -------------------------------------------------------------------------------- /tests/integrations/config/auth.py: -------------------------------------------------------------------------------- 1 | from tests.integrations.app.models.User import User 2 | 3 | GUARDS = { 4 | "default": "web", 5 | "web": {"model": User}, 6 | "password_reset_table": "password_resets", 7 | "password_reset_expiration": 1440, # in minutes. 24 hours. None if disabled 8 | } 9 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [flake8] 2 | exclude = 3 | .git, 4 | .github, 5 | .vscode, 6 | __pycache__, 7 | templates, 8 | node_modules, 9 | venv 10 | max-complexity = 10 11 | max-line-length = 99 12 | 13 | omit = 14 | */config/* 15 | setup.py 16 | */stubs/* 17 | wsgi.py 18 | tests/ -------------------------------------------------------------------------------- /tests/integrations/databases/seeds/database_seeder.py: -------------------------------------------------------------------------------- 1 | """Base Database Seeder Module.""" 2 | from masoniteorm.seeds import Seeder 3 | 4 | from .user_table_seeder import UserTableSeeder 5 | 6 | 7 | class DatabaseSeeder(Seeder): 8 | def run(self): 9 | """Run the database seeds.""" 10 | self.call(UserTableSeeder) 11 | -------------------------------------------------------------------------------- /src/masonite_modules/stubs/controller.skeleton: -------------------------------------------------------------------------------- 1 | """A __class__ Module.""" 2 | from masonite.views import View 3 | from masonite.controllers import Controller 4 | 5 | 6 | class __class__(Controller): 7 | """__class__ Controller Class.""" 8 | 9 | def show(self, view: View): 10 | return view.render("__pluralize__:index") 11 | -------------------------------------------------------------------------------- /modules/blogs/controllers/BlogController.py: -------------------------------------------------------------------------------- 1 | """A BlogController Module.""" 2 | from masonite.views import View 3 | from masonite.controllers import Controller 4 | 5 | 6 | class BlogController(Controller): 7 | """BlogController Controller Class.""" 8 | 9 | def show(self, view: View): 10 | return view.render("blogs:index") 11 | -------------------------------------------------------------------------------- /src/masonite_modules/config/modules.py: -------------------------------------------------------------------------------- 1 | """masonite_modules Settings""" 2 | 3 | """ 4 | |-------------------------------------------------------------------------- 5 | | A Heading of The Setting Being Set 6 | |-------------------------------------------------------------------------- 7 | | 8 | | A quick description 9 | | 10 | """ 11 | 12 | NAME = "modules" 13 | -------------------------------------------------------------------------------- /tests/integrations/app/controllers/WelcomeController.py: -------------------------------------------------------------------------------- 1 | """A WelcomeController Module.""" 2 | from masonite.views import View 3 | from masonite.controllers import Controller 4 | 5 | 6 | class WelcomeController(Controller): 7 | """WelcomeController Controller Class.""" 8 | 9 | def show(self, view: View): 10 | return view.render("welcome") 11 | -------------------------------------------------------------------------------- /tests/integrations/config/modules.py: -------------------------------------------------------------------------------- 1 | """masonite_modules Settings""" 2 | 3 | """ 4 | |-------------------------------------------------------------------------- 5 | | A Heading of The Setting Being Set 6 | |-------------------------------------------------------------------------- 7 | | 8 | | A quick description 9 | | 10 | """ 11 | 12 | NAME = "modules" 13 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.black] 2 | line-length = 99 3 | target-version = ['py38'] 4 | include = '\.pyi?$' 5 | exclude = ''' 6 | /( 7 | \.git 8 | \.github 9 | \.vscode 10 | | \.venv 11 | | docs 12 | | node_modules 13 | | tests/integrations/templates 14 | )/ 15 | ''' 16 | 17 | [tool.isort] 18 | profile = "black" 19 | multi_line_output = 3 20 | include_trailing_comma = true -------------------------------------------------------------------------------- /tests/integrations/config/application.py: -------------------------------------------------------------------------------- 1 | from masonite.environment import env 2 | 3 | 4 | KEY = env("APP_KEY", "-RkDOqXojJIlsF_I8wWiUq_KRZ0PtGWTOZ676u5HtLg=") 5 | 6 | HASHING = { 7 | "default": env("HASHING_FUNCTION", "bcrypt"), 8 | "bcrypt": {"rounds": 10}, 9 | "argon2": {"memory": 1024, "threads": 2, "time": 2}, 10 | } 11 | 12 | APP_URL = env("APP_URL", "http://localhost:8000/") 13 | -------------------------------------------------------------------------------- /craft: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Craft Command. 3 | This module is really used for backup only if the masonite CLI cannot import this for you. 4 | This can be used by running "python craft". This module is not ran when the CLI can 5 | successfully import commands for you. 6 | """ 7 | 8 | from wsgi import application 9 | 10 | if __name__ == '__main__': 11 | application.make('commands').run() 12 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | codecov: 2 | require_ci_to_pass: no 3 | 4 | github_checks: false 5 | 6 | coverage: 7 | precision: 2 8 | round: down 9 | range: "70...100" 10 | 11 | parsers: 12 | gcov: 13 | branch_detection: 14 | conditional: yes 15 | loop: yes 16 | method: no 17 | macro: no 18 | 19 | comment: 20 | layout: "footer" 21 | behavior: default 22 | require_changes: no 23 | -------------------------------------------------------------------------------- /tests/integrations/config/broadcast.py: -------------------------------------------------------------------------------- 1 | from masonite.environment import env 2 | 3 | 4 | BROADCASTS = { 5 | "default": "pusher", 6 | "pusher": { 7 | "driver": "pusher", 8 | "client": env("PUSHER_CLIENT"), 9 | "app_id": env("PUSHER_APP_ID"), 10 | "secret": env("PUSHER_SECRET"), 11 | "cluster": env("PUSHER_CLUSTER"), 12 | "ssl": False, 13 | }, 14 | } 15 | -------------------------------------------------------------------------------- /tests/integrations/app/models/User.py: -------------------------------------------------------------------------------- 1 | """User Model.""" 2 | from masoniteorm.models import Model 3 | from masoniteorm.scopes import SoftDeletesMixin 4 | from masonite.authentication import Authenticates 5 | 6 | 7 | class User(Model, SoftDeletesMixin, Authenticates): 8 | """User Model.""" 9 | 10 | __fillable__ = ["name", "email", "password"] 11 | __hidden__ = ["password"] 12 | __auth__ = "email" 13 | -------------------------------------------------------------------------------- /tests/integrations/app/middlewares/AuthenticationMiddleware.py: -------------------------------------------------------------------------------- 1 | from masonite.middleware import Middleware 2 | 3 | 4 | class AuthenticationMiddleware(Middleware): 5 | """Middleware to check if the user is logged in.""" 6 | 7 | def before(self, request, response): 8 | if not request.user(): 9 | return response.redirect(name="login") 10 | return request 11 | 12 | def after(self, request, response): 13 | return request 14 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | # Maintain dependencies for GitHub Actions 4 | - package-ecosystem: "github-actions" 5 | directory: "/" 6 | schedule: 7 | interval: "weekly" 8 | 9 | # Maintain dependencies for cookiecutter repo 10 | - package-ecosystem: "pip" 11 | directory: "/" 12 | schedule: 13 | interval: "weekly" 14 | # Allow up to 10 open pull requests for pip dependencies 15 | open-pull-requests-limit: 10 16 | -------------------------------------------------------------------------------- /wsgi.py: -------------------------------------------------------------------------------- 1 | from masonite.foundation import Application, Kernel 2 | from tests.integrations.config.providers import PROVIDERS 3 | from tests.integrations.Kernel import Kernel as ApplicationKernel 4 | 5 | 6 | """Start The Application Instance.""" 7 | application = Application("tests/integrations") 8 | 9 | """Now Bind important providers needed to make the framework work.""" 10 | application.register_providers(Kernel, ApplicationKernel) 11 | 12 | """Now Bind important application specific providers needed to make the application work.""" 13 | application.add_providers(*PROVIDERS) 14 | -------------------------------------------------------------------------------- /tests/integrations/databases/seeds/user_table_seeder.py: -------------------------------------------------------------------------------- 1 | """UserTableSeeder Seeder.""" 2 | from masoniteorm.seeds import Seeder 3 | from masonite.facades import Hash 4 | 5 | from tests.integrations.app.models.User import User 6 | 7 | 8 | class UserTableSeeder(Seeder): 9 | def run(self): 10 | """Run the database seeds.""" 11 | User.create( 12 | { 13 | "name": "Joe", 14 | "email": "user@example.com", 15 | "password": Hash.make("secret"), 16 | "phone": "+123456789", 17 | } 18 | ) 19 | -------------------------------------------------------------------------------- /tests/integrations/config/notification.py: -------------------------------------------------------------------------------- 1 | from masonite.environment import env 2 | 3 | 4 | DRIVERS = { 5 | "slack": { 6 | "token": env("SLACK_TOKEN", ""), # used for API mode 7 | "webhook": env("SLACK_WEBHOOK", ""), # used for webhook mode 8 | }, 9 | "vonage": { 10 | "key": env("VONAGE_KEY", ""), 11 | "secret": env("VONAGE_SECRET", ""), 12 | "sms_from": env("VONAGE_SMS_FROM", "+33000000000"), 13 | }, 14 | "database": { 15 | "connection": "sqlite", 16 | "table": "notifications", 17 | }, 18 | } 19 | 20 | DRY = False 21 | -------------------------------------------------------------------------------- /tests/integrations/databases/migrations/2021_01_09_033202_create_password_reset_table.py: -------------------------------------------------------------------------------- 1 | from masoniteorm.migrations import Migration 2 | 3 | 4 | class CreatePasswordResetTable(Migration): 5 | def up(self): 6 | """Run the migrations.""" 7 | with self.schema.create("password_resets") as table: 8 | table.string("email").unique() 9 | table.string("token") 10 | table.datetime("expires_at").nullable() 11 | table.datetime("created_at") 12 | 13 | def down(self): 14 | """Revert the migrations.""" 15 | self.schema.drop("password_resets") 16 | -------------------------------------------------------------------------------- /tests/integrations/config/cache.py: -------------------------------------------------------------------------------- 1 | # from masonite.environment import env 2 | 3 | 4 | STORES = { 5 | "default": "local", 6 | "local": { 7 | "driver": "file", 8 | "location": "storage/framework/cache" 9 | # 10 | }, 11 | "redis": { 12 | "driver": "redis", 13 | "host": "127.0.0.1", 14 | "port": "6379", 15 | "password": "", 16 | "name": "project_name", 17 | }, 18 | "memcache": { 19 | "driver": "memcache", 20 | "host": "127.0.0.1", 21 | "port": "11211", 22 | "password": "", 23 | "name": "project_name", 24 | }, 25 | } 26 | -------------------------------------------------------------------------------- /tests/integrations/templates/errors/500.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Error 500 9 | 10 | 11 | 12 | 13 | {% block content %} 14 |
15 |

Oops an error happened !

16 |
17 | {% endblock %} 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /tests/integrations/templates/errors/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Page Not Found 9 | 10 | 11 | 12 | 13 | {% block content %} 14 |
15 |

Oops this page does not exist !

16 |
17 | {% endblock %} 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /tests/integrations/config/mail.py: -------------------------------------------------------------------------------- 1 | from masonite.environment import env 2 | 3 | 4 | FROM_EMAIL = env("MAIL_FROM", "no-reply@masonite.com") 5 | 6 | DRIVERS = { 7 | "default": env("MAIL_DRIVER", "terminal"), 8 | "smtp": { 9 | "host": env("MAIL_HOST"), 10 | "port": env("MAIL_PORT"), 11 | "username": env("MAIL_USERNAME"), 12 | "password": env("MAIL_PASSWORD"), 13 | "from": FROM_EMAIL, 14 | }, 15 | "mailgun": { 16 | "domain": env("MAILGUN_DOMAIN"), 17 | "secret": env("MAILGUN_SECRET"), 18 | "from": FROM_EMAIL, 19 | }, 20 | "terminal": { 21 | "from": FROM_EMAIL, 22 | }, 23 | } 24 | -------------------------------------------------------------------------------- /tests/integrations/templates/maintenance.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Server Maintenance 9 | 10 | 11 | 12 | 13 | {% block content %} 14 |
15 |

Sorry, this site is currently down for maintenance.

16 |
17 | {% endblock %} 18 | 19 | 20 | -------------------------------------------------------------------------------- /tests/integrations/templates/errors/403.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Permission Denied 9 | 10 | 11 | 12 | 13 | {% block content %} 14 |
15 |

Oops looks like you don't have access to this page !

16 |
17 | {% endblock %} 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/workflows/pythonpublish.yml: -------------------------------------------------------------------------------- 1 | name: Upload Python Package 2 | 3 | on: 4 | release: 5 | types: [created] 6 | 7 | jobs: 8 | deploy: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v4 12 | - name: Set up Python 13 | uses: actions/setup-python@v4 14 | with: 15 | python-version: "3.x" 16 | - name: Install dependencies 17 | run: | 18 | make init 19 | - name: Publish only packages passing test 20 | run: | 21 | make test 22 | - name: Build and publish 23 | env: 24 | TWINE_USERNAME: __token__ 25 | TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} 26 | run: | 27 | make publish 28 | -------------------------------------------------------------------------------- /tests/integrations/config/filesystem.py: -------------------------------------------------------------------------------- 1 | from masonite.environment import env 2 | from masonite.utils.location import base_path 3 | 4 | 5 | DISKS = { 6 | "default": "local", 7 | "local": { 8 | "driver": "file", 9 | "path": base_path("tests/integrations/storage/framework/filesystem"), 10 | }, 11 | "s3": { 12 | "driver": "s3", 13 | "client": env("AWS_CLIENT"), 14 | "secret": env("AWS_SECRET"), 15 | "bucket": env("AWS_BUCKET"), 16 | }, 17 | } 18 | 19 | STATICFILES = { 20 | # folder # template alias 21 | "tests/integrations/storage/static": "static/", 22 | "tests/integrations/storage/compiled": "assets/", 23 | "tests/integrations/storage/public": "/", 24 | } 25 | -------------------------------------------------------------------------------- /src/masonite_modules/providers/module_provider.py: -------------------------------------------------------------------------------- 1 | """A ModuleProvider Service Provider.""" 2 | 3 | from masonite.packages import PackageProvider 4 | 5 | from ..masonite_module import MasoniteModule 6 | 7 | 8 | class ModuleProvider(PackageProvider): 9 | def configure(self): 10 | """Register objects into the Service Container.""" 11 | ( 12 | self.root("masonite_modules") 13 | .name("modules") 14 | .config("config/modules.py", publish=True) 15 | .routes("routes/route.py") 16 | ) 17 | 18 | def register(self): 19 | super().register() 20 | MasoniteModule(self.application).register() 21 | 22 | def boot(self): 23 | """Boots services required by the container.""" 24 | pass 25 | -------------------------------------------------------------------------------- /tests/integrations/config/queue.py: -------------------------------------------------------------------------------- 1 | # from masonite.environment import env 2 | 3 | 4 | DRIVERS = { 5 | "default": "async", 6 | "database": { 7 | "connection": "sqlite", 8 | "table": "jobs", 9 | "failed_table": "failed_jobs", 10 | "attempts": 3, 11 | "poll": 5, 12 | }, 13 | "redis": { 14 | # 15 | }, 16 | "amqp": { 17 | "username": "guest", 18 | "password": "guest", 19 | "port": "5672", 20 | "vhost": "", 21 | "host": "localhost", 22 | "channel": "default", 23 | "queue": "masonite4", 24 | }, 25 | "async": { 26 | "blocking": True, 27 | "callback": "handle", 28 | "mode": "threading", 29 | "workers": 1, 30 | }, 31 | } 32 | -------------------------------------------------------------------------------- /tests/integrations/databases/migrations/2021_01_09_043202_create_users_table.py: -------------------------------------------------------------------------------- 1 | from masoniteorm.migrations import Migration 2 | 3 | 4 | class CreateUsersTable(Migration): 5 | def up(self): 6 | """Run the migrations.""" 7 | with self.schema.create("users") as table: 8 | table.increments("id") 9 | table.string("name") 10 | table.string("email").unique() 11 | table.string("password") 12 | table.string("second_password").nullable() 13 | table.string("remember_token").nullable() 14 | table.string("phone").nullable() 15 | table.timestamp("verified_at").nullable() 16 | table.timestamps() 17 | table.soft_deletes() 18 | 19 | def down(self): 20 | """Revert the migrations.""" 21 | self.schema.drop("users") 22 | -------------------------------------------------------------------------------- /tests/integrations/templates/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | {% block title %}Masonite 4{% endblock %} 9 | 10 | 14 | {% block head %} 15 | 16 | {% endblock %} 17 | 18 | 19 | {% block content %}{% endblock %} 20 | {% block js %} 21 | 22 | {% endblock %} 23 | 24 | 25 | -------------------------------------------------------------------------------- /tests/integrations/resources/js/bootstrap.js: -------------------------------------------------------------------------------- 1 | /** 2 | * We'll load the axios HTTP library which allows us to easily issue requests 3 | * to our Masonite back-end. This library automatically handles sending the 4 | * CSRF token as a header based on the value of the "XSRF" token cookie. 5 | */ 6 | 7 | window.axios = require('axios'); 8 | 9 | window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'; 10 | 11 | /** 12 | * Next we will register the CSRF Token as a common header with Axios so that 13 | * all outgoing HTTP requests automatically have it attached. This is just 14 | * a simple convenience so we don't have to attach every token manually. 15 | */ 16 | 17 | let token = document.head.querySelector('meta[name="csrf-token"]'); 18 | 19 | if (token) { 20 | window.axios.defaults.headers.common['X-CSRF-TOKEN'] = token.content; 21 | } else { 22 | console.error('CSRF token not found: https://docs.masoniteproject.com/features/csrf#ajax-vue-axios'); 23 | } 24 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | .PHONY: help 2 | help: ## Show this help 3 | @egrep -h '\s##\s' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}' 4 | 5 | init: ## Install package dependencies 6 | cp .env-example .env 7 | pip install --upgrade pip 8 | # install test project and package dependencies 9 | pip install -r requirements.txt 10 | # install package and dev dependencies (see setup.py) 11 | pip install '.[dev]' 12 | test: ## Run package tests 13 | python -m pytest tests 14 | ci: ## [CI] Run package tests and lint 15 | make test 16 | make lint 17 | lint: ## Run code linting 18 | python -m flake8 . 19 | format: ## Format code with Black 20 | black src 21 | coverage: ## Run package tests and upload coverage reports 22 | python -m pytest --cov-report term --cov-report xml --cov=src/masonite/masonite_modules tests 23 | publish: ## Publish package to pypi 24 | python setup.py sdist bdist_wheel 25 | twine upload dist/* 26 | rm -fr build dist .egg src/masonite_masonite_modules.egg-info 27 | pypirc: ## Copy the template .pypirc in the repo to your home directory 28 | cp .pypirc ~/.pypirc -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022, Yubaraj Shrestha 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /modules/blogs/templates/layout.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Blog Module 5 | 6 | 31 | 32 | 33 |
34 |
35 | {% block content %}{% endblock %} 36 |
37 |
38 | 39 | -------------------------------------------------------------------------------- /tests/integrations/templates/welcome.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% block title %}Welcome on Masonite 4{% endblock %} 3 | 4 | {% block content %} 5 |
6 | Masonite Logo 7 |
8 | Masonite 4 9 |
10 | 11 | 32 | 33 |
34 | {% endblock %} 35 | -------------------------------------------------------------------------------- /src/masonite_modules/stubs/layout.skeleton: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | __singularize_cap__ Module 5 | 6 | 31 | 32 | 33 |
34 |
35 | {% block content %}{% endblock %} 36 |
37 |
38 | 39 | -------------------------------------------------------------------------------- /.github/workflows/pythonapp.yml: -------------------------------------------------------------------------------- 1 | name: Test Application 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | pull_request: 7 | branches: [master] 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | strategy: 13 | matrix: 14 | python-version: ["3.7", "3.8", "3.9", "3.10"] 15 | name: Python ${{ matrix.python-version }} 16 | steps: 17 | - uses: actions/checkout@v4 18 | - name: Set up Python ${{ matrix.python-version }} 19 | uses: actions/setup-python@v4 20 | with: 21 | python-version: ${{ matrix.python-version }} 22 | - name: Install dependencies 23 | run: | 24 | make init 25 | - name: Test with pytest and Build coverage 26 | run: | 27 | make coverage 28 | - name: Upload coverage 29 | uses: codecov/codecov-action@v3 30 | with: 31 | token: ${{ secrets.CODECOV_TOKEN }} 32 | fail_ci_if_error: false 33 | 34 | lint: 35 | runs-on: ubuntu-latest 36 | name: Lint 37 | steps: 38 | - uses: actions/checkout@v4 39 | - name: Set up Python 3.8 40 | uses: actions/setup-python@v4 41 | with: 42 | python-version: 3.8 43 | - name: Intall Flake8 44 | run: | 45 | pip install flake8 46 | - name: Lint 47 | run: make lint 48 | -------------------------------------------------------------------------------- /tests/integrations/config/providers.py: -------------------------------------------------------------------------------- 1 | from masonite.providers import ( 2 | RouteProvider, 3 | FrameworkProvider, 4 | ViewProvider, 5 | WhitenoiseProvider, 6 | ExceptionProvider, 7 | MailProvider, 8 | SessionProvider, 9 | QueueProvider, 10 | CacheProvider, 11 | EventProvider, 12 | StorageProvider, 13 | HelpersProvider, 14 | BroadcastProvider, 15 | AuthenticationProvider, 16 | AuthorizationProvider, 17 | HashServiceProvider, 18 | ORMProvider, 19 | ) 20 | 21 | 22 | from masonite.scheduling.providers import ScheduleProvider 23 | from masonite.notification.providers import NotificationProvider 24 | from masonite.validation.providers import ValidationProvider 25 | 26 | # register local package 27 | from src.masonite_modules import ModuleProvider 28 | 29 | 30 | PROVIDERS = [ 31 | FrameworkProvider, 32 | HelpersProvider, 33 | RouteProvider, 34 | ViewProvider, 35 | WhitenoiseProvider, 36 | ExceptionProvider, 37 | MailProvider, 38 | NotificationProvider, 39 | SessionProvider, 40 | CacheProvider, 41 | QueueProvider, 42 | ScheduleProvider, 43 | EventProvider, 44 | StorageProvider, 45 | BroadcastProvider, 46 | HashServiceProvider, 47 | AuthenticationProvider, 48 | ValidationProvider, 49 | AuthorizationProvider, 50 | ORMProvider, 51 | ] 52 | 53 | PROVIDERS += [ModuleProvider] 54 | -------------------------------------------------------------------------------- /src/masonite_modules/commands/module_install.py: -------------------------------------------------------------------------------- 1 | import os 2 | from masonite.commands import Command 3 | from masonite.utils.location import base_path 4 | from masonite.utils.filesystem import make_directory, render_stub_file, get_module_dir 5 | from masonite.configuration import config 6 | 7 | 8 | class ModuleInstallCommand(Command): 9 | """ 10 | Masonite Command Utility 11 | 12 | module:install 13 | {--f|force=? : Force overriding file if already exists} 14 | """ 15 | 16 | def __init__(self, application): 17 | super().__init__() 18 | self.app = application 19 | 20 | def modules_path(self): 21 | module_path = config("modules").get("name", "modules") 22 | return os.path.join(base_path(), module_path, "__init__.py") 23 | 24 | def handle(self): 25 | filepath = self.modules_path() 26 | 27 | make_directory(filepath) 28 | 29 | if os.path.exists(filepath) and not self.option("force"): 30 | self.warning( 31 | "Modules path already exists! Run the command with -f (force) to override." 32 | ) 33 | return -1 34 | 35 | init_path = os.path.join(get_module_dir(__file__), "../stubs/__init__.py") 36 | content = render_stub_file(init_path, "...") 37 | 38 | with open(filepath, "w") as f: 39 | f.write(content) 40 | 41 | self.info("Module Successfully installed!") 42 | -------------------------------------------------------------------------------- /tests/integrations/config/database.py: -------------------------------------------------------------------------------- 1 | from masonite.environment import LoadEnvironment, env 2 | from masoniteorm.connections import ConnectionResolver 3 | 4 | # Loads in the environment variables when this page is imported. 5 | LoadEnvironment() 6 | 7 | """ 8 | The connections here don't determine the database but determine the "connection". 9 | They can be named whatever you want. 10 | """ 11 | DATABASES = { 12 | "default": env("DB_CONNECTION", "sqlite"), 13 | "sqlite": { 14 | "driver": "sqlite", 15 | "database": env("SQLITE_DB_DATABASE", "masonite.sqlite3"), 16 | "prefix": "", 17 | "log_queries": env("DB_LOG"), 18 | }, 19 | "mysql": { 20 | "driver": "mysql", 21 | "host": env("DB_HOST"), 22 | "user": env("DB_USERNAME"), 23 | "password": env("DB_PASSWORD"), 24 | "database": env("DB_DATABASE"), 25 | "port": env("DB_PORT"), 26 | "prefix": "", 27 | "grammar": "mysql", 28 | "options": { 29 | "charset": "utf8mb4", 30 | }, 31 | "log_queries": env("DB_LOG"), 32 | }, 33 | "postgres": { 34 | "driver": "postgres", 35 | "host": env("DB_HOST"), 36 | "user": env("DB_USERNAME"), 37 | "password": env("DB_PASSWORD"), 38 | "database": env("DB_DATABASE"), 39 | "port": env("DB_PORT"), 40 | "prefix": "", 41 | "grammar": "postgres", 42 | "log_queries": env("DB_LOG"), 43 | }, 44 | "mssql": { 45 | "driver": "mssql", 46 | "host": env("MSSQL_DATABASE_HOST"), 47 | "user": env("MSSQL_DATABASE_USER"), 48 | "password": env("MSSQL_DATABASE_PASSWORD"), 49 | "database": env("MSSQL_DATABASE_DATABASE"), 50 | "port": env("MSSQL_DATABASE_PORT"), 51 | "prefix": "", 52 | "log_queries": env("DB_LOG"), 53 | }, 54 | } 55 | 56 | DB = ConnectionResolver().set_connection_details(DATABASES) 57 | -------------------------------------------------------------------------------- /src/masonite_modules/masonite_module.py: -------------------------------------------------------------------------------- 1 | import importlib 2 | import glob 3 | from masonite.utils.location import base_path 4 | from os.path import dirname, isfile, join 5 | from .commands.module_create import ModuleCreateCommand 6 | from .commands.module_install import ModuleInstallCommand 7 | from masonite.configuration import config 8 | 9 | 10 | ROOT_PATH = base_path() 11 | 12 | 13 | class MasoniteModule: 14 | def __init__(self, application): 15 | self.application = application 16 | 17 | @staticmethod 18 | def get_module_name(module): 19 | return module.replace(ROOT_PATH, "").replace(".py", "").lstrip("/").replace("/", ".") 20 | 21 | @staticmethod 22 | def get_modules(): 23 | module_config = config("modules") 24 | if module_config: 25 | module_name = module_config.get("name", "modules") 26 | modules = glob.glob(join(base_path(), module_name, "**/routes/*.py")) 27 | masonite_modules = [] 28 | 29 | for module in modules: 30 | if isfile(module): 31 | masonite_modules += [ 32 | importlib.import_module(MasoniteModule.get_module_name(module)) 33 | ] 34 | return masonite_modules 35 | return [] 36 | 37 | def register(self): 38 | module_config = config("modules") 39 | if module_config: 40 | module_name = module_config.get("name", "modules") 41 | modules = glob.glob(join(base_path(), module_name, "**/routes/*.py")) 42 | for module in modules: 43 | if isfile(module): 44 | template_path = ( 45 | dirname(module).replace(join(base_path(), module_name), "").split("/")[1] 46 | ) 47 | self.application.make("view").add_namespaced_location( 48 | template_path, f"{module_name}.{template_path}/templates" 49 | ) 50 | 51 | self.application.make("commands").add(ModuleInstallCommand(self.application)).add( 52 | ModuleCreateCommand(self.application) 53 | ) 54 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | with open("README.md", "r") as fh: 4 | long_description = fh.read() 5 | 6 | setup( 7 | name="masonite-modules", 8 | # Versions should comply with PEP440. For a discussion on single-sourcing 9 | # the version across setup.py and the project code, see 10 | # https://packaging.python.org/en/latest/single_source_version.html 11 | version="0.0.5", 12 | packages=[ 13 | "masonite_modules", 14 | "masonite_modules.commands", 15 | "masonite_modules.providers", 16 | "masonite_modules.config", 17 | "masonite_modules.routes", 18 | "masonite_modules.stubs", 19 | "masonite_modules.templates", 20 | ], 21 | package_dir={"": "src"}, 22 | description="Modularize your masonite project", 23 | long_description=long_description, 24 | long_description_content_type="text/markdown", 25 | # The project's main homepage. 26 | url="https://github.com/py-package/masonite-modules", 27 | # Author details 28 | author="Yubaraj Shrestha", 29 | author_email="yubaraj@pypackage.com", 30 | # Choose your license 31 | license="MIT license", 32 | # If your package should include things you specify in your MANIFEST.in file 33 | # Use this option if your package needs to include files that are not python files 34 | # like html templates or css files 35 | include_package_data=True, 36 | # See https://pypi.python.org/pypi?%3Aaction=list_classifiers 37 | classifiers=[ 38 | # How mature is this project? Common values are 39 | # 3 - Alpha 40 | # 4 - Beta 41 | # 5 - Production/Stable 42 | "Development Status :: 5 - Production/Stable", 43 | # Indicate who your project is intended for 44 | "Intended Audience :: Developers", 45 | "Topic :: Software Development :: Build Tools", 46 | "Environment :: Web Environment", 47 | # Pick your license as you wish (should match "license" above) 48 | "License :: OSI Approved :: MIT License", 49 | "Operating System :: OS Independent", 50 | # Specify the Python versions you support here. In particular, ensure 51 | # that you indicate whether you support Python 2, Python 3 or both. 52 | "Programming Language :: Python :: 3.7", 53 | "Programming Language :: Python :: 3.8", 54 | "Programming Language :: Python :: 3.9", 55 | "Topic :: Internet :: WWW/HTTP", 56 | "Topic :: Internet :: WWW/HTTP :: Dynamic Content", 57 | "Topic :: Internet :: WWW/HTTP :: WSGI", 58 | "Topic :: Software Development :: Libraries :: Application Frameworks", 59 | "Topic :: Software Development :: Libraries :: Python Modules", 60 | # List package on masonite packages website 61 | "Framework :: Masonite", 62 | ], 63 | # What does your project relate to? 64 | keywords="Masonite, Python, Development", 65 | # List run-time dependencies here. These will be installed by pip when 66 | # your project is installed. For an analysis of "install_requires" vs pip's 67 | # requirements files see: 68 | # https://packaging.python.org/en/latest/requirements.html 69 | install_requires=["masonite>=4.0,<5.0"], 70 | # List additional groups of dependencies here (e.g. development 71 | # dependencies). You can install these using the following syntax, 72 | # for example: 73 | # $ pip install -e .[dev,test] 74 | # $ pip install your-package[dev,test] 75 | extras_require={ 76 | "dev": [ 77 | "black", 78 | "flake8", 79 | "coverage", 80 | "pytest", 81 | "pytest-cov", 82 | "twine>=1.5.0", 83 | "wheel", 84 | ], 85 | }, 86 | # If there are data files included in your packages that need to be 87 | # installed, specify them here. If using Python 2.6 or less, then these 88 | # have to be included in MANIFEST.in as well. 89 | package_data={ 90 | # 'templates/index.html': [], 91 | }, 92 | ) 93 | -------------------------------------------------------------------------------- /src/masonite_modules/commands/module_create.py: -------------------------------------------------------------------------------- 1 | import os 2 | import inflection 3 | from masonite.commands import Command 4 | from masonite.utils.location import base_path 5 | from masonite.utils.filesystem import get_module_dir, make_full_directory 6 | from masonite.configuration import config 7 | 8 | 9 | class ModuleCreateCommand(Command): 10 | """ 11 | Create new module 12 | 13 | module:create 14 | {name : Name of the module} 15 | """ 16 | 17 | def __init__(self, application): 18 | super().__init__() 19 | self.app = application 20 | 21 | def module_path(self): 22 | module_path = config("modules").get("name", "modules") 23 | 24 | name = inflection.pluralize(self.argument("name")).lower() 25 | return os.path.join(base_path(), module_path, name) 26 | 27 | def controller_path(self): 28 | return os.path.join(self.module_path(), "controllers") 29 | 30 | def routes_path(self): 31 | return os.path.join(self.module_path(), "routes") 32 | 33 | def templates_path(self): 34 | return os.path.join(self.module_path(), "templates") 35 | 36 | def model_path(self): 37 | return os.path.join(self.module_path(), "models") 38 | 39 | def render_stub_file(self, stub_file, cls_name): 40 | module_path = config("modules").get("name", "modules") 41 | name = self.argument("name") 42 | singularize_cap = inflection.singularize(name).capitalize() 43 | singularize_sml = inflection.singularize(name).lower() 44 | pluralize = inflection.pluralize(name) 45 | 46 | with open(stub_file, "r") as f: 47 | content = f.read() 48 | content = content.replace("__class__", cls_name) 49 | content = content.replace("__singularize_cap__", singularize_cap) 50 | content = content.replace("__singularize_sml__", singularize_sml) 51 | content = content.replace("__pluralize__", pluralize) 52 | content = content.replace("__module__", module_path) 53 | return content 54 | 55 | def handle(self): 56 | name = inflection.pluralize(self.argument("name")).lower() 57 | moduleName = inflection.singularize(name).capitalize() 58 | 59 | make_full_directory(self.controller_path()) 60 | make_full_directory(self.model_path()) 61 | make_full_directory(self.templates_path()) 62 | make_full_directory(self.routes_path()) 63 | 64 | controller_content = self.render_stub_file( 65 | os.path.join(get_module_dir(__file__), "../stubs/controller.skeleton"), 66 | f"{moduleName}Controller", 67 | ) 68 | model_content = self.render_stub_file( 69 | os.path.join(get_module_dir(__file__), "../stubs/model.skeleton"), moduleName 70 | ) 71 | route_content = self.render_stub_file( 72 | os.path.join(get_module_dir(__file__), "../stubs/route.skeleton"), 73 | f"{moduleName}Controller", 74 | ) 75 | 76 | layout_content = self.render_stub_file( 77 | os.path.join(get_module_dir(__file__), "../stubs/layout.skeleton"), 78 | f"{moduleName}Layout", 79 | ) 80 | index_content = self.render_stub_file( 81 | os.path.join(get_module_dir(__file__), "../stubs/index.skeleton"), f"{moduleName}Index" 82 | ) 83 | 84 | controller_path = os.path.join(self.controller_path(), f"{moduleName}Controller.py") 85 | with open(controller_path, "w") as f: 86 | f.write(controller_content) 87 | 88 | model_path = os.path.join(self.model_path(), f"{moduleName}.py") 89 | with open(model_path, "w") as f: 90 | f.write(model_content) 91 | 92 | route_path = os.path.join(self.routes_path(), "route.py") 93 | with open(route_path, "w") as f: 94 | f.write(route_content) 95 | 96 | layout_path = os.path.join(self.templates_path(), "layout.html") 97 | index_path = os.path.join(self.templates_path(), "index.html") 98 | 99 | with open(layout_path, "w") as f: 100 | f.write(layout_content) 101 | 102 | with open(index_path, "w") as f: 103 | f.write(index_content) 104 | 105 | self.info("Module successfully created!") 106 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Masonite Modules 2 | 3 |

4 | 5 |

6 | 7 |

8 | 9 | Masonite Package 10 | 11 | GitHub Workflow Status (branch) 12 | 13 | PyPI 14 | Python Version 15 | GitHub release (latest by date including pre-releases) 16 | License 17 | Code style: black 18 |

19 | 20 | ## Introduction 21 | 22 | Modularize your masonite project. 23 | 24 | ## Installation 25 | 26 | ```bash 27 | pip install masonite-modules 28 | ``` 29 | 30 | ## Configuration 31 | 32 | Add ModuleProvider to your project in `config/providers.py`: 33 | 34 | ```python 35 | # config/providers.py 36 | # ... 37 | from masonite_modules import ModuleProvider 38 | 39 | # ... 40 | PROVIDERS = [ 41 | # ... 42 | # Third Party Providers 43 | ModuleProvider, 44 | # ... 45 | ] 46 | ``` 47 | 48 | Publish the package resources by doing: 49 | 50 | ```bash 51 | python craft package:publish modules 52 | ``` 53 | 54 | The default modules location will be `modules` in the root directory of your project. But if you want your own custom name then, update the module name in `config/modules.py` to your desired module path name. 55 | 56 | ```python 57 | NAME=modules 58 | ``` 59 | 60 | ## Installation 61 | 62 | Once you finish publishing package, you need to install the modules by doing: 63 | 64 | ```bash 65 | $ python craft module:install 66 | ``` 67 | 68 | Finally, you will see the modules directory created in your project root. 69 | 70 | ## Usage 71 | 72 | To create the module, simply run: 73 | 74 | ```bash 75 | $ python craft module:create 76 | 77 | # Example 78 | $ python craft module:create blog 79 | ``` 80 | 81 | The above command will create a new module called `blogs` in the `modules` directory. It will also create all the necessary files and directories inside `blogs` so you can start working on your module. 82 | 83 | Finally, visit `/` i.e. `/blogs` then you will see a welcome message. 84 | 85 | > Enjoy! 86 | 87 | ## License 88 | 89 | masonite_modules is open-sourced software licensed under the [MIT license](LICENSE). 90 | -------------------------------------------------------------------------------- /tests/integrations/Kernel.py: -------------------------------------------------------------------------------- 1 | from masonite.foundation import response_handler 2 | from masonite.storage import StorageCapsule 3 | from masonite.auth import Sign 4 | from masonite.environment import LoadEnvironment 5 | from masonite.utils.structures import load 6 | from masonite.utils.location import base_path 7 | from masonite.middleware import ( 8 | SessionMiddleware, 9 | EncryptCookies, 10 | LoadUserMiddleware, 11 | MaintenanceModeMiddleware, 12 | ) 13 | from masonite.routes import Route 14 | from masonite.configuration.Configuration import Configuration 15 | from masonite.configuration import config 16 | 17 | from tests.integrations.app.middlewares import VerifyCsrfToken, AuthenticationMiddleware 18 | 19 | 20 | class Kernel: 21 | 22 | http_middleware = [MaintenanceModeMiddleware, EncryptCookies] 23 | 24 | route_middleware = { 25 | "web": [SessionMiddleware, LoadUserMiddleware, VerifyCsrfToken], 26 | "auth": [AuthenticationMiddleware], 27 | } 28 | 29 | def __init__(self, app): 30 | self.application = app 31 | 32 | def register(self): 33 | # Register routes 34 | self.load_environment() 35 | self.register_configurations() 36 | self.register_middleware() 37 | self.register_routes() 38 | self.register_database() 39 | self.register_templates() 40 | self.register_storage() 41 | 42 | def load_environment(self): 43 | LoadEnvironment() 44 | 45 | def register_configurations(self): 46 | # load configuration 47 | self.application.bind("config.location", "tests/integrations/config") 48 | configuration = Configuration(self.application) 49 | configuration.load() 50 | self.application.bind("config", configuration) 51 | key = config("application.key") 52 | self.application.bind("key", key) 53 | self.application.bind("sign", Sign(key)) 54 | # set locations 55 | self.application.bind("resources.location", "tests/integrations/resources/") 56 | self.application.bind("controllers.location", "tests/integrations/app/controllers") 57 | self.application.bind("jobs.location", "tests/integrations/app/jobs") 58 | self.application.bind("providers.location", "tests/integrations/app/providers") 59 | self.application.bind("mailables.location", "tests/integrations/app/mailables") 60 | self.application.bind("listeners.location", "tests/integrations/app/listeners") 61 | self.application.bind("validation.location", "tests/integrations/app/validation") 62 | self.application.bind("notifications.location", "tests/integrations/app/notifications") 63 | self.application.bind("events.location", "tests/integrations/app/events") 64 | self.application.bind("tasks.location", "tests/integrations/app/tasks") 65 | self.application.bind("models.location", "tests/integrations/app/models") 66 | self.application.bind("observers.location", "tests/integrations/app/models/observers") 67 | self.application.bind("policies.location", "tests/integrations/app/policies") 68 | self.application.bind("commands.location", "tests/integrations/app/commands") 69 | self.application.bind("middlewares.location", "tests/integrations/app/middlewares") 70 | 71 | self.application.bind("server.runner", "masonite.commands.ServeCommand.main") 72 | 73 | def register_middleware(self): 74 | self.application.make("middleware").add(self.route_middleware).add(self.http_middleware) 75 | 76 | def register_routes(self): 77 | Route.set_controller_locations(self.application.make("controllers.location")) 78 | self.application.bind("routes.location", "tests/integrations/routes/web") 79 | self.application.make("router").add( 80 | Route.group( 81 | load(self.application.make("routes.location"), "ROUTES"), middleware=["web"] 82 | ) 83 | ) 84 | 85 | def register_database(self): 86 | from masoniteorm.query import QueryBuilder 87 | 88 | self.application.bind( 89 | "builder", 90 | QueryBuilder(connection_details=config("database.databases")), 91 | ) 92 | 93 | self.application.bind("migrations.location", "tests/integrations/databases/migrations") 94 | self.application.bind("seeds.location", "tests/integrations/databases/seeds") 95 | 96 | self.application.bind("resolver", config("database.db")) 97 | 98 | def register_templates(self): 99 | self.application.bind("views.location", "tests/integrations/templates/") 100 | 101 | def register_storage(self): 102 | storage = StorageCapsule() 103 | storage.add_storage_assets(config("filesystem.staticfiles")) 104 | self.application.bind("storage_capsule", storage) 105 | 106 | self.application.set_response_handler(response_handler) 107 | self.application.use_storage_path(base_path("storage")) 108 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guide 2 | 3 | ## Introduction 4 | 5 | When contributing to this repository, **please first discuss the change you wish to make via issue, email, or any other method with the owners or contributors of this repository** before making a change 😃 . Thank you ! 6 | 7 | ## Getting Started 8 | 9 | ### Get the code 10 | 11 | First you should configure your local environment to be able to make changes in this package. 12 | 13 | 1. Fork the `https://github.com/py-package/masonite-modules` repo. 14 | 2. Clone that repo into your computer: `git clone http://github.com/your-username/masonite_modules.git`. 15 | 3. Checkout the current release branch \(example: `master`\). 16 | 4. Run `git pull origin master` to get the current release version. 17 | 18 | ### Install the environment 19 | 20 | 1. You should create a Python virtual environment with `Python >= 3.6`. 21 | 2. Then install the dependencies and setup the project, in root directory with: 22 | 23 | ``` 24 | make init 25 | ``` 26 | 27 | **Note:** 28 | 29 | - The package will be locally installed in your venv (with `pip install .`). Meaning that you will be 30 | able to import it from the project contained in the package as if you installed it from PyPi. 31 | - When making changes to your packages you will need to uninstall the package and reinstall it with 32 | `pip uninstall masonite_modules && pip install .` 33 | 34 | ### Contribute 35 | 36 | - From there simply create: 37 | - a feature branch `feat/my-new-feature` 38 | - a fix branch `fix/my-new-fix` 39 | - Push to your origin repository: 40 | - `git push origin feat/my-new-feature` 41 | - Open a pull request (PR) and follow the PR process below 42 | 43 | 1. You should open an issue before making any pull requests. Not all features will be added to the package and some may be better off as another third party package. It wouldn't be good if you worked on a feature for several days and the pull request gets rejected for reasons that could have been discussed in an issue for several minutes. 44 | 2. Ensure any changes are well commented and any configuration files that are added have a flagpole comment on the variables it's setting. 45 | 3. Update the README.md if installation/configuration or usage has changed. 46 | 4. It's better to add unit tests for the changes you made. 47 | 5. The PR must pass Github CI checks. The PR can be merged in once you have a successful review from a maintainer. 48 | 6. The version will be bumped by the maintainer when merging, so don't edit package version in the PR. 49 | 50 | ### Testing 51 | 52 | - To add unit tests add tests under `tests/` directory, please read about [Masonite 53 | testing](https://docs.masoniteproject.com/useful-features/testing) in the official 54 | documentation 55 | 56 | - To test your package locally in a project, a default Masonite project is available 57 | at root. Just run `python craft serve` and navigate to `localhost:8000/` and 58 | you will see `Hello Package World` in your browser. 59 | 60 | ## Dev Guidelines 61 | 62 | ### Package development 63 | 64 | You should read guidelines on package creation in the [Official Documentation](https://docs.masoniteproject.com/advanced/creating-packages) 65 | 66 | ### Comments 67 | 68 | Comments are a vital part of any repository and should be used where needed. It is important not to overcomment something. If you find you need to constantly add comments, you're code may be too complex. Code should be self documenting \(with clearly defined variable and method names\) 69 | 70 | #### Types of comments to use 71 | 72 | There are 3 main type of comments you should use when developing for Masonite: 73 | 74 | **Module Docstrings** 75 | 76 | All modules should have a docstring at the top of every module file and should look something like: 77 | 78 | ```python 79 | """ This is a module to add support for Billing users """ 80 | from masonite.request import Request 81 | ... 82 | ``` 83 | 84 | **Method and Function Docstrings** 85 | 86 | All methods and functions should also contain a docstring with a brief description of what the module does 87 | 88 | For example: 89 | 90 | ```python 91 | def some_function(self): 92 | """ 93 | This is a function that does x action. 94 | Then give an exmaple of when to use it 95 | """ 96 | ... code ... 97 | ``` 98 | 99 | **Code Comments** 100 | 101 | If you're code MUST be complex enough that future developers will not understand it, add a `#` comment above it 102 | 103 | For normal code this will look something like: 104 | 105 | ```python 106 | # This code performs a complex task that may not be understood later on 107 | # You can add a second line like this 108 | complex_code = 'value' 109 | 110 | perform_some_complex_task() 111 | ``` 112 | 113 | **Flagpole Comments** 114 | 115 | Flag pole comments are a fantastic way to give developers an inside to what is really happening and for now should only be reserved for configuration files. A flag pole comment gets its name from how the comment looks 116 | 117 | ```text 118 | """ 119 | |-------------------------------------------------------------------------- 120 | | A Heading of The Setting Being Set 121 | |-------------------------------------------------------------------------- 122 | | 123 | | A quick description 124 | | 125 | """ 126 | 127 | SETTING = "some value" 128 | ``` 129 | 130 | It's important to note that there should have exactly 75 `-` above and below the header and have a trailing `|` at the bottom of the comment. 131 | --------------------------------------------------------------------------------