“Views” are methods to encapsulate the logic responsible for...:
Commonly views will look like this:
from uiro.controller import BaseController
from uiro.view import view_config
class MyController(BaseController):
@view_config(method='get')
def get_view(self, request, context):
return 'Hello world!'
This MyController class is WSGI application returns response containing text ‘Hello world!’ in it’s body.
Views are logic about interfaces. Storing business logic in views is not recommended.
On Unit testing, you should test target logic without any other things. View methods must be applied view_config decorator, so It seems that it is difficult to test views without any decorators.
Don’t worry, Uiro provide a feature allowing you to write tests without decorators. You can test views like this:
>>> target = MyController().get_view
>>> assert target('dummy_request', 'dummy_context') == 'Hello world!'
Constructing a WSGI application from views. It checks which view should be called and dispatching. You don’t need to write any logic for controllers. All of them have been determined by Uiro framework. It is only used as a container for views like above example.
You can change logic in controller, specifying some values to interface provided by it’s own. The best example of this is resource, it is object to some resources on app determined by a request. For more detail, see Apply resources for views.
Note
The number of APIs provided by Controllers should be as little as possible. Uiro should not force users to remember a lot of APIs. it will be labor for users and generally it will be difficult to use. and what is worth, changing APIs may be hard work so Uiro will become inflexible increasingly.
For many cases, necessary data for one view can be determined by only a request. And It should be separate from views, to increase testability and readability:
It will be applied request object and you can write logic to collect data in it. It can be specified resource attribute in your Controller. A controller apply request to class in resource attribute and pass it to each view methods.
You can use this behavior like this:
from .models import Page
class PageResource(object):
def __init__(self, request):
self.request = request
@property
def page(self):
return Page.query.filter_by(id=request.matched_dict['id']).one()
class Controller(BaseController):
resource = PageResource
@view_config(method='get'):
def get_view(request, context):
return {'page': context.page}
Hereby, you separated collection logic and view (user interface). When you test each views, you can pass dummy request and context easily. you can focus writing tests for about interfaces.
Note
It’s better to store resource classes in a separated module to correspond to each models. Above example, The PageResource class in page.py seems better. Then of cause, you will store another logic for the Page model in page.py too.