├── .gitignore ├── LICENSE ├── README.md ├── example.raml ├── local.ini.template ├── ramses_example └── __init__.py ├── requirements.dev ├── requirements.txt ├── schemas ├── profile.json ├── story.json └── user.json └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | venv 2 | .DS_Store 3 | 4 | # Byte-compiled / optimized / DLL files 5 | __pycache__/ 6 | *.py[cod] 7 | 8 | # C extensions 9 | *.so 10 | 11 | # Distribution / packaging 12 | .Python 13 | env/ 14 | build/ 15 | develop-eggs/ 16 | dist/ 17 | downloads/ 18 | eggs/ 19 | lib/ 20 | lib64/ 21 | parts/ 22 | sdist/ 23 | var/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | 46 | # Translations 47 | *.mo 48 | *.pot 49 | 50 | # Django stuff: 51 | *.log 52 | 53 | # Sphinx documentation 54 | docs/_build/ 55 | 56 | # PyBuilder 57 | target/ 58 | 59 | *.ini 60 | mock/ 61 | mock.sh 62 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # `ramses-example` 2 | Example of a Pyramid app using [ramses](https://github.com/ramses-tech/ramses) 3 | - example.raml: RAML specs 4 | - schemas/*.json: schemas and property definitions 5 | 6 | ## Installation 7 | ``` 8 | $ pip install -r requirements.txt 9 | $ cp local.ini.template local.ini 10 | $ nano local.ini 11 | ``` 12 | The setting `nefertari.engine` can be set to either `nefertari_mongodb` or `nefertari_sqla` 13 | 14 | ## Run 15 | ``` 16 | $ pserve local.ini 17 | ``` 18 | 19 | ## Login 20 | POST `/api/auth/login` 21 | 22 | ```json 23 | { 24 | "login": "", 25 | "password": "" 26 | } 27 | 28 | ``` 29 | or in the browser: 30 | ``` 31 | /api/auth/login?_m=POST&login=&password= 32 | ``` 33 | 34 | ## Add mock data 35 | ``` 36 | $ mkdir mock 37 | $ curl -o mock/Users.json https://raw.githubusercontent.com/ramses-tech/nefertari-example/master/mock/Users.json 38 | $ curl -o mock/Profiles.json https://raw.githubusercontent.com/ramses-tech/nefertari-example/master/mock/Profiles.json 39 | $ curl -o mock/Stories.json https://raw.githubusercontent.com/ramses-tech/nefertari-example/master/mock/Stories.json 40 | $ nefertari.post2api -f ./mock/Users.json -u http://localhost:6543/api/users 41 | $ nefertari.post2api -f ./mock/Profiles.json -u http://localhost:6543/api/users/{username}/profile 42 | $ nefertari.post2api -f ./mock/Stories.json -u http://localhost:6543/api/stories 43 | ``` 44 | NOTE: set auth = false in local.ini file before executing 45 | -------------------------------------------------------------------------------- /example.raml: -------------------------------------------------------------------------------- 1 | #%RAML 0.8 2 | --- 3 | title: Example REST API 4 | documentation: 5 | - title: Home 6 | content: | 7 | Welcome to the example API. 8 | baseUri: http://{host}:{port}/{version} 9 | version: v1 10 | mediaType: application/json 11 | protocols: [HTTP, HTTPS] 12 | securitySchemes: 13 | - x_token_auth: 14 | description: Authorization header token policy 15 | type: x-ApiKey 16 | - x_ticket_auth: 17 | description: Standard Pyramid Auth Ticket policy 18 | type: x-Ticket 19 | settings: 20 | secret: auth_tkt_secret 21 | hashalg: sha512 22 | cookie_name: ramses_auth_tkt 23 | http_only: true 24 | - item_owner_acl: 25 | description: ACL that allows everyone to read, authenticated to create and item owners to edit item 26 | type: x-ACL 27 | settings: 28 | collection: | 29 | allow g:admin all 30 | allow everyone view,options 31 | allow authenticated create 32 | item: | 33 | allow g:admin all 34 | allow {{item_owner}} view, update 35 | - user_self_acl: 36 | description: ACL that allows everyone to read, authenticated to create and owner to edit 37 | type: x-ACL 38 | settings: 39 | collection: | 40 | allow g:admin all 41 | allow everyone view,options 42 | item: | 43 | allow g:admin all 44 | allow everyone view,options 45 | allow {{user_self}} update 46 | - user_profile_acl: 47 | description: ACL gives all permissions to admins and profile's user 48 | type: x-ACL 49 | settings: 50 | collection: | 51 | allow g:admin all 52 | allow {{user_profile}} all 53 | item: | 54 | allow g:admin all 55 | allow {{user_profile}} all 56 | securedBy: [x_ticket_auth] 57 | 58 | /users: 59 | securedBy: [user_self_acl] 60 | displayName: All users 61 | get: 62 | description: Get all users 63 | post: 64 | description: Create a new user 65 | body: 66 | application/json: 67 | schema: !include schemas/user.json 68 | patch: 69 | description: Update multiple users 70 | head: 71 | description: Determine whether a given resource is available 72 | options: 73 | description: Retrieve the available HTTP verbs for a given resource 74 | 75 | /{username}: 76 | displayName: One user 77 | get: 78 | description: Get a particular user 79 | patch: 80 | put: 81 | description: Update a particular user 82 | delete: 83 | description: Delete a particular user 84 | 85 | /settings: 86 | displayName: User settings 87 | get: 88 | description: Get all settings of a particular user 89 | post: 90 | description: Change a user's settings 91 | 92 | /groups: 93 | displayName: User groups 94 | get: 95 | description: Get all groups of a particular user 96 | post: 97 | description: Change a user's groups 98 | 99 | /profile: 100 | securedBy: [user_profile_acl] 101 | displayName: User profile 102 | get: 103 | description: Get a user's profile 104 | post: 105 | description: Create a user's profile 106 | body: 107 | application/json: 108 | schema: !include schemas/profile.json 109 | patch: 110 | description: Update a user's profile 111 | 112 | /stories: 113 | securedBy: [item_owner_acl] 114 | displayName: All stories 115 | get: 116 | description: Get all stories 117 | post: 118 | description: Create a new story 119 | body: 120 | application/json: 121 | schema: !include schemas/story.json 122 | patch: 123 | description: Update multiple stories 124 | delete: 125 | description: Delete multiple stories 126 | head: 127 | description: Determine whether a given resource is available 128 | options: 129 | description: Retrieve the available HTTP verbs for a given resource 130 | 131 | /{id}: 132 | displayName: One story 133 | get: 134 | description: Get a particular story 135 | delete: 136 | description: Delete a particular story 137 | patch: 138 | put: 139 | description: Update a particular story 140 | head: 141 | description: Determine whether a given resource is available 142 | options: 143 | description: Retrieve the available HTTP verbs for a given resource 144 | -------------------------------------------------------------------------------- /local.ini.template: -------------------------------------------------------------------------------- 1 | [app:ramses_example] 2 | use = egg:ramses_example 3 | ramses.raml_schema = example.raml 4 | 5 | ; nefertari.engine = nefertari_mongodb 6 | nefertari.engine = nefertari_sqla 7 | 8 | auth = true 9 | auth_tkt_secret = verysecret 10 | enable_get_tunneling = true 11 | public_max_limit = 100 12 | database_acls = false 13 | 14 | system.user = system 15 | system.password = 123456 16 | system.email = user@domain.com 17 | 18 | # SQLA 19 | sqlalchemy.url = postgresql://localhost:5432/ramses_example 20 | 21 | # MongoDB settings 22 | mongodb.host = localhost 23 | mongodb.port = 27017 24 | mongodb.db = ramses_example 25 | 26 | # ElasticSearch 27 | elasticsearch.hosts = localhost:9200 28 | elasticsearch.sniff = false 29 | elasticsearch.index_name = ramses_example 30 | elasticsearch.index.disable = false 31 | elasticsearch.enable_refresh_query = true 32 | elasticsearch.enable_aggregations = true 33 | elasticsearch.enable_polymorphic_query = true 34 | 35 | # ramses_example 36 | host = localhost 37 | base_url = http://%(host)s 38 | 39 | # CORS 40 | cors.enable = false 41 | cors.allow_origins = %(base_url)s 42 | cors.allow_credentials = true 43 | 44 | request_timing.enable = true 45 | 46 | [composite:main] 47 | use = egg:Paste#urlmap 48 | /api/ = ramses_example 49 | 50 | [server:main] 51 | use = egg:waitress#main 52 | host = 0.0.0.0 53 | port = 6543 54 | threads = 3 55 | 56 | [loggers] 57 | keys = root, ramses_example, nefertari, ramses 58 | 59 | [handlers] 60 | keys = console 61 | 62 | [formatters] 63 | keys = generic 64 | 65 | [logger_root] 66 | level = INFO 67 | handlers = console 68 | 69 | [logger_ramses_example] 70 | level = INFO 71 | handlers = 72 | qualname = ramses_example 73 | 74 | [logger_nefertari] 75 | level = DEBUG 76 | handlers = 77 | qualname = nefertari 78 | 79 | [logger_ramses] 80 | level = DEBUG 81 | handlers = 82 | qualname = ramses 83 | 84 | [handler_console] 85 | class = StreamHandler 86 | args = (sys.stderr,) 87 | level = NOTSET 88 | formatter = generic 89 | 90 | [formatter_generic] 91 | format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(module)s.%(funcName)s: %(message)s 92 | -------------------------------------------------------------------------------- /ramses_example/__init__.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | from pyramid.config import Configurator 4 | from ramses import registry 5 | 6 | log = logging.getLogger(__name__) 7 | 8 | 9 | def my_is_admin(cls, user): 10 | """ Example of overriding """ 11 | log.info('Checking if user {} is admin'.format(user)) 12 | return 'admin' in user.groups 13 | registry.add('User.is_admin', classmethod(my_is_admin)) 14 | 15 | 16 | @registry.add 17 | def user_self(ace, request, obj): 18 | """ Give 'update' permission to user that is being created. """ 19 | from pyramid.security import Allow 20 | return [(Allow, str(obj.username), 'update')] 21 | 22 | 23 | @registry.add 24 | def user_profile(ace, request, obj): 25 | """ Give ALL_PERMISSIONS permission to profile's user. """ 26 | from pyramid.security import Allow, ALL_PERMISSIONS 27 | username = request.matchdict['users_username'] 28 | return [(Allow, str(username), ALL_PERMISSIONS)] 29 | 30 | 31 | @registry.add 32 | def item_owner(ace, request, obj): 33 | """ Give 'update' permission to item owner. """ 34 | from pyramid.security import Allow 35 | owner = obj.owner 36 | if hasattr(owner, 'username'): 37 | owner = owner.username 38 | if owner is not None: 39 | return [(Allow, str(owner), ['view','update'])] 40 | 41 | 42 | @registry.add 43 | def set_item_owner(event): 44 | """ Set owner of an item. """ 45 | user = getattr(event.view.request, 'user', None) 46 | if 'owner' not in event.fields and user is not None: 47 | event.set_field_value('owner', user) 48 | 49 | 50 | @registry.add 51 | def lowercase(**kwargs): 52 | """ Make :new_value: lowercase (and stripped) """ 53 | return (kwargs['new_value'] or '').lower().strip() 54 | 55 | 56 | @registry.add 57 | def encrypt(**kwargs): 58 | """ Crypt :new_value: if it's not crypted yet """ 59 | import cryptacular.bcrypt 60 | new_value = kwargs['new_value'] 61 | field = kwargs['field'] 62 | min_length = field.params['min_length'] 63 | if len(new_value) < min_length: 64 | raise ValueError( 65 | '`{}`: Value length must be more than {}'.format( 66 | field.name, field.params['min_length'])) 67 | 68 | crypt = cryptacular.bcrypt.BCRYPTPasswordManager() 69 | if new_value and not crypt.match(new_value): 70 | new_value = str(crypt.encode(new_value)) 71 | return new_value 72 | 73 | 74 | def main(global_config, **settings): 75 | config = Configurator(settings=settings) 76 | config.include('ramses') 77 | return config.make_wsgi_app() 78 | -------------------------------------------------------------------------------- /requirements.dev: -------------------------------------------------------------------------------- 1 | Paste==2.0.2 2 | pyramid==1.6.1 3 | waitress==0.8.9 4 | 5 | -e ../nefertari 6 | -e ../nefertari-mongodb 7 | -e ../nefertari-sqla 8 | -e ../ramses 9 | -e . 10 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | Paste==2.0.2 2 | pyramid==1.6.1 3 | waitress==0.8.9 4 | 5 | nefertari==0.7.0 6 | nefertari-mongodb==0.4.2 7 | nefertari-sqla==0.4.2 8 | ramses==0.5.3 9 | 10 | -e . 11 | -------------------------------------------------------------------------------- /schemas/profile.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "title": "Profile schema", 4 | "$schema": "http://json-schema.org/draft-04/schema", 5 | "properties": { 6 | "id": { 7 | "type": ["integer", "string"], 8 | "_db_settings": { 9 | "type": "id_field", 10 | "primary_key": true 11 | } 12 | }, 13 | "created_at": { 14 | "type": ["string", "null"], 15 | "format": "date-time", 16 | "_db_settings": { 17 | "type": "datetime", 18 | "default": "{{datetime.datetime.utcnow}}" 19 | } 20 | }, 21 | "updated_at": { 22 | "type": ["string", "null"], 23 | "format": "date-time", 24 | "_db_settings": { 25 | "type": "datetime", 26 | "onupdate": "{{datetime.datetime.utcnow}}" 27 | } 28 | }, 29 | "address": { 30 | "type": ["string", "null"], 31 | "_db_settings": { 32 | "type": "unicode_text" 33 | } 34 | }, 35 | "user_id": { 36 | "type": ["string", "null"], 37 | "_db_settings": { 38 | "type": "foreign_key", 39 | "ref_document": "User", 40 | "ref_column": "user.username", 41 | "ref_column_type": "string" 42 | } 43 | }, 44 | "user": { 45 | "type": ["string", "null"] 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /schemas/story.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "title": "Story schema", 4 | "$schema": "http://json-schema.org/draft-04/schema", 5 | "_auth_fields": ["id", "start_date", "due_date", "name", "description", "progress"], 6 | "_public_fields": ["id", "start_date", "due_date", "name"], 7 | "required": ["name", "description"], 8 | "_event_handlers": { 9 | "before_create": ["set_item_owner"] 10 | }, 11 | "properties": { 12 | "id": { 13 | "type": ["integer", "string"], 14 | "_db_settings": { 15 | "type": "id_field", 16 | "primary_key": true 17 | } 18 | }, 19 | "created_at": { 20 | "type": ["string", "null"], 21 | "format": "date-time", 22 | "_db_settings": { 23 | "type": "datetime", 24 | "default": "{{datetime.datetime.utcnow}}" 25 | } 26 | }, 27 | "updated_at": { 28 | "type": ["string", "null"], 29 | "format": "date-time", 30 | "_db_settings": { 31 | "type": "datetime", 32 | "onupdate": "{{datetime.datetime.utcnow}}" 33 | } 34 | }, 35 | "start_date": { 36 | "type": ["string", "null"], 37 | "format": "date-time", 38 | "_db_settings": { 39 | "type": "datetime" 40 | } 41 | }, 42 | "due_date": { 43 | "type": ["string", "null"], 44 | "format": "date-time", 45 | "_db_settings": { 46 | "type": "datetime" 47 | } 48 | }, 49 | "name": { 50 | "type": "string", 51 | "_db_settings": { 52 | "required": true, 53 | "type": "string" 54 | } 55 | }, 56 | "description": { 57 | "type": "string", 58 | "_db_settings": { 59 | "required": true, 60 | "type": "text" 61 | } 62 | }, 63 | "progress": { 64 | "type": ["number", "null"], 65 | "_db_settings": { 66 | "type": "float", 67 | "default": 0 68 | } 69 | }, 70 | "completed": { 71 | "type": ["boolean", "null"], 72 | "default": false, 73 | "_db_settings": { 74 | "type": "boolean", 75 | "default": false 76 | } 77 | }, 78 | "owner_id": { 79 | "type": ["string", "null"], 80 | "_db_settings": { 81 | "type": "foreign_key", 82 | "ref_document": "User", 83 | "ref_column": "user.username", 84 | "ref_column_type": "string" 85 | } 86 | }, 87 | "assignee_id": { 88 | "type": ["string", "null"], 89 | "_db_settings": { 90 | "type": "foreign_key", 91 | "ref_document": "User", 92 | "ref_column": "user.username", 93 | "ref_column_type": "string" 94 | } 95 | }, 96 | "signs_number": { 97 | "type": ["integer", "null"], 98 | "_db_settings": { 99 | "type": "big_integer" 100 | } 101 | }, 102 | "valid_date": { 103 | "type": ["string", "null"], 104 | "pattern": "^[0-9]{4}-(0?[0-9]|1[0-2])-(0?[0-9]|[1-2][0-9]|3[01])$", 105 | "_db_settings": { 106 | "type": "date" 107 | } 108 | }, 109 | "valid_time": { 110 | "type": ["string", "null"], 111 | "pattern": "^([01]?[0-9]|2[0-3]):[0-5][0-9](:[0-5][0-9])?$", 112 | "_db_settings": { 113 | "type": "time" 114 | } 115 | }, 116 | "reads": { 117 | "type": ["integer", "null"], 118 | "default": 0, 119 | "_db_settings": { 120 | "type": "integer", 121 | "default": 0 122 | } 123 | }, 124 | "rating": { 125 | "type": ["integer", "null"], 126 | "_db_settings": { 127 | "type": "small_integer" 128 | } 129 | }, 130 | "available_for": { 131 | "type": ["integer", "null"], 132 | "_db_settings": { 133 | "type": "interval" 134 | } 135 | }, 136 | "attachment": { 137 | "type": ["string", "null"], 138 | "_db_settings": { 139 | "type": "file" 140 | } 141 | }, 142 | "price": { 143 | "type": ["number", "null"], 144 | "_db_settings": { 145 | "type": "decimal", 146 | "scale": 10 147 | } 148 | }, 149 | "arbitrary_object": { 150 | "type": ["object", "null"], 151 | "_db_settings": { 152 | "type": "pickle" 153 | } 154 | }, 155 | "unicode_name": { 156 | "type": ["string", "null"], 157 | "_db_settings": { 158 | "type": "unicode" 159 | } 160 | }, 161 | "unicode_description": { 162 | "type": ["string", "null"], 163 | "_db_settings": { 164 | "type": "unicode_text" 165 | } 166 | }, 167 | "user": { 168 | "type": ["string", "null"] 169 | }, 170 | "assignee": { 171 | "type": ["string", "null"] 172 | } 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /schemas/user.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "title": "User schema", 4 | "$schema": "http://json-schema.org/draft-04/schema", 5 | "_auth_fields": ["username", "first_name", "last_name", "stories"], 6 | "_public_fields": ["username"], 7 | "_hidden_fields": ["password"], 8 | "_nested_relationships": ["profile"], 9 | "_auth_model": true, 10 | "required": ["username", "email", "password"], 11 | "properties": { 12 | "created_at": { 13 | "type": ["string", "null"], 14 | "format": "date-time", 15 | "_db_settings": { 16 | "type": "datetime", 17 | "default": "{{datetime.datetime.utcnow}}" 18 | } 19 | }, 20 | "updated_at": { 21 | "type": ["string", "null"], 22 | "format": "date-time", 23 | "_db_settings": { 24 | "type": "datetime", 25 | "onupdate": "{{datetime.datetime.utcnow}}" 26 | } 27 | }, 28 | "profile": { 29 | "type": ["integer", "string"], 30 | "_db_settings": { 31 | "type": "relationship", 32 | "document": "Profile", 33 | "backref_name": "user", 34 | "uselist": false 35 | } 36 | }, 37 | "stories": { 38 | "type": ["array", "null"], 39 | "items": { 40 | "oneOf": [ 41 | {"type": "string"}, 42 | {"type": "integer"} 43 | ] 44 | }, 45 | "_db_settings": { 46 | "type": "relationship", 47 | "document": "Story", 48 | "ondelete": "NULLIFY", 49 | "backref_name": "owner", 50 | "backref_ondelete": "NULLIFY", 51 | "foreign_keys": "Story.owner_id" 52 | } 53 | }, 54 | "assigned_stories": { 55 | "type": ["array", "null"], 56 | "items": { 57 | "oneOf": [ 58 | {"type": "string"}, 59 | {"type": "integer"} 60 | ] 61 | }, 62 | "_db_settings": { 63 | "type": "relationship", 64 | "document": "Story", 65 | "ondelete": "NULLIFY", 66 | "backref_name": "assignee", 67 | "backref_ondelete": "NULLIFY", 68 | "foreign_keys": "Story.assignee_id" 69 | } 70 | }, 71 | "username": { 72 | "type": "string", 73 | "minLength": 1, 74 | "maxLength": 50, 75 | "_db_settings": { 76 | "type": "string", 77 | "required": true, 78 | "min_length": 1, 79 | "max_length": 50, 80 | "unique": true, 81 | "primary_key": true 82 | }, 83 | "_processors": ["lowercase"] 84 | }, 85 | "email": { 86 | "type": "string", 87 | "format": "email", 88 | "_db_settings": { 89 | "type": "string", 90 | "required": true, 91 | "unique": true 92 | }, 93 | "_processors": ["lowercase"] 94 | }, 95 | "password": { 96 | "type": "string", 97 | "minLength": 3, 98 | "_db_settings": { 99 | "type": "string", 100 | "required": true, 101 | "min_length": 3 102 | }, 103 | "_processors": ["encrypt"] 104 | }, 105 | "first_name": { 106 | "type": ["string", "null"], 107 | "maxLength": 50, 108 | "default": "", 109 | "_db_settings": { 110 | "type": "string", 111 | "max_length": 50, 112 | "default": "" 113 | } 114 | }, 115 | "last_name": { 116 | "type": ["string", "null"], 117 | "maxLength": 50, 118 | "default": "", 119 | "_db_settings": { 120 | "type": "string", 121 | "max_length": 50, 122 | "default": "" 123 | } 124 | }, 125 | "last_login": { 126 | "type": ["string", "null"], 127 | "format": "date-time", 128 | "_db_settings": { 129 | "type": "datetime" 130 | } 131 | }, 132 | "groups": { 133 | "type": ["array", "null"], 134 | "items": { 135 | "type": "string", 136 | "enum": ["admin", "user"] 137 | }, 138 | "default": ["user"], 139 | "_db_settings": { 140 | "type": "list", 141 | "choices": ["admin", "user"], 142 | "default": ["user"], 143 | "item_type": "string" 144 | } 145 | }, 146 | "status": { 147 | "type": ["string", "null"], 148 | "enum": ["active", "inactive", "blocked"], 149 | "default": "active", 150 | "_db_settings": { 151 | "type": "choice", 152 | "choices": ["active", "inactive", "blocked"], 153 | "default": "active" 154 | } 155 | }, 156 | "settings": { 157 | "type": ["object", "null"], 158 | "_db_settings": { 159 | "type": "dict" 160 | } 161 | } 162 | } 163 | } -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | 3 | requires = ['pyramid'] 4 | 5 | setup(name='ramses_example', 6 | version='0.0.1', 7 | description='ramses_example', 8 | long_description='', 9 | classifiers=[ 10 | "Programming Language :: Python", 11 | "Programming Language :: Python :: 2", 12 | "Programming Language :: Python :: 2.7", 13 | "Programming Language :: Python :: 3", 14 | "Programming Language :: Python :: 3.4", 15 | "Framework :: Pyramid", 16 | "Topic :: Internet :: WWW/HTTP", 17 | "Topic :: Internet :: WWW/HTTP :: WSGI :: Application", 18 | ], 19 | author='', 20 | author_email='', 21 | url='', 22 | keywords='web pyramid pylons ramses', 23 | packages=find_packages(), 24 | include_package_data=True, 25 | zip_safe=False, 26 | install_requires=requires, 27 | tests_require=requires, 28 | test_suite="ramses_example", 29 | entry_points="""\ 30 | [paste.app_factory] 31 | main = ramses_example:main 32 | """) 33 | --------------------------------------------------------------------------------