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 [
|
2010-10-08 18:18:48 +00:00
|
|
|
|
| model 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
|
|
|
|
In concrete subclasses, the class method #path should return the base path
|
|
|
|
|
(string) for the application.
|
|
|
|
|
|
2010-02-13 20:23:35 +00:00
|
|
|
|
|
2010-05-19 11:04:11 +00:00
|
|
|
|
"""""""""""""""""""""""""""
|
|
|
|
|
" Applications & UI state "
|
|
|
|
|
"""""""""""""""""""""""""""
|
|
|
|
|
|
|
|
|
|
You don''t have to bother about instantiating applications, the framework
|
|
|
|
|
will handle session and application instances. Application instances are stored
|
|
|
|
|
in sessions. Each session stores one instance of the same application class.
|
|
|
|
|
|
|
|
|
|
Root widgets should be stored in applications to keep their state across requests.
|
|
|
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
2010-03-17 12:18:49 +00:00
|
|
|
|
Alternatively, you can override the class method #defaultSelectorFilter to supply
|
2010-03-03 10:39:35 +00:00
|
|
|
|
your own selectorFilter or plug it in using the class method #selectorFilter:'>
|
2009-06-18 20:14:52 +00:00
|
|
|
|
|
2009-11-20 13:33:26 +00:00
|
|
|
|
ILApplication class [
|
2010-03-03 10:39:35 +00:00
|
|
|
|
| selectorFilter |
|
2009-06-18 20:14:52 +00:00
|
|
|
|
|
|
|
|
|
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
|
|
|
|
]
|
|
|
|
|
|
2010-10-08 14:53:31 +00:00
|
|
|
|
absolutePath [
|
|
|
|
|
<category: 'accessing'>
|
|
|
|
|
^String streamContents: [:stream |
|
|
|
|
|
(self path startsWith: '/') ifFalse: [stream nextPut: $/].
|
|
|
|
|
stream nextPutAll: self path]
|
|
|
|
|
]
|
|
|
|
|
|
2009-06-18 20:14:52 +00:00
|
|
|
|
selectorFilter [
|
|
|
|
|
<category: 'accessing'>
|
|
|
|
|
^selectorFilter ifNil: [self defaultSelectorFilter]
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
selectorFilter: aBlock [
|
|
|
|
|
<category: 'accessing'>
|
|
|
|
|
selectorFilter := aBlock
|
|
|
|
|
]
|
2010-03-03 10:39:35 +00:00
|
|
|
|
|
2009-06-18 20:14:52 +00:00
|
|
|
|
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]]
|
|
|
|
|
]
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
model [
|
|
|
|
|
<category: 'accessing'>
|
|
|
|
|
^model
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
model: anObject [
|
|
|
|
|
<category: 'accessing'>
|
|
|
|
|
model := anObject
|
|
|
|
|
]
|
|
|
|
|
|
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
|
|
|
|
]
|
|
|
|
|
|
2010-08-12 11:55:12 +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
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
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 dispatchOverride ifNil: [
|
2010-10-08 14:53:31 +00:00
|
|
|
|
self dispatchOn: self router controller]
|
2009-06-18 20:14:52 +00:00
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
]
|
|
|
|
|
|
2010-10-08 14:53:31 +00:00
|
|
|
|
updatePage: aPage [
|
|
|
|
|
"Override to add elements to aPage.
|
|
|
|
|
super should always be called"
|
|
|
|
|
<category: 'updating'>
|
|
|
|
|
|
2012-12-27 01:05:03 +00:00
|
|
|
|
aPage head javascript src: '/javascripts/jquery-1.8.3.min.js'.
|
2010-10-08 14:53:31 +00:00
|
|
|
|
aPage head javascript src: '/javascripts/no_conflict.js'.
|
|
|
|
|
aPage head javascript src: '/javascripts/iliad.js'.
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
updateFromRoute: aRoute [
|
2010-10-06 10:36:49 +00:00
|
|
|
|
<category: 'updating'>
|
|
|
|
|
"Override this method to update to state of the application
|
|
|
|
|
from the request url route.
|
|
|
|
|
|
|
|
|
|
This method will be called for each new request"
|
|
|
|
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
updateBaseUrl: anUrl [
|
|
|
|
|
<category: 'updating'>
|
|
|
|
|
"Update the base url used for the current context"
|
|
|
|
|
|
|
|
|
|
]
|
|
|
|
|
|
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.
|
2009-12-17 16:30:09 +00:00
|
|
|
|
page body build: self.
|
2010-06-12 14:02:02 +00:00
|
|
|
|
self updatePage: page.
|
2010-07-09 13:55:38 +00:00
|
|
|
|
self context builtWidgets do: [:each | each buildHead: page head].
|
2009-07-13 03:10:51 +00:00
|
|
|
|
page respondOn: aResponse
|
2009-06-18 20:14:52 +00:00
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
defaultPageClass [
|
|
|
|
|
<category: 'defaults'>
|
2010-05-25 10:18:56 +00:00
|
|
|
|
^ILHTMLPage
|
2009-06-18 20:14:52 +00:00
|
|
|
|
]
|
|
|
|
|
|
2010-03-02 17:24:12 +00:00
|
|
|
|
rootElementClass [
|
2010-01-12 17:00:52 +00:00
|
|
|
|
<category: 'defaults'>
|
2010-05-25 11:37:53 +00:00
|
|
|
|
^ILHTMLBuilderElement
|
2010-01-12 17:00:52 +00:00
|
|
|
|
]
|
|
|
|
|
|
2010-01-13 10:50:18 +00:00
|
|
|
|
newRootElement [
|
|
|
|
|
<category: 'private'>
|
2010-03-02 17:24:12 +00:00
|
|
|
|
^self rootElementClass new
|
2010-01-13 10:50:18 +00:00
|
|
|
|
]
|
2009-06-18 20:14:52 +00:00
|
|
|
|
]
|