├── __init__.py ├── app ├── __init__.py ├── controllers │ ├── __init__.py │ └── home.py ├── start.bat ├── start.sh ├── views │ ├── home │ │ └── home.tpl │ └── mainLayout.tpl ├── resources │ ├── images │ │ ├── glyphicons-halflings.png │ │ └── glyphicons-halflings-white.png │ ├── css │ │ ├── style.css │ │ └── bootstrap-responsive.css │ └── js │ │ ├── html5.js │ │ └── bootstrap.js ├── bottle_preRequest.py └── app.py ├── model ├── __init__.py ├── example │ ├── __init__.py │ └── ExampleService.py ├── Service.py ├── helpers │ └── RestHelpers.py ├── StringHelper.py ├── Factory.py ├── DAO.py └── DateHelper.py ├── tests ├── __init__.py └── TestExampleService.py ├── .gitignore ├── LICENSE ├── config.py └── README.md /__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /model/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/controllers/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /model/example/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/start.bat: -------------------------------------------------------------------------------- 1 | python ./app.py 2 | -------------------------------------------------------------------------------- /app/start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | python ./app.py 3 | -------------------------------------------------------------------------------- /app/views/home/home.tpl: -------------------------------------------------------------------------------- 1 |
{{!message}}
2 | 3 | % rebase mainLayout title = "Home" -------------------------------------------------------------------------------- /app/resources/images/glyphicons-halflings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adampresley/bottlepy-bootstrap/HEAD/app/resources/images/glyphicons-halflings.png -------------------------------------------------------------------------------- /app/resources/images/glyphicons-halflings-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adampresley/bottlepy-bootstrap/HEAD/app/resources/images/glyphicons-halflings-white.png -------------------------------------------------------------------------------- /app/resources/css/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding-top: 60px; 3 | padding-bottom: 40px; 4 | } 5 | 6 | .sidebar-nav { 7 | padding: 9px 0; 8 | } 9 | 10 | .clear { 11 | clear: both; 12 | } -------------------------------------------------------------------------------- /model/Service.py: -------------------------------------------------------------------------------- 1 | class Service: 2 | db = None 3 | 4 | def __init__(self, db): 5 | self.db = db 6 | 7 | def inject(self, key, value): 8 | self.__dict__[key] = value 9 | 10 | -------------------------------------------------------------------------------- /model/helpers/RestHelpers.py: -------------------------------------------------------------------------------- 1 | from bottle import response 2 | 3 | def error(message): 4 | response.status = 500 5 | return message 6 | 7 | def notFound(): 8 | response.status = 404 9 | return "Resource not found" -------------------------------------------------------------------------------- /model/example/ExampleService.py: -------------------------------------------------------------------------------- 1 | from model.Service import Service 2 | 3 | class ExampleService(Service): 4 | def getGreetingMessage(self, name): 5 | return "Hello %s. Greetings to you!" % name 6 | 7 | def getTwoPlusTwo(self): 8 | return 2 + 2 9 | -------------------------------------------------------------------------------- /app/controllers/home.py: -------------------------------------------------------------------------------- 1 | from bottle import view, route, request 2 | 3 | @route("/") 4 | @view("home") 5 | def home(): 6 | exampleService = request.factory.getExampleService() 7 | 8 | viewData = { "message": exampleService.getGreetingMessage("Adam Presley") } 9 | return viewData 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[cod] 2 | 3 | # C extensions 4 | *.so 5 | 6 | # Packages 7 | *.egg 8 | *.egg-info 9 | dist 10 | build 11 | eggs 12 | parts 13 | bin 14 | var 15 | sdist 16 | develop-eggs 17 | .installed.cfg 18 | lib 19 | lib64 20 | 21 | # Installer logs 22 | pip-log.txt 23 | 24 | # Unit test / coverage reports 25 | .coverage 26 | .tox 27 | nosetests.xml 28 | 29 | # Translations 30 | *.mo 31 | 32 | # Mr Developer 33 | .mr.developer.cfg 34 | .project 35 | .pydevproject 36 | -------------------------------------------------------------------------------- /model/StringHelper.py: -------------------------------------------------------------------------------- 1 | import re, random, string 2 | from model.Service import Service 3 | 4 | class StringHelper(Service): 5 | articles = ["a", "an", "the", "of", "is"] 6 | 7 | def randomLetterString(self, numCharacters = 8): 8 | return "".join(random.choice(string.ascii_letters) for i in range(numCharacters)) 9 | 10 | def tagsToTuple(self, tags): 11 | return tuple(self.titleCase(tag) for tag in tags.split(",") if tag.strip()) 12 | 13 | def titleCase(self, s): 14 | wordList = s.split(" ") 15 | result = [wordList[0].capitalize()] 16 | 17 | for word in wordList[1:]: 18 | result.append(word in self.articles and word or word.capitalize()) 19 | 20 | return " ".join(result) 21 | 22 | def validEmail(self, email): 23 | return re.match(r"[^@]+@[^@]+\.[^@]+", email) 24 | -------------------------------------------------------------------------------- /model/Factory.py: -------------------------------------------------------------------------------- 1 | from model.DateHelper import DateHelper 2 | from model.example.ExampleService import ExampleService 3 | from model.StringHelper import StringHelper 4 | 5 | class Factory: 6 | db = None 7 | 8 | def __init__(self, db): 9 | self.db = db 10 | 11 | def getDateHelper(self): 12 | return self._getService(DateHelper(self.db), []) 13 | 14 | def getExampleService(self): 15 | return self._getService(ExampleService(self.db), []) 16 | 17 | def getStringHelper(self): 18 | return self._getService(StringHelper(self.db), []) 19 | 20 | 21 | def _getService(self, service, stuff): 22 | for item in stuff: 23 | service.inject(item[0], item[1]) 24 | 25 | return service 26 | 27 | def _getDAO(self, dao): 28 | dao.inject("dateHelper", self.getDateHelper()) 29 | return dao 30 | -------------------------------------------------------------------------------- /tests/TestExampleService.py: -------------------------------------------------------------------------------- 1 | import unittest, os, sys 2 | from mock import Mock, MagicMock 3 | 4 | sys.path.insert(1, "../") 5 | 6 | from model.example.ExampleService import ExampleService 7 | 8 | class TestExampleService(unittest.TestCase): 9 | def setUp(self): 10 | self.exampleService = ExampleService(None) 11 | 12 | 13 | def test_getGreetingMessage_WithNameAdam_ReturnsStringWithNameAdam(self): 14 | expected = "Hello Adam. Greetings to you!" 15 | actual = self.exampleService.getGreetingMessage(name = "Adam") 16 | 17 | self.assertEqual(expected, actual, "The expected string did not come back") 18 | 19 | def test_getTwoPlusTwo_Returns4(self): 20 | expected = 4 21 | actual = self.exampleService.getTwoPlusTwo() 22 | 23 | self.assertEqual(expected, actual, "Expected 2 + 2 to equal 4") 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Adam Presley 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 4 | documentation files (the "Software"), to deal in the Software without restriction, including without limitation 5 | the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, 6 | and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 7 | 8 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 9 | 10 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 11 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 12 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 13 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /model/DAO.py: -------------------------------------------------------------------------------- 1 | class DAO: 2 | db = None 3 | 4 | def __init__(self, db): 5 | self.db = db 6 | 7 | def escapeString(self, s): 8 | return self.db.connection.escape_string(s) 9 | 10 | def execute(self, sql, params = (), writeToConsole = False): 11 | rowsAffected = 0 12 | 13 | try: 14 | rowsAffected = self.db.execute(sql, params) 15 | 16 | if writeToConsole: 17 | print self.getLastExecuted() 18 | 19 | except Exception as e: 20 | print "An error occured in %s: %s" % (self, e) 21 | print "Last executed SQL:" 22 | print self.getLastExecuted() 23 | 24 | raise e 25 | 26 | return rowsAffected 27 | 28 | def executeMany(self, sql, paramArray = []): 29 | try: 30 | self.db.executemany(sql, paramArray) 31 | 32 | except Exception as e: 33 | print "An error occured in %s: %s" % (self, e.message) 34 | print "Last executed SQL:" 35 | print self.getLastExecuted() 36 | 37 | raise e 38 | 39 | def fetchAll(self): 40 | return self.db.fetchall() 41 | 42 | def fetchOne(self): 43 | return self.db.fetchone() 44 | 45 | def getLastExecuted(self): 46 | return self.db._last_executed 47 | 48 | def inject(self, key, value): 49 | self.__dict__[key] = value 50 | 51 | def lastRowId(self): 52 | return self.db.lastrowid -------------------------------------------------------------------------------- /app/bottle_preRequest.py: -------------------------------------------------------------------------------- 1 | import bottle, config, re, MySQLdb 2 | import MySQLdb.cursors as cursors 3 | from bottle import request, response, redirect 4 | 5 | from model.Factory import Factory 6 | 7 | def preRequest(callback): 8 | def wrapper(*args, **kwargs): 9 | if not request.path.startswith("/resources"): 10 | # 11 | # Setup session and environment stuff 12 | # 13 | request.session = request.environ.get("beaker.session") 14 | request.all = dict(request.query.items() + request.forms.items()) 15 | 16 | # 17 | # Setup database object. Uncomment lines 19-24, 37-38 and comment line 25 to 18 | # enable MySQL 19 | # 20 | # request.dbConnection = MySQLdb.connect(config.DBSERVER, config.DBUSER, config.DBPASS, config.DBNAME, cursorclass = cursors.DictCursor, charset = "utf8", port = 3306) 21 | # request.db = request.dbConnection.cursor() 22 | 23 | # request.db.execute("set time_zone=%s", ("+0:00",)) 24 | # request.db.close() 25 | # request.db = request.dbConnection.cursor() 26 | request.db = None 27 | 28 | request.factory = Factory(request.db) 29 | 30 | # 31 | # Finally call the the next method in the chain 32 | # 33 | body = callback(*args, **kwargs) 34 | 35 | # 36 | # Commit and close db connection 37 | # 38 | # request.dbConnection.commit() 39 | # request.dbConnection.close() 40 | 41 | return body 42 | 43 | else: 44 | body = callback(*args, **kwargs) 45 | return body 46 | 47 | return wrapper 48 | -------------------------------------------------------------------------------- /config.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | DEBUG = True 4 | BIND_TO_OUTSIDE_IP = False 5 | BIND_TO_PORT = 8080 6 | ROOT_PATH = os.path.join(os.path.abspath(os.path.dirname(__file__)), "app") 7 | RESOURCES_PATH = os.path.join(ROOT_PATH, "resources") 8 | BASE_TEMPLATE_PATH = os.path.join(ROOT_PATH, "views") 9 | SESSION_PATH = os.path.join(ROOT_PATH, "sessions") 10 | 11 | 12 | ENV = "dev" 13 | 14 | ENVIRONMENTS = { 15 | "dev": { 16 | "DBSERVER": "localhost", 17 | "DBUSER": "user", 18 | "DBPASS": "password" 19 | }, 20 | "test": { 21 | "DBSERVER": "localhost", 22 | "DBUSER": "user", 23 | "DBPASS": "password" 24 | }, 25 | "beta": { 26 | "DBSERVER": "localhost", 27 | "DBUSER": "user", 28 | "DBPASS": "password" 29 | }, 30 | "demo": { 31 | "DBSERVER": "localhost", 32 | "DBUSER": "user", 33 | "DBPASS": "password" 34 | }, 35 | "prod": { 36 | "DBSERVER": "localhost", 37 | "DBUSER": "user", 38 | "DBPASS": "password" 39 | }, 40 | } 41 | 42 | DBNAME = "dbname" 43 | DBSERVER = ENVIRONMENTS[ENV]["DBSERVER"] 44 | DBUSER = ENVIRONMENTS[ENV]["DBUSER"] 45 | DBPASS = ENVIRONMENTS[ENV]["DBPASS"] 46 | 47 | 48 | # 49 | # Session settings 50 | # 51 | SESSION_OPTS = { 52 | "session.type": "ext:database", 53 | "session.cookie_expires": 14400, 54 | "session.auto": True, 55 | "session.url": "mysql+mysqldb://{0}:{1}@{2}/{3}".format(DBUSER, DBPASS, DBSERVER, DBNAME), 56 | "session.table_name": "admin_session", 57 | "session.lock_dir": os.path.join(SESSION_PATH, "lock"), 58 | "session.data_dir": os.path.join(SESSION_PATH, "data") 59 | } 60 | -------------------------------------------------------------------------------- /app/resources/js/html5.js: -------------------------------------------------------------------------------- 1 | /*! HTML5 Shiv vpre3.6 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed 2 | Uncompressed source: https://github.com/aFarkas/html5shiv */ 3 | (function(a,b){function h(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function i(){var a=l.elements;return typeof a=="string"?a.split(" "):a}function j(a){var b={},c=a.createElement,f=a.createDocumentFragment,g=f();a.createElement=function(a){if(!l.shivMethods)return c(a);var f;return b[a]?f=b[a].cloneNode():e.test(a)?f=(b[a]=c(a)).cloneNode():f=c(a),f.canHaveChildren&&!d.test(a)?g.appendChild(f):f},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+i().join().replace(/\w+/g,function(a){return c(a),g.createElement(a),'c("'+a+'")'})+");return n}")(l,g)}function k(a){var b;return a.documentShived?a:(l.shivCSS&&!f&&(b=!!h(a,"article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block}audio{display:none}canvas,video{display:inline-block;*display:inline;*zoom:1}[hidden]{display:none}audio[controls]{display:inline-block;*display:inline;*zoom:1}mark{background:#FF0;color:#000}")),g||(b=!j(a)),b&&(a.documentShived=b),a)}var c=a.html5||{},d=/^<|^(?:button|form|map|select|textarea|object|iframe|option|optgroup)$/i,e=/^<|^(?:a|b|button|code|div|fieldset|form|h1|h2|h3|h4|h5|h6|i|iframe|img|input|label|li|link|ol|option|p|param|q|script|select|span|strong|style|table|tbody|td|textarea|tfoot|th|thead|tr|ul)$/i,f,g;(function(){var c=b.createElement("a");c.innerHTML="75 | This is my Bottle (Python) Bootstrapper. I use this to quickly startup a Python web-application 76 | using Bottlepy, jQuery, Twitter Bootstrap, Beaker session management, and SQLAlchemy. 77 |
78 | 79 |80 | Learn more » 81 |
82 |