wee 0.4.0 → 0.5.0
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/ChangeLog +75 -0
- data/README +17 -9
- data/Rakefile +2 -2
- data/TODO +20 -0
- data/benchmark/Makefile +14 -9
- data/benchmark/counter.rb +11 -30
- data/benchmark/report_req.rb +12 -0
- data/doc/rdoc/classes/Array.html +12 -12
- data/doc/rdoc/classes/Cache/StorageCache.html +38 -38
- data/doc/rdoc/classes/Cache/Strategy/CapacityBounded.html +30 -30
- data/doc/rdoc/classes/Cache/Strategy/LFU.html +24 -24
- data/doc/rdoc/classes/Cache/Strategy/LRU.html +24 -24
- data/doc/rdoc/classes/Cache/Strategy/Unbounded.html +24 -24
- data/doc/rdoc/classes/Enumerable.html +6 -6
- data/doc/rdoc/classes/Object.html +12 -12
- data/doc/rdoc/classes/OgApplication.html +126 -0
- data/doc/rdoc/classes/OgScaffolder.html +401 -0
- data/doc/rdoc/classes/OgSession.html +172 -0
- data/doc/rdoc/classes/String.html +12 -12
- data/doc/rdoc/classes/Struct.html +12 -12
- data/doc/rdoc/classes/Wee.html +5 -62
- data/doc/rdoc/classes/Wee/AnswerDecoration.html +9 -9
- data/doc/rdoc/classes/Wee/Application.html +107 -39
- data/doc/rdoc/classes/Wee/Brush.html +22 -18
- data/doc/rdoc/classes/Wee/Brush/ActionCallbackMixin.html +17 -11
- data/doc/rdoc/classes/Wee/Brush/ActionURLCallbackMixin.html +18 -10
- data/doc/rdoc/classes/Wee/Brush/AnchorTag.html +30 -64
- data/doc/rdoc/classes/Wee/Brush/FileUploadTag.html +8 -10
- data/doc/rdoc/classes/Wee/Brush/FormTag.html +27 -79
- data/doc/rdoc/classes/Wee/Brush/GenericEncodedTextBrush.html +12 -12
- data/doc/rdoc/classes/Wee/Brush/GenericSingleTagBrush.html +146 -0
- data/doc/rdoc/classes/Wee/Brush/GenericTagBrush.html +179 -65
- data/doc/rdoc/classes/Wee/Brush/GenericTextBrush.html +12 -12
- data/doc/rdoc/classes/Wee/Brush/ImageButtonTag.html +16 -18
- data/doc/rdoc/classes/Wee/Brush/ImageTag.html +203 -0
- data/doc/rdoc/classes/Wee/Brush/InputCallbackMixin.html +17 -11
- data/doc/rdoc/classes/Wee/Brush/InputTag.html +15 -15
- data/doc/rdoc/classes/Wee/Brush/JavascriptTag.html +147 -0
- data/doc/rdoc/classes/Wee/Brush/Page.html +17 -17
- data/doc/rdoc/classes/Wee/Brush/SelectListTag.html +25 -50
- data/doc/rdoc/classes/Wee/Brush/SelectOptionTag.html +7 -38
- data/doc/rdoc/classes/Wee/Brush/SubmitButtonTag.html +7 -7
- data/doc/rdoc/classes/Wee/Brush/TableDataTag.html +15 -16
- data/doc/rdoc/classes/Wee/Brush/TableHeaderTag.html +7 -7
- data/doc/rdoc/classes/Wee/Brush/TableRowTag.html +65 -50
- data/doc/rdoc/classes/Wee/Brush/TableTag.html +7 -7
- data/doc/rdoc/classes/Wee/Brush/TextAreaTag.html +14 -64
- data/doc/rdoc/classes/Wee/Brush/TextInputTag.html +7 -7
- data/doc/rdoc/classes/Wee/Brush/ToCallback.html +146 -0
- data/doc/rdoc/classes/Wee/CallbackRegistry.html +40 -40
- data/doc/rdoc/classes/Wee/CallbackStream.html +18 -18
- data/doc/rdoc/classes/Wee/Canvas.html +24 -24
- data/doc/rdoc/classes/Wee/Component.html +232 -149
- data/doc/rdoc/classes/Wee/Component/OnAnswer.html +153 -0
- data/doc/rdoc/classes/Wee/Decoration.html +42 -42
- data/doc/rdoc/classes/Wee/Delegate.html +27 -27
- data/doc/rdoc/classes/Wee/ErrorResponse.html +12 -12
- data/doc/rdoc/classes/Wee/FormDecoration.html +148 -0
- data/doc/rdoc/classes/Wee/GenericResponse.html +6 -6
- data/doc/rdoc/classes/Wee/HtmlCanvas.html +296 -215
- data/doc/rdoc/classes/Wee/HtmlWriter.html +83 -81
- data/doc/rdoc/classes/Wee/LiteralMethodCallback.html +21 -16
- data/doc/rdoc/classes/Wee/MessageBox.html +180 -0
- data/doc/rdoc/classes/Wee/PageDecoration.html +30 -30
- data/doc/rdoc/classes/Wee/Presenter.html +237 -69
- data/doc/rdoc/classes/Wee/RedirectResponse.html +6 -6
- data/doc/rdoc/classes/Wee/RefreshResponse.html +6 -6
- data/doc/rdoc/classes/Wee/Request.html +18 -18
- data/doc/rdoc/classes/Wee/RequestHandler.html +43 -39
- data/doc/rdoc/classes/Wee/Response.html +24 -24
- data/doc/rdoc/classes/Wee/Session.html +746 -72
- data/doc/rdoc/classes/Wee/SimpleIdGenerator.html +18 -18
- data/doc/rdoc/classes/Wee/Snapshot.html +19 -19
- data/doc/rdoc/classes/Wee/Utils.html +138 -2
- data/doc/rdoc/classes/Wee/Utils/LRUCache.html +7 -7
- data/doc/rdoc/classes/Wee/ValueHolder.html +18 -18
- data/doc/rdoc/classes/Wee/WEBrickAdaptor.html +43 -68
- data/doc/rdoc/classes/Wee/WrapperDecoration.html +150 -0
- data/doc/rdoc/created.rid +1 -1
- data/doc/rdoc/files/README.html +29 -15
- data/doc/rdoc/files/lib/wee/adaptors/webrick_rb.html +1 -1
- data/doc/rdoc/files/lib/wee/application_rb.html +1 -1
- data/doc/rdoc/files/lib/wee/components/form_decoration_rb.html +101 -0
- data/doc/rdoc/files/lib/wee/components/messagebox_rb.html +101 -0
- data/doc/rdoc/files/lib/wee/components/page_decoration_rb.html +1 -1
- data/doc/rdoc/files/lib/wee/components/wrapper_decoration_rb.html +101 -0
- data/doc/rdoc/files/lib/wee/components_rb.html +4 -1
- data/doc/rdoc/files/lib/wee/continuation/core/component_rb.html +101 -0
- data/doc/rdoc/files/lib/wee/continuation/session_rb.html +110 -0
- data/doc/rdoc/files/lib/wee/continuation_rb.html +116 -0
- data/doc/rdoc/files/lib/wee/core/callback_rb.html +1 -1
- data/doc/rdoc/files/lib/wee/core/component_rb.html +1 -1
- data/doc/rdoc/files/lib/wee/core/presenter_rb.html +1 -1
- data/doc/rdoc/files/lib/wee/core_rb.html +3 -3
- data/doc/rdoc/files/lib/wee/databases/og_rb.html +108 -0
- data/doc/rdoc/files/lib/wee/renderer/html/brushes_rb.html +1 -1
- data/doc/rdoc/files/lib/wee/renderer/html/canvas_rb.html +1 -1
- data/doc/rdoc/files/lib/wee/renderer/html/writer_rb.html +1 -1
- data/doc/rdoc/files/lib/wee/requesthandler_rb.html +1 -1
- data/doc/rdoc/files/lib/wee/session_rb.html +1 -2
- data/doc/rdoc/files/lib/wee/utils/autoreload_rb.html +1 -1
- data/doc/rdoc/files/lib/wee/utils/cache_rb.html +1 -1
- data/doc/rdoc/files/lib/wee/utils/helper_rb.html +1 -8
- data/doc/rdoc/files/lib/wee/utils_rb.html +110 -0
- data/doc/rdoc/files/lib/wee_rb.html +1 -1
- data/doc/rdoc/fr_class_index.html +11 -1
- data/doc/rdoc/fr_file_index.html +8 -0
- data/doc/rdoc/fr_method_index.html +269 -228
- data/examples/calculator.rb +69 -0
- data/examples/calendar.rb +5 -17
- data/examples/example.rb +2 -2
- data/examples/hw.rb +17 -0
- data/examples/live-update.rb +45 -0
- data/examples/og-test.rb +51 -0
- data/lib/wee.rb +1 -1
- data/lib/wee/adaptors/webrick.rb +2 -0
- data/lib/wee/application.rb +16 -0
- data/lib/wee/components.rb +3 -0
- data/lib/wee/components/form_decoration.rb +7 -0
- data/{test → lib/wee}/components/messagebox.rb +1 -1
- data/lib/wee/components/page_decoration.rb +5 -5
- data/lib/wee/components/wrapper_decoration.rb +7 -0
- data/lib/wee/continuation.rb +5 -0
- data/lib/wee/continuation/core/component.rb +55 -0
- data/lib/wee/continuation/session.rb +217 -0
- data/lib/wee/core/callback.rb +11 -6
- data/lib/wee/core/component.rb +45 -33
- data/lib/wee/core/presenter.rb +68 -0
- data/lib/wee/databases/og.rb +114 -0
- data/lib/wee/renderer/html/brushes.rb +179 -98
- data/lib/wee/renderer/html/canvas.rb +37 -13
- data/lib/wee/renderer/html/writer.rb +34 -32
- data/lib/wee/requesthandler.rb +6 -3
- data/lib/wee/session.rb +73 -54
- data/lib/wee/utils.rb +5 -0
- data/lib/wee/utils/autoreload.rb +1 -1
- data/lib/wee/utils/cache.rb +0 -2
- data/lib/wee/utils/helper.rb +40 -8
- data/test/components/calltest-cont.rb +16 -0
- data/test/components/calltest.rb +15 -10
- data/test/stress.rb +31 -28
- data/test/stress_and_measure.rb +53 -0
- data/test/stressed_application.rb +15 -0
- data/test/test_html_writer.rb +9 -4
- metadata +236 -195
- data/benchmark/bench.sh +0 -24
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
require 'wee'
|
|
2
|
+
|
|
3
|
+
class RpnCalculator < Wee::Component
|
|
4
|
+
def initialize
|
|
5
|
+
super()
|
|
6
|
+
add_decoration(Wee::FormDecoration.new)
|
|
7
|
+
|
|
8
|
+
@number_stack = []
|
|
9
|
+
@input = ""
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def render
|
|
13
|
+
# the number stack
|
|
14
|
+
|
|
15
|
+
r.ul { @number_stack.each {|num| r.li(num)} }
|
|
16
|
+
|
|
17
|
+
# the display
|
|
18
|
+
|
|
19
|
+
r.text_input.value(@input).readonly
|
|
20
|
+
|
|
21
|
+
r.space
|
|
22
|
+
|
|
23
|
+
r.submit_button.value("Enter").callback {
|
|
24
|
+
@number_stack << @input.to_f
|
|
25
|
+
@input = ""
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
r.break
|
|
29
|
+
|
|
30
|
+
# the number buttons
|
|
31
|
+
|
|
32
|
+
(0..9).each {|num|
|
|
33
|
+
r.submit_button.value(num).callback { @input << num.to_s }
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
# the decimal point
|
|
37
|
+
|
|
38
|
+
r.submit_button.value(".").disabled(@input.include?(".")).callback {
|
|
39
|
+
@input << "."
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
# binary operators
|
|
43
|
+
|
|
44
|
+
['+', '-', '*', '/'].each { |op|
|
|
45
|
+
r.submit_button.value(op).callback {
|
|
46
|
+
unless @input.empty?
|
|
47
|
+
@number_stack << @input.to_f
|
|
48
|
+
@input = ""
|
|
49
|
+
end
|
|
50
|
+
r2, r1 = @number_stack.pop, @number_stack.pop
|
|
51
|
+
@number_stack.push(r1.send(op, r2))
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
if __FILE__ == $0
|
|
59
|
+
require 'wee/adaptors/webrick'
|
|
60
|
+
require 'wee/utils'
|
|
61
|
+
|
|
62
|
+
app = Wee::Utils.app_for {
|
|
63
|
+
comp = RpnCalculator.new
|
|
64
|
+
comp.add_decoration(Wee::PageDecoration.new('RPN Calculator'))
|
|
65
|
+
comp
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
Wee::WEBrickAdaptor.register('/calc' => app).start
|
|
69
|
+
end
|
data/examples/calendar.rb
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
$LOAD_PATH.unshift << "../lib"
|
|
4
4
|
require 'wee'
|
|
5
5
|
require 'wee/adaptors/webrick'
|
|
6
|
-
require 'wee/utils
|
|
6
|
+
require 'wee/utils'
|
|
7
7
|
require 'date'
|
|
8
8
|
|
|
9
9
|
class Date
|
|
@@ -184,7 +184,7 @@ class MiniCalendar < Wee::Component
|
|
|
184
184
|
#
|
|
185
185
|
def render_header
|
|
186
186
|
r.table_row do
|
|
187
|
-
r.table_header.colspan(4).with { r.
|
|
187
|
+
r.table_header.colspan(4).with { r.encode_text(month_heading) }
|
|
188
188
|
r.table_header { r.anchor.callback { go_prev }.with(prev_month_abbr) }
|
|
189
189
|
r.table_header { r.anchor.callback { go_next }.with(next_month_abbr) }
|
|
190
190
|
r.table_header { browse? ? r.space : r.anchor.callback { back }.style('color: black').with('X') }
|
|
@@ -194,7 +194,7 @@ class MiniCalendar < Wee::Component
|
|
|
194
194
|
# Render Calendar footer
|
|
195
195
|
#
|
|
196
196
|
def render_footer
|
|
197
|
-
r.table_row { r.table_header.colspan(7).with { r.
|
|
197
|
+
r.table_row { r.table_header.colspan(7).with { r.encode_text(today_string) } }
|
|
198
198
|
end
|
|
199
199
|
|
|
200
200
|
# Render Calendar
|
|
@@ -316,7 +316,7 @@ if __FILE__ == $0
|
|
|
316
316
|
#
|
|
317
317
|
def render_icon
|
|
318
318
|
icon = 'http://www.softcomplex.com/products/tigra_calendar/img/cal.gif'
|
|
319
|
-
r.
|
|
319
|
+
r.image.src(icon).width(16).height(16).border(0).alt('Calendar')
|
|
320
320
|
end
|
|
321
321
|
|
|
322
322
|
# Render Calendar demo
|
|
@@ -349,18 +349,6 @@ if __FILE__ == $0
|
|
|
349
349
|
end
|
|
350
350
|
end
|
|
351
351
|
|
|
352
|
-
|
|
353
|
-
def initialize
|
|
354
|
-
super do
|
|
355
|
-
self.root_component = CustomCalendarDemo.new
|
|
356
|
-
self.page_store = Wee::Utils::LRUCache.new(10) # backtrack up to 10 pages
|
|
357
|
-
end
|
|
358
|
-
end
|
|
359
|
-
end
|
|
360
|
-
|
|
361
|
-
app = Wee::Application.new {|app|
|
|
362
|
-
app.default_request_handler { MySession.new }
|
|
363
|
-
app.id_generator = Wee::SimpleIdGenerator.new(rand(1_000_000))
|
|
364
|
-
}
|
|
352
|
+
app = Wee::Utils.app_for(CustomCalendarDemo)
|
|
365
353
|
Wee::WEBrickAdaptor.register('/app' => app).start
|
|
366
354
|
end
|
data/examples/example.rb
CHANGED
|
@@ -204,6 +204,6 @@ end
|
|
|
204
204
|
|
|
205
205
|
if __FILE__ == $0
|
|
206
206
|
require 'wee/adaptors/webrick'
|
|
207
|
-
require 'wee/utils
|
|
208
|
-
Wee::WEBrickAdaptor.register('/app' => Wee::
|
|
207
|
+
require 'wee/utils'
|
|
208
|
+
Wee::WEBrickAdaptor.register('/app' => Wee::Utils.app_for(MainPage)).start
|
|
209
209
|
end
|
data/examples/hw.rb
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
require 'rubygems'
|
|
2
|
+
require 'wee'
|
|
3
|
+
require 'wee/adaptors/webrick'
|
|
4
|
+
require 'wee/utils'
|
|
5
|
+
|
|
6
|
+
class HelloWorld < Wee::Component
|
|
7
|
+
def initialize
|
|
8
|
+
super
|
|
9
|
+
add_decoration(Wee::PageDecoration.new("Hello World"))
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def render
|
|
13
|
+
r.h1 "Hello World from Wee!"
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
Wee::WEBrickAdaptor.register('/app' => Wee::Utils.app_for(HelloWorld)).start
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
require 'rubygems'
|
|
2
|
+
require 'wee'
|
|
3
|
+
require 'wee/adaptors/webrick'
|
|
4
|
+
require 'wee/utils'
|
|
5
|
+
|
|
6
|
+
class LiveUpdateTest < Wee::Component
|
|
7
|
+
def initialize
|
|
8
|
+
super
|
|
9
|
+
add_decoration(Wee::PageDecoration.new("Hello World"))
|
|
10
|
+
@live_updates = 0
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def render
|
|
14
|
+
r.h1 "Hello World from Wee!"
|
|
15
|
+
r.anchor.callback { @live_updates += 1; do_live_update }.with('live update')
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def render_live_update
|
|
19
|
+
r.page { r.text "Live-updates works! This is no. #{ @live_updates }" }
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def do_live_update
|
|
23
|
+
# generate a response
|
|
24
|
+
response = Wee::GenericResponse.new('text/html', '')
|
|
25
|
+
|
|
26
|
+
# get the current context we are in
|
|
27
|
+
context = session.current_context
|
|
28
|
+
|
|
29
|
+
# a rendering context is needed to use 'r' (if you want, you can
|
|
30
|
+
# simply omit this and just return the response with some html/xml filled
|
|
31
|
+
# in.
|
|
32
|
+
rendering_context = Wee::RenderingContext.new(context.request,
|
|
33
|
+
context.response, session.current_page.callbacks,
|
|
34
|
+
Wee::HtmlWriter.new(response.content))
|
|
35
|
+
|
|
36
|
+
with_renderer_for(rendering_context) do
|
|
37
|
+
# call your own render method for the live-update
|
|
38
|
+
render_live_update
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
throw :wee_live_update, response
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
Wee::WEBrickAdaptor.register('/app' => Wee::Utils.app_for(LiveUpdateTest)).start
|
data/examples/og-test.rb
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# -----------------------------------------
|
|
2
|
+
# The datamodel
|
|
3
|
+
# -----------------------------------------
|
|
4
|
+
|
|
5
|
+
require 'og'
|
|
6
|
+
|
|
7
|
+
class Customer
|
|
8
|
+
prop_accessor :address, String, :sql => 'VARCHAR(100) NOT NULL'
|
|
9
|
+
prop_accessor :email, String, :sql => 'VARCHAR(50) NOT NULL'
|
|
10
|
+
prop_accessor :password, String, :sql => 'VARCHAR(10) NOT NULL'
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# -----------------------------------------
|
|
14
|
+
# The Wee part
|
|
15
|
+
# -----------------------------------------
|
|
16
|
+
|
|
17
|
+
require 'wee'
|
|
18
|
+
|
|
19
|
+
class CustomerList < Wee::Component
|
|
20
|
+
def initialize
|
|
21
|
+
super()
|
|
22
|
+
add_decoration(Wee::PageDecoration.new("Hello World"))
|
|
23
|
+
|
|
24
|
+
add_child(@customer_list = OgScaffolder.new(Customer))
|
|
25
|
+
add_child(@customer_list2 = OgScaffolder.new(Customer))
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def render
|
|
29
|
+
r.render @customer_list
|
|
30
|
+
r.render @customer_list2
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
if __FILE__ == $0
|
|
35
|
+
require 'wee/adaptors/webrick'
|
|
36
|
+
require 'wee/utils'
|
|
37
|
+
require 'wee/databases/og'
|
|
38
|
+
|
|
39
|
+
DB_CONFIG = {
|
|
40
|
+
:address => "localhost",
|
|
41
|
+
:database => "mneumann",
|
|
42
|
+
:backend => "psql",
|
|
43
|
+
:user => "mneumann",
|
|
44
|
+
:password => "",
|
|
45
|
+
:connection_count => 10
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
app = Wee::Utils.app_for(CustomerList, :application => OgApplication, :session => OgSession)
|
|
49
|
+
app.db = Og::Database.new(DB_CONFIG)
|
|
50
|
+
Wee::WEBrickAdaptor.register('/app' => app).start
|
|
51
|
+
end
|
data/lib/wee.rb
CHANGED
data/lib/wee/adaptors/webrick.rb
CHANGED
data/lib/wee/application.rb
CHANGED
|
@@ -102,6 +102,22 @@ class Wee::Application
|
|
|
102
102
|
context.response = Wee::ErrorResponse.new(exn)
|
|
103
103
|
end
|
|
104
104
|
|
|
105
|
+
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
106
|
+
# :section: Properties
|
|
107
|
+
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
108
|
+
|
|
109
|
+
attr_accessor :properties
|
|
110
|
+
|
|
111
|
+
# Returns an "owned" property for the given +klass+.
|
|
112
|
+
|
|
113
|
+
def get_property(prop, klass)
|
|
114
|
+
if @properties
|
|
115
|
+
@properties.fetch(klass, {})[prop]
|
|
116
|
+
else
|
|
117
|
+
nil
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
105
121
|
private
|
|
106
122
|
|
|
107
123
|
# MUST be called while holding @mutex
|
data/lib/wee/components.rb
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
class Wee::PageDecoration < Wee::
|
|
1
|
+
class Wee::PageDecoration < Wee::WrapperDecoration
|
|
2
2
|
def initialize(title='')
|
|
3
3
|
@title = title
|
|
4
4
|
super()
|
|
@@ -6,9 +6,9 @@ class Wee::PageDecoration < Wee::Decoration
|
|
|
6
6
|
|
|
7
7
|
def global?() true end
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
private
|
|
10
|
+
|
|
11
|
+
def render_wrapper
|
|
12
|
+
r.page.title(@title).with { yield }
|
|
13
13
|
end
|
|
14
14
|
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
class Wee::Component
|
|
2
|
+
|
|
3
|
+
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
4
|
+
# :section: Call/Answer
|
|
5
|
+
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
6
|
+
|
|
7
|
+
protected
|
|
8
|
+
|
|
9
|
+
# Call another component. The calling component is neither rendered nor are
|
|
10
|
+
# it's callbacks processed until the called component answers using method
|
|
11
|
+
# #answer.
|
|
12
|
+
#
|
|
13
|
+
# [+component+]
|
|
14
|
+
# The component to be called.
|
|
15
|
+
#
|
|
16
|
+
# <b>How it works</b>
|
|
17
|
+
#
|
|
18
|
+
# At first a continuation is created. The component to be called is then
|
|
19
|
+
# wrapped with an AnswerDecoration and the continuation is assigned to it's
|
|
20
|
+
# +on_answer+ attribute. Then a Delegate decoration is added to the calling
|
|
21
|
+
# component (self), which delegates to the component to be called
|
|
22
|
+
# (+component+). Then we unwind the calling stack back to the Session by
|
|
23
|
+
# throwing <i>:wee_back_to_session</i>. This means, that there is only ever
|
|
24
|
+
# one action callback invoked per request. When at a later point in time the
|
|
25
|
+
# called component invokes #answer, this will throw a <i>:wee_answer</i>
|
|
26
|
+
# exception which is catched in the AnswerDecoration. The AnswerDecoration
|
|
27
|
+
# then jumps back to the continuation we created at the beginning, and
|
|
28
|
+
# finally method #call returns.
|
|
29
|
+
#
|
|
30
|
+
# Note that #call returns to an "old" stack-frame from a previous request.
|
|
31
|
+
# That is why we throw <i>:wee_back_to_session</i> after invoking an action
|
|
32
|
+
# callback, and that's why only ever one is invoked. We could remove this
|
|
33
|
+
# limitation without problems, but then there would be a difference between
|
|
34
|
+
# those action callbacks that call other components and those that do not.
|
|
35
|
+
|
|
36
|
+
def call(component, return_callback=:use_continuation, *additional_args)
|
|
37
|
+
add_decoration(delegate = Wee::Delegate.new(component))
|
|
38
|
+
component.add_decoration(answer = Wee::AnswerDecoration.new)
|
|
39
|
+
|
|
40
|
+
if return_callback == :use_continuation
|
|
41
|
+
result = callcc {|cc|
|
|
42
|
+
answer.on_answer = cc
|
|
43
|
+
throw :wee_back_to_session
|
|
44
|
+
}
|
|
45
|
+
remove_decoration(delegate)
|
|
46
|
+
component.remove_decoration(answer)
|
|
47
|
+
return result
|
|
48
|
+
else
|
|
49
|
+
answer.on_answer = OnAnswer.new(self, component, delegate, answer,
|
|
50
|
+
return_callback, additional_args)
|
|
51
|
+
throw :wee_back_to_session
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
end
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
require 'wee/page'
|
|
2
|
+
require 'thread'
|
|
3
|
+
require 'timeout'
|
|
4
|
+
|
|
5
|
+
class Wee::Session < Wee::RequestHandler
|
|
6
|
+
attr_accessor :root_component, :page_store
|
|
7
|
+
|
|
8
|
+
def self.current
|
|
9
|
+
sess = Thread.current[:wee_session]
|
|
10
|
+
raise "not in session" if sess.nil?
|
|
11
|
+
return sess
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def initialize(&block)
|
|
15
|
+
Thread.current[:wee_session] = self
|
|
16
|
+
|
|
17
|
+
@idgen = Wee::SimpleIdGenerator.new
|
|
18
|
+
@in_queue, @out_queue = SizedQueue.new(1), SizedQueue.new(1)
|
|
19
|
+
|
|
20
|
+
block.call(self)
|
|
21
|
+
|
|
22
|
+
raise ArgumentError, "No root component specified" if @root_component.nil?
|
|
23
|
+
raise ArgumentError, "No page_store specified" if @page_store.nil?
|
|
24
|
+
|
|
25
|
+
@initial_snapshot = snapshot()
|
|
26
|
+
|
|
27
|
+
start_request_response_loop
|
|
28
|
+
super()
|
|
29
|
+
ensure
|
|
30
|
+
Thread.current[:wee_session] = nil
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def snapshot
|
|
34
|
+
@root_component.backtrack_state_chain(snap = Wee::Snapshot.new)
|
|
35
|
+
return snap.freeze
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# called by application to send the session a request
|
|
39
|
+
def handle_request(context)
|
|
40
|
+
super
|
|
41
|
+
|
|
42
|
+
# Send a request to the session. If the session is currently busy
|
|
43
|
+
# processing another request, this will block.
|
|
44
|
+
@in_queue.push(context)
|
|
45
|
+
|
|
46
|
+
# Wait for the response.
|
|
47
|
+
return @out_queue.pop
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def start_request_response_loop
|
|
51
|
+
Thread.abort_on_exception = true
|
|
52
|
+
Thread.new {
|
|
53
|
+
Thread.current[:wee_session] = self
|
|
54
|
+
loop {
|
|
55
|
+
@context = nil
|
|
56
|
+
|
|
57
|
+
# get a request, check whether this session is alive after every 5
|
|
58
|
+
# seconds.
|
|
59
|
+
while @context.nil?
|
|
60
|
+
begin
|
|
61
|
+
Timeout.timeout(5) {
|
|
62
|
+
@context = @in_queue.pop
|
|
63
|
+
}
|
|
64
|
+
rescue Timeout::Error
|
|
65
|
+
break unless alive?
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# abort thread if no longer alive
|
|
70
|
+
break if not alive?
|
|
71
|
+
|
|
72
|
+
raise "invalid request" if @context.nil?
|
|
73
|
+
|
|
74
|
+
begin
|
|
75
|
+
awake
|
|
76
|
+
process_request
|
|
77
|
+
sleep
|
|
78
|
+
rescue Exception => exn
|
|
79
|
+
@context.response = Wee::ErrorResponse.new(exn)
|
|
80
|
+
end
|
|
81
|
+
@out_queue.push(@context)
|
|
82
|
+
}
|
|
83
|
+
p "session loop terminated" if $DEBUG
|
|
84
|
+
}
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def create_page(snapshot)
|
|
88
|
+
idgen = Wee::SimpleIdGenerator.new
|
|
89
|
+
page = Wee::Page.new(snapshot, Wee::CallbackRegistry.new(idgen))
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Is called before process_request is invoked
|
|
93
|
+
# Can be used to setup e.g. a database connection.
|
|
94
|
+
def awake
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Is called after process_request is run
|
|
98
|
+
# Can be used to release e.g. a database connection.
|
|
99
|
+
def sleep
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def process_request
|
|
103
|
+
if @context.request.page_id.nil?
|
|
104
|
+
|
|
105
|
+
# No page_id was specified in the URL. This means that we start with a
|
|
106
|
+
# fresh component and a fresh page_id, then redirect to render itself.
|
|
107
|
+
|
|
108
|
+
handle_new_page_view(@context, @initial_snapshot)
|
|
109
|
+
|
|
110
|
+
elsif @page = @page_store.fetch(@context.request.page_id, false)
|
|
111
|
+
|
|
112
|
+
# A valid page_id was specified and the corresponding page exists.
|
|
113
|
+
|
|
114
|
+
@page.snapshot.restore
|
|
115
|
+
|
|
116
|
+
p @context.request.fields if $DEBUG
|
|
117
|
+
|
|
118
|
+
if @context.request.fields.empty?
|
|
119
|
+
|
|
120
|
+
# No action/inputs were specified -> render page
|
|
121
|
+
#
|
|
122
|
+
# 1. Reset the action/input fields (as they are regenerated in the
|
|
123
|
+
# rendering process).
|
|
124
|
+
# 2. Render the page (respond).
|
|
125
|
+
# 3. Store the page back into the store
|
|
126
|
+
|
|
127
|
+
@page = create_page(@page.snapshot) # remove all action/input handlers
|
|
128
|
+
respond(@context, @page.callbacks) # render
|
|
129
|
+
@page_store[@context.request.page_id] = @page # store
|
|
130
|
+
|
|
131
|
+
else
|
|
132
|
+
|
|
133
|
+
# Actions/inputs were specified.
|
|
134
|
+
#
|
|
135
|
+
# We process the request and invoke actions/inputs. Then we generate a
|
|
136
|
+
# new page view.
|
|
137
|
+
|
|
138
|
+
callback_stream = Wee::CallbackStream.new(@page.callbacks, @context.request.fields)
|
|
139
|
+
|
|
140
|
+
if callback_stream.all_of_type(:action).size > 1
|
|
141
|
+
raise "Not allowed to specify more than one action callback"
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
live_update_response = catch(:wee_live_update) {
|
|
145
|
+
catch(:wee_back_to_session) {
|
|
146
|
+
@root_component.process_callbacks_chain(callback_stream)
|
|
147
|
+
}
|
|
148
|
+
nil
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if live_update_response
|
|
152
|
+
# replace existing page with new snapshot
|
|
153
|
+
@page.snapshot = self.snapshot
|
|
154
|
+
@page_store[@context.request.page_id] = @page
|
|
155
|
+
@context.response = live_update_response
|
|
156
|
+
else
|
|
157
|
+
handle_new_page_view(@context)
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
else
|
|
163
|
+
|
|
164
|
+
# A page_id was specified in the URL, but there's no page for it in the
|
|
165
|
+
# page store. Either the page has timed out, or an invalid page_id was
|
|
166
|
+
# specified.
|
|
167
|
+
#
|
|
168
|
+
# TODO:: Display an "invalid page or page timed out" message, which
|
|
169
|
+
# forwards to /app/session-id
|
|
170
|
+
|
|
171
|
+
raise "Not yet implemented"
|
|
172
|
+
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def current_context
|
|
177
|
+
@context
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def current_page
|
|
181
|
+
@page
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
185
|
+
# :section: Properties
|
|
186
|
+
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
187
|
+
|
|
188
|
+
attr_accessor :properties
|
|
189
|
+
|
|
190
|
+
# Returns an "owned" property for the given +klass+.
|
|
191
|
+
|
|
192
|
+
def get_property(prop, klass)
|
|
193
|
+
if @properties
|
|
194
|
+
@properties.fetch(klass, {})[prop]
|
|
195
|
+
else
|
|
196
|
+
nil
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
private
|
|
201
|
+
|
|
202
|
+
def handle_new_page_view(context, snapshot=nil)
|
|
203
|
+
new_page_id = @idgen.next.to_s
|
|
204
|
+
new_page = create_page(snapshot || self.snapshot())
|
|
205
|
+
@page_store[new_page_id] = new_page
|
|
206
|
+
redirect_url = context.request.build_url(context.request.request_handler_id, new_page_id)
|
|
207
|
+
context.response = Wee::RedirectResponse.new(redirect_url)
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
def respond(context, callbacks)
|
|
211
|
+
context.response = Wee::GenericResponse.new('text/html', '')
|
|
212
|
+
|
|
213
|
+
rctx = Wee::RenderingContext.new(context.request, context.response, callbacks, Wee::HtmlWriter.new(context.response.content))
|
|
214
|
+
@root_component.do_render_chain(rctx)
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
end
|