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.
- data/INSTALL +7 -0
- data/README +268 -0
- data/Rakefile +26 -0
- data/TODO +116 -0
- data/benchmark/Centrino1300/result.2000.counter.action +45 -0
- data/benchmark/Centrino1300/result.2000.counter.render +43 -0
- data/benchmark/Centrino1300/result.2000.filehandler +43 -0
- data/benchmark/Centrino600/result.2000.counter.action +47 -0
- data/benchmark/Centrino600/result.2000.counter.render +45 -0
- data/benchmark/Centrino600/result.2000.filehandler +43 -0
- data/benchmark/Makefile +48 -0
- data/benchmark/bench.sh +24 -0
- data/benchmark/counter.rb +96 -0
- data/benchmark/filehandler.rb +6 -0
- data/doc/rdoc/classes/Array.html +172 -0
- data/doc/rdoc/classes/Cache.html +126 -0
- data/doc/rdoc/classes/Cache/StorageCache.html +320 -0
- data/doc/rdoc/classes/Cache/Strategy.html +128 -0
- data/doc/rdoc/classes/Cache/Strategy/CapacityBounded.html +269 -0
- data/doc/rdoc/classes/Cache/Strategy/LFU.html +238 -0
- data/doc/rdoc/classes/Cache/Strategy/LFU/Item.html +111 -0
- data/doc/rdoc/classes/Cache/Strategy/LRU.html +238 -0
- data/doc/rdoc/classes/Cache/Strategy/LRU/Item.html +111 -0
- data/doc/rdoc/classes/Cache/Strategy/Unbounded.html +225 -0
- data/doc/rdoc/classes/Cache/Strategy/Unbounded/Item.html +111 -0
- data/doc/rdoc/classes/Enumerable.html +146 -0
- data/doc/rdoc/classes/LiteralMethod.html +196 -0
- data/doc/rdoc/classes/Object.html +178 -0
- data/doc/rdoc/classes/String.html +172 -0
- data/doc/rdoc/classes/Struct.html +174 -0
- data/doc/rdoc/classes/Wee.html +160 -0
- data/doc/rdoc/classes/Wee/AnswerDecoration.html +182 -0
- data/doc/rdoc/classes/Wee/Application.html +337 -0
- data/doc/rdoc/classes/Wee/Brush.html +245 -0
- data/doc/rdoc/classes/Wee/Brush/ActionCallbackMixin.html +149 -0
- data/doc/rdoc/classes/Wee/Brush/ActionMixin.html +146 -0
- data/doc/rdoc/classes/Wee/Brush/ActionURLCallbackMixin.html +157 -0
- data/doc/rdoc/classes/Wee/Brush/AnchorTag.html +210 -0
- data/doc/rdoc/classes/Wee/Brush/AssignMixin.html +141 -0
- data/doc/rdoc/classes/Wee/Brush/CallbackMixin.html +141 -0
- data/doc/rdoc/classes/Wee/Brush/FormTag.html +225 -0
- data/doc/rdoc/classes/Wee/Brush/GenericEncodedTextBrush.html +176 -0
- data/doc/rdoc/classes/Wee/Brush/GenericTagBrush.html +283 -0
- data/doc/rdoc/classes/Wee/Brush/GenericTextBrush.html +176 -0
- data/doc/rdoc/classes/Wee/Brush/ImageButtonTag.html +195 -0
- data/doc/rdoc/classes/Wee/Brush/InputCallbackMixin.html +149 -0
- data/doc/rdoc/classes/Wee/Brush/InputTag.html +172 -0
- data/doc/rdoc/classes/Wee/Brush/Page.html +193 -0
- data/doc/rdoc/classes/Wee/Brush/SelectListTag.html +267 -0
- data/doc/rdoc/classes/Wee/Brush/SelectOptionTag.html +177 -0
- data/doc/rdoc/classes/Wee/Brush/SubmitButtonTag.html +154 -0
- data/doc/rdoc/classes/Wee/Brush/TableDataTag.html +173 -0
- data/doc/rdoc/classes/Wee/Brush/TableHeaderTag.html +146 -0
- data/doc/rdoc/classes/Wee/Brush/TableRowTag.html +277 -0
- data/doc/rdoc/classes/Wee/Brush/TableTag.html +146 -0
- data/doc/rdoc/classes/Wee/Brush/TextAreaTag.html +229 -0
- data/doc/rdoc/classes/Wee/Brush/TextInputTag.html +154 -0
- data/doc/rdoc/classes/Wee/Callback.html +231 -0
- data/doc/rdoc/classes/Wee/CallbackRegistry.html +308 -0
- data/doc/rdoc/classes/Wee/CallbackStream.html +227 -0
- data/doc/rdoc/classes/Wee/Canvas.html +235 -0
- data/doc/rdoc/classes/Wee/Component.html +933 -0
- data/doc/rdoc/classes/Wee/Context.html +111 -0
- data/doc/rdoc/classes/Wee/Decoration.html +338 -0
- data/doc/rdoc/classes/Wee/Delegate.html +247 -0
- data/doc/rdoc/classes/Wee/ErrorPage.html +175 -0
- data/doc/rdoc/classes/Wee/ErrorResponse.html +180 -0
- data/doc/rdoc/classes/Wee/GenericResponse.html +162 -0
- data/doc/rdoc/classes/Wee/HtmlCanvas.html +751 -0
- data/doc/rdoc/classes/Wee/HtmlWriter.html +351 -0
- data/doc/rdoc/classes/Wee/LiteralMethodCallback.html +180 -0
- data/doc/rdoc/classes/Wee/MethodCallback.html +193 -0
- data/doc/rdoc/classes/Wee/Page.html +111 -0
- data/doc/rdoc/classes/Wee/Presenter.html +521 -0
- data/doc/rdoc/classes/Wee/RedirectResponse.html +150 -0
- data/doc/rdoc/classes/Wee/RefreshResponse.html +157 -0
- data/doc/rdoc/classes/Wee/RenderingContext.html +111 -0
- data/doc/rdoc/classes/Wee/Request.html +268 -0
- data/doc/rdoc/classes/Wee/RequestHandler.html +336 -0
- data/doc/rdoc/classes/Wee/Response.html +260 -0
- data/doc/rdoc/classes/Wee/Session.html +469 -0
- data/doc/rdoc/classes/Wee/SimpleIdGenerator.html +198 -0
- data/doc/rdoc/classes/Wee/Snapshot.html +211 -0
- data/doc/rdoc/classes/Wee/StateHolder.html +149 -0
- data/doc/rdoc/classes/Wee/StateRegistry.html +434 -0
- data/doc/rdoc/classes/Wee/StateRegistry/Snapshot.html +320 -0
- data/doc/rdoc/classes/Wee/StateRegistry/WithObject.html +153 -0
- data/doc/rdoc/classes/Wee/Utils.html +111 -0
- data/doc/rdoc/classes/Wee/Utils/LRUCache.html +148 -0
- data/doc/rdoc/classes/Wee/ValueHolder.html +220 -0
- data/doc/rdoc/classes/Wee/WEBrickAdaptor.html +330 -0
- data/doc/rdoc/created.rid +1 -0
- data/doc/rdoc/files/INSTALL.html +118 -0
- data/doc/rdoc/files/README.html +466 -0
- data/doc/rdoc/files/lib/cache/cache_rb.html +101 -0
- data/doc/rdoc/files/lib/wee/adaptors/webrick_rb.html +108 -0
- data/doc/rdoc/files/lib/wee/application_rb.html +112 -0
- data/doc/rdoc/files/lib/wee/callback_rb.html +107 -0
- data/doc/rdoc/files/lib/wee/component_ext_rb.html +101 -0
- data/doc/rdoc/files/lib/wee/component_rb.html +109 -0
- data/doc/rdoc/files/lib/wee/context_rb.html +101 -0
- data/doc/rdoc/files/lib/wee/core/callback_rb.html +115 -0
- data/doc/rdoc/files/lib/wee/core/component_rb.html +108 -0
- data/doc/rdoc/files/lib/wee/core/decoration_rb.html +133 -0
- data/doc/rdoc/files/lib/wee/core/presenter_rb.html +112 -0
- data/doc/rdoc/files/lib/wee/core/snapshot_rb.html +113 -0
- data/doc/rdoc/files/lib/wee/core/valueholder_rb.html +110 -0
- data/doc/rdoc/files/lib/wee/core_rb.html +131 -0
- data/doc/rdoc/files/lib/wee/decoration_rb.html +101 -0
- data/doc/rdoc/files/lib/wee/holder_rb.html +101 -0
- data/doc/rdoc/files/lib/wee/html_canvas_rb.html +108 -0
- data/doc/rdoc/files/lib/wee/html_writer_rb.html +108 -0
- data/doc/rdoc/files/lib/wee/idgen_rb.html +101 -0
- data/doc/rdoc/files/lib/wee/page_rb.html +101 -0
- data/doc/rdoc/files/lib/wee/presenter_rb.html +112 -0
- data/doc/rdoc/files/lib/wee/renderer/html/brushes_rb.html +101 -0
- data/doc/rdoc/files/lib/wee/renderer/html/canvas_rb.html +101 -0
- data/doc/rdoc/files/lib/wee/renderer/html/writer_rb.html +108 -0
- data/doc/rdoc/files/lib/wee/rendering/html/brushes_rb.html +101 -0
- data/doc/rdoc/files/lib/wee/rendering/html/canvas_rb.html +101 -0
- data/doc/rdoc/files/lib/wee/rendering/html/writer_rb.html +108 -0
- data/doc/rdoc/files/lib/wee/request_rb.html +113 -0
- data/doc/rdoc/files/lib/wee/requesthandler_rb.html +101 -0
- data/doc/rdoc/files/lib/wee/response_rb.html +108 -0
- data/doc/rdoc/files/lib/wee/session_rb.html +109 -0
- data/doc/rdoc/files/lib/wee/snapshot_ext_rb.html +101 -0
- data/doc/rdoc/files/lib/wee/snapshot_rb.html +107 -0
- data/doc/rdoc/files/lib/wee/state_registry_rb.html +110 -0
- data/doc/rdoc/files/lib/wee/stuff_rb.html +144 -0
- data/doc/rdoc/files/lib/wee/utils/cache_rb.html +108 -0
- data/doc/rdoc/files/lib/wee/webrick_rb.html +108 -0
- data/doc/rdoc/files/lib/wee_rb.html +132 -0
- data/doc/rdoc/fr_class_index.html +93 -0
- data/doc/rdoc/fr_file_index.html +51 -0
- data/doc/rdoc/fr_method_index.html +242 -0
- data/doc/rdoc/index.html +24 -0
- data/doc/rdoc/rdoc-style.css +208 -0
- data/examples/ObjectSpaceBrowser.rb +199 -0
- data/examples/calendar.rb +366 -0
- data/examples/cc.rb +94 -0
- data/examples/draw.rb +91 -0
- data/examples/example.rb +223 -0
- data/examples/test.rb +66 -0
- data/examples/window.rb +53 -0
- data/lib/cache/cache.rb +9 -0
- data/lib/wee.rb +18 -8
- data/lib/wee/adaptors/webrick.rb +73 -0
- data/lib/wee/application.rb +69 -71
- data/lib/wee/context.rb +2 -12
- data/lib/wee/core.rb +15 -0
- data/lib/wee/core/callback.rb +108 -0
- data/lib/wee/core/component.rb +314 -0
- data/lib/wee/core/decoration.rb +129 -0
- data/lib/wee/core/presenter.rb +132 -0
- data/lib/wee/core/snapshot.rb +21 -0
- data/lib/wee/core/valueholder.rb +19 -0
- data/lib/wee/idgen.rb +13 -0
- data/lib/wee/page.rb +1 -1
- data/lib/wee/renderer/html/brushes.rb +435 -0
- data/lib/wee/renderer/html/canvas.rb +148 -0
- data/lib/wee/{html_writer.rb → renderer/html/writer.rb} +31 -16
- data/lib/wee/request.rb +57 -0
- data/lib/wee/requesthandler.rb +77 -0
- data/lib/wee/response.rb +77 -0
- data/lib/wee/session.rb +70 -64
- data/lib/wee/{snapshot.rb → snapshot_ext.rb} +4 -4
- data/test/components/calltest.rb +16 -0
- data/test/components/counter.rb +17 -0
- data/test/components/messagebox.rb +15 -0
- data/test/components/page.rb +14 -0
- data/test/components/page_decoration.rb +7 -0
- data/test/stress.rb +64 -0
- data/test/test_component.rb +106 -0
- data/test/test_html_canvas.rb +25 -0
- data/test/test_html_writer.rb +27 -0
- data/test/test_request.rb +13 -0
- data/test/utils/cross.rb +65 -0
- data/test/utils/generic_plotter.rb +28 -0
- data/test/utils/gnuplot.rb +31 -0
- data/test/utils/measure_memory.rb +9 -0
- data/test/utils/memory_plotter.rb +10 -0
- data/test/utils/object_plotter.rb +10 -0
- data/test/utils/webrick_background.rb +31 -0
- data/wee.gemspec +22 -0
- metadata +222 -18
- data/lib/wee/component.rb +0 -126
- data/lib/wee/delegate_decoration.rb +0 -22
- data/lib/wee/handler_registry.rb +0 -89
- data/lib/wee/holder.rb +0 -14
- data/lib/wee/html_canvas.rb +0 -379
- data/lib/wee/state_registry.rb +0 -173
- data/lib/wee/stuff.rb +0 -29
- 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(" "*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|
|
|
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
|
data/lib/wee/request.rb
ADDED
|
@@ -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
|
data/lib/wee/response.rb
ADDED
|
@@ -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[
|
|
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[
|
|
14
|
+
Thread.current[:wee_session] = self
|
|
22
15
|
|
|
23
|
-
@
|
|
24
|
-
@
|
|
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 =
|
|
24
|
+
@initial_snapshot = snapshot()
|
|
33
25
|
|
|
26
|
+
start_request_response_loop
|
|
27
|
+
super()
|
|
34
28
|
ensure
|
|
35
|
-
Thread.current[
|
|
29
|
+
Thread.current[:wee_session] = nil
|
|
36
30
|
end
|
|
37
31
|
|
|
38
|
-
def
|
|
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
|
-
|
|
39
|
+
super
|
|
43
40
|
|
|
44
|
-
|
|
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
|
-
|
|
45
|
+
# Wait for the response.
|
|
46
|
+
return @out_queue.pop
|
|
47
|
+
end
|
|
47
48
|
|
|
48
|
-
|
|
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.
|
|
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
|
-
|
|
68
|
-
context.response['Content-Type'] = res.content_type
|
|
69
|
-
context.response.body = res.content
|
|
84
|
+
p @context.request.fields if $DEBUG
|
|
70
85
|
|
|
71
|
-
|
|
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
|
|
79
|
-
# stored in memory).
|
|
93
|
+
# 3. Store the page back into the store
|
|
80
94
|
|
|
81
|
-
page =
|
|
82
|
-
context
|
|
83
|
-
|
|
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
|
-
|
|
94
|
-
|
|
95
|
-
|
|
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 =
|
|
122
|
-
new_page =
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
139
|
-
@
|
|
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
|