mullen-wee 2.2.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.
Files changed (73) hide show
  1. data/README.rdoc +127 -0
  2. data/Rakefile +25 -0
  3. data/aWee.gemspec +26 -0
  4. data/examples/ObjectSpaceBrowser.rb +191 -0
  5. data/examples/ajax.rb +73 -0
  6. data/examples/apotomo-webhunter/main.rb +75 -0
  7. data/examples/apotomo-webhunter/public/images/bear_trap_charged.png +0 -0
  8. data/examples/apotomo-webhunter/public/images/bear_trap_snapped.png +0 -0
  9. data/examples/apotomo-webhunter/public/images/cheese.png +0 -0
  10. data/examples/apotomo-webhunter/public/images/dark_forest.jpg +0 -0
  11. data/examples/apotomo-webhunter/public/images/mouse.png +0 -0
  12. data/examples/apotomo-webhunter/public/javascripts/jquery-1.3.2.min.js +19 -0
  13. data/examples/apotomo-webhunter/public/javascripts/wee-jquery.js +19 -0
  14. data/examples/apotomo-webhunter/public/stylesheets/forest.css +33 -0
  15. data/examples/arc_challenge.rb +42 -0
  16. data/examples/arc_challenge2.rb +46 -0
  17. data/examples/cheese_task.rb +27 -0
  18. data/examples/continuations.rb +28 -0
  19. data/examples/demo.rb +135 -0
  20. data/examples/demo/calculator.rb +63 -0
  21. data/examples/demo/calendar.rb +333 -0
  22. data/examples/demo/counter.rb +38 -0
  23. data/examples/demo/editable_counter.rb +36 -0
  24. data/examples/demo/example.rb +142 -0
  25. data/examples/demo/file_upload.rb +19 -0
  26. data/examples/demo/messagebox.rb +15 -0
  27. data/examples/demo/radio.rb +33 -0
  28. data/examples/demo/window.rb +71 -0
  29. data/examples/hw.rb +11 -0
  30. data/examples/i18n/app.rb +16 -0
  31. data/examples/i18n/locale/de/app.po +25 -0
  32. data/examples/i18n/locale/en/app.po +25 -0
  33. data/examples/pager.rb +102 -0
  34. data/lib/wee.rb +109 -0
  35. data/lib/wee/application.rb +89 -0
  36. data/lib/wee/callback.rb +109 -0
  37. data/lib/wee/component.rb +363 -0
  38. data/lib/wee/decoration.rb +251 -0
  39. data/lib/wee/dialog.rb +171 -0
  40. data/lib/wee/external_resource.rb +39 -0
  41. data/lib/wee/html_brushes.rb +795 -0
  42. data/lib/wee/html_canvas.rb +254 -0
  43. data/lib/wee/html_document.rb +52 -0
  44. data/lib/wee/html_writer.rb +71 -0
  45. data/lib/wee/id_generator.rb +81 -0
  46. data/lib/wee/jquery.rb +11 -0
  47. data/lib/wee/jquery/jquery-1.3.2.min.js +19 -0
  48. data/lib/wee/jquery/wee-jquery.js +19 -0
  49. data/lib/wee/locale.rb +56 -0
  50. data/lib/wee/lru_cache.rb +91 -0
  51. data/lib/wee/presenter.rb +44 -0
  52. data/lib/wee/renderer.rb +72 -0
  53. data/lib/wee/request.rb +56 -0
  54. data/lib/wee/response.rb +68 -0
  55. data/lib/wee/rightjs.rb +11 -0
  56. data/lib/wee/rightjs/rightjs-1.5.2.min.js +9 -0
  57. data/lib/wee/rightjs/wee-rightjs.js +18 -0
  58. data/lib/wee/root_component.rb +45 -0
  59. data/lib/wee/session.rb +366 -0
  60. data/lib/wee/state.rb +102 -0
  61. data/lib/wee/task.rb +16 -0
  62. data/test/bm_render.rb +34 -0
  63. data/test/component_spec.rb +40 -0
  64. data/test/stress/plotter.rb +84 -0
  65. data/test/stress/stress_client.rb +51 -0
  66. data/test/stress/stress_local.rb +86 -0
  67. data/test/stress/stress_server.rb +83 -0
  68. data/test/test_component.rb +106 -0
  69. data/test/test_html_canvas.rb +25 -0
  70. data/test/test_html_writer.rb +32 -0
  71. data/test/test_lru_cache.rb +51 -0
  72. data/test/test_request.rb +42 -0
  73. metadata +185 -0
@@ -0,0 +1,38 @@
1
+ class Counter < Wee::Component
2
+ attr_accessor :count
3
+
4
+ def initialize(initial_count=0)
5
+ @count = initial_count
6
+ add_decoration Wee::StyleDecoration.new(self)
7
+ end
8
+
9
+ def state(s) super
10
+ s.add_ivar(self, :@count)
11
+ end
12
+
13
+ def dec
14
+ @count -= 1
15
+ end
16
+
17
+ def inc
18
+ @count += 1
19
+ end
20
+
21
+ def style
22
+ ".wee-Counter a { border: 1px dotted blue; margin: 2px; }"
23
+ end
24
+
25
+ def render(r)
26
+ r.div.oid.css_class('wee-Counter').with {
27
+ r.anchor.callback_method(:dec).with("--")
28
+ r.space
29
+ render_count(r)
30
+ r.space
31
+ r.anchor.callback_method(:inc).with("++")
32
+ }
33
+ end
34
+
35
+ def render_count(r)
36
+ r.text @count.to_s
37
+ end
38
+ end
@@ -0,0 +1,36 @@
1
+ # NEEDS: FormDecoration
2
+
3
+ require 'demo/counter'
4
+
5
+ class EditableCounter < Counter
6
+
7
+ def initialize(initial_count=0)
8
+ super
9
+ @show_edit_field = false
10
+ end
11
+
12
+ def state(s)
13
+ super
14
+ s.add_ivar(self, :@show_edit_field, @show_edit_field)
15
+ end
16
+
17
+ def render_count(r)
18
+ if @show_edit_field
19
+ r.text_input.value(@count).size(6).callback {|val| self.count = val}
20
+ r.submit_button.value('S').callback { submit }
21
+ else
22
+ r.anchor.callback { submit }.with(@count)
23
+ end
24
+ end
25
+
26
+ def submit
27
+ if @count.to_s !~ /^\d+$/
28
+ call Wee::MessageBox.new("You entered an invalid counter! Please try again!")
29
+ @count = 0
30
+ else
31
+ @show_edit_field = !@show_edit_field
32
+ end
33
+ @count = @count.to_i
34
+ end
35
+
36
+ end
@@ -0,0 +1,142 @@
1
+ $LOAD_PATH.unshift "../lib"
2
+ require 'rubygems'
3
+ require 'wee'
4
+
5
+ require 'demo/window'
6
+ require 'demo/editable_counter'
7
+ require 'demo/messagebox'
8
+
9
+ class RegexpValidatedInput < Wee::Component
10
+
11
+ def initialize(init_value, regexp)
12
+ super()
13
+ @regexp = regexp
14
+ self.value = init_value
15
+ @error = false
16
+ end
17
+
18
+ def state(s)
19
+ super
20
+ s.add(self)
21
+ end
22
+
23
+ def value
24
+ @value
25
+ end
26
+
27
+ def value=(new_value)
28
+ raise unless new_value =~ @regexp
29
+ @input = @value = new_value
30
+ end
31
+
32
+ def render(r)
33
+ r.form do
34
+ r.text_input.value(@input).callback {|val| self.input = val }
35
+ r.text %(<div style="color: red">Invalid input</div>) if @error
36
+ end
37
+ end
38
+
39
+ def input
40
+ @input
41
+ end
42
+
43
+ def input=(str)
44
+ @input = str
45
+
46
+ if @input =~ @regexp
47
+ @value = str
48
+ @error = false
49
+ else
50
+ @error = true
51
+ end
52
+ end
53
+
54
+ end
55
+
56
+ class MainPage < Wee::Component
57
+ def initialize
58
+ super()
59
+ @counters = (1..10).map {|i|
60
+ Window.new {|w|
61
+ w.title = "Cnt #{ i }"
62
+ w.pos_x = "200px"
63
+ w.pos_y = "#{i*50}px"
64
+ w << EditableCounter.new(i)
65
+ }
66
+ }
67
+
68
+ @val_inp = RegexpValidatedInput.new('Michael Neumann', /^\w+\s+\w+$/)
69
+
70
+ @arr = []
71
+ @text = ""
72
+
73
+ @list1 = (0..9).to_a
74
+ @selected1 = []
75
+ @list2 = []
76
+ @selected2 = []
77
+ end
78
+
79
+ def children
80
+ [@val_inp, *@counters]
81
+ end
82
+
83
+ def state(s)
84
+ super
85
+ s.add(@counters)
86
+ state_decoration(s)
87
+ s.add(@arr)
88
+ s.add(@text)
89
+
90
+ s.add(@list1)
91
+ s.add(@selected1)
92
+ s.add(@list2)
93
+ s.add(@selected2)
94
+ end
95
+
96
+ attr_accessor :text
97
+
98
+ def render(r)
99
+ r.page.title("Counter Test").with do
100
+
101
+ r.form do
102
+ r.select_list(@list1).size(10).multiple.selected(@selected1).callback {|choosen| @selected1.replace(choosen)}
103
+ r.submit_button.value('-&gt;').callback { @list2.push(*@selected1); @list1.replace(@list1-@selected1); @selected1.replace([]) }
104
+ r.submit_button.value('&lt;-').callback { @list1.push(*@selected2); @list2.replace(@list2-@selected2); @selected2.replace([]) }
105
+ r.select_list(@list2).size(10).multiple.selected(@selected2).callback {|choosen| @selected2.replace(choosen)}
106
+ end
107
+
108
+ r.form do
109
+
110
+ @counters.each { |cnt|
111
+ r.render(cnt)
112
+ }
113
+
114
+ r.render(@val_inp)
115
+
116
+ @arr.each do |a|
117
+ r.text(a)
118
+ r.break
119
+ end
120
+
121
+ end
122
+
123
+ r.form do
124
+ r.text_input.value(@text).callback{|val| @text = val}
125
+ r.submit_button.callback{add}.value('add')
126
+ end
127
+
128
+ end
129
+ end
130
+
131
+ def add
132
+ call Wee::MessageBox.new("Do you really want to add '" + @text + "'?") do |res|
133
+ if res
134
+ call Wee::MessageBox.new("Do you really really really want to add '" + @text + "'?") do |res2|
135
+ @arr << @text if res2
136
+ end
137
+ end
138
+ end
139
+ end
140
+ end
141
+
142
+ Wee.run(MainPage) if __FILE__ == $0
@@ -0,0 +1,19 @@
1
+ class FileUploadTest < Wee::Component
2
+ def render(r)
3
+ r.file_upload.callback {|f| call Uploaded.new(f[:tempfile]) }
4
+ r.break
5
+ r.submit_button.name('Upload')
6
+ end
7
+
8
+ class Uploaded < Wee::Component
9
+ def initialize(file)
10
+ super()
11
+ @file = file
12
+ end
13
+
14
+ def render(r)
15
+ r.pre { r.encode_text @file.read }
16
+ r.anchor.callback_method(:answer).with('back')
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,15 @@
1
+ class Wee::MessageBox < Wee::Component
2
+ def initialize(text)
3
+ super()
4
+ @text = text
5
+ end
6
+
7
+ def render(r)
8
+ r.bold(@text)
9
+ r.form do
10
+ r.submit_button.value('OK').callback { answer true }
11
+ r.space
12
+ r.submit_button.value('Cancel').callback { answer false }
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,33 @@
1
+ class RadioTest < Wee::Component
2
+ def render(r)
3
+ grp1 = r.new_radio_group
4
+ grp2 = r.new_radio_group
5
+
6
+ r.paragraph
7
+ r.text "Group1"
8
+ r.text " (your choice: #{@g1})" if @g1
9
+ r.break
10
+
11
+ r.text "R1: "
12
+ r.radio_button.group(grp1).checked(@g1.nil? || @g1 == 'R1').callback { @g1 = 'R1' }
13
+ r.break
14
+
15
+ r.text "R2: "
16
+ r.radio_button.group(grp1).checked(@g1 == 'R2').callback { @g1 = 'R2' }
17
+
18
+ r.paragraph
19
+ r.text "Group2"
20
+ r.text " (your choice: #{@g2})" if @g2
21
+ r.break
22
+
23
+ r.text "R1: "
24
+ r.radio_button.group(grp2).checked(@g2.nil? || @g2 == 'R1').callback { @g2 = 'R1' }
25
+ r.break
26
+
27
+ r.text "R2: "
28
+ r.radio_button.group(grp2).checked(@g2 == 'R2').callback { @g2 = 'R2' }
29
+
30
+ r.paragraph
31
+ r.submit_button.value('Submit')
32
+ end
33
+ end
@@ -0,0 +1,71 @@
1
+ class Window < Wee::Component
2
+
3
+ attr_accessor :title, :pos_x, :pos_y
4
+
5
+ def initialize(&block)
6
+ super()
7
+ @status = :normal
8
+ @pos_x, @pos_y = "0px", "0px"
9
+ @children = []
10
+ block.call(self) if block
11
+ end
12
+
13
+ def <<(c)
14
+ @children << c
15
+ end
16
+
17
+ def children() @children end
18
+
19
+ def state(s)
20
+ super
21
+ s.add_ivar(self, :@status, @status)
22
+ end
23
+
24
+ def process_callbacks(callbacks)
25
+ return if @status == :closed
26
+ super
27
+ end
28
+
29
+ def render(r)
30
+ return if @status == :closed
31
+
32
+ r.table.cellspacing(0).style("border:solid 1px grey; position: absolute; left: #{@pos_x}; top: #{@pos_y};").with do
33
+ r.table_row.style("background-color: lightblue; width: 100%").with do
34
+ r.table_data.style("text-align: left; width: 66%").with(@title)
35
+ r.table_data.style("text-align: right").with do
36
+ if @status == :minimized
37
+ r.anchor.callback{maximize}.with("^")
38
+ else
39
+ r.anchor.callback{minimize}.with("_")
40
+ end
41
+ r.space
42
+ r.anchor.callback{close}.with("x")
43
+ end
44
+ end
45
+ r.table_row do
46
+ r.table_data.colspan(2).with do
47
+ if @status == :normal
48
+ for child in self.children do
49
+ r.render(child)
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+
57
+ public
58
+
59
+ def minimize
60
+ @status = :minimized
61
+ end
62
+
63
+ def maximize
64
+ @status = :normal
65
+ end
66
+
67
+ def close
68
+ @status = :closed
69
+ end
70
+
71
+ end
@@ -0,0 +1,11 @@
1
+ $LOAD_PATH.unshift "../lib"
2
+ require 'rubygems' if RUBY_VERSION < "1.9"
3
+ require 'wee'
4
+
5
+ class HelloWorld < Wee::RootComponent
6
+ def render(r)
7
+ r.h1 "Hello World from Wee!"
8
+ end
9
+ end
10
+
11
+ Wee.run(HelloWorld) if __FILE__ == $0
@@ -0,0 +1,16 @@
1
+ $LOAD_PATH.unshift "../../lib"
2
+ require 'rubygems'
3
+ require 'wee'
4
+ require 'wee/locale'
5
+
6
+ class HelloWorld < Wee::RootComponent
7
+ def render(r)
8
+ r.h1 _("Hello World!")
9
+ r.select_list(%w(en de)).selected(session.locale).labels(["English", "Deutsch"]).callback {|lang| session.locale = lang}
10
+ r.submit_button.value(_("Set"))
11
+ end
12
+ end
13
+
14
+ Wee::Application.load_locale("app", %w(en de), "en", :path => "locale", :type => :po)
15
+
16
+ HelloWorld.run if __FILE__ == $0
@@ -0,0 +1,25 @@
1
+ # SOME DESCRIPTIVE TITLE.
2
+ # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
3
+ # This file is distributed under the same license as the PACKAGE package.
4
+ # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
5
+ #
6
+ #, fuzzy
7
+ msgid ""
8
+ msgstr ""
9
+ "Project-Id-Version: PACKAGE VERSION\n"
10
+ "POT-Creation-Date: 2010-01-05 00:27+0100\n"
11
+ "PO-Revision-Date: 2010-01-05 00:27+0100\n"
12
+ "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
13
+ "Language-Team: LANGUAGE <LL@li.org>\n"
14
+ "MIME-Version: 1.0\n"
15
+ "Content-Type: text/plain; charset=UTF-8\n"
16
+ "Content-Transfer-Encoding: 8bit\n"
17
+ "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
18
+
19
+ #: app.rb:8
20
+ msgid "Hello World!"
21
+ msgstr "Hallo Welt!"
22
+
23
+ #: app.rb:10
24
+ msgid "Set"
25
+ msgstr "Einstellen"
@@ -0,0 +1,25 @@
1
+ # SOME DESCRIPTIVE TITLE.
2
+ # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
3
+ # This file is distributed under the same license as the PACKAGE package.
4
+ # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
5
+ #
6
+ #, fuzzy
7
+ msgid ""
8
+ msgstr ""
9
+ "Project-Id-Version: PACKAGE VERSION\n"
10
+ "POT-Creation-Date: 2010-01-05 00:27+0100\n"
11
+ "PO-Revision-Date: 2010-01-05 00:27+0100\n"
12
+ "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
13
+ "Language-Team: LANGUAGE <LL@li.org>\n"
14
+ "MIME-Version: 1.0\n"
15
+ "Content-Type: text/plain; charset=UTF-8\n"
16
+ "Content-Transfer-Encoding: 8bit\n"
17
+ "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
18
+
19
+ #: app.rb:8
20
+ msgid "Hello World!"
21
+ msgstr ""
22
+
23
+ #: app.rb:10
24
+ msgid "Set"
25
+ msgstr ""
@@ -0,0 +1,102 @@
1
+ class Pager < Wee::Component
2
+ attr_accessor :num_entries, :entries_per_page
3
+ attr_reader :current_page
4
+
5
+ def initialize(num_entries=0)
6
+ super()
7
+ @num_entries = num_entries
8
+ @current_page = 0
9
+ @entries_per_page = 20
10
+ yield self if block_given?
11
+ end
12
+
13
+ # Returns the number of pages
14
+
15
+ def num_pages
16
+ n, rest = @num_entries.divmod(@entries_per_page)
17
+ if rest > 0 then n + 1 else n end
18
+ end
19
+
20
+ # Returns the index of the first entry on the current page
21
+
22
+ def current_start_index
23
+ @current_page * @entries_per_page
24
+ end
25
+
26
+ # Returns the index of the last page
27
+
28
+ def last_page_index
29
+ num_pages() - 1
30
+ end
31
+
32
+ # Go to first page
33
+
34
+ def first
35
+ goto(0)
36
+ end
37
+
38
+ # Go to last page
39
+
40
+ def last
41
+ goto(last_page_index())
42
+ end
43
+
44
+ # Go to previous page
45
+
46
+ def prev
47
+ goto(@current_page - 1)
48
+ end
49
+
50
+ # Go to next page
51
+
52
+ def next
53
+ goto(@current_page + 1)
54
+ end
55
+
56
+ # Go to page with index +page+
57
+ # Note that page-indices start with zero!
58
+
59
+ def goto(page)
60
+ @current_page = page
61
+ validate
62
+ end
63
+
64
+ def render(r)
65
+ return if num_pages() <= 0
66
+ render_arrow(r, :first, "<<", "Go to first page"); r.space(2)
67
+ render_arrow(r, :prev, "<", "Go to previous page"); r.space(2)
68
+ render_index(r); r.space(2)
69
+ render_arrow(r, :next, ">", "Go to next page"); r.space(2)
70
+ render_arrow(r, :last, ">>", "Go to last page")
71
+ end
72
+
73
+ private
74
+
75
+ def render_arrow(r, sym, text, tooltip=text)
76
+ r.anchor.callback_method(sym).tooltip(tooltip).with { r.encode_text(text) }
77
+ end
78
+
79
+ def render_index(r)
80
+ last = last_page_index()
81
+ (0 .. last).each do |i|
82
+ if i == @current_page
83
+ render_page_num(r, i, true)
84
+ else
85
+ render_page_num(r, i, false)
86
+ end
87
+ r.space if i < last
88
+ end
89
+ end
90
+
91
+ def render_page_num(r, num, current)
92
+ if current
93
+ r.bold(num+1)
94
+ else
95
+ r.anchor.callback{ goto(num) }.with(num+1)
96
+ end
97
+ end
98
+
99
+ def validate
100
+ @current_page = [[0, @current_page].max, last_page_index()].min
101
+ end
102
+ end