This repository has been archived by the owner on Nov 22, 2017. It is now read-only.
thraxil/bourbon
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
master
Could not load branches
Nothing to show
Could not load tags
Nothing to show
{{ refName }}
default
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Code
-
Clone
Use Git or checkout with SVN using the web URL.
Work fast with our official CLI. Learn more about the CLI.
- Open with GitHub Desktop
- Download ZIP
Sign In Required
Please sign in to use Codespaces.
Launching GitHub Desktop
If nothing happens, download GitHub Desktop and try again.
Launching GitHub Desktop
If nothing happens, download GitHub Desktop and try again.
Launching Xcode
If nothing happens, download Xcode and try again.
Launching Visual Studio Code
Your codespace will open once ready.
There was a problem preparing your codespace, please try again.
Note: this is an archive of an old project. I originally wrote it back in about 2007-2008. It was (IMO) good code for the time, but probably isn't something that you want to use now. I'm just posting it up here for posterity. It was originally documented here: https://code.google.com/archive/p/microapps/wikis/Bourbon.wiki Bourbon ======= Bourbon is a small framework designed to make it easy to develop and deploy simple microapps. The idea is that you define a database model (with SQLAlchemy currently) and some minimal controller classes that tie that model to some url patterns (via Selector and WSGICollection) and the usual CRUD methods are then accessible via HTTP. Current Features: * Basic HTTP method to -> CRUD mapping. * JSON loading and rendering * automatic transaction support * strict adherence to rfc2616 (HTTP 1.1) * nested structures * paste deployment with .ini config files * paste template for "quickstart": paster create -t bourbon myapp Todo: * use paste.fixture for more help with testing * content negotiation * command line tools for database init, etc. * better automatic figuring out of primary key columns and parent-child relationships with the url mapping * more hooks to override the default behavior * abstract out ORM requirements so SQLObject or other mappers can be used instead of SQLAlchemy * eliminate need for 'python setup.py develop' step * logging configuration Example: ======== Say we want to create a very simple microapp that stores articles, which each may belong to a category. We'll call our app "storytime". We first use paster to create a skeleton bourbon project for us: $ paster create -t bourbon storytime Enter whatever information you want when it prompts you. The result will be a directory called 'storytime' with the following structure: ------------------------------------- /setup.py /development.ini /storytime.db /storytime/ __init__.py model.py controllers.py wsgiapp.py ------------------------------------- Next we need to define our model in storytime/model.py. Add the following to it: ------------------------------------- # tables category_table = Table('category', metadata, Column('name', String(256), primary_key=True)) story_table = Table('story', metadata, Column('slug',String(256), primary_key=True), Column('title',String(256), index=True, nullable=False), Column('author_name',String(256)), Column('body',String(30000)), Column('created',DateTime,default=datetime.now), Column('category_name',String(256), ForeignKey('category.name'))) # domain classes class Category(object): pass class Story(object): pass # mappers categorymapper = mapper(Category,category_table,properties = { 'stories' : relation(Story, cascade="all, delete-orphan",backref='category')}) storymapper = mapper(Story,story_table) ---------------------------------------- Pretty basic. SQLAlchemy can be a bit verbose, but it's straightforward. Note that the model contains nothing Bourbon related. It's just a plain SQLAlchemy model and could be imported into and used with anything else. storytime/controllers.py sets up the HTTP -> model mapping. We need to create some controller objects and map them to urls. Add two controller classes: ---------------------------------------- class CategoryCollection(Resource): domain_class = Category id_column = Category.c.name class StoryCollection(Resource): domain_class = Story id_column = Story.c.slug parent = dict(column = Story.c.category_name, param = "categoryname", parent_class = Category, parent_class_id_column = Category.c.name) ---------------------------------------- and then add two corresponding url mappers to the end: ---------------------------------------- urls.add('/[{id:word}][;{noun}]', _ANY_=CategoryCollection()) urls.add('/{categoryname}/[{id:word}][;{noun}][/]', _ANY_=StoryCollection()) ---------------------------------------- This is best understood starting with the last couuple lines. Those set up url mappings using selector, mapping all HTTP methods to our controllers and putting a couple particular variables into the WSGI environ so Bourbon can automatically construct/fetch the right model objects. development.ini is a standard paste.deploy config file that 'paster serve' can run. Edit it to change the port number and/or the database connection info (defaults to port 9080 and a sqlite database named for the package). ####################################### currently, it seems necessary to run $ python setup.py develop to get the entry points picked up. I'm still not sure why, but I hope to make that step not necessary ####################################### You should now manually initalialize the database (commandline tools for this are forthcoming): $ python >>> from storytime.model import * >>> engine = create_engine("sqlite:///storytime.db") >>> metadata.create_all(connectable=engine) Then you can run the app with paster server: $ paster serve development.ini And you now have HTTP+JSON access to the database on port 9080. So, eg. $ curl http://localhost:9080/ {"members": [], "next": null} add some categories: $ curl -X PUT http://localhost:9080/world $ curl -X PUT http://localhost:9080/local $ curl http://localhost:9080/ {"members": [{"href": "world"}, {"href": "local"}], "next": null} GET /world gets the category itself $ curl http://localhost:9080/world {"name": "world"} while GET /world/ gets the collection of stories in the category (currently none) $ curl http://localhost:9080/world/ {"members": [], "next": null} DELETE the local category: $ curl -X DELETE http://localhost:9080/local $ curl http://localhost:9080/ {"members": [{"href": "world"}], "next": null} add a story to the world category $ curl -X PUT -d '{"title" : "World Peace Achieved", "author_name" : \ "Dan Rather", "body" : "Yay!"}' http://localhost:9080/world/rather-world-peace an alternate way to add a story: $ curl -X POST -d '{"title" : "Caffeinated Donuts Invented", "author_name" : \ "Dan Rather", "body" : "Better than world peace!", "slug" : \ "rather-donuts"}' http://localhost:9080/world/ The second approach uses a POST to the collection instead of a direct PUT to the resource location. This is how you'll want to do it anytime you use auto-generated keys, or if, eg, the slug was being automatically generated from the title or something. In the style of the Atom API and Ruby on Rails' SimplyREST, you can also define additional actions for controllers to respond to. That's what the '{noun}' stuff in the url mapping was for. You just need to add a method to the appropriate controller object with a name of '<http method>_<noun>'. Eg, to respond to a GET request on '/world;edit_form' you would add a method to CategoryCollection: def get_edit_form(self, environ, start_response): # make a form, call start_response() and # yield the content Within that method, you may want to access the SQLAlchemy session and the model object that the controller is mapped to. you can do that with self._session() and self._fetch(environ) respectively. All methods are automatically wrapped in a transaction. Raising an exception will cause it to rollback.
About
WSGI REST framework
Resources
License
Stars
Watchers
Forks
Releases
No releases published
Packages 0
No packages published