smalltalk
/
osmo-st-all
Archived
1
0
Fork 0
This repository has been archived on 2022-02-17. You can view files and clone it, but cannot push or open issues or pull requests.
osmo-st-all/Core/Buildables/ILApplication.st

417 lines
10 KiB
Smalltalk
Raw Normal View History

2009-06-18 20:14:52 +00:00
"======================================================================
|
2009-11-20 13:33:26 +00:00
| Iliad.ILApplication class definition
2009-06-18 20:14:52 +00:00
|
======================================================================"
"======================================================================
|
| Copyright (c) 2008-2010
2009-06-18 20:14:52 +00:00
| Nicolas Petton <petton.nicolas@gmail.com>,
| Sébastien Audier <sebastien.audier@gmail.com>
|
| Some parts of this file reuse code from HttpView2 written by Giovanni
| Corriga and Göran Krampe http://www.squeaksource.com/HttpView2/
|
| This file is part of the Iliad framework.
|
| Permission is hereby granted, free of charge, to any person obtaining
| a copy of this software and associated documentation files (the
| 'Software'), to deal in the Software without restriction, including
| without limitation the rights to use, copy, modify, merge, publish,
| distribute, sublicense, and/or sell copies of the Software, and to
| permit persons to whom the Software is furnished to do so, subject to
| the following conditions:
|
| The above copyright notice and this permission notice shall be
| included in all copies or substantial portions of the Software.
|
| THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
| EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
| MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
| IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
| CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
| TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
| SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
======================================================================"
ILBuildable subclass: ILApplication [
| model originalRoutePosition page |
2009-06-18 20:14:52 +00:00
<category: 'Iliad-Core-Buildables'>
2010-02-13 20:23:35 +00:00
<comment: 'I am the Iliad implementation of an application.
I am is the root object of a buildable object tree. Applications have a
set of controllers, methods used to dispatch requests to the corresponding
sub-tree of buildable objects (oftenly a composition of stateful widgets).
2009-06-18 20:14:52 +00:00
You don''t have to bother about instantiating applications, the framework
will handle session and application instances.
2010-02-13 20:23:35 +00:00
2009-06-18 20:14:52 +00:00
In concrete subclasses, the class method #path should return the base path
(string) for the application.
2010-02-13 20:23:35 +00:00
""""""""""""""""""""""
" Controller methods "
""""""""""""""""""""""
Like widgets, I am stateful.
Unlike widgets I know how to dispatch a request with #dispatch :
the controller method corresponding to the url will be called.
2009-06-18 20:14:52 +00:00
Controller methods must:
2010-02-13 20:23:35 +00:00
- answer a buildable object (a block closure or an instance of ILWidget for example).
- be in the ''controllers'' method protocol (with the default selector filter)
2009-06-18 20:14:52 +00:00
The default controller method is #index.
2009-06-18 20:14:52 +00:00
2010-02-13 20:23:35 +00:00
""""""""""""""""""
" selectorFilter "
""""""""""""""""""
The class inst var <selectorFilter> is used to filter controller methods.
2010-02-13 20:23:35 +00:00
By default it allows all methods in the ''controllers'' protocol.
Alternatively, ou can override the class method #defaultSelectorFilter to supply
2009-06-18 20:14:52 +00:00
your own selectorFilter or plug it in using the class method #selectorFilter:
2010-02-13 20:23:35 +00:00
"""""""""""""""""""""
" Application modes "
"""""""""""""""""""""
Applications can be in one of the following modes: deployment, verbose or
2009-06-18 20:14:52 +00:00
debug. The default mode is verbose.
You can switch between modes with class methods in the <accessing-modes>
protocol.
When errors occur, the framework with handle them differently depending on
the application mode:
- in deployment mode, it will respond an error 500;
- in verbose mode, it will also respond an error 500, but with error details;
2010-02-13 20:23:35 +00:00
- in debug mode, a debugger window will be opened on the error;'>
2009-06-18 20:14:52 +00:00
2009-11-20 13:33:26 +00:00
ILApplication class [
2009-06-18 20:14:52 +00:00
| selectorFilter mode |
path [
2010-02-13 20:23:35 +00:00
"Base path of the application.
Override this method in concrete subclasses.
2009-06-18 20:14:52 +00:00
It should return a string"
<category: 'accessing'>
2009-07-09 22:53:31 +00:00
^''
2009-06-18 20:14:52 +00:00
]
selectorFilter [
<category: 'accessing'>
^selectorFilter ifNil: [self defaultSelectorFilter]
]
selectorFilter: aBlock [
<category: 'accessing'>
selectorFilter := aBlock
]
mode [
<category: 'accessing'>
^mode ifNil: [mode := #verbose]
]
mode: aSymbol [
<category: 'accessing'>
mode := aSymbol
]
verboseMode [
<category: 'accessing-modes'>
self mode: #verbose
]
debugMode [
<category: 'accessing-modes'>
self mode: #debug
]
deploymentMode [
<category: 'accessing-modes'>
self mode: #deployment
]
defaultSelectorFilter [
"Override this method to supply your own selectorFilter
or plug it in using #selectorFilter:"
<category: 'defaults'>
^[:selector |
(self canUnderstand: selector) and: [
(self
categoryOfElement: selector
inClassOrSuperclass: self) = 'controllers']]
2009-06-18 20:14:52 +00:00
]
categoryOfElement: aSelector inClassOrSuperclass: aClass [
2010-02-13 20:23:35 +00:00
"Find the first category of <aSelector> up the superclass chain."
2009-06-18 20:14:52 +00:00
<category: 'private'>
^aClass ifNotNil: [
^(aClass whichCategoryIncludesSelector: aSelector) ifNil: [
self
categoryOfElement: aSelector
inClassOrSuperclass: aClass superclass]]
]
isDebugMode [
<category: 'testing'>
^self mode = #debug
]
isVerboseMode [
<category: 'testing'>
^self mode = #verbose
]
isDeploymentMode [
<category: 'testing'>
^self isDebugMode not and: [
self isVerboseMode not]
]
]
model [
<category: 'accessing'>
^model
]
model: anObject [
<category: 'accessing'>
model := anObject
]
route [
<category: 'accessing'>
^self context route
]
hashRoute [
<category: 'accessing'>
^self context hashRoute
]
originalRoutePosition [
<category: 'accessing'>
^originalRoutePosition
]
page [
<category: 'accessing'>
^page
]
2009-06-18 20:14:52 +00:00
selectorFilter [
<category: 'accessing'>
^self class selectorFilter
]
2010-01-13 10:50:18 +00:00
widgetFor: aBuildable [
"Convenience method. This is useful for building anonymous widgets.
ex: myWidget := self widgetFor: [:e | e h1: 'Hello world!']"
<category: 'accessing'>
^ILPluggableWidget new
contentsBlock: aBuildable;
yourself
]
buildContents [
"Call #dispatch. A buildable is expected from #dispatch"
2009-06-18 20:14:52 +00:00
<category: 'building'>
^self newRootElement
build: self dispatch;
yourself
2009-06-18 20:14:52 +00:00
]
updatePage: aPage [
"Override to add elements to aPage.
2009-06-18 20:14:52 +00:00
super should always be called"
<category: 'building'>
aPage head javascript src: '/javascripts/jquery132min.js'.
aPage head javascript src: '/javascripts/no_conflict.js'.
aPage head javascript src: '/javascripts/iliad.js'.
2009-06-18 20:14:52 +00:00
]
allowedSelector: aSelector [
"Answer true if <aSelector> is ok to call from a URL.
Default implementation is to use the pluggable filter block."
<category: 'dispatching'>
^self selectorFilter copy value: aSelector
]
dispatch [
"Dispatch to correct controller method.
2009-06-18 20:14:52 +00:00
If dispatchOverride returns something
different from nil, consider it handled."
<category: 'dispatching'>
self setRoutePosition.
^self dispatchOverride ifNil: [
self dispatchOn: self route next]
]
dispatchOn: aMethod [
"Dispatch to correct method:
- If <aMethod> is empty we call #index
- If the selector is allowed to be executed then we just call it"
<category: 'dispatching'>
| m |
(aMethod isNil or: [aMethod isEmpty])
ifTrue: [m := #index]
ifFalse: [m := aMethod asSymbol].
(self allowedSelector: m)
ifTrue: [^self perform: m]
2009-11-20 13:33:26 +00:00
ifFalse: [ILDispatchError signal]
2009-06-18 20:14:52 +00:00
]
dispatchOverride [
"Handle special urls. Subclass implementors
should call super first and see if it was handled."
<category: 'dispatching'>
^nil
]
redirectTo: anUrlString [
"Abort all other request handling.
Redirect to anUrlString"
<category: 'redirecting'>
self session
redirectUrl: anUrlString;
redirect
]
redirectToIndex [
"Abort all other request handling.
Redirect to the index method of this class"
2009-06-18 20:14:52 +00:00
<category: 'redirecting'>
self redirectToLocal: 'index'
]
redirectToLocal: aString [
2009-06-18 20:14:52 +00:00
"Abort all other request handling.
Make a redirection to another controller method in this application"
2009-06-18 20:14:52 +00:00
<category: 'redirecting'>
self redirectTo: self originalUrl , '/' , aString
2009-06-18 20:14:52 +00:00
]
redirectToApplication: aClass [
"Abort all other request handling.
Redirect to the index method of <aClass>"
<category: 'redirecting'>
self redirectToApplication: aClass controller: ''
]
redirectToApplication: anApplicationClass controller: aString [
"Abort all other request handling.
Redirect to the controller named <aString> of <aClass>"
<category: 'redirecting'>
self redirectTo: (self context urlBuilder absolutePathFor: anApplicationClass), '/', aString
]
respond: aBlock [
"Abort all other request handling"
<category: 'redirecting'>
| response |
2009-11-20 13:33:26 +00:00
response := ILResponse new.
aBlock value: response.
self returnResponse: response
]
returnResponse: aResponse [
"Abort all other request handling"
<category: 'redirecting'>
ILResponseNotification new
response: aResponse;
signal
]
2009-06-18 20:14:52 +00:00
index [
"default view method"
<category: 'controllers'>
2009-06-18 20:14:52 +00:00
^[:e | ]
]
respondOn: aResponse [
<category: 'converting'>
page := self defaultPageClass new.
self updatePage: page.
page body build: self.
page respondOn: aResponse
2009-06-18 20:14:52 +00:00
]
defaultPageClass [
<category: 'defaults'>
2009-11-20 13:33:26 +00:00
^ILXHTMLPage
2009-06-18 20:14:52 +00:00
]
defaultRootElementClass [
<category: 'defaults'>
^ILXHTMLElement
]
2009-06-18 20:14:52 +00:00
isDebugMode [
<category: 'testing'>
^self class isDebugMode
]
isVerboseMode [
<category: 'testing'>
^self class isVerboseMode
]
isDeploymentMode [
<category: 'testing'>
^self class isDeploymentMode
]
2010-01-13 10:50:18 +00:00
newRootElement [
<category: 'private'>
^self defaultRootElementClass new
]
2009-06-18 20:14:52 +00:00
originalUrl [
"Answer an url String representing this application's
original position within the url of the request."
<category: 'private'>
| curPos result |
curPos := self route position.
self route position: originalRoutePosition.
result := self route currentPath.
^result
]
setRoutePosition [
<category: 'private'>
originalRoutePosition
ifNil: [originalRoutePosition := self route position]
ifNotNil: [self route position: originalRoutePosition]
]
]