wee 0.1.0 → 0.3.1

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 (193) hide show
  1. data/INSTALL +7 -0
  2. data/README +268 -0
  3. data/Rakefile +26 -0
  4. data/TODO +116 -0
  5. data/benchmark/Centrino1300/result.2000.counter.action +45 -0
  6. data/benchmark/Centrino1300/result.2000.counter.render +43 -0
  7. data/benchmark/Centrino1300/result.2000.filehandler +43 -0
  8. data/benchmark/Centrino600/result.2000.counter.action +47 -0
  9. data/benchmark/Centrino600/result.2000.counter.render +45 -0
  10. data/benchmark/Centrino600/result.2000.filehandler +43 -0
  11. data/benchmark/Makefile +48 -0
  12. data/benchmark/bench.sh +24 -0
  13. data/benchmark/counter.rb +96 -0
  14. data/benchmark/filehandler.rb +6 -0
  15. data/doc/rdoc/classes/Array.html +172 -0
  16. data/doc/rdoc/classes/Cache.html +126 -0
  17. data/doc/rdoc/classes/Cache/StorageCache.html +320 -0
  18. data/doc/rdoc/classes/Cache/Strategy.html +128 -0
  19. data/doc/rdoc/classes/Cache/Strategy/CapacityBounded.html +269 -0
  20. data/doc/rdoc/classes/Cache/Strategy/LFU.html +238 -0
  21. data/doc/rdoc/classes/Cache/Strategy/LFU/Item.html +111 -0
  22. data/doc/rdoc/classes/Cache/Strategy/LRU.html +238 -0
  23. data/doc/rdoc/classes/Cache/Strategy/LRU/Item.html +111 -0
  24. data/doc/rdoc/classes/Cache/Strategy/Unbounded.html +225 -0
  25. data/doc/rdoc/classes/Cache/Strategy/Unbounded/Item.html +111 -0
  26. data/doc/rdoc/classes/Enumerable.html +146 -0
  27. data/doc/rdoc/classes/LiteralMethod.html +196 -0
  28. data/doc/rdoc/classes/Object.html +178 -0
  29. data/doc/rdoc/classes/String.html +172 -0
  30. data/doc/rdoc/classes/Struct.html +174 -0
  31. data/doc/rdoc/classes/Wee.html +160 -0
  32. data/doc/rdoc/classes/Wee/AnswerDecoration.html +182 -0
  33. data/doc/rdoc/classes/Wee/Application.html +337 -0
  34. data/doc/rdoc/classes/Wee/Brush.html +245 -0
  35. data/doc/rdoc/classes/Wee/Brush/ActionCallbackMixin.html +149 -0
  36. data/doc/rdoc/classes/Wee/Brush/ActionMixin.html +146 -0
  37. data/doc/rdoc/classes/Wee/Brush/ActionURLCallbackMixin.html +157 -0
  38. data/doc/rdoc/classes/Wee/Brush/AnchorTag.html +210 -0
  39. data/doc/rdoc/classes/Wee/Brush/AssignMixin.html +141 -0
  40. data/doc/rdoc/classes/Wee/Brush/CallbackMixin.html +141 -0
  41. data/doc/rdoc/classes/Wee/Brush/FormTag.html +225 -0
  42. data/doc/rdoc/classes/Wee/Brush/GenericEncodedTextBrush.html +176 -0
  43. data/doc/rdoc/classes/Wee/Brush/GenericTagBrush.html +283 -0
  44. data/doc/rdoc/classes/Wee/Brush/GenericTextBrush.html +176 -0
  45. data/doc/rdoc/classes/Wee/Brush/ImageButtonTag.html +195 -0
  46. data/doc/rdoc/classes/Wee/Brush/InputCallbackMixin.html +149 -0
  47. data/doc/rdoc/classes/Wee/Brush/InputTag.html +172 -0
  48. data/doc/rdoc/classes/Wee/Brush/Page.html +193 -0
  49. data/doc/rdoc/classes/Wee/Brush/SelectListTag.html +267 -0
  50. data/doc/rdoc/classes/Wee/Brush/SelectOptionTag.html +177 -0
  51. data/doc/rdoc/classes/Wee/Brush/SubmitButtonTag.html +154 -0
  52. data/doc/rdoc/classes/Wee/Brush/TableDataTag.html +173 -0
  53. data/doc/rdoc/classes/Wee/Brush/TableHeaderTag.html +146 -0
  54. data/doc/rdoc/classes/Wee/Brush/TableRowTag.html +277 -0
  55. data/doc/rdoc/classes/Wee/Brush/TableTag.html +146 -0
  56. data/doc/rdoc/classes/Wee/Brush/TextAreaTag.html +229 -0
  57. data/doc/rdoc/classes/Wee/Brush/TextInputTag.html +154 -0
  58. data/doc/rdoc/classes/Wee/Callback.html +231 -0
  59. data/doc/rdoc/classes/Wee/CallbackRegistry.html +308 -0
  60. data/doc/rdoc/classes/Wee/CallbackStream.html +227 -0
  61. data/doc/rdoc/classes/Wee/Canvas.html +235 -0
  62. data/doc/rdoc/classes/Wee/Component.html +933 -0
  63. data/doc/rdoc/classes/Wee/Context.html +111 -0
  64. data/doc/rdoc/classes/Wee/Decoration.html +338 -0
  65. data/doc/rdoc/classes/Wee/Delegate.html +247 -0
  66. data/doc/rdoc/classes/Wee/ErrorPage.html +175 -0
  67. data/doc/rdoc/classes/Wee/ErrorResponse.html +180 -0
  68. data/doc/rdoc/classes/Wee/GenericResponse.html +162 -0
  69. data/doc/rdoc/classes/Wee/HtmlCanvas.html +751 -0
  70. data/doc/rdoc/classes/Wee/HtmlWriter.html +351 -0
  71. data/doc/rdoc/classes/Wee/LiteralMethodCallback.html +180 -0
  72. data/doc/rdoc/classes/Wee/MethodCallback.html +193 -0
  73. data/doc/rdoc/classes/Wee/Page.html +111 -0
  74. data/doc/rdoc/classes/Wee/Presenter.html +521 -0
  75. data/doc/rdoc/classes/Wee/RedirectResponse.html +150 -0
  76. data/doc/rdoc/classes/Wee/RefreshResponse.html +157 -0
  77. data/doc/rdoc/classes/Wee/RenderingContext.html +111 -0
  78. data/doc/rdoc/classes/Wee/Request.html +268 -0
  79. data/doc/rdoc/classes/Wee/RequestHandler.html +336 -0
  80. data/doc/rdoc/classes/Wee/Response.html +260 -0
  81. data/doc/rdoc/classes/Wee/Session.html +469 -0
  82. data/doc/rdoc/classes/Wee/SimpleIdGenerator.html +198 -0
  83. data/doc/rdoc/classes/Wee/Snapshot.html +211 -0
  84. data/doc/rdoc/classes/Wee/StateHolder.html +149 -0
  85. data/doc/rdoc/classes/Wee/StateRegistry.html +434 -0
  86. data/doc/rdoc/classes/Wee/StateRegistry/Snapshot.html +320 -0
  87. data/doc/rdoc/classes/Wee/StateRegistry/WithObject.html +153 -0
  88. data/doc/rdoc/classes/Wee/Utils.html +111 -0
  89. data/doc/rdoc/classes/Wee/Utils/LRUCache.html +148 -0
  90. data/doc/rdoc/classes/Wee/ValueHolder.html +220 -0
  91. data/doc/rdoc/classes/Wee/WEBrickAdaptor.html +330 -0
  92. data/doc/rdoc/created.rid +1 -0
  93. data/doc/rdoc/files/INSTALL.html +118 -0
  94. data/doc/rdoc/files/README.html +466 -0
  95. data/doc/rdoc/files/lib/cache/cache_rb.html +101 -0
  96. data/doc/rdoc/files/lib/wee/adaptors/webrick_rb.html +108 -0
  97. data/doc/rdoc/files/lib/wee/application_rb.html +112 -0
  98. data/doc/rdoc/files/lib/wee/callback_rb.html +107 -0
  99. data/doc/rdoc/files/lib/wee/component_ext_rb.html +101 -0
  100. data/doc/rdoc/files/lib/wee/component_rb.html +109 -0
  101. data/doc/rdoc/files/lib/wee/context_rb.html +101 -0
  102. data/doc/rdoc/files/lib/wee/core/callback_rb.html +115 -0
  103. data/doc/rdoc/files/lib/wee/core/component_rb.html +108 -0
  104. data/doc/rdoc/files/lib/wee/core/decoration_rb.html +133 -0
  105. data/doc/rdoc/files/lib/wee/core/presenter_rb.html +112 -0
  106. data/doc/rdoc/files/lib/wee/core/snapshot_rb.html +113 -0
  107. data/doc/rdoc/files/lib/wee/core/valueholder_rb.html +110 -0
  108. data/doc/rdoc/files/lib/wee/core_rb.html +131 -0
  109. data/doc/rdoc/files/lib/wee/decoration_rb.html +101 -0
  110. data/doc/rdoc/files/lib/wee/holder_rb.html +101 -0
  111. data/doc/rdoc/files/lib/wee/html_canvas_rb.html +108 -0
  112. data/doc/rdoc/files/lib/wee/html_writer_rb.html +108 -0
  113. data/doc/rdoc/files/lib/wee/idgen_rb.html +101 -0
  114. data/doc/rdoc/files/lib/wee/page_rb.html +101 -0
  115. data/doc/rdoc/files/lib/wee/presenter_rb.html +112 -0
  116. data/doc/rdoc/files/lib/wee/renderer/html/brushes_rb.html +101 -0
  117. data/doc/rdoc/files/lib/wee/renderer/html/canvas_rb.html +101 -0
  118. data/doc/rdoc/files/lib/wee/renderer/html/writer_rb.html +108 -0
  119. data/doc/rdoc/files/lib/wee/rendering/html/brushes_rb.html +101 -0
  120. data/doc/rdoc/files/lib/wee/rendering/html/canvas_rb.html +101 -0
  121. data/doc/rdoc/files/lib/wee/rendering/html/writer_rb.html +108 -0
  122. data/doc/rdoc/files/lib/wee/request_rb.html +113 -0
  123. data/doc/rdoc/files/lib/wee/requesthandler_rb.html +101 -0
  124. data/doc/rdoc/files/lib/wee/response_rb.html +108 -0
  125. data/doc/rdoc/files/lib/wee/session_rb.html +109 -0
  126. data/doc/rdoc/files/lib/wee/snapshot_ext_rb.html +101 -0
  127. data/doc/rdoc/files/lib/wee/snapshot_rb.html +107 -0
  128. data/doc/rdoc/files/lib/wee/state_registry_rb.html +110 -0
  129. data/doc/rdoc/files/lib/wee/stuff_rb.html +144 -0
  130. data/doc/rdoc/files/lib/wee/utils/cache_rb.html +108 -0
  131. data/doc/rdoc/files/lib/wee/webrick_rb.html +108 -0
  132. data/doc/rdoc/files/lib/wee_rb.html +132 -0
  133. data/doc/rdoc/fr_class_index.html +93 -0
  134. data/doc/rdoc/fr_file_index.html +51 -0
  135. data/doc/rdoc/fr_method_index.html +242 -0
  136. data/doc/rdoc/index.html +24 -0
  137. data/doc/rdoc/rdoc-style.css +208 -0
  138. data/examples/ObjectSpaceBrowser.rb +199 -0
  139. data/examples/calendar.rb +366 -0
  140. data/examples/cc.rb +94 -0
  141. data/examples/draw.rb +91 -0
  142. data/examples/example.rb +223 -0
  143. data/examples/test.rb +66 -0
  144. data/examples/window.rb +53 -0
  145. data/lib/cache/cache.rb +9 -0
  146. data/lib/wee.rb +18 -8
  147. data/lib/wee/adaptors/webrick.rb +73 -0
  148. data/lib/wee/application.rb +69 -71
  149. data/lib/wee/context.rb +2 -12
  150. data/lib/wee/core.rb +15 -0
  151. data/lib/wee/core/callback.rb +108 -0
  152. data/lib/wee/core/component.rb +314 -0
  153. data/lib/wee/core/decoration.rb +129 -0
  154. data/lib/wee/core/presenter.rb +132 -0
  155. data/lib/wee/core/snapshot.rb +21 -0
  156. data/lib/wee/core/valueholder.rb +19 -0
  157. data/lib/wee/idgen.rb +13 -0
  158. data/lib/wee/page.rb +1 -1
  159. data/lib/wee/renderer/html/brushes.rb +435 -0
  160. data/lib/wee/renderer/html/canvas.rb +148 -0
  161. data/lib/wee/{html_writer.rb → renderer/html/writer.rb} +31 -16
  162. data/lib/wee/request.rb +57 -0
  163. data/lib/wee/requesthandler.rb +77 -0
  164. data/lib/wee/response.rb +77 -0
  165. data/lib/wee/session.rb +70 -64
  166. data/lib/wee/{snapshot.rb → snapshot_ext.rb} +4 -4
  167. data/test/components/calltest.rb +16 -0
  168. data/test/components/counter.rb +17 -0
  169. data/test/components/messagebox.rb +15 -0
  170. data/test/components/page.rb +14 -0
  171. data/test/components/page_decoration.rb +7 -0
  172. data/test/stress.rb +64 -0
  173. data/test/test_component.rb +106 -0
  174. data/test/test_html_canvas.rb +25 -0
  175. data/test/test_html_writer.rb +27 -0
  176. data/test/test_request.rb +13 -0
  177. data/test/utils/cross.rb +65 -0
  178. data/test/utils/generic_plotter.rb +28 -0
  179. data/test/utils/gnuplot.rb +31 -0
  180. data/test/utils/measure_memory.rb +9 -0
  181. data/test/utils/memory_plotter.rb +10 -0
  182. data/test/utils/object_plotter.rb +10 -0
  183. data/test/utils/webrick_background.rb +31 -0
  184. data/wee.gemspec +22 -0
  185. metadata +222 -18
  186. data/lib/wee/component.rb +0 -126
  187. data/lib/wee/delegate_decoration.rb +0 -22
  188. data/lib/wee/handler_registry.rb +0 -89
  189. data/lib/wee/holder.rb +0 -14
  190. data/lib/wee/html_canvas.rb +0 -379
  191. data/lib/wee/state_registry.rb +0 -173
  192. data/lib/wee/stuff.rb +0 -29
  193. data/lib/wee/webrick.rb +0 -15
@@ -0,0 +1,148 @@
1
+ module Wee
2
+
3
+ class Canvas
4
+ def initialize
5
+ @parent_brush = nil
6
+ @current_brush = nil
7
+ end
8
+
9
+ def close
10
+ @current_brush.close if @current_brush
11
+ @current_brush = nil
12
+ end
13
+
14
+ def set_brush(brush)
15
+ # tell previous brush to finish
16
+ @current_brush.close if @current_brush
17
+
18
+ brush.parent = @parent_brush
19
+ brush.canvas = self
20
+ @current_brush = brush
21
+
22
+ return brush
23
+ end
24
+
25
+ def nest(&block)
26
+ @parent_brush = @current_brush
27
+ @current_brush = nil
28
+ block.call
29
+ @current_brush.close if @current_brush
30
+ @parent_brush = @parent_brush.parent
31
+ end
32
+ end
33
+
34
+ class HtmlCanvas < Canvas
35
+ attr_reader :rendering_context # the current Wee::RenderingContext
36
+ attr_reader :document
37
+ attr_accessor :current_component
38
+
39
+ def initialize(rendering_context)
40
+ super()
41
+ @rendering_context = rendering_context
42
+ @document = rendering_context.document
43
+ end
44
+
45
+ def bold(*args, &block)
46
+ handle(Brush::GenericTagBrush.new("b"), *args, &block)
47
+ end
48
+
49
+ def method_missing(id, *args, &block)
50
+ handle(Brush::GenericTagBrush.new(id.to_s), *args, &block)
51
+ end
52
+
53
+ def table(*args, &block)
54
+ handle(Brush::TableTag.new, *args, &block)
55
+ end
56
+
57
+ def table_row(*args, &block)
58
+ handle(Brush::TableRowTag.new, *args, &block)
59
+ end
60
+
61
+ def table_data(*args, &block)
62
+ handle(Brush::TableDataTag.new, *args, &block)
63
+ end
64
+
65
+ def table_header(*args, &block)
66
+ handle(Brush::TableHeaderTag.new, *args, &block)
67
+ end
68
+
69
+ def form(*args, &block)
70
+ handle(Brush::FormTag.new, *args, &block)
71
+ end
72
+
73
+ def input(*args, &block)
74
+ handle(Brush::InputTag.new, *args, &block)
75
+ end
76
+
77
+ def text_input(*args, &block)
78
+ handle(Brush::TextInputTag.new, *args, &block)
79
+ end
80
+
81
+ def text_area(*args, &block)
82
+ handle(Brush::TextAreaTag.new, *args, &block)
83
+ end
84
+
85
+ def option(*args, &block)
86
+ handle(Brush::SelectOptionTag.new, *args, &block)
87
+ end
88
+
89
+ def select_list(items)
90
+ handle(Brush::SelectListTag.new(items))
91
+ end
92
+
93
+ def submit_button(*args, &block)
94
+ handle(Brush::SubmitButtonTag.new, *args, &block)
95
+ end
96
+
97
+ def image_button(*args, &block)
98
+ handle(Wee::Brush::ImageButtonTag.new, *args, &block)
99
+ end
100
+
101
+ def page(*args, &block)
102
+ handle(Brush::Page.new, *args, &block)
103
+ end
104
+
105
+ def anchor(*args, &block)
106
+ handle(Brush::AnchorTag.new, *args, &block)
107
+ end
108
+
109
+ def space(n=1)
110
+ set_brush(Brush::GenericTextBrush.new("&nbsp;"*n))
111
+ end
112
+
113
+ def break
114
+ set_brush(Brush::GenericTagBrush.new("br"))
115
+ end
116
+
117
+ def image
118
+ set_brush(Brush::GenericTagBrush.new("img"))
119
+ end
120
+
121
+ def text(str)
122
+ set_brush(Brush::GenericTextBrush.new(str))
123
+ end
124
+ alias << text
125
+
126
+ def encode_text(str)
127
+ set_brush(Brush::GenericEncodedTextBrush.new(str))
128
+ end
129
+
130
+ def render(obj)
131
+ self.close
132
+ obj.do_render_chain(@rendering_context)
133
+ nil
134
+ end
135
+
136
+ private
137
+
138
+ def handle(brush, *args, &block)
139
+ set_brush(brush)
140
+ if not args.empty? or block
141
+ brush.with(*args, &block)
142
+ else
143
+ brush
144
+ end
145
+ end
146
+ end
147
+
148
+ end # module Wee
@@ -1,3 +1,22 @@
1
+ require 'cgi'
2
+
3
+ # A class used to write out HTML documents easily.
4
+ #
5
+ # Usage:
6
+ #
7
+ # w = Wee::HtmlWriter.new(doc='')
8
+ # w.start_tag('html')
9
+ # w.start_tag('body')
10
+ # w.start_tag('a', 'href' => 'http://...')
11
+ # w.text('link')
12
+ # w.end_tag('a')
13
+ # w.end_tag('body')
14
+ # w.end_tag('html')
15
+ #
16
+ # p w.valid? # => true
17
+ # p doc # => '<html><body><a href="http://...">link</a></body></html>'
18
+ #
19
+
1
20
  class Wee::HtmlWriter
2
21
  attr_accessor :port
3
22
 
@@ -13,7 +32,13 @@ class Wee::HtmlWriter
13
32
  @tag_stack.push(tag)
14
33
 
15
34
  @port << "<#{ tag }"
16
- attributes.each {|k, v| @port << %[ #{ k }="#{ v }"] }
35
+ attributes.each {|k, v|
36
+ if v
37
+ @port << %[ #{ k }="#{ v }"]
38
+ else
39
+ @port << %[ #{ k }]
40
+ end
41
+ }
17
42
 
18
43
  self
19
44
  end
@@ -36,28 +61,18 @@ class Wee::HtmlWriter
36
61
  @port << ">"
37
62
  @open_start_tag = false
38
63
  end
64
+
39
65
  @port << str.to_s
40
66
 
41
67
  self
42
68
  end
43
69
  alias << text
44
70
 
71
+ def encode_text(str)
72
+ text(CGI.escapeHTML(str.to_s))
73
+ end
74
+
45
75
  def valid?
46
76
  @tag_stack.empty?
47
77
  end
48
-
49
- end
50
-
51
- if __FILE__ == $0
52
- doc = ''
53
- w = Wee::HtmlWriter.new(doc)
54
-
55
- w.start_tag('html')
56
-
57
- w.start_tag('blah')
58
- w.end_tag('blah')
59
-
60
- w.end_tag('html')
61
- p w.valid?
62
- p doc
63
78
  end
@@ -0,0 +1,57 @@
1
+ # Represents a request.
2
+ #
3
+ # NOTE that if there are fields named "xxx" and "xxx.yyy", the value of
4
+ # fields['xxx'] is a Hash {nil => val of "xxx", 'yyy' => val of 'xxx.yyy'}.
5
+ # This is for the image-button to work correctly.
6
+
7
+ class Wee::Request
8
+
9
+ attr_reader :request_handler_id, :page_id, :fields
10
+
11
+ def initialize(app_path, path, headers, fields)
12
+ @app_path, @path, @headers = app_path, path, headers
13
+
14
+ fields ||= Hash.new
15
+ @fields = Hash.new
16
+
17
+ # sorted by decreasing key length, e.g. "2.x" comes before "2"
18
+ fields.keys.sort_by {|k| -k.length}.each do |key|
19
+ val = fields[key]
20
+ if key.include?(".")
21
+ a, b = key.split(".", 2)
22
+ @fields[a] ||= Hash.new
23
+ @fields[a][b] = val
24
+ else
25
+ if @fields.has_key?(key)
26
+ @fields[key][nil] = val
27
+ else
28
+ @fields[key] = val
29
+ end
30
+ end
31
+ end
32
+
33
+ full_app_path, req_path = @path.split('@', 2)
34
+ @request_handler_id = @page_id = nil
35
+ @request_handler_id, @page_id = req_path.split('/', 2) if req_path
36
+ end
37
+
38
+ def application_path
39
+ @app_path
40
+ end
41
+
42
+ def build_url(request_handler_id=nil, page_id=nil, callback_id=nil)
43
+ raise ArgumentError if request_handler_id.nil? and not page_id.nil?
44
+
45
+ arr = [request_handler_id, page_id].compact
46
+
47
+ url = ""
48
+ url << @app_path
49
+ unless arr.empty?
50
+ url << '/' if url[-1,1] != '/' # /app@ -> /app/@
51
+ url << ('@' + arr.join('/'))
52
+ end
53
+ url << ('?' + callback_id) if callback_id
54
+
55
+ return url
56
+ end
57
+ end
@@ -0,0 +1,77 @@
1
+ class Wee::RequestHandler
2
+
3
+ # Points to the Wee::Application object for which this handler is registered.
4
+
5
+ attr_accessor :application
6
+
7
+ # Each request handler of an application has a unique id, which should be
8
+ # non-guessable, that means it has to be cryptographically secure.
9
+ #
10
+ # This id is used to uniquely identify a RequestHandler from each other. This
11
+ # is the same id used as a session id in class Wee::Session.
12
+
13
+ attr_accessor :id
14
+
15
+ # Expire after this number of seconds of inactivity. If this value is +nil+,
16
+ # the RequestHandler will never expire due to inactivity (but may still due
17
+ # to <i>max_lifetime</i>).
18
+
19
+ attr_accessor :expire_after
20
+
21
+ # The lifetime of this handler is limited to this number of seconds. A value
22
+ # of +nil+ means infinite lifetime.
23
+
24
+ attr_accessor :max_lifetime
25
+
26
+ # The maximum number of requests this handler should serve. A value of +nil+
27
+ # means infinity.
28
+
29
+ attr_accessor :max_requests
30
+
31
+
32
+ # Terminates the handler.
33
+
34
+ def teminate
35
+ @running = false
36
+ end
37
+
38
+ # Query whether this handler is still alive.
39
+
40
+ def alive?
41
+ return false if not @running
42
+ return false if @max_requests and @request_count >= @max_requests
43
+
44
+ now = Time.now
45
+ inactivity = now - @last_access
46
+ lifetime = now - @creation_time
47
+
48
+ return false if @expire_after and inactivity > @expire_after
49
+ return false if @max_lifetime and lifetime > @max_lifetime
50
+ return true
51
+ end
52
+
53
+ # Extend #handle_request in your own subclass.
54
+
55
+ def handle_request(context)
56
+ @request_count += 1
57
+ @last_access = Time.now
58
+ end
59
+
60
+ def statistics
61
+ now = Time.now
62
+ {
63
+ :last_access => @last_access, # The time when this handler was last accessed
64
+ :inactivity => now - @last_access, # The number of seconds of inactivity
65
+ :creation_time => @creation_time, # The time when this handler was created
66
+ :lifetime => now - @creation_time, # The uptime or lifetime of this handler in seconds
67
+ :request_count => @request_count # The number of requests served by this handler
68
+ }
69
+ end
70
+
71
+ def initialize
72
+ @last_access = @creation_time = Time.now
73
+ @request_count = 0
74
+ @running = true
75
+ end
76
+
77
+ end
@@ -0,0 +1,77 @@
1
+ require 'time'
2
+
3
+ class Wee::Response
4
+ DEFAULT_HEADER = { 'Content-Type' => 'text/html' }.freeze
5
+
6
+ attr_accessor :status, :content
7
+ attr_reader :header
8
+
9
+ def initialize(mime_type = 'text/html', content='')
10
+ @status = 200
11
+ @header = DEFAULT_HEADER.dup
12
+ @content = content
13
+ end
14
+
15
+ def content_type
16
+ @header['Content-Type']
17
+ end
18
+
19
+ def content_type=(mime_type)
20
+ @header['Content-Type'] = mime_type
21
+ end
22
+
23
+ def <<(str)
24
+ @content << str
25
+ end
26
+
27
+ end
28
+
29
+ class Wee::GenericResponse < Wee::Response
30
+
31
+ EXPIRE_OFFSET = 3600*24*365*20 # 20 years
32
+
33
+ def initialize(mime_type = 'text/html', content='')
34
+ super
35
+ @header['Expires'] = (Time.now + EXPIRE_OFFSET).rfc822
36
+ end
37
+
38
+ end
39
+
40
+ class Wee::RedirectResponse < Wee::GenericResponse
41
+ def initialize(location)
42
+ super('text/html', %[<title>302 - Redirect</title><h1>302 - Redirect</h1><p>You are being redirected to <a href="#{location}">#{location}</a>])
43
+ @status = 302
44
+ @header['Location'] = location
45
+ end
46
+ end
47
+
48
+ class Wee::RefreshResponse < Wee::GenericResponse
49
+ def initialize(message, location, seconds=10)
50
+ super('text/html', %[<html>
51
+ <head>
52
+ <meta http-equiv="REFRESH" content="#{seconds};URL=#{location}">
53
+ <title>#{message}</title>
54
+ </head>
55
+ <body>
56
+ <h1>#{message}</h1>
57
+ You are being redirected to <a href="#{location}">#{location}</a>
58
+ </body>
59
+ </html>])
60
+ end
61
+ end
62
+
63
+ class Wee::ErrorResponse < Wee::Response
64
+ def initialize(exception)
65
+ super('text/html', '')
66
+ @exception = exception
67
+ render(@content)
68
+ end
69
+
70
+ def render(c)
71
+ c << "<html><head><title>Error occured</title></head><body>"
72
+ c << "<p>#{ @exception }"
73
+ c << @exception.backtrace.join("<br/>")
74
+ c << "</p>"
75
+ c << "</body></html>"
76
+ end
77
+ end
data/lib/wee/session.rb CHANGED
@@ -1,87 +1,100 @@
1
1
  require 'wee/page'
2
- require 'wee/state_registry'
3
- require 'wee/handler_registry'
4
2
  require 'thread'
5
3
 
6
- class Wee::Session
4
+ class Wee::Session < Wee::RequestHandler
7
5
  attr_accessor :root_component, :page_store
8
- attr_reader :state_registry
9
6
 
10
7
  def self.current
11
- sess = Thread.current['Wee::Session']
8
+ sess = Thread.current[:wee_session]
12
9
  raise "not in session" if sess.nil?
13
10
  return sess
14
11
  end
15
12
 
16
- def register_object_for_backtracking(obj)
17
- @state_registry << obj
18
- end
19
-
20
13
  def initialize(&block)
21
- Thread.current['Wee::Session'] = self
14
+ Thread.current[:wee_session] = self
22
15
 
23
- @next_page_id = 0
24
- @mutex = Mutex.new
25
- @state_registry = Wee::StateRegistry.new
16
+ @idgen = Wee::SimpleIdGenerator.new
17
+ @in_queue, @out_queue = SizedQueue.new(1), SizedQueue.new(1)
26
18
 
27
19
  block.call(self)
28
20
 
29
21
  raise ArgumentError, "No root component specified" if @root_component.nil?
30
22
  raise ArgumentError, "No page_store specified" if @page_store.nil?
31
23
 
32
- @initial_snapshot = @state_registry.snapshot
24
+ @initial_snapshot = snapshot()
33
25
 
26
+ start_request_response_loop
27
+ super()
34
28
  ensure
35
- Thread.current['Wee::Session'] = nil
29
+ Thread.current[:wee_session] = nil
36
30
  end
37
31
 
38
- def setup(context)
32
+ def snapshot
33
+ @root_component.backtrack_state_chain(snap = Wee::Snapshot.new)
34
+ return snap.freeze
39
35
  end
40
36
 
37
+ # called by application to send the session a request
41
38
  def handle_request(context)
42
- Thread.current['Wee::Session'] = self
39
+ super
43
40
 
44
- @mutex.synchronize do
41
+ # Send a request to the session. If the session is currently busy
42
+ # processing another request, this will block.
43
+ @in_queue.push(context)
45
44
 
46
- setup(context)
45
+ # Wait for the response.
46
+ return @out_queue.pop
47
+ end
47
48
 
48
- if context.page_id.nil?
49
+ def start_request_response_loop
50
+ Thread.abort_on_exception = true
51
+ Thread.new {
52
+ Thread.current[:wee_session] = self
53
+ loop {
54
+ @context = @in_queue.pop
55
+ begin
56
+ process_request
57
+ rescue Exception => exn
58
+ @context.response = Wee::ErrorResponse.new(exn)
59
+ end
60
+ @out_queue.push(@context)
61
+ }
62
+ }
63
+ end
64
+
65
+ def create_page(snapshot)
66
+ idgen = Wee::SimpleIdGenerator.new
67
+ page = Wee::Page.new(snapshot, Wee::CallbackRegistry.new(idgen))
68
+ end
69
+
70
+ def process_request
71
+ if @context.request.page_id.nil?
49
72
 
50
73
  # No page_id was specified in the URL. This means that we start with a
51
74
  # fresh component and a fresh page_id, then redirect to render itself.
52
75
 
53
- handle_new_page_view(context, @initial_snapshot)
76
+ handle_new_page_view(@context, @initial_snapshot)
54
77
 
55
- elsif page = @page_store.fetch(context.page_id, false)
78
+ elsif page = @page_store.fetch(@context.request.page_id, false)
56
79
 
57
80
  # A valid page_id was specified and the corresponding page exists.
58
81
 
59
- page.snapshot.apply unless context.resource_id
60
-
61
- raise "invalid request URL! both request_id and handler_id given!" if context.resource_id and context.handler_id
62
-
63
- if context.resource_id
64
- # This is a resource request
65
- res = page.handler_registry.get_resource(context.resource_id)
82
+ page.snapshot.restore
66
83
 
67
- context.response.status = 200
68
- context.response['Content-Type'] = res.content_type
69
- context.response.body = res.content
84
+ p @context.request.fields if $DEBUG
70
85
 
71
- elsif context.handler_id.nil?
86
+ if @context.request.fields.empty?
72
87
 
73
88
  # No action/inputs were specified -> render page
74
89
  #
75
90
  # 1. Reset the action/input fields (as they are regenerated in the
76
91
  # rendering process).
77
92
  # 2. Render the page (respond).
78
- # 3. Store the page back into the store (only neccessary if page is not
79
- # stored in memory).
93
+ # 3. Store the page back into the store
80
94
 
81
- page = Wee::Page.new(page.snapshot, Wee::HandlerRegistry.new) # remove all action/input handlers
82
- context.handler_registry = page.handler_registry
83
- respond(context.freeze) # render
84
- @page_store[context.page_id] = page # store
95
+ page = create_page(page.snapshot) # remove all action/input handlers
96
+ respond(@context, page.callbacks) # render
97
+ @page_store[@context.request.page_id] = page # store
85
98
 
86
99
  else
87
100
 
@@ -90,9 +103,16 @@ class Wee::Session
90
103
  # We process the request and invoke actions/inputs. Then we generate a
91
104
  # new page view.
92
105
 
93
- context.handler_registry = page.handler_registry
94
- @root_component.decoration.process_request(context.freeze)
95
- handle_new_page_view(context)
106
+ callback_stream = Wee::CallbackStream.new(page.callbacks, @context.request.fields)
107
+
108
+ if callback_stream.all_of_type(:action).size > 1
109
+ raise "Not allowed to specify more than one action callback"
110
+ end
111
+
112
+ catch(:wee_back_to_session) {
113
+ @root_component.process_callbacks_chain(callback_stream)
114
+ }
115
+ handle_new_page_view(@context)
96
116
 
97
117
  end
98
118
 
@@ -108,37 +128,23 @@ class Wee::Session
108
128
  raise "Not yet implemented"
109
129
 
110
130
  end
111
-
112
- end # mutex
113
-
114
- ensure
115
- Thread.current['Wee::Session'] = nil
116
131
  end
117
132
 
118
133
  private
119
134
 
120
135
  def handle_new_page_view(context, snapshot=nil)
121
- new_page_id = create_new_page_id()
122
- new_page = Wee::Page.new(snapshot || @state_registry.snapshot, Wee::HandlerRegistry.new)
136
+ new_page_id = @idgen.next.to_s
137
+ new_page = create_page(snapshot || self.snapshot())
123
138
  @page_store[new_page_id] = new_page
124
-
125
- redirect_url = "#{ context.application.path }/s:#{ context.session_id }/p:#{ new_page_id }"
126
- context.response.set_redirect(WEBrick::HTTPStatus::MovedPermanently, redirect_url)
139
+ redirect_url = context.request.build_url(context.request.request_handler_id, new_page_id)
140
+ context.response = Wee::RedirectResponse.new(redirect_url)
127
141
  end
128
142
 
129
- def respond(context)
130
- context.response.status = 200
131
- context.response['Content-Type'] = 'text/html'
132
-
133
- rctx = Wee::RenderingContext.new(context, Wee::HtmlWriter.new(context.response.body))
134
- renderer = Wee::HtmlCanvas.new(rctx)
135
- @root_component.render_on(renderer)
136
- end
143
+ def respond(context, callbacks)
144
+ context.response = Wee::GenericResponse.new('text/html', '')
137
145
 
138
- def create_new_page_id
139
- @next_page_id.to_s
140
- ensure
141
- @next_page_id += 1
146
+ rctx = Wee::RenderingContext.new(context.request, context.response, callbacks, Wee::HtmlWriter.new(context.response.content))
147
+ @root_component.do_render_chain(rctx)
142
148
  end
143
149
 
144
150
  end