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.
- data/README.rdoc +127 -0
- data/Rakefile +25 -0
- data/aWee.gemspec +26 -0
- data/examples/ObjectSpaceBrowser.rb +191 -0
- data/examples/ajax.rb +73 -0
- data/examples/apotomo-webhunter/main.rb +75 -0
- data/examples/apotomo-webhunter/public/images/bear_trap_charged.png +0 -0
- data/examples/apotomo-webhunter/public/images/bear_trap_snapped.png +0 -0
- data/examples/apotomo-webhunter/public/images/cheese.png +0 -0
- data/examples/apotomo-webhunter/public/images/dark_forest.jpg +0 -0
- data/examples/apotomo-webhunter/public/images/mouse.png +0 -0
- data/examples/apotomo-webhunter/public/javascripts/jquery-1.3.2.min.js +19 -0
- data/examples/apotomo-webhunter/public/javascripts/wee-jquery.js +19 -0
- data/examples/apotomo-webhunter/public/stylesheets/forest.css +33 -0
- data/examples/arc_challenge.rb +42 -0
- data/examples/arc_challenge2.rb +46 -0
- data/examples/cheese_task.rb +27 -0
- data/examples/continuations.rb +28 -0
- data/examples/demo.rb +135 -0
- data/examples/demo/calculator.rb +63 -0
- data/examples/demo/calendar.rb +333 -0
- data/examples/demo/counter.rb +38 -0
- data/examples/demo/editable_counter.rb +36 -0
- data/examples/demo/example.rb +142 -0
- data/examples/demo/file_upload.rb +19 -0
- data/examples/demo/messagebox.rb +15 -0
- data/examples/demo/radio.rb +33 -0
- data/examples/demo/window.rb +71 -0
- data/examples/hw.rb +11 -0
- data/examples/i18n/app.rb +16 -0
- data/examples/i18n/locale/de/app.po +25 -0
- data/examples/i18n/locale/en/app.po +25 -0
- data/examples/pager.rb +102 -0
- data/lib/wee.rb +109 -0
- data/lib/wee/application.rb +89 -0
- data/lib/wee/callback.rb +109 -0
- data/lib/wee/component.rb +363 -0
- data/lib/wee/decoration.rb +251 -0
- data/lib/wee/dialog.rb +171 -0
- data/lib/wee/external_resource.rb +39 -0
- data/lib/wee/html_brushes.rb +795 -0
- data/lib/wee/html_canvas.rb +254 -0
- data/lib/wee/html_document.rb +52 -0
- data/lib/wee/html_writer.rb +71 -0
- data/lib/wee/id_generator.rb +81 -0
- data/lib/wee/jquery.rb +11 -0
- data/lib/wee/jquery/jquery-1.3.2.min.js +19 -0
- data/lib/wee/jquery/wee-jquery.js +19 -0
- data/lib/wee/locale.rb +56 -0
- data/lib/wee/lru_cache.rb +91 -0
- data/lib/wee/presenter.rb +44 -0
- data/lib/wee/renderer.rb +72 -0
- data/lib/wee/request.rb +56 -0
- data/lib/wee/response.rb +68 -0
- data/lib/wee/rightjs.rb +11 -0
- data/lib/wee/rightjs/rightjs-1.5.2.min.js +9 -0
- data/lib/wee/rightjs/wee-rightjs.js +18 -0
- data/lib/wee/root_component.rb +45 -0
- data/lib/wee/session.rb +366 -0
- data/lib/wee/state.rb +102 -0
- data/lib/wee/task.rb +16 -0
- data/test/bm_render.rb +34 -0
- data/test/component_spec.rb +40 -0
- data/test/stress/plotter.rb +84 -0
- data/test/stress/stress_client.rb +51 -0
- data/test/stress/stress_local.rb +86 -0
- data/test/stress/stress_server.rb +83 -0
- data/test/test_component.rb +106 -0
- data/test/test_html_canvas.rb +25 -0
- data/test/test_html_writer.rb +32 -0
- data/test/test_lru_cache.rb +51 -0
- data/test/test_request.rb +42 -0
- 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 = " ".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
|