mullen-wee 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
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,251 @@
1
+ require 'wee/presenter'
2
+
3
+ module Wee
4
+
5
+ #
6
+ # Abstract base class of all decorations. Forwards the methods
7
+ # #process_callbacks, #render! and #state to the next decoration in
8
+ # the chain. Subclasses should provide special behaviour in these methods,
9
+ # otherwise the decoration does not make sense.
10
+ #
11
+ # For example, a HeaderFooterDecoration class could draw a header and footer
12
+ # around the decorations or components below itself:
13
+ #
14
+ # class HeaderFooterDecoration < Wee::Decoration
15
+ # alias render! render_presenter!
16
+ # def render(r)
17
+ # r.text "header"
18
+ # r.render_decoration(@next)
19
+ # r.text "footer"
20
+ # end
21
+ # end
22
+ #
23
+ class Decoration < Presenter
24
+
25
+ #
26
+ # Points to the next decoration in the chain. A decoration is responsible for
27
+ # all decorations or components "below" it (everything that follows this
28
+ # decoration in the chain). In other words, it's the owner of everything
29
+ # "below" itself.
30
+ #
31
+ attr_accessor :next
32
+
33
+ #
34
+ # Is this decoration a global or a local one? By default all decorations are
35
+ # local unless this method is overwritten.
36
+ #
37
+ # A global decoration is added in front of the decoration chain, a local
38
+ # decoration is added in front of all other local decorations but after all
39
+ # global decorations.
40
+ #
41
+ def global?() false end
42
+
43
+ #
44
+ # Forwards method call to the next decoration in the chain.
45
+ #
46
+ def process_callbacks(callbacks)
47
+ @next.process_callbacks(callbacks)
48
+ end
49
+
50
+ alias render_presenter! render!
51
+ #
52
+ # Forwards method call to the next decoration in the chain.
53
+ #
54
+ def render!(r)
55
+ @next.render!(r)
56
+ end
57
+
58
+ #
59
+ # We have to save the @next attribute to be able to correctly backtrack
60
+ # calls, as method Wee::Component#call modifies it in the call to
61
+ # <tt>component.remove_decoration(answer)</tt>. Removing the
62
+ # answer-decoration has the advantage to be able to call a component more
63
+ # than once!
64
+ #
65
+ def state(s)
66
+ @next.state(s)
67
+ s.add_ivar(self, :@next, @next)
68
+ end
69
+
70
+ end # class Decoration
71
+
72
+ #
73
+ # A Wee::Delegate breaks the decoration chain and forwards the methods
74
+ # #process_callbacks, #render! and #state to the corresponding *chain*
75
+ # method of it's _delegate_ component (a Wee::Component).
76
+ #
77
+ class Delegate < Decoration
78
+
79
+ def initialize(delegate)
80
+ @delegate = delegate
81
+ end
82
+
83
+ #
84
+ # Forwards method to the corresponding top-level *chain* method of the
85
+ # _delegate_ component.
86
+ #
87
+ def process_callbacks(callbacks)
88
+ @delegate.decoration.process_callbacks(callbacks)
89
+ end
90
+
91
+ #
92
+ # Forwards method to the corresponding top-level *chain* method of the
93
+ # _delegate_ component.
94
+ #
95
+ def render!(r)
96
+ @delegate.decoration.render!(r)
97
+ end
98
+
99
+ #
100
+ # Forwards method to the corresponding top-level *chain* method of the
101
+ # _delegate_ component. We also take snapshots of all non-visible
102
+ # components, thus we follow the @next decoration (via super).
103
+ #
104
+ def state(s)
105
+ super
106
+ @delegate.decoration.state(s)
107
+ end
108
+
109
+ end # class Delegate
110
+
111
+ #
112
+ # A Wee::AnswerDecoration is wrapped around a component that will call
113
+ # Component#answer. This makes it possible to use such components without the
114
+ # need to call them (Component#call), e.g. as child components of other
115
+ # components.
116
+ #
117
+ class AnswerDecoration < Decoration
118
+
119
+ #
120
+ # Used to unwind the component call chain in Component#answer.
121
+ #
122
+ class Answer < Exception
123
+ attr_reader :args
124
+ def initialize(args) @args = args end
125
+ end
126
+
127
+ attr_accessor :answer_callback
128
+
129
+ class Interceptor
130
+ attr_accessor :action_callback, :answer_callback
131
+
132
+ def initialize(action_callback, answer_callback)
133
+ @action_callback, @answer_callback = action_callback, answer_callback
134
+ end
135
+
136
+ def call
137
+ @action_callback.call
138
+ rescue Answer => answer
139
+ # return to the calling component
140
+ @answer_callback.call(answer)
141
+ end
142
+ end
143
+
144
+ #
145
+ # When a component answers, <tt>@answer_callback.call(answer)</tt>
146
+ # will be executed, where +answer+ is of class Answer which includes the
147
+ # arguments passed to Component#answer.
148
+ #
149
+ def process_callbacks(callbacks)
150
+ if action_callback = super
151
+ Interceptor.new(action_callback, @answer_callback)
152
+ else
153
+ nil
154
+ end
155
+ end
156
+
157
+ end # class AnswerDecoration
158
+
159
+
160
+ class WrapperDecoration < Decoration
161
+
162
+ alias render! render_presenter!
163
+
164
+ #
165
+ # Overwrite this method, and call render_inner(r)
166
+ # where you want the inner content to be drawn.
167
+ #
168
+ def render(r)
169
+ render_inner(r)
170
+ end
171
+
172
+ def render_inner(r)
173
+ r.render_decoration(@next)
174
+ end
175
+
176
+ end # class WrapperDecoration
177
+
178
+
179
+ #
180
+ # Renders a <div> tag with a unique "id" around the wrapped component.
181
+ # Useful for components that want to update their content in-place using
182
+ # AJAX.
183
+ #
184
+ class OidDecoration < WrapperDecoration
185
+ def render(r)
186
+ r.div.oid.with { render_inner(r) }
187
+ end
188
+ end # class OidDecoration
189
+
190
+ #
191
+ # Renders a CSS style for a component class.
192
+ #
193
+ # Only works when used together with a PageDecoration,
194
+ # or an existing :styles divert location.
195
+ #
196
+ # The style is not rendered when in an AJAX request.
197
+ # This is the desired behaviour as it is assumed that
198
+ # a component is first rendered via a regular request
199
+ # and then updated via AJAX requests.
200
+ #
201
+ # It is only rendered once for all instances of a given
202
+ # component.
203
+ #
204
+ # A method #style must exist returning the CSS style.
205
+ #
206
+ class StyleDecoration < WrapperDecoration
207
+ def initialize(component)
208
+ @component = component
209
+ end
210
+
211
+ def render(r)
212
+ r.render_style(@component)
213
+ render_inner(r)
214
+ end
215
+ end # class StyleDecoration
216
+
217
+ class FormDecoration < WrapperDecoration
218
+
219
+ def global?() true end
220
+
221
+ def render(r)
222
+ r.form { render_inner(r) }
223
+ end
224
+
225
+ end # class FormDecoration
226
+
227
+ class PageDecoration < WrapperDecoration
228
+
229
+ def initialize(title='', stylesheets=[], javascripts=[])
230
+ @title = title
231
+ @stylesheets = stylesheets
232
+ @javascripts = javascripts
233
+ super()
234
+ end
235
+
236
+ def global?() true end
237
+
238
+ def render(r)
239
+ r.page.title(@title).head {
240
+ @stylesheets.each {|s| r.link_css(s) }
241
+ @javascripts.each {|j| r.javascript.src(j) }
242
+ r.style.type('text/css').with { r.define_divert(:styles) }
243
+ r.javascript.with { r.define_divert(:javascripts) }
244
+ }.with {
245
+ render_inner(r)
246
+ }
247
+ end
248
+
249
+ end # class PageDecoration
250
+
251
+ end # module Wee
@@ -0,0 +1,171 @@
1
+ require 'wee/component'
2
+
3
+ module Wee
4
+ class Dialog < Component; end
5
+
6
+ #
7
+ # Abstract class
8
+ #
9
+ class FormDialog < Dialog
10
+ def initialize(caption)
11
+ @caption = caption
12
+ end
13
+
14
+ def render(r)
15
+ r.div.css_class('wee').with {
16
+ render_caption(r)
17
+ render_form(r)
18
+ }
19
+ end
20
+
21
+ def render_caption(r)
22
+ r.h3 @caption if @caption
23
+ end
24
+
25
+ def render_form(r)
26
+ r.form.with {
27
+ render_body(r)
28
+ render_buttons(r)
29
+ }
30
+ end
31
+
32
+ def render_body(r)
33
+ end
34
+
35
+ def render_buttons(r)
36
+ return if buttons.empty?
37
+ r.div.css_class('dialog-buttons').with {
38
+ buttons.each do |title, return_value, sym, method|
39
+ sym ||= title.downcase
40
+ r.span.css_class("dialog-button-#{sym}").with {
41
+ if method
42
+ r.submit_button.callback_method(method).value(title)
43
+ else
44
+ r.submit_button.callback_method(:answer, return_value).value(title)
45
+ end
46
+ }
47
+ end
48
+ }
49
+ end
50
+
51
+ def buttons
52
+ []
53
+ end
54
+ end # class FormDialog
55
+
56
+ class MessageDialog < FormDialog
57
+ def initialize(caption, *buttons)
58
+ super(caption)
59
+ @buttons = buttons
60
+ end
61
+
62
+ def buttons
63
+ @buttons
64
+ end
65
+ end
66
+
67
+ class InformDialog < FormDialog
68
+ def buttons
69
+ [['Ok', nil, :ok]]
70
+ end
71
+ end # class InformDialog
72
+
73
+ class ConfirmDialog < FormDialog
74
+ def buttons
75
+ [['Yes', true, :yes], ['No', false, :no]]
76
+ end
77
+ end # class ConfirmDialog
78
+
79
+ class SingleSelectionDialog < FormDialog
80
+ attr_accessor :selected_item
81
+
82
+ def initialize(items, caption=nil, selected_item=nil)
83
+ super(caption)
84
+ @items = items
85
+ @selected_item = selected_item
86
+ end
87
+
88
+ def state(s) super
89
+ s.add_ivar(self, :@selected_item)
90
+ end
91
+
92
+ def render_body(r)
93
+ r.select_list(@items).selected(@selected_item).callback_method(:selected_item=)
94
+ end
95
+
96
+ def buttons
97
+ [['Ok', nil, :ok, :ok], ['Cancel', nil, :cancel, :cancel]]
98
+ end
99
+
100
+ def ok
101
+ answer @selected_item
102
+ end
103
+
104
+ def cancel
105
+ answer nil
106
+ end
107
+ end # class SingleSelectionDialog
108
+
109
+ class TextInputDialog < Wee::FormDialog
110
+ attr_accessor :text
111
+
112
+ def initialize(caption=nil, text="", size=50)
113
+ super(caption)
114
+ @text = text
115
+ @size = size
116
+ end
117
+
118
+ def state(s) super
119
+ s.add_ivar(self, :@text)
120
+ end
121
+
122
+ def render_body(r)
123
+ r.text_input.size(@size).callback_method(:set_text).value(@text || "")
124
+ end
125
+
126
+ def set_text(text)
127
+ @text = text.strip
128
+ end
129
+
130
+ def buttons
131
+ [['Ok', nil, :ok, :ok], ['Cancel', nil, :cancel, :cancel]]
132
+ end
133
+
134
+ def ok
135
+ answer @text
136
+ end
137
+
138
+ def cancel
139
+ answer nil
140
+ end
141
+ end # class TextInputDialog
142
+
143
+ class TextAreaDialog < TextInputDialog
144
+ def initialize(caption=nil, text="", cols=50, rows=5)
145
+ super(caption, text, cols)
146
+ @rows = rows
147
+ end
148
+
149
+ def render_body(r)
150
+ r.text_area.cols(@size).rows(@rows).callback_method(:set_text).with(@text || "")
151
+ end
152
+ end # class TextAreaDialog
153
+
154
+ #
155
+ # Extend class Component with shortcuts for the dialogs above
156
+ #
157
+ class Component
158
+ def confirm(question, &block)
159
+ call! ConfirmDialog.new(question), &block
160
+ end
161
+
162
+ def inform(message, &block)
163
+ call! InformDialog.new(message), &block
164
+ end
165
+
166
+ def choose_from(items, caption=nil, selected_item=nil, &block)
167
+ call! SingleSelectionDialog.new(items, caption, selected_item), &block
168
+ end
169
+ end # class Component
170
+
171
+ end # module Wee
@@ -0,0 +1,39 @@
1
+ module Wee
2
+
3
+ class ExternalResource
4
+ def initialize(mount_path=nil)
5
+ @mount_path = mount_path || "/" + self.class.name.to_s.downcase.gsub("::", "_")
6
+ end
7
+
8
+ def install(builder)
9
+ rd = resource_dir()
10
+ builder.map(@mount_path) do
11
+ run Rack::File.new(rd)
12
+ end
13
+ end
14
+
15
+ def javascripts
16
+ []
17
+ end
18
+
19
+ def stylesheets
20
+ []
21
+ end
22
+
23
+ protected
24
+
25
+ def resource_dir
26
+ raise
27
+ end
28
+
29
+ def file_relative(_file, *subdirs)
30
+ File.expand_path(File.join(File.dirname(_file), *subdirs))
31
+ end
32
+
33
+ def mount_path_relative(*paths)
34
+ paths.map {|path| "#{@mount_path}/#{path}"}
35
+ end
36
+
37
+ end # class ExternalResource
38
+
39
+ end