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,254 @@
1
+ require 'wee/renderer'
2
+
3
+ module Wee
4
+
5
+ class HtmlCanvas < Renderer
6
+
7
+ def initialize(*args)
8
+ super
9
+ @current_brush = nil
10
+ end
11
+
12
+ def close
13
+ @current_brush.close if @current_brush
14
+ @current_brush = nil
15
+ end
16
+
17
+ def nest
18
+ old_brush = @current_brush
19
+ # we don't want that Brush#close is calledas #nest
20
+ # is called from #with -> this avoids an infinite loop
21
+ @current_brush = nil
22
+ yield
23
+ @current_brush.close if @current_brush
24
+ @current_brush = old_brush
25
+ end
26
+
27
+ def self.brush_tag(attr, klass, *args_to_new)
28
+ args_to_new = args_to_new.map {|a| a.inspect}.join(", ")
29
+ if klass.instance_method(:with).arity != 0
30
+ class_eval %{
31
+ def #{attr}(*args, &block)
32
+ handle(#{klass}.new(#{args_to_new}), *args, &block)
33
+ end
34
+ }
35
+ elsif klass.nesting?
36
+ class_eval %{
37
+ def #{attr}(&block)
38
+ handle2(#{klass}.new(#{args_to_new}), &block)
39
+ end
40
+ }
41
+ else
42
+ class_eval %{
43
+ def #{attr}
44
+ handle3(#{klass}.new(#{args_to_new}))
45
+ end
46
+ }
47
+ end
48
+ end
49
+
50
+ def self.generic_tag(*attrs)
51
+ attrs.each {|attr| brush_tag attr, Brush::GenericTagBrush, attr }
52
+ end
53
+
54
+ def self.generic_single_tag(*attrs)
55
+ attrs.each {|attr| brush_tag attr, Brush::GenericSingleTagBrush, attr }
56
+ end
57
+
58
+ generic_tag :html, :head, :body, :title, :label
59
+ generic_tag :h1, :h2, :h3, :h4, :h5
60
+ generic_tag :div, :span, :ul, :ol, :li, :pre
61
+ generic_single_tag :hr
62
+
63
+ brush_tag :link, Brush::LinkTag
64
+ brush_tag :table, Brush::TableTag
65
+ brush_tag :table_row, Brush::TableRowTag
66
+ brush_tag :table_data, Brush::TableDataTag
67
+ brush_tag :table_header, Brush::TableHeaderTag
68
+ brush_tag :form, Brush::FormTag
69
+ brush_tag :input, Brush::InputTag
70
+ brush_tag :hidden_input, Brush::HiddenInputTag
71
+ brush_tag :password_input, Brush::PasswordInputTag
72
+ brush_tag :text_input, Brush::TextInputTag
73
+ brush_tag :radio_button, Brush::RadioButtonTag
74
+ brush_tag :check_box, Brush::CheckboxTag; alias checkbox check_box
75
+ brush_tag :text_area, Brush::TextAreaTag
76
+ brush_tag :option, Brush::SelectOptionTag
77
+ brush_tag :submit_button, Brush::SubmitButtonTag
78
+ brush_tag :image_button, Brush::ImageButtonTag
79
+ brush_tag :file_upload, Brush::FileUploadTag
80
+ brush_tag :page, Brush::Page
81
+ brush_tag :anchor, Brush::AnchorTag
82
+ brush_tag :javascript, Brush::JavascriptTag
83
+ brush_tag :image, Brush::ImageTag
84
+ brush_tag :style, Brush::StyleTag
85
+
86
+ brush_tag :bold, Brush::GenericTagBrush, :b
87
+ brush_tag :paragraph, Brush::GenericTagBrush, :p
88
+ brush_tag :break, Brush::GenericSingleTagBrush, :br
89
+
90
+ def select_list(items, &block)
91
+ handle2(Brush::SelectListTag.new(items), &block)
92
+ end
93
+
94
+ HTML_NBSP = "&nbsp;".freeze
95
+
96
+ def space(n=1)
97
+ text(HTML_NBSP*n)
98
+ end
99
+
100
+ def text(str)
101
+ @current_brush.close if @current_brush
102
+ @current_brush = nil
103
+ @document.text(str)
104
+ nil
105
+ end
106
+
107
+ alias << text
108
+
109
+ def encode_text(str)
110
+ @current_brush.close if @current_brush
111
+ @current_brush = nil
112
+ @document.encode_text(str)
113
+ nil
114
+ end
115
+
116
+ def css(str)
117
+ style.type('text/css').with(str)
118
+ end
119
+
120
+ #
121
+ # Depends on an existing divert location :styles.
122
+ #
123
+ def render_style(component)
124
+ once(component.class) { try_divert(:styles, component.style) }
125
+ end
126
+
127
+ #
128
+ # converts \n into <br/>
129
+ #
130
+ def multiline_text(str, encode=true)
131
+ @current_brush.close if @current_brush
132
+ @current_brush = nil
133
+
134
+ first = true
135
+ str.each_line do |line|
136
+ @document.single_tag(:br) unless first
137
+ first = false
138
+
139
+ if encode
140
+ @document.encode_text(line)
141
+ else
142
+ @document.text(line)
143
+ end
144
+ end
145
+ end
146
+
147
+ #
148
+ # Define a divert location
149
+ #
150
+ def define_divert(tag)
151
+ @document.define_divert(tag)
152
+ end
153
+
154
+ #
155
+ # Change into an existing divert location and
156
+ # append +txt+ or the contents of +block+.
157
+ #
158
+ def divert(tag, txt=nil, &block)
159
+ @document.divert(tag, txt, &block)
160
+ end
161
+
162
+ #
163
+ # If the divert +tag+ exists, divert, otherwise
164
+ # do nothing.
165
+ #
166
+ def try_divert(tag, txt=nil, &block)
167
+ if @document.has_divert?(tag)
168
+ divert(tag, txt, &block)
169
+ true
170
+ else
171
+ false
172
+ end
173
+ end
174
+
175
+ #
176
+ # Render specific markup only once. For example style and/or
177
+ # javascript of a component which has many instances.
178
+ #
179
+ def once(tag)
180
+ return if @document.set.has_key?(tag)
181
+ @document.set[tag] = true
182
+ yield if block_given?
183
+ end
184
+
185
+ HTML_TYPE_CSS = 'text/css'.freeze
186
+ HTML_REL_STYLESHEET = 'stylesheet'.freeze
187
+
188
+ def link_css(url)
189
+ link.type(HTML_TYPE_CSS).rel(HTML_REL_STYLESHEET).href(url)
190
+ end
191
+
192
+ def new_radio_group
193
+ Wee::Brush::RadioButtonTag::RadioGroup.new(self)
194
+ end
195
+
196
+ def url_for_callback(callback, type=:action, hash=nil)
197
+ url_for_callback_id(register_callback(type, callback), hash)
198
+ end
199
+
200
+ def url_for_callback_id(callback_id, hash=nil)
201
+ if hash
202
+ build_url(hash.update(:callback_id => callback_id))
203
+ else
204
+ build_url(:callback_id => callback_id)
205
+ end
206
+ end
207
+
208
+ def build_url(*args)
209
+ @request.build_url(*args)
210
+ end
211
+
212
+ def register_callback(type, callback)
213
+ cbs = @callbacks
214
+ if cbs.respond_to?("#{type}_callbacks")
215
+ cbs.send("#{type}_callbacks").register(@current_component, callback)
216
+ else
217
+ raise
218
+ end
219
+ end
220
+
221
+ protected
222
+
223
+ def set_brush(brush)
224
+ brush.setup(self, @document)
225
+
226
+ @current_brush.close if @current_brush
227
+ @current_brush = brush
228
+
229
+ return brush
230
+ end
231
+
232
+ def handle(brush, *args, &block)
233
+ if block or not args.empty?
234
+ set_brush(brush)
235
+ brush.with(*args, &block)
236
+ else
237
+ set_brush(brush)
238
+ end
239
+ end
240
+
241
+ def handle2(brush, &block)
242
+ if block
243
+ set_brush(brush)
244
+ brush.with(&block)
245
+ else
246
+ set_brush(brush)
247
+ end
248
+ end
249
+
250
+ alias handle3 set_brush
251
+
252
+ end # class HtmlCanvas
253
+
254
+ end # module Wee
@@ -0,0 +1,52 @@
1
+ require 'wee/html_writer'
2
+
3
+ module Wee
4
+
5
+ #
6
+ # Represents a complete HTML document.
7
+ #
8
+ class HtmlDocument < HtmlWriter
9
+ def initialize
10
+ super([])
11
+ end
12
+
13
+ def set
14
+ @set ||= {}
15
+ end
16
+
17
+ def has_divert?(tag)
18
+ @divert and @divert[tag]
19
+ end
20
+
21
+ def define_divert(tag)
22
+ raise ArgumentError if has_divert?(tag)
23
+ @divert ||= {}
24
+ @port << (@divert[tag] = [])
25
+ end
26
+
27
+ def divert(tag, txt=nil, &block)
28
+ raise ArgumentError unless has_divert?(tag)
29
+ raise ArgumentError if txt and block
30
+
31
+ divert = @divert[tag]
32
+
33
+ if txt
34
+ divert << txt
35
+ end
36
+
37
+ if block
38
+ old_port = @port
39
+ begin
40
+ @port = divert
41
+ block.call
42
+ ensure
43
+ @port = old_port
44
+ end
45
+ end
46
+ end
47
+
48
+ def to_s
49
+ @port.join
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,71 @@
1
+ require 'rack'
2
+
3
+ module Wee
4
+
5
+ #
6
+ # A class used to write out HTML documents easily.
7
+ #
8
+ # Usage:
9
+ #
10
+ # w = Wee::HtmlWriter.new(doc='')
11
+ # w.start_tag('html')
12
+ # w.start_tag('body')
13
+ # w.start_tag('a', 'href' => 'http://...')
14
+ # w.text('link')
15
+ # w.end_tag('a')
16
+ # w.end_tag('body')
17
+ # w.end_tag('html')
18
+ #
19
+ # p doc # => '<html><body><a href="http://...">link</a></body></html>'
20
+ #
21
+ class HtmlWriter
22
+
23
+ attr_accessor :port
24
+
25
+ def initialize(port=[])
26
+ @port = port
27
+ end
28
+
29
+ CLOSING = ">".freeze
30
+ SINGLE_CLOSING = " />".freeze
31
+
32
+ def start_tag(tag, attributes=nil, single=false)
33
+ if attributes
34
+ @port << "<#{tag}"
35
+ attributes.each {|k, v|
36
+ if v
37
+ @port << %[ #{ k }="#{ v }"]
38
+ else
39
+ @port << %[ #{ k }]
40
+ end
41
+ }
42
+ @port << (single ? SINGLE_CLOSING : CLOSING)
43
+ else
44
+ @port << (single ? "<#{tag} />" : "<#{tag}>")
45
+ end
46
+ end
47
+
48
+ def single_tag(tag, attributes=nil)
49
+ start_tag(tag, attributes, true)
50
+ end
51
+
52
+ def end_tag(tag)
53
+ @port << "</#{tag}>"
54
+ end
55
+
56
+ def text(str)
57
+ @port << str.to_s
58
+ end
59
+
60
+ def encode_text(str)
61
+ @port << Rack::Utils.escape_html(str.to_s)
62
+ end
63
+
64
+ def write(str)
65
+ @port << str
66
+ end
67
+ alias << write
68
+
69
+ end # class HtmlWriter
70
+
71
+ end # module Wee
@@ -0,0 +1,81 @@
1
+ module Wee
2
+
3
+ #
4
+ # Abstract base class of all id generators.
5
+ #
6
+ class IdGenerator
7
+ def next
8
+ raise "subclass responsibility"
9
+ end
10
+ end
11
+
12
+ #
13
+ # Sequential id generator.
14
+ #
15
+ # Returned ids are guaranteed to be unique, but they are easily guessable.
16
+ #
17
+ class IdGenerator::Sequential < IdGenerator
18
+ def initialize(initial_value=0)
19
+ @value = initial_value - 1
20
+ end
21
+
22
+ def next
23
+ (@value += 1).to_s(36)
24
+ end
25
+ end
26
+
27
+ #
28
+ # Returned ids are unique with a very high probability and it's
29
+ # very hard to guess the next or any used id.
30
+ #
31
+ class IdGenerator::Secure < IdGenerator
32
+
33
+ require 'digest/md5'
34
+ begin
35
+ require 'securerandom'
36
+ rescue LoadError
37
+ end
38
+
39
+ def initialize(salt='wee')
40
+ @salt = salt
41
+
42
+ @use_secure = false
43
+ if defined?(::SecureRandom)
44
+ begin
45
+ @use_secure = true if next_secure != next_secure
46
+ rescue NotImplementedError
47
+ end
48
+ end
49
+ end
50
+
51
+ def next
52
+ pack(@use_secure ? next_secure : next_md5)
53
+ end
54
+
55
+ protected
56
+
57
+ def next_md5
58
+ now = Time.now
59
+ dig = Digest::MD5.new
60
+ dig.update(now.to_s)
61
+ dig.update(now.usec.to_s)
62
+ dig.update(rand(0).to_s)
63
+ dig.update($$.to_s)
64
+ dig.update(@salt.to_s)
65
+ dig.digest
66
+ end
67
+
68
+ def next_secure
69
+ SecureRandom.random_bytes(16)
70
+ end
71
+
72
+ def pack(str)
73
+ packed = [str].pack('m')
74
+ packed.tr!("=\r\n", '')
75
+ packed.tr!('+/', '-_')
76
+ packed
77
+ end
78
+
79
+ end
80
+
81
+ end # module Wee