sws 0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/doc/DOC.otl +34 -0
- data/doc/Makefile +13 -0
- data/doc/architecture.dia +0 -0
- data/doc/docbook/architecture.png +0 -0
- data/doc/docbook/concepts.docbook +474 -0
- data/doc/docbook/installation.docbook +57 -0
- data/doc/docbook/introduction.docbook +130 -0
- data/doc/docbook/sws_manual.docbook +35 -0
- data/doc/docbook/todo.docbook +38 -0
- data/doc/docbook/tutorial.docbook +594 -0
- data/examples/README +1 -0
- data/examples/addressbook/CardBrowse/CardBrowse.html +43 -0
- data/examples/addressbook/CardBrowse/CardBrowse.rb +65 -0
- data/examples/addressbook/CardBrowse/CardBrowse.sws +92 -0
- data/examples/addressbook/Login/LoginPage.html +12 -0
- data/examples/addressbook/Login/LoginPage.rb +19 -0
- data/examples/addressbook/Login/LoginPage.sws +15 -0
- data/examples/addressbook/README +1 -0
- data/examples/addressbook/addressbook.rb +70 -0
- data/examples/addressbook/application.yaml +8 -0
- data/examples/addressbook/db.yaml +7 -0
- data/examples/component_demo/CheckBoxDemo/CheckBoxDemo.html +11 -0
- data/examples/component_demo/CheckBoxDemo/CheckBoxDemo.rb +21 -0
- data/examples/component_demo/CheckBoxDemo/CheckBoxDemo.sws +25 -0
- data/examples/component_demo/ComponentDemo.rb +21 -0
- data/examples/component_demo/ConditionalDemo/ConditionalDemo.html +18 -0
- data/examples/component_demo/ConditionalDemo/ConditionalDemo.rb +2 -0
- data/examples/component_demo/ConditionalDemo/ConditionalDemo.sws +22 -0
- data/examples/component_demo/FileUploadDemo/FileUploadDemo.html +10 -0
- data/examples/component_demo/FileUploadDemo/FileUploadDemo.rb +9 -0
- data/examples/component_demo/FileUploadDemo/FileUploadDemo.sws +33 -0
- data/examples/component_demo/FormFieldsDemo/FormFieldsDemo.html +12 -0
- data/examples/component_demo/FormFieldsDemo/FormFieldsDemo.rb +21 -0
- data/examples/component_demo/FormFieldsDemo/FormFieldsDemo.sws +40 -0
- data/examples/component_demo/FormListsDemo/FormListsDemo.html +11 -0
- data/examples/component_demo/FormListsDemo/FormListsDemo.rb +37 -0
- data/examples/component_demo/FormListsDemo/FormListsDemo.sws +47 -0
- data/examples/component_demo/GenericDemo/GenericDemo.html +4 -0
- data/examples/component_demo/GenericDemo/GenericDemo.rb +2 -0
- data/examples/component_demo/GenericDemo/GenericDemo.sws +10 -0
- data/examples/component_demo/HyperlinkDemo/HyperlinkDemo.html +8 -0
- data/examples/component_demo/HyperlinkDemo/HyperlinkDemo.rb +20 -0
- data/examples/component_demo/HyperlinkDemo/HyperlinkDemo.sws +19 -0
- data/examples/component_demo/ImageLinkDemo/ImageLinkDemo.html +11 -0
- data/examples/component_demo/ImageLinkDemo/ImageLinkDemo.rb +2 -0
- data/examples/component_demo/ImageLinkDemo/ImageLinkDemo.sws +14 -0
- data/examples/component_demo/PageWrapper/PageWrapper.html +23 -0
- data/examples/component_demo/PageWrapper/PageWrapper.rb +2 -0
- data/examples/component_demo/PageWrapper/PageWrapper.sws +42 -0
- data/examples/component_demo/README +1 -0
- data/examples/component_demo/RepetitionDemo/RepetitionDemo.html +13 -0
- data/examples/component_demo/RepetitionDemo/RepetitionDemo.rb +19 -0
- data/examples/component_demo/RepetitionDemo/RepetitionDemo.sws +20 -0
- data/examples/component_demo/StringDemo/StringDemo.html +5 -0
- data/examples/component_demo/StringDemo/StringDemo.rb +16 -0
- data/examples/component_demo/StringDemo/StringDemo.sws +14 -0
- data/examples/component_demo/application.yaml +28 -0
- data/examples/component_demo/poweredby.jpg +0 -0
- data/examples/component_demo/style.css +1 -0
- data/examples/movies/Menu/Menu.html +3 -0
- data/examples/movies/Menu/Menu.rb +7 -0
- data/examples/movies/Menu/Menu.sws +7 -0
- data/examples/movies/MovieBrowse/MovieBrowse.html +68 -0
- data/examples/movies/MovieBrowse/MovieBrowse.rb +178 -0
- data/examples/movies/MovieBrowse/MovieBrowse.sws +127 -0
- data/examples/movies/README +1 -0
- data/examples/movies/UserBrowse/UserBrowse.html +50 -0
- data/examples/movies/UserBrowse/UserBrowse.rb +69 -0
- data/examples/movies/UserBrowse/UserBrowse.sws +49 -0
- data/examples/movies/application.yaml +11 -0
- data/examples/movies/da.rb +36 -0
- data/examples/movies/dbmovies.rb +44 -0
- data/examples/movies/frameworks/TestFramework/framework.yaml +4 -0
- data/examples/movies/frameworks/TestFramework/resources/im1.jpg +0 -0
- data/examples/movies/images/pbr1b.jpg +0 -0
- data/examples/movies/movies.rb +28 -0
- data/examples/movies/movies.sds +119 -0
- data/examples/movies/movies.sqlite +0 -0
- data/examples/movies/movies_mysql.sql +28 -0
- data/examples/movies/movies_postgres.sql +27 -0
- data/examples/movies/movies_sqlite.sql +28 -0
- data/lib/sws.rb +89 -0
- data/lib/sws/Core/components/CheckBox/CheckBox.api +5 -0
- data/lib/sws/Core/components/CheckBox/CheckBox.rb +45 -0
- data/lib/sws/Core/components/CheckBoxList/CheckBoxList.api +13 -0
- data/lib/sws/Core/components/CheckBoxList/CheckBoxList.rb +54 -0
- data/lib/sws/Core/components/Conditional/Conditional.api +3 -0
- data/lib/sws/Core/components/Conditional/Conditional.html +1 -0
- data/lib/sws/Core/components/Conditional/Conditional.rb +39 -0
- data/lib/sws/Core/components/Conditional/Conditional.sws +2 -0
- data/lib/sws/Core/components/Content/Content.rb +18 -0
- data/lib/sws/Core/components/ExceptionPage/ExceptionPage.html +13 -0
- data/lib/sws/Core/components/ExceptionPage/ExceptionPage.rb +18 -0
- data/lib/sws/Core/components/ExceptionPage/ExceptionPage.sws +16 -0
- data/lib/sws/Core/components/FileUpload/FileUpload.api +16 -0
- data/lib/sws/Core/components/FileUpload/FileUpload.rb +62 -0
- data/lib/sws/Core/components/Form/Form.api +9 -0
- data/lib/sws/Core/components/Form/Form.html +3 -0
- data/lib/sws/Core/components/Form/Form.rb +55 -0
- data/lib/sws/Core/components/Form/Form.sws +12 -0
- data/lib/sws/Core/components/GenericContainer/GenericContainer.api +10 -0
- data/lib/sws/Core/components/GenericContainer/GenericContainer.html +1 -0
- data/lib/sws/Core/components/GenericContainer/GenericContainer.rb +39 -0
- data/lib/sws/Core/components/GenericContainer/GenericContainer.sws +12 -0
- data/lib/sws/Core/components/GenericElement/GenericElement.api +10 -0
- data/lib/sws/Core/components/GenericElement/GenericElement.rb +34 -0
- data/lib/sws/Core/components/HiddenField/HiddenField.api +7 -0
- data/lib/sws/Core/components/HiddenField/HiddenField.rb +37 -0
- data/lib/sws/Core/components/Hyperlink/Hyperlink.api +13 -0
- data/lib/sws/Core/components/Hyperlink/Hyperlink.html +1 -0
- data/lib/sws/Core/components/Hyperlink/Hyperlink.rb +102 -0
- data/lib/sws/Core/components/Hyperlink/Hyperlink.sws +12 -0
- data/lib/sws/Core/components/Image/Image.api +11 -0
- data/lib/sws/Core/components/Image/Image.rb +49 -0
- data/lib/sws/Core/components/ImageButton/ImageButton.api +16 -0
- data/lib/sws/Core/components/ImageButton/ImageButton.rb +76 -0
- data/lib/sws/Core/components/Link/Link.api +11 -0
- data/lib/sws/Core/components/Link/Link.rb +39 -0
- data/lib/sws/Core/components/PasswordField/PasswordField.api +7 -0
- data/lib/sws/Core/components/PasswordField/PasswordField.rb +41 -0
- data/lib/sws/Core/components/RadioButton/RadioButton.api +7 -0
- data/lib/sws/Core/components/RadioButton/RadioButton.rb +44 -0
- data/lib/sws/Core/components/RadioButtonList/RadioButtonList.api +20 -0
- data/lib/sws/Core/components/RadioButtonList/RadioButtonList.rb +76 -0
- data/lib/sws/Core/components/Repetition/Repetition.api +10 -0
- data/lib/sws/Core/components/Repetition/Repetition.html +1 -0
- data/lib/sws/Core/components/Repetition/Repetition.rb +137 -0
- data/lib/sws/Core/components/Repetition/Repetition.sws +2 -0
- data/lib/sws/Core/components/ResetButton/ResetButton.api +5 -0
- data/lib/sws/Core/components/ResetButton/ResetButton.rb +28 -0
- data/lib/sws/Core/components/Select/Select.api +24 -0
- data/lib/sws/Core/components/Select/Select.rb +57 -0
- data/lib/sws/Core/components/String/String.api +5 -0
- data/lib/sws/Core/components/String/String.rb +28 -0
- data/lib/sws/Core/components/SubmitButton/SubmitButton.api +6 -0
- data/lib/sws/Core/components/SubmitButton/SubmitButton.rb +53 -0
- data/lib/sws/Core/components/TextArea/TextArea.api +9 -0
- data/lib/sws/Core/components/TextArea/TextArea.rb +28 -0
- data/lib/sws/Core/components/TextField/TextField.api +9 -0
- data/lib/sws/Core/components/TextField/TextField.rb +46 -0
- data/lib/sws/Core/framework.yaml +25 -0
- data/lib/sws/JSComponents/components/JSMenu/JSMenu.api +5 -0
- data/lib/sws/JSComponents/components/JSMenu/JSMenu.html +58 -0
- data/lib/sws/JSComponents/components/JSMenu/JSMenu.rb +34 -0
- data/lib/sws/JSComponents/components/JSMenu/JSMenu.sws +37 -0
- data/lib/sws/JSComponents/framework.yaml +3 -0
- data/lib/sws/adaptor.rb +334 -0
- data/lib/sws/application.rb +604 -0
- data/lib/sws/component.rb +656 -0
- data/lib/sws/cookie.rb +27 -0
- data/lib/sws/direct_action.rb +38 -0
- data/lib/sws/extensions.rb +49 -0
- data/lib/sws/parsers.rb +374 -0
- data/lib/sws/request.rb +308 -0
- data/lib/sws/response.rb +70 -0
- data/lib/sws/session.rb +195 -0
- data/lib/sws/slot.rb +198 -0
- metadata +263 -0
|
@@ -0,0 +1,656 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
module SWS
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
# Represents a HTML page or its part. Probably most important class of SWS -
|
|
7
|
+
# it is used to build pages and to create reusable parts. It consists of a few
|
|
8
|
+
# files, which define the component object - Ruby class file (.rb), HTML
|
|
9
|
+
# template (.html), slot definition file (.api) and bindings file (.sws).
|
|
10
|
+
class Component
|
|
11
|
+
|
|
12
|
+
# Request object the receiver is handling
|
|
13
|
+
attr_reader :request
|
|
14
|
+
|
|
15
|
+
# The Component up the hierarchy, eg. for SubmitButton it can be Form. For
|
|
16
|
+
# the topmost component see #page
|
|
17
|
+
attr_reader :parent
|
|
18
|
+
|
|
19
|
+
# Array of components contained within the receiver. Opposite to #parent
|
|
20
|
+
attr_reader :subcomponents
|
|
21
|
+
|
|
22
|
+
# Array of Slot objects defined for the receiver
|
|
23
|
+
attr_accessor :slots
|
|
24
|
+
|
|
25
|
+
# Method of the receiver that will be called during #perform_action phase
|
|
26
|
+
attr_accessor :method_to_call
|
|
27
|
+
|
|
28
|
+
# Component that defines bindings for the receiver. Usually parent, but not
|
|
29
|
+
# always.
|
|
30
|
+
attr_accessor :definition_component
|
|
31
|
+
|
|
32
|
+
# Name of the receiver
|
|
33
|
+
attr_accessor :name
|
|
34
|
+
|
|
35
|
+
# The Request (HTTP) parameters that conform to the receiver
|
|
36
|
+
attr_accessor :parameters
|
|
37
|
+
|
|
38
|
+
# HTML tag attributes
|
|
39
|
+
attr_accessor :html_attrs
|
|
40
|
+
|
|
41
|
+
# Action subcomponents for page
|
|
42
|
+
attr_reader :action_components
|
|
43
|
+
|
|
44
|
+
# Number of requests processed by this component - necessary when handling
|
|
45
|
+
# backtracking
|
|
46
|
+
attr_reader :request_number
|
|
47
|
+
|
|
48
|
+
# Character encoding - defaults to Application.default_encoding
|
|
49
|
+
attr_accessor :encoding
|
|
50
|
+
|
|
51
|
+
attr_accessor :tokens
|
|
52
|
+
# Cache for component filenames
|
|
53
|
+
@@component_infos = Hash.new do |hash,component|
|
|
54
|
+
hash[component] = Application.instance.get_component( component )
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
#List of slots synchronized with instance variables
|
|
58
|
+
@@synchronized_slots = Hash.new do |hash,component|
|
|
59
|
+
hash[component] = Array.new
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Create new Component. If you want to create new page (toplevel
|
|
63
|
+
# component), the only argument is a Request object. If you want to create
|
|
64
|
+
# another component (which you shouldn't need to), the arguments are:
|
|
65
|
+
# named of the component, Request object, parent component and slots Hash
|
|
66
|
+
def initialize ( request, name, parent, slots )
|
|
67
|
+
|
|
68
|
+
#use this if you are creating new page (no slots, parent or name)...
|
|
69
|
+
# TODO: create child class Page
|
|
70
|
+
if ( parent == nil)
|
|
71
|
+
initialize_page( request )
|
|
72
|
+
#...and this when creating new component (but in such case Component.create is probably better)
|
|
73
|
+
else
|
|
74
|
+
initialize_component( request, name, parent, slots )
|
|
75
|
+
end
|
|
76
|
+
#common initialization for both "constructors"
|
|
77
|
+
initialize_common()
|
|
78
|
+
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
protected
|
|
83
|
+
def initialize_common ()
|
|
84
|
+
|
|
85
|
+
@parameters = Hash.new
|
|
86
|
+
#TODO: reconsider if necessary - is initialized in remove_subcomponents anyway
|
|
87
|
+
#(which is always called when parsing component definition)
|
|
88
|
+
@subcomponents = Array.new
|
|
89
|
+
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def initialize_page ( request = nil )
|
|
94
|
+
|
|
95
|
+
@request = request
|
|
96
|
+
@request_number = 0
|
|
97
|
+
@action_components = Hash.new
|
|
98
|
+
@request_subcomponents = Array.new
|
|
99
|
+
@encoding = app().default_encoding
|
|
100
|
+
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def initialize_component ( request, name, parent, slots )
|
|
105
|
+
|
|
106
|
+
@request = request
|
|
107
|
+
@slots = slots
|
|
108
|
+
@parent = parent
|
|
109
|
+
@name = name
|
|
110
|
+
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
public
|
|
115
|
+
|
|
116
|
+
# Defines a slot (symbol or name) that should be synchronized with instance
|
|
117
|
+
# variable of the same name
|
|
118
|
+
def Component.synchronize_slot ( *args )
|
|
119
|
+
args.each { |slot_name| @@synchronized_slots[self.to_s] << slot_name.to_s }
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# Returns the name of the instance variable the slot should be synchronized
|
|
123
|
+
# with or nil if it should not be synchronized
|
|
124
|
+
def synchronize_slot? ( slot_name )
|
|
125
|
+
return @@synchronized_slots[self.class.to_s].include?( slot_name.to_s )
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# Synchronizes variable with slots
|
|
129
|
+
def synchronize_slots ()
|
|
130
|
+
|
|
131
|
+
@@synchronized_slots[self.class.to_s].each do |slot_name|
|
|
132
|
+
slot = @slots[slot_name]
|
|
133
|
+
unless slot
|
|
134
|
+
raise NameError.new( "Synchronized slot '#{slot_name}' not found in #{self}" )
|
|
135
|
+
else
|
|
136
|
+
# Slots are synchronized in Slot.value, so it's enough to call it
|
|
137
|
+
slot.value
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# Returns filename for HTML template of this component
|
|
144
|
+
def template_filename ()
|
|
145
|
+
return @@component_infos[self.class.to_s].template
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
# Returns filename for component definition (.sws file)
|
|
150
|
+
def definition_filename ()
|
|
151
|
+
return @@component_infos[self.class.to_s].definition
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
# Return filename for slot definition (.api file)
|
|
156
|
+
def api_filename ()
|
|
157
|
+
return @@component_infos[self.class.to_s].api
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
# Shortcut for Application instance
|
|
162
|
+
def app ()
|
|
163
|
+
return Application.instance
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
# Session object accessor
|
|
168
|
+
def session ()
|
|
169
|
+
return @request.session
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
# Returns most top-level component
|
|
174
|
+
def page ()
|
|
175
|
+
|
|
176
|
+
return self if ( @parent == nil )
|
|
177
|
+
|
|
178
|
+
component = @parent
|
|
179
|
+
while ( component.parent != nil )
|
|
180
|
+
component = component.parent
|
|
181
|
+
end
|
|
182
|
+
return component
|
|
183
|
+
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def set_request_subcomponents ()
|
|
188
|
+
$log_sws_component.debug( "#{self.class.name} #{object_id}: Storing subcomponents for request #{@request_number}" )
|
|
189
|
+
@request_subcomponents [@request_number] = @subcomponents
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
# A string to be added to URL to indicate this component will handle the
|
|
193
|
+
# request
|
|
194
|
+
def url_string
|
|
195
|
+
return "#{object_id}.#{@request_number}"
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
# Return new component of that name for given request. Can be used instead
|
|
199
|
+
# of constructor if you have class_name for the component as string and
|
|
200
|
+
# not the Class object itself - this method is used for components in order
|
|
201
|
+
# to require them dynamically.
|
|
202
|
+
def Component.create ( class_name, request, name = nil, parent = nil, slots = Hash.new )
|
|
203
|
+
|
|
204
|
+
klass = @@component_infos[class_name].component_class
|
|
205
|
+
|
|
206
|
+
# TODO: why was this here?
|
|
207
|
+
#name = class_name if ( name == nil )
|
|
208
|
+
component = klass.new( request, name, parent, slots )
|
|
209
|
+
return component
|
|
210
|
+
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
# Clears subcomponents array
|
|
215
|
+
def remove_subcomponents ()
|
|
216
|
+
@subcomponents.clear()
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
# Main component procedure - performs all phases of request handling.
|
|
220
|
+
# Returns component object which will process next request
|
|
221
|
+
def process_request ( request, request_number = nil )
|
|
222
|
+
|
|
223
|
+
# TODO: check here if backtracked and omit some initial phases if so
|
|
224
|
+
@request = request
|
|
225
|
+
$log_sws_component.debug( "Request number in process_request: #{request_number}" )
|
|
226
|
+
request_number ||= @request_number
|
|
227
|
+
$log_sws_component.debug( "#{self.class.name} #{object_id}: Subcomponents for request #{request_number}" )
|
|
228
|
+
@subcomponents = @request_subcomponents[request_number] || Array.new
|
|
229
|
+
unless request_number == @request_number
|
|
230
|
+
$log_sws_component.debug( "---------BACKTRACK from #{@request_number} to #{request_number}" )
|
|
231
|
+
end
|
|
232
|
+
@request_number += 1
|
|
233
|
+
$log_sws_component.debug( "#{self.class.name} #{object_id}: request number is now #{@request_number}" )
|
|
234
|
+
awake()
|
|
235
|
+
process_parameters()
|
|
236
|
+
new_component = perform_action() || self
|
|
237
|
+
|
|
238
|
+
# If below is true variable new_component is used as response.
|
|
239
|
+
# It replaces standard return [SWS::Component, SWS::Response]
|
|
240
|
+
# with [SWS::Response, nil].
|
|
241
|
+
if( new_component.instance_of?( SWS::Response ) )
|
|
242
|
+
return new_component, nil
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
response = Response.new( request )
|
|
246
|
+
@subcomponents = Array.new
|
|
247
|
+
new_component.create_component_tree()
|
|
248
|
+
new_component.set_request_subcomponents()
|
|
249
|
+
new_component.append_to_response( response )
|
|
250
|
+
|
|
251
|
+
response.cookies << @request.session.to_cookie
|
|
252
|
+
response.headers["Content-type"] = "text/html;charset=#{@encoding}"
|
|
253
|
+
#response.headers["Pragma"] = "no-cache"
|
|
254
|
+
#response.headers["Expires"] = "0"
|
|
255
|
+
#response.headers["Cache-control"] = "private, no-cache, no-store, must-revalidate, max-age = 0"
|
|
256
|
+
|
|
257
|
+
sleep()
|
|
258
|
+
return new_component,response
|
|
259
|
+
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
# A generic method used for per-request initialization of the component.
|
|
264
|
+
# Called at the very begining of response process handling . Default
|
|
265
|
+
# implementation does recursively nothing :) - that is, it calls the
|
|
266
|
+
# awake() method for all subcomponents and their subcomponents and
|
|
267
|
+
# their...
|
|
268
|
+
def awake ()
|
|
269
|
+
@subcomponents.each { |com| com.awake() }
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
# A generic method used for per-request deinitialization of the component.
|
|
274
|
+
# Called at the very end of handling response process. Default
|
|
275
|
+
# implementation does recursively nothing :) - that is, it calls the
|
|
276
|
+
# sleep() method for all subcomponents and their subcomponents and
|
|
277
|
+
# their...
|
|
278
|
+
def sleep ()
|
|
279
|
+
@subcomponents.each { |com| com.sleep() }
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
# Takes values from request (HTTP parameters) and binds them to slots.
|
|
284
|
+
# Component to bind to is determined by splitting the parameter name. Each
|
|
285
|
+
# parameter is passed to #tokenize_binding( key, value ) method. After
|
|
286
|
+
# processing the parameters #process_bindings() is called.
|
|
287
|
+
def process_parameters ()
|
|
288
|
+
|
|
289
|
+
@request.params.each do |key,value|
|
|
290
|
+
stripped_key = key.sub( /^[^.]*?\./,"" )
|
|
291
|
+
$log_sws_component.debug( "Received key #{key}, stripped #{stripped_key} with value #{value}" )
|
|
292
|
+
tokenize_binding( stripped_key, value )
|
|
293
|
+
end
|
|
294
|
+
process_bindings()
|
|
295
|
+
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
# Takes parameters and binds them to slots - recursively for
|
|
300
|
+
# subcomponents. Default implementation (overriden in several Component
|
|
301
|
+
# subclasses) just calls #update_binding( key,value )
|
|
302
|
+
def process_bindings ()
|
|
303
|
+
|
|
304
|
+
@subcomponents.each do |subcomponent|
|
|
305
|
+
subcomponent.process_bindings()
|
|
306
|
+
end
|
|
307
|
+
@parameters.each { |key,value| update_binding( key,value ) }
|
|
308
|
+
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
# Updates @parameters Hash or calls the same method recursively on proper
|
|
313
|
+
# subcomponent (the name of which is determined by the parameter name to
|
|
314
|
+
# the first dot).
|
|
315
|
+
def tokenize_binding ( key,value )
|
|
316
|
+
|
|
317
|
+
if ( md = /^([^.]*?)\.(.*)$/.match( key ) ) #contains dot - extract component name and rest of binding
|
|
318
|
+
#TODO: what if the same component is present twice in template?
|
|
319
|
+
#but probably using form elements twice is not a good idea :)
|
|
320
|
+
component = subcomponent_for_name( md[1] )
|
|
321
|
+
if ( component == nil )
|
|
322
|
+
$log_sws_component.debug( "Ignoring key #{key}, component #{self}" )
|
|
323
|
+
else
|
|
324
|
+
component.tokenize_binding( md[2],value )
|
|
325
|
+
end
|
|
326
|
+
else #no dot - only slot name left - parameter belongs to self
|
|
327
|
+
@parameters[key] = value
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
end
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
# Updates binding described by key to value. Default implementation
|
|
334
|
+
# (often overriden in specific Component subclasses to perform custom
|
|
335
|
+
# initialization) just updates proper Slot value
|
|
336
|
+
def update_binding ( key, value )
|
|
337
|
+
|
|
338
|
+
slot = @slots[key]
|
|
339
|
+
$log_sws_component.debug( "slot #{key}: binding #{slot}, component: #{self}" )
|
|
340
|
+
if slot
|
|
341
|
+
slot.value = value
|
|
342
|
+
else
|
|
343
|
+
# This happens in cases like submitting INPUT type=image in Firefox,
|
|
344
|
+
# which (besides usual name.x and name.y parameters) additionaly submits
|
|
345
|
+
# "name" parameter (without any suffix) :(. So in order to avoid
|
|
346
|
+
# exception we need to ignore the parameter. TODO: refactor the whole
|
|
347
|
+
# parameter processing logic, as this workaround introduces some
|
|
348
|
+
# overhead.
|
|
349
|
+
subcomponent = subcomponent_for_name( key )
|
|
350
|
+
if subcomponent
|
|
351
|
+
$log_sws_component.debug( "Ignoring parameter #{key} with value #{value} as it refers directly to subcomponent of component #{self}" )
|
|
352
|
+
else
|
|
353
|
+
$log_sws_component.debug( "Ignoring bogus parameter #{key} with value #{value} for component #{self}" )
|
|
354
|
+
end
|
|
355
|
+
end
|
|
356
|
+
|
|
357
|
+
end
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
# Returns subcomponent of the receiver for given name, or nil if not found
|
|
361
|
+
def subcomponent_for_name ( name )
|
|
362
|
+
return @subcomponents.find { |subcomponent| subcomponent.name == name }
|
|
363
|
+
end
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
# Performs the selected action recursively on all subcomponents. This is
|
|
367
|
+
# the phase of response handling process where the topmost component can
|
|
368
|
+
# change - it is changed to the return value of first action that returns
|
|
369
|
+
# other value than nil
|
|
370
|
+
def perform_action ()
|
|
371
|
+
|
|
372
|
+
next_page = nil
|
|
373
|
+
@subcomponents.each do |subcomponent|
|
|
374
|
+
next_page = subcomponent.perform_action()
|
|
375
|
+
$log_sws_component.debug( "PERFORM: #{next_page} for component #{subcomponent}" )
|
|
376
|
+
return next_page if ( next_page )
|
|
377
|
+
end
|
|
378
|
+
return next_page
|
|
379
|
+
|
|
380
|
+
end
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
# Recursively generates HTML code for all subcomponents and adds it to the
|
|
384
|
+
# Response object
|
|
385
|
+
def append_to_response ( response )
|
|
386
|
+
|
|
387
|
+
@subcomponents.each do |child|
|
|
388
|
+
child.append_to_response( response )
|
|
389
|
+
end
|
|
390
|
+
|
|
391
|
+
end
|
|
392
|
+
|
|
393
|
+
|
|
394
|
+
# Parses template file for the receiver and uses generated token tree to
|
|
395
|
+
# create component tree. Is called even if the same component is still used
|
|
396
|
+
# after #perform_action() (but in this case it has old subcomponents removed
|
|
397
|
+
# before)
|
|
398
|
+
def create_component_tree ()
|
|
399
|
+
|
|
400
|
+
$log_sws_component.debug( "Creating component tree for #{self}" )
|
|
401
|
+
|
|
402
|
+
if ( content? ) # If the receiver is Content, expand content tokens
|
|
403
|
+
definition_component.tokens.each { |token| @parent.expand_token( token ) }
|
|
404
|
+
else
|
|
405
|
+
template_parser = TemplateParser.new( template_filename() )
|
|
406
|
+
root_tokens = template_parser.parse()
|
|
407
|
+
root_tokens.each { |token| expand_token( token ) }
|
|
408
|
+
end
|
|
409
|
+
|
|
410
|
+
end
|
|
411
|
+
|
|
412
|
+
|
|
413
|
+
protected
|
|
414
|
+
|
|
415
|
+
# Strongly coupled with #create_component_tree - expands a token: determines
|
|
416
|
+
# its type and creates coresponding component object
|
|
417
|
+
def expand_token ( token )
|
|
418
|
+
|
|
419
|
+
$log_sws_component.debug( "expanding #{token}, #{token.text?}, #{token.data.nil?}" )
|
|
420
|
+
|
|
421
|
+
if ( token.comment? )
|
|
422
|
+
|
|
423
|
+
$log_sws_component.debug( "Comment: #{token.data}" )
|
|
424
|
+
# TODO: comments should be appended conditionally
|
|
425
|
+
append_text_component( token.data )
|
|
426
|
+
|
|
427
|
+
elsif ( token.text? )
|
|
428
|
+
|
|
429
|
+
$log_sws_component.debug( "Text: #{token.data}" )
|
|
430
|
+
append_text_component( token.data )
|
|
431
|
+
|
|
432
|
+
elsif ( token.tag? )
|
|
433
|
+
|
|
434
|
+
if ( token.standalone? )
|
|
435
|
+
append_text_component( token.data )
|
|
436
|
+
else
|
|
437
|
+
$log_sws_component.debug( "Tag - container" )
|
|
438
|
+
append_text_component( token.data ) # opening tag
|
|
439
|
+
token.children.each { |child| expand_token( child ) }
|
|
440
|
+
append_text_component( "</#{token.name}>" ) # closing tag
|
|
441
|
+
end
|
|
442
|
+
|
|
443
|
+
elsif ( token.sws? )
|
|
444
|
+
|
|
445
|
+
child_component = DefinitionParser.instance.component_for_token_with_parent( token,self )
|
|
446
|
+
$log_sws_component.debug( "Added child #{child_component.name} for component #{self}" )
|
|
447
|
+
if ( child_component.container? )
|
|
448
|
+
$log_sws_component.debug( "found container component: #{token.children}" )
|
|
449
|
+
child_component.tokens = token.children
|
|
450
|
+
child_component.create_component_tree()
|
|
451
|
+
elsif ( child_component.content? )
|
|
452
|
+
$log_sws_component.debug( "found content component" )
|
|
453
|
+
child_component.create_component_tree()
|
|
454
|
+
end
|
|
455
|
+
@subcomponents << child_component
|
|
456
|
+
|
|
457
|
+
end
|
|
458
|
+
|
|
459
|
+
$log_sws_component.debug( "closing #{token}, #{token.text?}" )
|
|
460
|
+
|
|
461
|
+
end
|
|
462
|
+
|
|
463
|
+
|
|
464
|
+
# Appends a TextComponent to the component tree
|
|
465
|
+
def append_text_component ( data )
|
|
466
|
+
|
|
467
|
+
unless ( data )
|
|
468
|
+
raise( "NULL text component!!!!!!" )
|
|
469
|
+
end
|
|
470
|
+
|
|
471
|
+
child_component = TextComponent.new( @request,nil,self,nil )
|
|
472
|
+
child_component.content = data
|
|
473
|
+
@subcomponents << child_component
|
|
474
|
+
|
|
475
|
+
end
|
|
476
|
+
|
|
477
|
+
|
|
478
|
+
# Returns "other_tag_string" and html_attrs in form of tag attributes
|
|
479
|
+
def generic_attr_string ()
|
|
480
|
+
|
|
481
|
+
result = ""
|
|
482
|
+
other_tag_slot = @slots["other_tag_string"]
|
|
483
|
+
if ( other_tag_slot && other_tag_slot.bound_string )
|
|
484
|
+
result << " #{other_tag_slot.value()}"
|
|
485
|
+
end
|
|
486
|
+
if ( @html_attrs )
|
|
487
|
+
@html_attrs.each_pair { |name,value| result << " #{name}=\"#{value}\"" }
|
|
488
|
+
end
|
|
489
|
+
return result
|
|
490
|
+
|
|
491
|
+
end
|
|
492
|
+
|
|
493
|
+
|
|
494
|
+
# Returns HTML tag attribute string for those parameters, which are the
|
|
495
|
+
# names of bound slots
|
|
496
|
+
def bound_slot_string ( *args )
|
|
497
|
+
|
|
498
|
+
bound_slots = args.find_all { |slot| slot_bound?( slot ) }
|
|
499
|
+
return bound_slots.empty? ? "" : " " + bound_slots.collect { |slot| @slots[slot].to_tag_attribute }.join( " " )
|
|
500
|
+
|
|
501
|
+
end
|
|
502
|
+
|
|
503
|
+
public
|
|
504
|
+
|
|
505
|
+
# Returns true if Slot for given name is bound.
|
|
506
|
+
def slot_bound? ( slot_name )
|
|
507
|
+
return @slots[slot_name].bound_string != nil
|
|
508
|
+
end
|
|
509
|
+
|
|
510
|
+
|
|
511
|
+
# Returns true if the receiver is a container component - only containers
|
|
512
|
+
# can contain a content.
|
|
513
|
+
def container? ()
|
|
514
|
+
return true
|
|
515
|
+
end
|
|
516
|
+
|
|
517
|
+
|
|
518
|
+
# Returns true if the component is content - it will be replaced by the
|
|
519
|
+
# content of the parent component. Currently only SWS::Component::Content
|
|
520
|
+
# return true here.
|
|
521
|
+
def content? ()
|
|
522
|
+
return false
|
|
523
|
+
end
|
|
524
|
+
|
|
525
|
+
end
|
|
526
|
+
|
|
527
|
+
|
|
528
|
+
# Represents pure text - component representation of text token. Used to
|
|
529
|
+
# append text tokens from parsed tree to the Response object
|
|
530
|
+
class TextComponent < Component
|
|
531
|
+
|
|
532
|
+
# HTML content
|
|
533
|
+
attr_accessor :content
|
|
534
|
+
|
|
535
|
+
|
|
536
|
+
# Appends HTML content to the Response
|
|
537
|
+
def append_to_response ( response )
|
|
538
|
+
response << @content
|
|
539
|
+
end
|
|
540
|
+
|
|
541
|
+
end
|
|
542
|
+
|
|
543
|
+
|
|
544
|
+
# Base class for all components being the part of a form
|
|
545
|
+
class FormElement < Component
|
|
546
|
+
|
|
547
|
+
|
|
548
|
+
# Returns base name attribute of HTML tag created from names of all
|
|
549
|
+
# parents separated by '.'. Used for creating HTTP parameters containing
|
|
550
|
+
# values entered by user.
|
|
551
|
+
def element_name ()
|
|
552
|
+
|
|
553
|
+
name = @name
|
|
554
|
+
component = @parent
|
|
555
|
+
while ( component != nil )
|
|
556
|
+
name = "#{component.name}.#{name}"
|
|
557
|
+
component = component.parent
|
|
558
|
+
end
|
|
559
|
+
return name
|
|
560
|
+
|
|
561
|
+
end
|
|
562
|
+
|
|
563
|
+
|
|
564
|
+
# All form parts are not container components - they do not contains other
|
|
565
|
+
# components or HTML content
|
|
566
|
+
def container? ()
|
|
567
|
+
return false
|
|
568
|
+
end
|
|
569
|
+
|
|
570
|
+
end
|
|
571
|
+
|
|
572
|
+
|
|
573
|
+
# Base class for RadioButtonList and MultipleSelectionList
|
|
574
|
+
class ListElement < FormElement
|
|
575
|
+
|
|
576
|
+
|
|
577
|
+
# Returns value of "list" slot
|
|
578
|
+
def list ()
|
|
579
|
+
return @slots["list"].value()
|
|
580
|
+
end
|
|
581
|
+
|
|
582
|
+
|
|
583
|
+
# Returns value of "item" slot
|
|
584
|
+
def item ()
|
|
585
|
+
return @slots["item"].value()
|
|
586
|
+
end
|
|
587
|
+
|
|
588
|
+
|
|
589
|
+
def append_to_response ( response )
|
|
590
|
+
response << generate_html()
|
|
591
|
+
end
|
|
592
|
+
|
|
593
|
+
end
|
|
594
|
+
|
|
595
|
+
|
|
596
|
+
# Base class for lists with multiple selections allowed: CheckBoxList and Select
|
|
597
|
+
class MultipleSelectionList < ListElement
|
|
598
|
+
|
|
599
|
+
|
|
600
|
+
# Returns value of "selections" slot or empty Array instead of nil
|
|
601
|
+
def selections ()
|
|
602
|
+
@selections ||= (@slots["selections"].value() || Array.new )
|
|
603
|
+
return @selections
|
|
604
|
+
end
|
|
605
|
+
|
|
606
|
+
|
|
607
|
+
# Sets the value of the "selections" slot
|
|
608
|
+
def selections= ( selections )
|
|
609
|
+
|
|
610
|
+
if ( selections == nil )
|
|
611
|
+
|
|
612
|
+
@slots["selections"].value = []
|
|
613
|
+
|
|
614
|
+
elsif ( selections.is_a?( Array ) )
|
|
615
|
+
|
|
616
|
+
#in the selections array we have indices of elements in "list" array
|
|
617
|
+
selected = selections.collect { |sel| list[sel.to_i] }
|
|
618
|
+
@slots["selections"].value = selected
|
|
619
|
+
|
|
620
|
+
else #single value - create an Array containing it
|
|
621
|
+
|
|
622
|
+
if ( selections == "__empty_string" )
|
|
623
|
+
@slots["selections"].value = nil
|
|
624
|
+
else
|
|
625
|
+
@slots["selections"].value = [ list[selections.to_i] ]
|
|
626
|
+
end
|
|
627
|
+
|
|
628
|
+
end
|
|
629
|
+
|
|
630
|
+
end
|
|
631
|
+
|
|
632
|
+
|
|
633
|
+
# #if ( selections == nil ) #nothing is selected - return empty array
|
|
634
|
+
# #@slots["selections"].value = Array.new()
|
|
635
|
+
# #else
|
|
636
|
+
# #WARNING: selections will be an Array if multiple and a single object if not
|
|
637
|
+
# selected = Array.new()
|
|
638
|
+
# #if we have single selection, make it an Array
|
|
639
|
+
# selections = [selections] unless ( selections.is_a? (Array) )
|
|
640
|
+
# #in the selections array we have indices of elements in "list" array
|
|
641
|
+
# selections.each { |sel| selected << list[sel.to_i] }
|
|
642
|
+
# @slots["selections"].value = selected
|
|
643
|
+
# end
|
|
644
|
+
# end
|
|
645
|
+
def process_bindings ()
|
|
646
|
+
if ( @parameters["selections"] )
|
|
647
|
+
self.selections = @parameters["selections"]
|
|
648
|
+
else
|
|
649
|
+
self.selections = nil
|
|
650
|
+
end
|
|
651
|
+
end
|
|
652
|
+
|
|
653
|
+
end
|
|
654
|
+
|
|
655
|
+
|
|
656
|
+
end
|