Skip to content

Commit 85fbf79

Browse files
Merge pull request #51 from josephmancuso/v0.3.1
v0.3.1 Release
2 parents 990d6eb + ff0f10d commit 85fbf79

File tree

8 files changed

+472
-15
lines changed

8 files changed

+472
-15
lines changed

CONTRIBUTING.md

+78-2
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,83 @@ Craft commands make up a large part of the workflow for Masonite. Follow these i
6565

6666
This repository is the repository where `craft new project_name` will install from. It takes the zip of the repository, unzips it and renames the folder to the project name. This repository is not directly editable by contributors as it's a pretty static repo that is only updated on josephmancuso/masonite releases. We will take the contents of this repo, strip it down (remove .travis and docs/ stuff) data and leave only the core project.
6767

68+
## Comments
69+
70+
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)
71+
72+
### Types of comments to use
73+
74+
There are 4 main type of comments you should use when developing for Masonite:
75+
76+
#### Module Docstrings
77+
78+
All modules should have a docstring at the top of every module file and should look something like:
79+
80+
```python
81+
''' This is a module to add support for Billing users '''
82+
from masonite.request import Request
83+
...
84+
```
85+
86+
#### Method and Function Docstrings
87+
88+
All methods and functions should also contain a docstring with a brief description of what the module does
89+
90+
For example:
91+
92+
```python
93+
def some_function(self):
94+
''' This is a function that does x action.
95+
Then give an exmaple of when to use it
96+
'''
97+
... code ...
98+
```
99+
100+
#### Code Comments
101+
102+
If you're code MUST be complex enough that future developers will not understand it, add a `#` comment above it
103+
104+
For normal code this will look something like:
105+
106+
```python
107+
# This code performs a complex task that may not be understood later on
108+
# You can add a second line like this
109+
complex_code = 'value'
110+
111+
perform_some_complex_task()
112+
```
113+
114+
If you are using if statements it should look something like:
115+
116+
```python
117+
if x in y:
118+
# This comment should be inside the if statement
119+
... code ...
120+
else:
121+
# this comment should be inside the else statement
122+
... code ...
123+
```
124+
125+
#### Flagpole Comments
126+
127+
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
128+
129+
```
130+
'''
131+
|--------------------------------------------------------------------------
132+
| A Heading of The Setting Being Set
133+
|--------------------------------------------------------------------------
134+
|
135+
| A quick description
136+
|
137+
'''
138+
139+
SETTING = 'some value'
140+
```
141+
142+
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.
143+
144+
68145
## Pull Request Process
69146

70147
1. Ensure any changes are well commented and any configuration files that are added have a flagpole comment on the
@@ -74,8 +151,7 @@ This repository is the repository where `craft new project_name` will install fr
74151
3. Must add unit testing for any changes made.
75152
4. Increase the version numbers in any examples files and the README.md to the new version that this
76153
Pull Request would represent. The versioning scheme we use is [SemVer](http://semver.org/).
77-
5. You may merge the Pull Request in once you have the sign-off of two other developers, or the repo owner,
78-
so that we can merge it in
154+
5. The PR must pass the Travis build. The Pull Request can be merged in once you have the sign-off of two other developers, or the repo owner, so that we can merge it in.
79155

80156
## Code of Conduct
81157

bootstrap/start.py

+30-10
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
''' Start of Application. This function is the gunicorn server '''
2-
32
import os
43
import re
54

6-
75
from dotenv import find_dotenv, load_dotenv
86
from masonite.request import Request
97
from masonite.routes import Route
@@ -13,25 +11,31 @@
1311
load_dotenv(find_dotenv())
1412

1513
# Run once and only once the server is ran
14+
# This will not actually compile if the libsass module in not installed
1615
Storage().compile_sass()
1716

1817
def app(environ, start_response):
1918
''' Framework Engine '''
2019
os.environ.setdefault('REQUEST_METHOD', environ['REQUEST_METHOD'])
2120
os.environ.setdefault('URI_PATH', environ['PATH_INFO'])
2221

23-
# if this is a post request
22+
2423
router = Route(environ)
2524

2625
if router.is_post():
2726
# Set POST parameters given into the query string to centralize parsing and retrieval.
27+
# This was made to directly support the Request.input() method.
28+
# There is no known reason to differentiate between GET and POST when retrieving
29+
# form paramters. This line below simply puts both POST form params in the
30+
# same query_string as GET params.
2831
environ['QUERY_STRING'] = router.set_post_params()
2932

3033
# Get all routes from the routes/web.py file
3134
import routes.web
3235
routes = routes.web.ROUTES
3336

34-
# Instantiate the Request object
37+
# Instantiate the Request object.
38+
# This is the `request` object passed into controller methods
3539
request = Request(environ)
3640

3741
# Check all http routes
@@ -40,11 +44,19 @@ def app(environ, start_response):
4044
regex = router.compile_route_to_regex(route)
4145

4246
# Make a better match trailing slashes `/` at the end of routes
47+
# Sometimes a user will end with a trailing slash. Because the
48+
# user may create routes like `/url/route` and `/url/route/`
49+
# and how the regex is compiled down, we may need to adjust for
50+
# urls that end or dont end with a trailing slash. This is sort
51+
# of a quirk and could possibly be fixed.
4352
if route.route_url.endswith('/'):
4453
matchurl = re.compile(regex.replace(r'\/\/$', r'\/$'))
4554
else:
4655
matchurl = re.compile(regex.replace(r'\/$', r'$'))
4756

57+
# This will create a dictionary of paramters given. This is a sort of a short
58+
# but complex way to retrieve the url paramters. This is the code used to
59+
# convert /url/@firstname/@lastname to {'firstmane': 'joseph', 'lastname': 'mancuso'}
4860
try:
4961
parameter_dict = {}
5062
for index, value in enumerate(matchurl.match(router.url).groups()):
@@ -53,7 +65,11 @@ def app(environ, start_response):
5365
except AttributeError:
5466
pass
5567

68+
69+
# If the route url matches the regex and the method type and the route can continue
70+
# (will be used with middleware later)
5671
if matchurl.match(router.url) and route.method_type == environ['REQUEST_METHOD'] and route.continueroute is True:
72+
# Show a helper in the terminal of which route has been visited
5773
print(route.method_type + ' Route: ' + router.url)
5874
data = router.get(route.route, route.output(request))
5975
break
@@ -62,11 +78,13 @@ def app(environ, start_response):
6278

6379
## Check all API Routes
6480
if data == 'Route not found. Error 404':
65-
# look at the API routes files
81+
# Look at the API routes files
6682
import routes.api
6783
routes = routes.api.ROUTES
6884

6985
for route in routes:
86+
87+
# If the base url matches
7088
if route.url in router.url:
7189
data = route.fetch(request).output
7290
if data:
@@ -76,28 +94,30 @@ def app(environ, start_response):
7694
else:
7795
data = 'Route not found. Error 404'
7896

79-
## Set redirection if a route has been called to redirect to
97+
# Set redirection if a route has been called to redirect to.
98+
# redirect_route is a property set on the request object when
99+
# methods like request.redirectTo('nameRoute') are called
80100
if request.redirect_route:
81101
for route in routes:
82102
if route.named_route == request.redirect_route:
83103
print(route.method_type + ' Named Route: ' + router.url)
84104
data = router.get(route.route, route.output(request))
85105
request.redirect_url = route.route_url
86106

87-
107+
# Convert the data that is retrieved above to bytes so the wsgi server can handle it.
88108
data = bytes(data, 'utf-8')
89109

90-
91110
if not request.redirect_url:
92-
# Normal HTTP response
111+
# Normal HTTP response.
93112
start_response("200 OK", [
94113
("Content-Type", "text/html; charset=utf-8"),
95114
("Content-Length", str(len(data)))
96115
] + request.get_cookies())
97116
else:
98-
# Redirection
117+
# Redirection. In order to redirect the response types need to be 302 instead of 200
99118
start_response("302 OK", [
100119
('Location', request.redirect_url)
101120
] + request.get_cookies())
102121

122+
# This will output the data to the browser.
103123
return iter([data])

config/packages.py

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
''' Packages Configuration '''
2+
3+
'''
4+
|--------------------------------------------------------------------------
5+
| Site Packages
6+
|--------------------------------------------------------------------------
7+
|
8+
| A list of additional locations that are loaded into the craft command
9+
| when certain actions are performed like the `publish` command. Put
10+
| the directory of your virtual environment's site-packages directory
11+
| to load your virtual environments python package.
12+
|
13+
| ----------
14+
| @example
15+
| SITE_PACKAGES = [
16+
| 'venv/lib/python3.6/site-packages'
17+
| ]
18+
| ----------
19+
|
20+
'''
21+
22+
SITE_PACKAGES = []

docs/Creating-Packages.md

+145
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
## Introduction
2+
3+
Creating packages is very simple for Masonite which is a common theme across the framework. With Masonite Package you'll easily be able to integrate and scaffold other Masonite projects with ease.
4+
5+
### Getting Started
6+
7+
As a developer, you will be responsible for both making packages and consuming packages. In this documentation we'll talk about both. We'll start by talking about how to make a package and then talk about how to use that package or other third party packages.
8+
9+
#### Creating a Package
10+
11+
Like other parts of Masonite, in order to make a package, we can use a craft command. The `craft package` command will scaffold out a simple Masonite package and is fully able to be uploaded directly to PyPi.
12+
13+
Let's create our package:
14+
15+
$ craft package testpackage
16+
17+
This will create a file structure like:
18+
19+
```
20+
testpackage/
21+
__init__.py
22+
integration.py
23+
MANIFEST.in
24+
setup.py
25+
```
26+
27+
The `integration.py` file is important and should not be removed. This is the file that will be used when our users use the `craft publish` command.
28+
29+
If we open this file we'll notice a single `boot()` function. Whenever the user (the developer using Masonite) uses the `craft publish testpackage` command, craft will execute `testpackage.integration.boot()` so it's wise to load anything you want to be executed on in this function.
30+
31+
You'll notice a helper function imported for you at the top. This `create_or_append_config()` function does exactly what it says. It will take a config file from your package and puts it into the project by either creating it (if it does not exist) or appending it (if it does exist). We'll talk about helper functions later on.
32+
33+
#### Creating a Config Package
34+
35+
Lets create a simple package that will add or append a config file from our package and into the project.
36+
37+
First lets create a config file inside `testpackage/snippets/configs/services.py`. We should now have a project structure like:
38+
39+
```
40+
testpackage/
41+
__init__.py
42+
integration.py
43+
snippets/
44+
configs/
45+
services.py
46+
MANIFEST.in
47+
setup.py
48+
```
49+
50+
Great! inside the `services.py` lets put a configuration setting:
51+
52+
```
53+
TESTPACKAGE_PAYMENTS = {
54+
'stripe': {
55+
'some key': 'some value',
56+
'another key': 'another value'
57+
}
58+
}
59+
```
60+
61+
Perfect! Now we'll just need to tell PyPi to include this file when we upload it to PyPi. We can do this in our `MANIFEST.in` file.
62+
63+
```
64+
include testpackage/snippets/configs/*
65+
```
66+
67+
Almost done. Now we just need to put our `masonite.package` helper function in our boot file. The location we put in our `create_or_append_config()` function should be an absolute path location to our package. To do this, Masonite Package has put a variable called `package_directory` inside our `integrations.py` file. Our boot method should look something like:
68+
69+
```python
70+
def boot():
71+
create_or_append_config(
72+
os.path.join(
73+
package_directory, 'snippets/configs/services.py'
74+
)
75+
)
76+
```
77+
78+
This will append the configuration file that has the same name as our package configuration file. In this case the configuration file we are creating or appending to is `config/services.py`. If we want to append to another configuration file we can simply change the name of our package configuration file.
79+
80+
#### Working With Our Package
81+
82+
We can either test our package locally or upload our package to PyPi.
83+
84+
To test our package locally, if you use virtual environments, just go to your Masonite project and activate your virtual environment. Navigate to the folder where you created your package and run:
85+
86+
$ pip install .
87+
88+
If you want to be able to make changes without having to constantly reinstall your package then run
89+
90+
$ pip install --editable .
91+
92+
This will install your new package into your virtual environment. Go back to your project root so we can run our `craft publish` command. If we run `craft publish testpackage` we should get a module not found error.
93+
94+
It's important to note that `craft publish` does not have access to your virtual environment by default, only your system packages. What we can do is add our virtual environments `site_packages` directory to our `config/packages.py` config file which should look something like:
95+
96+
```python
97+
SITE_PACKAGES = [
98+
'venv/lib/python3.6/site-packages'
99+
]
100+
```
101+
102+
This will take that path and add it to the `sys.path` for the `craft publish` script.
103+
104+
Now if we run `craft publish` we should see that our new configuration file is now in `config/services.py` file. Awesome! We tried making package support super easy. You of course don't need to integrate directly or scaffold the project but the option is there if you choose to make a package to do so.
105+
106+
#### Uploading to PyPi
107+
108+
If you have never set up a package before then you'll need to [check how to make a `.pypirc` file](http://peterdowns.com/posts/first-time-with-pypi.html). This file will hold our PyPi credentials.
109+
110+
To upload to PyPi we just have to change the name of our package in the `setup.py` file. Now that you have a super awesome name, we'll just need to run:
111+
112+
$ python setup.py sdist upload
113+
114+
which should upload our package with our credentials in our `.pypirc` file. Make sure you click the link above and see how to make once.
115+
116+
#### Consuming a package.
117+
118+
Now that your package is on PyPi we can just run:
119+
120+
$ pip install super_awesome_package
121+
$ craft publish super_awesome_package
122+
123+
Again, not all packages will need to be published. Only packages that need to scaffold the project.
124+
125+
## Helper Functions
126+
127+
`create_or_append_config(location)` will create a configuration file based on a configuration file from your package.
128+
129+
`append_web_routes(location)` will append web routes to the `routes/web.py` file. Your web routes should have a `+=` to the `ROUTES` constant and should look something like:
130+
131+
```python
132+
ROUTES += [
133+
# your package routes here
134+
]
135+
```
136+
137+
`append_api_routes(location)` will append api routes to a masonite project under `routes/api.py`. Your api routes should have a `+=` to the `ROUTES` constant and should look something like:
138+
139+
```python
140+
ROUTES += [
141+
# your package routes here
142+
]
143+
```
144+
145+
`create_controller(location)` will take a controller from your package and append it under the `app.http.controllers` namespace.

0 commit comments

Comments
 (0)