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
|
|
|
|
|
|
|
|
|
|
======================================================================"
|
|
|
|
|
|
|
|
|
|
"======================================================================
|
|
|
|
|
|
|
2010-01-12 17:00: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.
|
|
|
|
|
|
|
|
|
|
|
======================================================================"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2010-01-12 17:00:52 +00:00
|
|
|
|
ILBuildable subclass: ILApplication [
|
2009-10-31 22:27:31 +00:00
|
|
|
|
| model originalRoutePosition page |
|
2009-06-18 20:14:52 +00:00
|
|
|
|
|
2009-06-25 20:44:59 +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 "
|
|
|
|
|
""""""""""""""""""""""
|
|
|
|
|
|
2010-01-12 17:00:52 +00:00
|
|
|
|
Like widgets, I am stateful.
|
|
|
|
|
Unlike widgets I know how to dispatch a request with #dispatch :
|
2009-10-01 12:15:14 +00:00
|
|
|
|
the controller method corresponding to the url will be called.
|
2009-06-18 20:14:52 +00:00
|
|
|
|
|
2010-01-12 17:00: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).
|
2009-10-01 12:15:14 +00:00
|
|
|
|
- be in the ''controllers'' method protocol (with the default selector filter)
|
2009-06-18 20:14:52 +00:00
|
|
|
|
|
2009-10-01 12:15:14 +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 "
|
|
|
|
|
""""""""""""""""""
|
|
|
|
|
|
2009-10-01 12:15:14 +00:00
|
|
|
|
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
|
2009-10-01 12:15:14 +00:00
|
|
|
|
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
|
|
|
|
|
]
|
|
|
|
|
|
2009-10-31 22:27:31 +00:00
|
|
|
|
hashRoute [
|
|
|
|
|
<category: 'accessing'>
|
|
|
|
|
^self context hashRoute
|
|
|
|
|
]
|
|
|
|
|
|
2009-09-11 08:52:54 +00:00
|
|
|
|
originalRoutePosition [
|
|
|
|
|
<category: 'accessing'>
|
|
|
|
|
^originalRoutePosition
|
|
|
|
|
]
|
|
|
|
|
|
2009-10-31 22:27:31 +00:00
|
|
|
|
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
|
2010-01-12 17:00:52 +00:00
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
buildContents [
|
2009-10-01 12:15:14 +00:00
|
|
|
|
"Call #dispatch. A buildable is expected from #dispatch"
|
2009-06-18 20:14:52 +00:00
|
|
|
|
<category: 'building'>
|
|
|
|
|
|
2010-01-12 17:00:52 +00:00
|
|
|
|
^self newRootElement
|
|
|
|
|
build: self dispatch;
|
|
|
|
|
yourself
|
2009-06-18 20:14:52 +00:00
|
|
|
|
]
|
|
|
|
|
|
2009-07-13 03:10:51 +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'>
|
|
|
|
|
|
2009-10-01 09:52:59 +00:00
|
|
|
|
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 [
|
2009-10-01 12:15:14 +00:00
|
|
|
|
"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.
|
2009-10-01 12:15:14 +00:00
|
|
|
|
Redirect to the index method of this class"
|
2009-06-18 20:14:52 +00:00
|
|
|
|
<category: 'redirecting'>
|
|
|
|
|
|
|
|
|
|
self redirectToLocal: 'index'
|
|
|
|
|
]
|
|
|
|
|
|
2009-10-01 12:15:14 +00:00
|
|
|
|
redirectToLocal: aString [
|
2009-06-18 20:14:52 +00:00
|
|
|
|
"Abort all other request handling.
|
2009-10-01 12:15:14 +00:00
|
|
|
|
Make a redirection to another controller method in this application"
|
2009-06-18 20:14:52 +00:00
|
|
|
|
<category: 'redirecting'>
|
2009-09-16 20:41:51 +00:00
|
|
|
|
|
2009-10-01 12:15:14 +00:00
|
|
|
|
self redirectTo: self originalUrl , '/' , aString
|
2009-06-18 20:14:52 +00:00
|
|
|
|
]
|
|
|
|
|
|
2009-09-16 20:41:51 +00:00
|
|
|
|
redirectToApplication: aClass [
|
|
|
|
|
"Abort all other request handling.
|
2009-10-01 12:15:14 +00:00
|
|
|
|
Redirect to the index method of <aClass>"
|
2009-09-16 20:41:51 +00:00
|
|
|
|
<category: 'redirecting'>
|
|
|
|
|
|
2009-10-01 12:15:14 +00:00
|
|
|
|
self redirectToApplication: aClass controller: ''
|
2009-09-16 20:41:51 +00:00
|
|
|
|
]
|
|
|
|
|
|
2009-10-01 12:15:14 +00:00
|
|
|
|
redirectToApplication: anApplicationClass controller: aString [
|
2009-09-16 20:41:51 +00:00
|
|
|
|
"Abort all other request handling.
|
2009-10-01 12:15:14 +00:00
|
|
|
|
Redirect to the controller named <aString> of <aClass>"
|
2009-09-16 20:41:51 +00:00
|
|
|
|
<category: 'redirecting'>
|
|
|
|
|
|
|
|
|
|
self redirectTo: (self context urlBuilder absolutePathFor: anApplicationClass), '/', aString
|
|
|
|
|
]
|
|
|
|
|
|
2009-10-01 09:52:59 +00:00
|
|
|
|
respond: aBlock [
|
|
|
|
|
"Abort all other request handling"
|
|
|
|
|
<category: 'redirecting'>
|
|
|
|
|
|
|
|
|
|
| response |
|
2009-11-20 13:33:26 +00:00
|
|
|
|
response := ILResponse new.
|
2009-10-01 09:52:59 +00:00
|
|
|
|
aBlock value: response.
|
|
|
|
|
self returnResponse: response
|
|
|
|
|
]
|
|
|
|
|
|
2010-01-28 17:27:15 +00:00
|
|
|
|
returnResponse: aResponse [
|
2009-10-01 09:52:59 +00:00
|
|
|
|
"Abort all other request handling"
|
|
|
|
|
<category: 'redirecting'>
|
2010-01-28 17:27:15 +00:00
|
|
|
|
|
|
|
|
|
ILResponseNotification new
|
|
|
|
|
response: aResponse;
|
|
|
|
|
signal
|
2009-10-01 09:52:59 +00:00
|
|
|
|
]
|
|
|
|
|
|
2009-06-18 20:14:52 +00:00
|
|
|
|
index [
|
|
|
|
|
"default view method"
|
2009-10-01 12:15:14 +00:00
|
|
|
|
<category: 'controllers'>
|
2009-06-18 20:14:52 +00:00
|
|
|
|
|
|
|
|
|
^[:e | ]
|
|
|
|
|
]
|
|
|
|
|
|
2009-06-22 00:32:14 +00:00
|
|
|
|
respondOn: aResponse [
|
|
|
|
|
<category: 'converting'>
|
2009-07-13 03:10:51 +00:00
|
|
|
|
page := self defaultPageClass new.
|
|
|
|
|
self updatePage: page.
|
2009-12-17 16:30:09 +00:00
|
|
|
|
page body build: self.
|
2009-07-13 03:10:51 +00:00
|
|
|
|
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
|
|
|
|
]
|
|
|
|
|
|
2010-01-12 17:00: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]
|
|
|
|
|
]
|
|
|
|
|
]
|