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.
Files changed (158) hide show
  1. data/doc/DOC.otl +34 -0
  2. data/doc/Makefile +13 -0
  3. data/doc/architecture.dia +0 -0
  4. data/doc/docbook/architecture.png +0 -0
  5. data/doc/docbook/concepts.docbook +474 -0
  6. data/doc/docbook/installation.docbook +57 -0
  7. data/doc/docbook/introduction.docbook +130 -0
  8. data/doc/docbook/sws_manual.docbook +35 -0
  9. data/doc/docbook/todo.docbook +38 -0
  10. data/doc/docbook/tutorial.docbook +594 -0
  11. data/examples/README +1 -0
  12. data/examples/addressbook/CardBrowse/CardBrowse.html +43 -0
  13. data/examples/addressbook/CardBrowse/CardBrowse.rb +65 -0
  14. data/examples/addressbook/CardBrowse/CardBrowse.sws +92 -0
  15. data/examples/addressbook/Login/LoginPage.html +12 -0
  16. data/examples/addressbook/Login/LoginPage.rb +19 -0
  17. data/examples/addressbook/Login/LoginPage.sws +15 -0
  18. data/examples/addressbook/README +1 -0
  19. data/examples/addressbook/addressbook.rb +70 -0
  20. data/examples/addressbook/application.yaml +8 -0
  21. data/examples/addressbook/db.yaml +7 -0
  22. data/examples/component_demo/CheckBoxDemo/CheckBoxDemo.html +11 -0
  23. data/examples/component_demo/CheckBoxDemo/CheckBoxDemo.rb +21 -0
  24. data/examples/component_demo/CheckBoxDemo/CheckBoxDemo.sws +25 -0
  25. data/examples/component_demo/ComponentDemo.rb +21 -0
  26. data/examples/component_demo/ConditionalDemo/ConditionalDemo.html +18 -0
  27. data/examples/component_demo/ConditionalDemo/ConditionalDemo.rb +2 -0
  28. data/examples/component_demo/ConditionalDemo/ConditionalDemo.sws +22 -0
  29. data/examples/component_demo/FileUploadDemo/FileUploadDemo.html +10 -0
  30. data/examples/component_demo/FileUploadDemo/FileUploadDemo.rb +9 -0
  31. data/examples/component_demo/FileUploadDemo/FileUploadDemo.sws +33 -0
  32. data/examples/component_demo/FormFieldsDemo/FormFieldsDemo.html +12 -0
  33. data/examples/component_demo/FormFieldsDemo/FormFieldsDemo.rb +21 -0
  34. data/examples/component_demo/FormFieldsDemo/FormFieldsDemo.sws +40 -0
  35. data/examples/component_demo/FormListsDemo/FormListsDemo.html +11 -0
  36. data/examples/component_demo/FormListsDemo/FormListsDemo.rb +37 -0
  37. data/examples/component_demo/FormListsDemo/FormListsDemo.sws +47 -0
  38. data/examples/component_demo/GenericDemo/GenericDemo.html +4 -0
  39. data/examples/component_demo/GenericDemo/GenericDemo.rb +2 -0
  40. data/examples/component_demo/GenericDemo/GenericDemo.sws +10 -0
  41. data/examples/component_demo/HyperlinkDemo/HyperlinkDemo.html +8 -0
  42. data/examples/component_demo/HyperlinkDemo/HyperlinkDemo.rb +20 -0
  43. data/examples/component_demo/HyperlinkDemo/HyperlinkDemo.sws +19 -0
  44. data/examples/component_demo/ImageLinkDemo/ImageLinkDemo.html +11 -0
  45. data/examples/component_demo/ImageLinkDemo/ImageLinkDemo.rb +2 -0
  46. data/examples/component_demo/ImageLinkDemo/ImageLinkDemo.sws +14 -0
  47. data/examples/component_demo/PageWrapper/PageWrapper.html +23 -0
  48. data/examples/component_demo/PageWrapper/PageWrapper.rb +2 -0
  49. data/examples/component_demo/PageWrapper/PageWrapper.sws +42 -0
  50. data/examples/component_demo/README +1 -0
  51. data/examples/component_demo/RepetitionDemo/RepetitionDemo.html +13 -0
  52. data/examples/component_demo/RepetitionDemo/RepetitionDemo.rb +19 -0
  53. data/examples/component_demo/RepetitionDemo/RepetitionDemo.sws +20 -0
  54. data/examples/component_demo/StringDemo/StringDemo.html +5 -0
  55. data/examples/component_demo/StringDemo/StringDemo.rb +16 -0
  56. data/examples/component_demo/StringDemo/StringDemo.sws +14 -0
  57. data/examples/component_demo/application.yaml +28 -0
  58. data/examples/component_demo/poweredby.jpg +0 -0
  59. data/examples/component_demo/style.css +1 -0
  60. data/examples/movies/Menu/Menu.html +3 -0
  61. data/examples/movies/Menu/Menu.rb +7 -0
  62. data/examples/movies/Menu/Menu.sws +7 -0
  63. data/examples/movies/MovieBrowse/MovieBrowse.html +68 -0
  64. data/examples/movies/MovieBrowse/MovieBrowse.rb +178 -0
  65. data/examples/movies/MovieBrowse/MovieBrowse.sws +127 -0
  66. data/examples/movies/README +1 -0
  67. data/examples/movies/UserBrowse/UserBrowse.html +50 -0
  68. data/examples/movies/UserBrowse/UserBrowse.rb +69 -0
  69. data/examples/movies/UserBrowse/UserBrowse.sws +49 -0
  70. data/examples/movies/application.yaml +11 -0
  71. data/examples/movies/da.rb +36 -0
  72. data/examples/movies/dbmovies.rb +44 -0
  73. data/examples/movies/frameworks/TestFramework/framework.yaml +4 -0
  74. data/examples/movies/frameworks/TestFramework/resources/im1.jpg +0 -0
  75. data/examples/movies/images/pbr1b.jpg +0 -0
  76. data/examples/movies/movies.rb +28 -0
  77. data/examples/movies/movies.sds +119 -0
  78. data/examples/movies/movies.sqlite +0 -0
  79. data/examples/movies/movies_mysql.sql +28 -0
  80. data/examples/movies/movies_postgres.sql +27 -0
  81. data/examples/movies/movies_sqlite.sql +28 -0
  82. data/lib/sws.rb +89 -0
  83. data/lib/sws/Core/components/CheckBox/CheckBox.api +5 -0
  84. data/lib/sws/Core/components/CheckBox/CheckBox.rb +45 -0
  85. data/lib/sws/Core/components/CheckBoxList/CheckBoxList.api +13 -0
  86. data/lib/sws/Core/components/CheckBoxList/CheckBoxList.rb +54 -0
  87. data/lib/sws/Core/components/Conditional/Conditional.api +3 -0
  88. data/lib/sws/Core/components/Conditional/Conditional.html +1 -0
  89. data/lib/sws/Core/components/Conditional/Conditional.rb +39 -0
  90. data/lib/sws/Core/components/Conditional/Conditional.sws +2 -0
  91. data/lib/sws/Core/components/Content/Content.rb +18 -0
  92. data/lib/sws/Core/components/ExceptionPage/ExceptionPage.html +13 -0
  93. data/lib/sws/Core/components/ExceptionPage/ExceptionPage.rb +18 -0
  94. data/lib/sws/Core/components/ExceptionPage/ExceptionPage.sws +16 -0
  95. data/lib/sws/Core/components/FileUpload/FileUpload.api +16 -0
  96. data/lib/sws/Core/components/FileUpload/FileUpload.rb +62 -0
  97. data/lib/sws/Core/components/Form/Form.api +9 -0
  98. data/lib/sws/Core/components/Form/Form.html +3 -0
  99. data/lib/sws/Core/components/Form/Form.rb +55 -0
  100. data/lib/sws/Core/components/Form/Form.sws +12 -0
  101. data/lib/sws/Core/components/GenericContainer/GenericContainer.api +10 -0
  102. data/lib/sws/Core/components/GenericContainer/GenericContainer.html +1 -0
  103. data/lib/sws/Core/components/GenericContainer/GenericContainer.rb +39 -0
  104. data/lib/sws/Core/components/GenericContainer/GenericContainer.sws +12 -0
  105. data/lib/sws/Core/components/GenericElement/GenericElement.api +10 -0
  106. data/lib/sws/Core/components/GenericElement/GenericElement.rb +34 -0
  107. data/lib/sws/Core/components/HiddenField/HiddenField.api +7 -0
  108. data/lib/sws/Core/components/HiddenField/HiddenField.rb +37 -0
  109. data/lib/sws/Core/components/Hyperlink/Hyperlink.api +13 -0
  110. data/lib/sws/Core/components/Hyperlink/Hyperlink.html +1 -0
  111. data/lib/sws/Core/components/Hyperlink/Hyperlink.rb +102 -0
  112. data/lib/sws/Core/components/Hyperlink/Hyperlink.sws +12 -0
  113. data/lib/sws/Core/components/Image/Image.api +11 -0
  114. data/lib/sws/Core/components/Image/Image.rb +49 -0
  115. data/lib/sws/Core/components/ImageButton/ImageButton.api +16 -0
  116. data/lib/sws/Core/components/ImageButton/ImageButton.rb +76 -0
  117. data/lib/sws/Core/components/Link/Link.api +11 -0
  118. data/lib/sws/Core/components/Link/Link.rb +39 -0
  119. data/lib/sws/Core/components/PasswordField/PasswordField.api +7 -0
  120. data/lib/sws/Core/components/PasswordField/PasswordField.rb +41 -0
  121. data/lib/sws/Core/components/RadioButton/RadioButton.api +7 -0
  122. data/lib/sws/Core/components/RadioButton/RadioButton.rb +44 -0
  123. data/lib/sws/Core/components/RadioButtonList/RadioButtonList.api +20 -0
  124. data/lib/sws/Core/components/RadioButtonList/RadioButtonList.rb +76 -0
  125. data/lib/sws/Core/components/Repetition/Repetition.api +10 -0
  126. data/lib/sws/Core/components/Repetition/Repetition.html +1 -0
  127. data/lib/sws/Core/components/Repetition/Repetition.rb +137 -0
  128. data/lib/sws/Core/components/Repetition/Repetition.sws +2 -0
  129. data/lib/sws/Core/components/ResetButton/ResetButton.api +5 -0
  130. data/lib/sws/Core/components/ResetButton/ResetButton.rb +28 -0
  131. data/lib/sws/Core/components/Select/Select.api +24 -0
  132. data/lib/sws/Core/components/Select/Select.rb +57 -0
  133. data/lib/sws/Core/components/String/String.api +5 -0
  134. data/lib/sws/Core/components/String/String.rb +28 -0
  135. data/lib/sws/Core/components/SubmitButton/SubmitButton.api +6 -0
  136. data/lib/sws/Core/components/SubmitButton/SubmitButton.rb +53 -0
  137. data/lib/sws/Core/components/TextArea/TextArea.api +9 -0
  138. data/lib/sws/Core/components/TextArea/TextArea.rb +28 -0
  139. data/lib/sws/Core/components/TextField/TextField.api +9 -0
  140. data/lib/sws/Core/components/TextField/TextField.rb +46 -0
  141. data/lib/sws/Core/framework.yaml +25 -0
  142. data/lib/sws/JSComponents/components/JSMenu/JSMenu.api +5 -0
  143. data/lib/sws/JSComponents/components/JSMenu/JSMenu.html +58 -0
  144. data/lib/sws/JSComponents/components/JSMenu/JSMenu.rb +34 -0
  145. data/lib/sws/JSComponents/components/JSMenu/JSMenu.sws +37 -0
  146. data/lib/sws/JSComponents/framework.yaml +3 -0
  147. data/lib/sws/adaptor.rb +334 -0
  148. data/lib/sws/application.rb +604 -0
  149. data/lib/sws/component.rb +656 -0
  150. data/lib/sws/cookie.rb +27 -0
  151. data/lib/sws/direct_action.rb +38 -0
  152. data/lib/sws/extensions.rb +49 -0
  153. data/lib/sws/parsers.rb +374 -0
  154. data/lib/sws/request.rb +308 -0
  155. data/lib/sws/response.rb +70 -0
  156. data/lib/sws/session.rb +195 -0
  157. data/lib/sws/slot.rb +198 -0
  158. metadata +263 -0
@@ -0,0 +1,308 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ module SWS
4
+
5
+
6
+ # Struct containing data of a file passed as form-data
7
+ FileData = Struct.new(
8
+ :content_disposition,
9
+ :filename,
10
+ :content_type,
11
+ :charset,
12
+ :content_transfer_encoding,
13
+ :contents
14
+ )
15
+
16
+ # Class representing HTTP request
17
+ class Request
18
+
19
+ # Method of the request (POST,GET)
20
+ attr_reader :request_method
21
+
22
+ # Path the request was made to
23
+ attr_reader :path
24
+
25
+ # Version of the HTTP protocol
26
+ attr_reader :http_version
27
+
28
+ # Query string (as in CGI). May be used freely, as SWS does not use it
29
+ # internally.
30
+ attr_reader :query_string
31
+
32
+ # Hash: key - header name, value - header value
33
+ attr_reader :headers
34
+
35
+ # Form parameters passed to the request (Hash)
36
+ attr_reader :params
37
+
38
+ # Cookies passed to the request (Hash)
39
+ attr_reader :cookies
40
+
41
+ # User agent string
42
+ attr_reader :user_agent
43
+
44
+ # Name of the server host
45
+ attr_reader :host
46
+
47
+ # IP of the remote host
48
+ attr_reader :remote_address
49
+
50
+ # Content of the request
51
+ attr_accessor :content
52
+
53
+ # Session object connected with this request
54
+ attr_writer :session
55
+
56
+
57
+ # Creates new Request object
58
+ def initialize ( request_method, path, query_string, http_version)
59
+
60
+ @request_method = request_method
61
+ @path = path
62
+ @http_version = http_version
63
+ @query_string = query_string
64
+ @headers = Hash.new
65
+ @params = Hash.new
66
+ @cookies = Hash.new
67
+ @content = nil
68
+ @session = nil
69
+
70
+ end
71
+
72
+
73
+ # Request true if content is not empty
74
+ def has_content? ()
75
+ return @content != nil
76
+ end
77
+
78
+
79
+ # Returns string representation of the receiver
80
+ def to_s ()
81
+
82
+ params = ""
83
+ @params.each { |key,value| params << "Key: #{key}, value: #{value}\n" }
84
+ return "Method: #{request_method}, path: #{path}, params:\n #{params}"
85
+
86
+ end
87
+
88
+
89
+ # Retrieves session from cookie (or only returns it if already retrieved)
90
+ def session ()
91
+ #TODO: allow session to be stored in url
92
+
93
+ if ( @session == nil )
94
+ session_id = @cookies["session"]
95
+ @session = Session.sessions[session_id]
96
+ end
97
+ return @session
98
+
99
+ end
100
+
101
+
102
+ # Erases receiver's content - useful when new session is created on not refreshed HTML
103
+ def erase_content ()
104
+ #TODO: erase cookies and headers too?
105
+ @content = nil
106
+ @params = Hash.new()
107
+
108
+ end
109
+
110
+
111
+ # Parses headers hash and generates a bunch of more useful parameters and
112
+ # cookies
113
+ def process_headers ()
114
+
115
+ if ( @headers[ "cookie" ] )
116
+ @headers["cookie"].split( /;\s+/ ).each do |cookie_pair|
117
+ name, value = cookie_pair.split( /=/ )
118
+ @cookies[name] = value.url_decode
119
+ end
120
+ end
121
+
122
+ @user_agent = @headers["user-agent"]
123
+ @host = @headers["host"]
124
+ @remote_address = @headers["remote-address"]
125
+
126
+ #TODO: maybe add the rest of headers?
127
+
128
+ end
129
+
130
+
131
+ # Parses request content and extracts form parameters
132
+ def process_content ()
133
+
134
+
135
+ $log_sws_request.debug( "Content: #{@content}" )
136
+ content_type = @headers["content-type"]
137
+
138
+ if ( content_type =~ /application\/x-www-form-urlencoded/i )
139
+
140
+ process_url_encoded_content()
141
+
142
+ elsif ( content_type =~ /multipart\/form-data/i )
143
+
144
+ process_multipart_form_data_content( content_type )
145
+
146
+ else
147
+
148
+ raise( "Unsupported content type #{content_type}" )
149
+
150
+ end
151
+
152
+ end
153
+
154
+
155
+ protected
156
+
157
+ def process_url_encoded_content ()
158
+
159
+ $log_sws_request.debug( "Content type: x-www-form-urlencoded" )
160
+ @content.split( '&' ).each do |param|
161
+ key,value = param.split( '=' )
162
+ unless ( value == nil ) #can be nil eg. in empty textfield
163
+ value.gsub!( /\+/," " )
164
+ value.url_decode!()
165
+ end
166
+ $log_sws_request.debug( "Param: #{key}, value: #{value}" )
167
+ add_param( key,value )
168
+ end
169
+
170
+ end
171
+
172
+
173
+ def process_multipart_form_data_content ( content_type )
174
+
175
+ $log_sws_request.debug( "Content type: multipart/form-data" )
176
+ boundary = content_type.sub( /^.*boundary=/,"" )
177
+
178
+ $log_sws_request.debug( "Boundary: #{boundary}" )
179
+
180
+ # For some unknown reason (RFC, presumably :) all browsers add '--'
181
+ # before the boundary and at the end of content
182
+ params = @content.split( /--#{boundary}-{0,2}\r\n/m )
183
+ params.delete_if { |param| param == "" }
184
+ #TODO: why the statement below was used? It breaks file uploads
185
+ #|| param =~ /^--/m }
186
+ params.each do |param|
187
+
188
+ $log_sws_request.debug( "Param: #{param}" )
189
+ headers,value = param.split( /\r\n\r\n/,2 )
190
+
191
+ $log_sws_request.debug( "headers: #{headers}" )
192
+ if ( headers =~ /^content-disposition: form-data; name="([^"]+)"$/i )
193
+
194
+ key = $1
195
+ $log_sws_request.debug( "Single value, #{key}: #{value}" )
196
+ # should work properly with form-data content
197
+ # currently other types are unsupported
198
+
199
+ # TODO: refactoring for proper coding/decoding
200
+ # value.gsub!( /\+/," " )
201
+ # value.url_decode!()
202
+
203
+ # Don't know why, but in some strings - like
204
+ # "nazwy_projekt�w_do_um�w\r\n" - using /\r\n/ in regexp doesn't strip
205
+ # these chars properly
206
+ value.sub!( /(\x0D\x0A)+$/, "" )
207
+ add_param( key, value )
208
+
209
+ else
210
+
211
+ $log_sws_request.debug( "File value" )
212
+ file_data = FileData.new
213
+ file_data.contents = value
214
+ headers.each_line do |header|
215
+ header.sub!( /\r\n$/,"" )
216
+ case header
217
+ when /^content-disposition/i
218
+ parse_multipart_form_data_content_disposition( file_data,header )
219
+ when /^content-type/i
220
+ parse_multipart_form_data_content_type( file_data,header )
221
+ when /^content-transfer-encoding/i
222
+ parse_multipart_form_data_content_transfer_encoding( file_data,header )
223
+ else
224
+ raise( "Unknown multipart/form-data header: --#{header}--" )
225
+ end
226
+ end
227
+
228
+ end
229
+
230
+ end
231
+
232
+ end
233
+
234
+
235
+ def process_multipart_mixed_content ( content_type )
236
+
237
+ raise( "Multipart/mixed content-type is not yet supported" )
238
+
239
+ end
240
+
241
+
242
+ def parse_multipart_form_data_content_disposition ( file_data, header )
243
+
244
+ #TODO: avoid exception on malformed header
245
+ file_data.content_disposition, params =
246
+ /^content-disposition: (.*?);\s*(.*)$/i.match( header ).captures
247
+ params.split( /;\s*/ ).each do |param|
248
+ key, value = /^(\w+)="?(.*?)"?$/.match( param ).captures
249
+ case key
250
+ when "name" then @params[value] = file_data
251
+ when "filename" then file_data.filename = value
252
+ else
253
+ raise( "Unknown content-disposition param in multipart/form-data: #{key}" )
254
+ end
255
+ end
256
+
257
+ end
258
+
259
+
260
+ def parse_multipart_form_data_content_type ( file_data, header )
261
+
262
+ md = /^content-type: (.*?);\s*(.*)$/i.match( header )
263
+ if ( md )
264
+ file_data.content_type,params = md.captures
265
+ params.split( /;\s*/ ).each do |param|
266
+ $log_sws_request.debug( "Param: #{param}" )
267
+ key,value = /^(\w+)="?(.*?)"?$/.match( param ).captures
268
+ case key
269
+ when "charset" then file_data.charset = value
270
+ else
271
+ raise( "Unknown param of content-type in multipart/form-data: #{key}" )
272
+ end
273
+ end
274
+ else
275
+ file_data.content_type = /^content-type: (.*)$/i.match( header ).captures[0]
276
+ end
277
+
278
+ end
279
+
280
+
281
+ def parse_multipart_form_data_content_transfer_encoding ( file_data, header )
282
+
283
+ file_data.content_transfer_encoding =
284
+ /^,*:\s*(.*)$/.match( header ).captures[0]
285
+
286
+ end
287
+
288
+
289
+ def add_param( key, value )
290
+
291
+ if ( @params[key] ) #that means this param has multiple values
292
+ if ( @params[key].instance_of?( Array ) )
293
+ #param already has multiple values assigned,
294
+ #so just add next value to the array
295
+ @params[key] << value
296
+ else #value in params has must be converted to an Array
297
+ values = [@params[key],value]
298
+ @params[key] = values
299
+ end
300
+ else
301
+ @params[key] = value
302
+ end
303
+
304
+ end
305
+
306
+ end
307
+
308
+ end
@@ -0,0 +1,70 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ module SWS
4
+
5
+
6
+ # Represents HTML reponse generated by SWS
7
+ class Response
8
+
9
+
10
+ # Hash of HTTP headers
11
+ attr_reader :headers
12
+
13
+ # Array of Cookie objects
14
+ attr_reader :cookies
15
+
16
+ # The version of HTTP protocol
17
+ attr_accessor :http_version
18
+
19
+ # Status of the response
20
+ attr_accessor :status
21
+
22
+ # The Request the receiver is associated with
23
+ attr_reader :request
24
+
25
+ attr_reader :content
26
+
27
+
28
+ # Creates new empty Response object
29
+ def initialize ( request,status = "200 SWS/OK" )
30
+
31
+ @content = ""
32
+ @headers = Hash.new
33
+ @cookies = Array.new
34
+ @http_version = "HTTP/1.1"
35
+ @status = status
36
+ @request = request
37
+
38
+ end
39
+
40
+ def Response.redirect( request, url )
41
+
42
+ response = Response.new( request, "302 Found" )
43
+ response.headers["Location"] = url
44
+ return response
45
+
46
+ end
47
+
48
+ # Appends a string to the receiver
49
+ def << ( string )
50
+ @content << string
51
+ end
52
+
53
+
54
+ # Returns string representation of the receiver
55
+ def to_s ()
56
+
57
+ data = ""
58
+ @headers.each_pair { |key, value| data << "#{key}: #{value}\r\n" }
59
+ @cookies.each { |cookie| data << "#{cookie}\r\n" }
60
+ # End headers
61
+ data << "\r\n"
62
+ data << @content
63
+ return data
64
+
65
+ end
66
+
67
+ end
68
+
69
+ end
70
+
@@ -0,0 +1,195 @@
1
+ #!/usr/bin/ruby
2
+
3
+ require "digest/md5"
4
+
5
+ module SWS
6
+
7
+
8
+ # This is a class representing user sessions. Sessions are based on id,
9
+ # which is passed in cookie
10
+ class Session
11
+
12
+ # Hash for created sessions: key - id, value - Session object (can be subclasses of Session!)
13
+ @@sessions = Hash.new
14
+
15
+ # Session id
16
+ attr_reader :sid
17
+
18
+ # Time when the session was last active
19
+ attr_accessor :last_access_time
20
+
21
+ private
22
+
23
+ # Creates new Session object and registers it
24
+ def initialize ()
25
+
26
+ generate_sid()
27
+ #register session
28
+ @@sessions[@sid] = self
29
+
30
+ # Last top-level components created
31
+ @component_cache = ComponentCache.new()
32
+
33
+ end
34
+
35
+
36
+ # Generates session id
37
+ def generate_sid ()
38
+
39
+ md5digest = Digest::MD5.new()
40
+ md5digest.update(Time.now.to_f.to_s + rand.to_s + $$.to_s)
41
+ @sid = (md5digest.hexdigest)
42
+
43
+ end
44
+
45
+
46
+ public
47
+
48
+ # Returns sessions Hash containing all registered sessions
49
+ def Session.sessions ()
50
+ return @@sessions
51
+ end
52
+
53
+
54
+ # Deletes session from registered ones
55
+ def Session.delete_session ( session )
56
+ @@sessions.delete( session.sid )
57
+ end
58
+
59
+
60
+ # Returns session id represented as cookie in order to append it to
61
+ # response.
62
+ # TODO: deal with "path" (set it to base app path)
63
+ def to_cookie ()
64
+ return Cookie.new( "session",@sid )
65
+ end
66
+
67
+
68
+ # Returns string representation of the receiver
69
+ def to_s ()
70
+ return "#{self.class}, sid: #{@sid}"
71
+ end
72
+
73
+ def add_to_cache ( component )
74
+ @component_cache.add_component( component )
75
+ end
76
+
77
+ def get_from_cache ( component_id )
78
+ return @component_cache.component( component_id )
79
+ end
80
+
81
+ def old? ( component_id )
82
+ return @component_cache.old?( component_id )
83
+ end
84
+
85
+ end
86
+
87
+
88
+
89
+ class SessionCleaner < Thread
90
+
91
+ # By default cleaning will be performed every 10 minutes and each session
92
+ # inactive for 6 hours will be deleted
93
+ def initialize ( sessions, mutex, session_timeout = 21600, cleanup_interval = 600 )
94
+
95
+ super do |sessions, mutex, session_timeout, cleanup_interval|
96
+ loop do
97
+ mutex.synchronize {
98
+ sessions.delete_if() {
99
+ |key, value| ( Time.now - value.last_access_time ) > session_timeout
100
+ }
101
+ }
102
+
103
+ sleep cleanup_interval
104
+ end
105
+ end
106
+
107
+ end
108
+
109
+ end
110
+
111
+
112
+
113
+ # TODO: not sure if it works entirely correctly, but for now it works good
114
+ # enough
115
+ class ComponentCache
116
+
117
+ def ComponentCache.max_components_per_session= ( max )
118
+ @@max_components_per_session = max
119
+ end
120
+
121
+
122
+ def initialize
123
+ @components = Array.new
124
+ # Object_ids of components removed from cache. Useful when handling
125
+ # backtracking.
126
+ @old_component_ids = Array.new
127
+ end
128
+
129
+
130
+ # Adds a component to cache.
131
+ # Returns false if component was nil.
132
+ # Size of component cache can be customized in application.yaml
133
+ def add_component ( component )
134
+
135
+ unless @components.include?( component )
136
+ @components << component
137
+ if @components.length > @@max_components_per_session
138
+ # As checking for only one copy of a component is already done when
139
+ # inserting into @components, we don't need to do this here. We only
140
+ # store object_ids to limit memory footprint.
141
+ @old_component_ids << @components.shift.object_id
142
+ end
143
+ end
144
+
145
+ end
146
+
147
+
148
+ def component ( component_id )
149
+
150
+ if( component_id == @components.last.object_id )
151
+ return @components.last
152
+ else
153
+ component = @components.find { |c| c.object_id == component_id }
154
+ end
155
+
156
+ unless component
157
+ return nil
158
+ else
159
+ # Old behaviour (for optimalization)
160
+ # Remove all components that are after found component - they are in the
161
+ # "branch" the client shouldn't have access to. Of course they are added
162
+ # to @old_component_ids for the case user backtracks to them.
163
+ #
164
+ # Accidentally, this works correctly even if component ==
165
+ # @components.last (that means there was no backtracking) - that is, it
166
+ # doesn't remove anything.
167
+ #
168
+ #@old_component_ids.concat(
169
+ # @components.slice!( @components.index( component ) + 1 .. -1 ).collect { |com| com.object_id }
170
+ #)
171
+
172
+ # @components.push( @components.delete( component ) )
173
+ # puts @components.each { |c| print "-- ", c.object_id, "\n" }
174
+ # puts component_id
175
+ return component
176
+ end
177
+
178
+ end
179
+
180
+
181
+ def old? ( component_id )
182
+ return @old_component_ids.include?( component_id )
183
+ end
184
+
185
+ # This method returns last component in array - this is used
186
+ # when we need direct access to some common component atributes
187
+ # (i.e. request.headers)
188
+ # TODO: maybe it is unnecessary (i mean: there is better way to do this)
189
+ def last
190
+ return @components.last
191
+ end
192
+
193
+ end
194
+
195
+ end