wee 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (146) hide show
  1. data/ChangeLog +75 -0
  2. data/README +17 -9
  3. data/Rakefile +2 -2
  4. data/TODO +20 -0
  5. data/benchmark/Makefile +14 -9
  6. data/benchmark/counter.rb +11 -30
  7. data/benchmark/report_req.rb +12 -0
  8. data/doc/rdoc/classes/Array.html +12 -12
  9. data/doc/rdoc/classes/Cache/StorageCache.html +38 -38
  10. data/doc/rdoc/classes/Cache/Strategy/CapacityBounded.html +30 -30
  11. data/doc/rdoc/classes/Cache/Strategy/LFU.html +24 -24
  12. data/doc/rdoc/classes/Cache/Strategy/LRU.html +24 -24
  13. data/doc/rdoc/classes/Cache/Strategy/Unbounded.html +24 -24
  14. data/doc/rdoc/classes/Enumerable.html +6 -6
  15. data/doc/rdoc/classes/Object.html +12 -12
  16. data/doc/rdoc/classes/OgApplication.html +126 -0
  17. data/doc/rdoc/classes/OgScaffolder.html +401 -0
  18. data/doc/rdoc/classes/OgSession.html +172 -0
  19. data/doc/rdoc/classes/String.html +12 -12
  20. data/doc/rdoc/classes/Struct.html +12 -12
  21. data/doc/rdoc/classes/Wee.html +5 -62
  22. data/doc/rdoc/classes/Wee/AnswerDecoration.html +9 -9
  23. data/doc/rdoc/classes/Wee/Application.html +107 -39
  24. data/doc/rdoc/classes/Wee/Brush.html +22 -18
  25. data/doc/rdoc/classes/Wee/Brush/ActionCallbackMixin.html +17 -11
  26. data/doc/rdoc/classes/Wee/Brush/ActionURLCallbackMixin.html +18 -10
  27. data/doc/rdoc/classes/Wee/Brush/AnchorTag.html +30 -64
  28. data/doc/rdoc/classes/Wee/Brush/FileUploadTag.html +8 -10
  29. data/doc/rdoc/classes/Wee/Brush/FormTag.html +27 -79
  30. data/doc/rdoc/classes/Wee/Brush/GenericEncodedTextBrush.html +12 -12
  31. data/doc/rdoc/classes/Wee/Brush/GenericSingleTagBrush.html +146 -0
  32. data/doc/rdoc/classes/Wee/Brush/GenericTagBrush.html +179 -65
  33. data/doc/rdoc/classes/Wee/Brush/GenericTextBrush.html +12 -12
  34. data/doc/rdoc/classes/Wee/Brush/ImageButtonTag.html +16 -18
  35. data/doc/rdoc/classes/Wee/Brush/ImageTag.html +203 -0
  36. data/doc/rdoc/classes/Wee/Brush/InputCallbackMixin.html +17 -11
  37. data/doc/rdoc/classes/Wee/Brush/InputTag.html +15 -15
  38. data/doc/rdoc/classes/Wee/Brush/JavascriptTag.html +147 -0
  39. data/doc/rdoc/classes/Wee/Brush/Page.html +17 -17
  40. data/doc/rdoc/classes/Wee/Brush/SelectListTag.html +25 -50
  41. data/doc/rdoc/classes/Wee/Brush/SelectOptionTag.html +7 -38
  42. data/doc/rdoc/classes/Wee/Brush/SubmitButtonTag.html +7 -7
  43. data/doc/rdoc/classes/Wee/Brush/TableDataTag.html +15 -16
  44. data/doc/rdoc/classes/Wee/Brush/TableHeaderTag.html +7 -7
  45. data/doc/rdoc/classes/Wee/Brush/TableRowTag.html +65 -50
  46. data/doc/rdoc/classes/Wee/Brush/TableTag.html +7 -7
  47. data/doc/rdoc/classes/Wee/Brush/TextAreaTag.html +14 -64
  48. data/doc/rdoc/classes/Wee/Brush/TextInputTag.html +7 -7
  49. data/doc/rdoc/classes/Wee/Brush/ToCallback.html +146 -0
  50. data/doc/rdoc/classes/Wee/CallbackRegistry.html +40 -40
  51. data/doc/rdoc/classes/Wee/CallbackStream.html +18 -18
  52. data/doc/rdoc/classes/Wee/Canvas.html +24 -24
  53. data/doc/rdoc/classes/Wee/Component.html +232 -149
  54. data/doc/rdoc/classes/Wee/Component/OnAnswer.html +153 -0
  55. data/doc/rdoc/classes/Wee/Decoration.html +42 -42
  56. data/doc/rdoc/classes/Wee/Delegate.html +27 -27
  57. data/doc/rdoc/classes/Wee/ErrorResponse.html +12 -12
  58. data/doc/rdoc/classes/Wee/FormDecoration.html +148 -0
  59. data/doc/rdoc/classes/Wee/GenericResponse.html +6 -6
  60. data/doc/rdoc/classes/Wee/HtmlCanvas.html +296 -215
  61. data/doc/rdoc/classes/Wee/HtmlWriter.html +83 -81
  62. data/doc/rdoc/classes/Wee/LiteralMethodCallback.html +21 -16
  63. data/doc/rdoc/classes/Wee/MessageBox.html +180 -0
  64. data/doc/rdoc/classes/Wee/PageDecoration.html +30 -30
  65. data/doc/rdoc/classes/Wee/Presenter.html +237 -69
  66. data/doc/rdoc/classes/Wee/RedirectResponse.html +6 -6
  67. data/doc/rdoc/classes/Wee/RefreshResponse.html +6 -6
  68. data/doc/rdoc/classes/Wee/Request.html +18 -18
  69. data/doc/rdoc/classes/Wee/RequestHandler.html +43 -39
  70. data/doc/rdoc/classes/Wee/Response.html +24 -24
  71. data/doc/rdoc/classes/Wee/Session.html +746 -72
  72. data/doc/rdoc/classes/Wee/SimpleIdGenerator.html +18 -18
  73. data/doc/rdoc/classes/Wee/Snapshot.html +19 -19
  74. data/doc/rdoc/classes/Wee/Utils.html +138 -2
  75. data/doc/rdoc/classes/Wee/Utils/LRUCache.html +7 -7
  76. data/doc/rdoc/classes/Wee/ValueHolder.html +18 -18
  77. data/doc/rdoc/classes/Wee/WEBrickAdaptor.html +43 -68
  78. data/doc/rdoc/classes/Wee/WrapperDecoration.html +150 -0
  79. data/doc/rdoc/created.rid +1 -1
  80. data/doc/rdoc/files/README.html +29 -15
  81. data/doc/rdoc/files/lib/wee/adaptors/webrick_rb.html +1 -1
  82. data/doc/rdoc/files/lib/wee/application_rb.html +1 -1
  83. data/doc/rdoc/files/lib/wee/components/form_decoration_rb.html +101 -0
  84. data/doc/rdoc/files/lib/wee/components/messagebox_rb.html +101 -0
  85. data/doc/rdoc/files/lib/wee/components/page_decoration_rb.html +1 -1
  86. data/doc/rdoc/files/lib/wee/components/wrapper_decoration_rb.html +101 -0
  87. data/doc/rdoc/files/lib/wee/components_rb.html +4 -1
  88. data/doc/rdoc/files/lib/wee/continuation/core/component_rb.html +101 -0
  89. data/doc/rdoc/files/lib/wee/continuation/session_rb.html +110 -0
  90. data/doc/rdoc/files/lib/wee/continuation_rb.html +116 -0
  91. data/doc/rdoc/files/lib/wee/core/callback_rb.html +1 -1
  92. data/doc/rdoc/files/lib/wee/core/component_rb.html +1 -1
  93. data/doc/rdoc/files/lib/wee/core/presenter_rb.html +1 -1
  94. data/doc/rdoc/files/lib/wee/core_rb.html +3 -3
  95. data/doc/rdoc/files/lib/wee/databases/og_rb.html +108 -0
  96. data/doc/rdoc/files/lib/wee/renderer/html/brushes_rb.html +1 -1
  97. data/doc/rdoc/files/lib/wee/renderer/html/canvas_rb.html +1 -1
  98. data/doc/rdoc/files/lib/wee/renderer/html/writer_rb.html +1 -1
  99. data/doc/rdoc/files/lib/wee/requesthandler_rb.html +1 -1
  100. data/doc/rdoc/files/lib/wee/session_rb.html +1 -2
  101. data/doc/rdoc/files/lib/wee/utils/autoreload_rb.html +1 -1
  102. data/doc/rdoc/files/lib/wee/utils/cache_rb.html +1 -1
  103. data/doc/rdoc/files/lib/wee/utils/helper_rb.html +1 -8
  104. data/doc/rdoc/files/lib/wee/utils_rb.html +110 -0
  105. data/doc/rdoc/files/lib/wee_rb.html +1 -1
  106. data/doc/rdoc/fr_class_index.html +11 -1
  107. data/doc/rdoc/fr_file_index.html +8 -0
  108. data/doc/rdoc/fr_method_index.html +269 -228
  109. data/examples/calculator.rb +69 -0
  110. data/examples/calendar.rb +5 -17
  111. data/examples/example.rb +2 -2
  112. data/examples/hw.rb +17 -0
  113. data/examples/live-update.rb +45 -0
  114. data/examples/og-test.rb +51 -0
  115. data/lib/wee.rb +1 -1
  116. data/lib/wee/adaptors/webrick.rb +2 -0
  117. data/lib/wee/application.rb +16 -0
  118. data/lib/wee/components.rb +3 -0
  119. data/lib/wee/components/form_decoration.rb +7 -0
  120. data/{test → lib/wee}/components/messagebox.rb +1 -1
  121. data/lib/wee/components/page_decoration.rb +5 -5
  122. data/lib/wee/components/wrapper_decoration.rb +7 -0
  123. data/lib/wee/continuation.rb +5 -0
  124. data/lib/wee/continuation/core/component.rb +55 -0
  125. data/lib/wee/continuation/session.rb +217 -0
  126. data/lib/wee/core/callback.rb +11 -6
  127. data/lib/wee/core/component.rb +45 -33
  128. data/lib/wee/core/presenter.rb +68 -0
  129. data/lib/wee/databases/og.rb +114 -0
  130. data/lib/wee/renderer/html/brushes.rb +179 -98
  131. data/lib/wee/renderer/html/canvas.rb +37 -13
  132. data/lib/wee/renderer/html/writer.rb +34 -32
  133. data/lib/wee/requesthandler.rb +6 -3
  134. data/lib/wee/session.rb +73 -54
  135. data/lib/wee/utils.rb +5 -0
  136. data/lib/wee/utils/autoreload.rb +1 -1
  137. data/lib/wee/utils/cache.rb +0 -2
  138. data/lib/wee/utils/helper.rb +40 -8
  139. data/test/components/calltest-cont.rb +16 -0
  140. data/test/components/calltest.rb +15 -10
  141. data/test/stress.rb +31 -28
  142. data/test/stress_and_measure.rb +53 -0
  143. data/test/stressed_application.rb +15 -0
  144. data/test/test_html_writer.rb +9 -4
  145. metadata +236 -195
  146. data/benchmark/bench.sh +0 -24
@@ -16,7 +16,7 @@ class Wee::CallbackRegistry
16
16
 
17
17
  # Register +callback+ for +object+ under +type+ and return a unique callback id.
18
18
 
19
- def register_for(object, type=nil, &callback)
19
+ def register_for(object, type, callback)
20
20
  c = (@callbacks[type] ||= Hash.new)
21
21
  o = (@obj_to_id_map[type] ||= Hash.new)
22
22
  cid = @idgen.next.to_s
@@ -30,7 +30,7 @@ class Wee::CallbackRegistry
30
30
  # :section: Friend methods for Wee::CallbackStream
31
31
  # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
32
32
 
33
- def get_ids_for(object, type=nil)
33
+ def get_ids_for(object, type)
34
34
  if o = @obj_to_id_map[type]
35
35
  o[object] || []
36
36
  else
@@ -38,7 +38,7 @@ class Wee::CallbackRegistry
38
38
  end
39
39
  end
40
40
 
41
- def get_callback_for(id, type=nil)
41
+ def get_callback_for(id, type)
42
42
  if c = @callbacks[type]
43
43
  c[id]
44
44
  else
@@ -46,7 +46,7 @@ class Wee::CallbackRegistry
46
46
  end
47
47
  end
48
48
 
49
- def all_of_type(type=nil)
49
+ def all_of_type(type)
50
50
  if c = @callbacks[type]
51
51
  c
52
52
  else
@@ -98,11 +98,16 @@ end
98
98
 
99
99
  # A serializable callback.
100
100
  class Wee::LiteralMethodCallback
101
- def initialize(obj, method_id=:call)
101
+ def initialize(obj, method_id=:call, *additional_args)
102
102
  @obj, @method_id = obj, method_id
103
+ @additional_args = additional_args unless additional_args.empty?
103
104
  end
104
105
 
105
106
  def call(*args)
106
- @obj.send(@method_id, *args)
107
+ if @additional_args
108
+ @obj.send(@method_id, *(args+@additional_args))
109
+ else
110
+ @obj.send(@method_id, *args)
111
+ end
107
112
  end
108
113
  end
@@ -114,6 +114,7 @@ class Wee::Component < Wee::Presenter
114
114
 
115
115
  def add_child(child)
116
116
  self.children << child
117
+ child
117
118
  end
118
119
 
119
120
  # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@@ -282,46 +283,57 @@ class Wee::Component < Wee::Presenter
282
283
  # [+component+]
283
284
  # The component to be called.
284
285
  #
286
+ # [+return_callback+]
287
+ # Is invoked when the called component answers.
288
+ # Either a symbol or any object that responds to #call. If it's a symbol,
289
+ # then the corresponding method of the current component will be called.
290
+ #
291
+ # [+additional_args+]
292
+ # Additional arguments that are passed (after the arguments given in
293
+ # #answer) to the +return_callback+
294
+ #
285
295
  # <b>How it works</b>
286
296
  #
287
- # At first a continuation is created. The component to be called is then
288
- # wrapped with an AnswerDecoration and the continuation is assigned to it's
289
- # +on_answer+ attribute. Then a Delegate decoration is added to the calling
290
- # component (self), which delegates to the component to be called
291
- # (+component+). Then we unwind the calling stack back to the Session by
292
- # throwing <i>:wee_back_to_session</i>. This means, that there is only ever
293
- # one action callback invoked per request. When at a later point in time the
294
- # called component invokes #answer, this will throw a <i>:wee_answer</i>
295
- # exception which is catched in the AnswerDecoration. The AnswerDecoration
296
- # then jumps back to the continuation we created at the beginning, and
297
- # finally method #call returns.
297
+
298
+ # The component to be called is wrapped with an AnswerDecoration and the
299
+ # +return_callback+ parameter is assigned to it's +on_answer+ attribute (not
300
+ # directly as there are cleanup actions to be taken before the
301
+ # +return_callback+ can be invoked, hence we wrap it in the OnAnswer class).
302
+ # Then a Delegate decoration is added to the calling component (self), which
303
+ # delegates to the component to be called (+component+).
298
304
  #
299
- # Note that #call returns to an "old" stack-frame from a previous request.
300
- # That is why we throw <i>:wee_back_to_session</i> after invoking an action
301
- # callback, and that's why only ever one is invoked. We could remove this
302
- # limitation without problems, but then there would be a difference between
303
- # those action callbacks that call other components and those that do not.
305
+ # Then we unwind the calling stack back to the Session by throwing
306
+ # <i>:wee_back_to_session</i>. This means, that there is only ever one action
307
+ # callback invoked per request. This is not neccessary, we could simply omit
308
+ # this, but then we'd break compatibility with the implementation using
309
+ # continuations.
310
+ #
311
+ # When at a later point in time the called component invokes #answer, this
312
+ # will throw a <i>:wee_answer</i> exception which is catched in the
313
+ # AnswerDecoration. The AnswerDecoration then invokes the +on_answer+
314
+ # callback which cleans up the decorations we added during #call, and finally
315
+ # passes control to the +return_callback+.
304
316
 
305
- def call(component, return_callback=:use_continuation)
317
+ def call(component, return_callback=nil, *additional_args)
306
318
  add_decoration(delegate = Wee::Delegate.new(component))
307
319
  component.add_decoration(answer = Wee::AnswerDecoration.new)
320
+ answer.on_answer = OnAnswer.new(self, component, delegate, answer,
321
+ return_callback, additional_args)
322
+ throw :wee_back_to_session
323
+ end
308
324
 
309
- if return_callback == :use_continuation
310
- result = callcc {|cc|
311
- answer.on_answer = cc
312
- throw :wee_back_to_session
313
- }
314
- remove_decoration(delegate)
315
- component.remove_decoration(answer)
316
- return result
317
- else
318
- # TODO: make this marshallable!
319
- answer.on_answer = proc {|*args|
320
- remove_decoration(delegate)
321
- component.remove_decoration(answer)
322
- return_callback.call(*args)
323
- }
324
- throw :wee_back_to_session
325
+ class OnAnswer < Struct.new(:calling_component, :called_component, :delegate,
326
+ :answer, :return_callback, :additional_args)
327
+
328
+ def call(*args)
329
+ calling_component.remove_decoration(delegate)
330
+ called_component.remove_decoration(answer)
331
+ return if return_callback.nil?
332
+ if return_callback.respond_to?(:call)
333
+ return_callback.call(*(args + additional_args))
334
+ else
335
+ calling_component.send(return_callback, *(args + additional_args))
336
+ end
325
337
  end
326
338
  end
327
339
 
@@ -129,4 +129,72 @@ class Wee::Presenter
129
129
  Wee::Session.current
130
130
  end
131
131
 
132
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
133
+ # :section: Properties
134
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
135
+
136
+ attr_accessor :properties
137
+
138
+ # Returns an "owned" property.
139
+
140
+ def get_property(prop)
141
+ if @properties
142
+ @properties[prop]
143
+ else
144
+ nil
145
+ end
146
+ end
147
+
148
+ # Tries to lookup a property from different places. +nil+ as property value
149
+ # is not allowed!
150
+ #
151
+ # Search order:
152
+ #
153
+ # 1. self.get_property(prop)
154
+ #
155
+ # 2. session.get_property(prop, self.class)
156
+ #
157
+ # 3. application.get_property(prop, self.class)
158
+ #
159
+ # 4. session.get_property(prop, nil)
160
+ #
161
+ # 5. application.get_property(prop, nil)
162
+ #
163
+ # 6. @@properties[prop]
164
+ #
165
+
166
+ def lookup_property(prop)
167
+ val = get_property(prop)
168
+ return val if val != nil
169
+
170
+ sess = session()
171
+ app = sess.application
172
+ klass = self.class
173
+
174
+ val = sess.get_property(prop, klass)
175
+ return val if val != nil
176
+
177
+ val = app.get_property(prop, klass)
178
+ return val if val != nil
179
+
180
+ val = sess.get_property(prop, nil)
181
+ return val if val != nil
182
+
183
+ val = app.get_property(prop, nil)
184
+ return val if val != nil
185
+
186
+ if defined?(@@properties)
187
+ val = @@properties[prop]
188
+ return val if val != nil
189
+ end
190
+
191
+ return nil
192
+ end
193
+
194
+ # This is currently only used for describing which properties are required by
195
+ # the underlying component.
196
+
197
+ def self.uses_property(*args)
198
+ end
199
+
132
200
  end
@@ -0,0 +1,114 @@
1
+ require 'og'
2
+
3
+ class OgApplication < Wee::Application
4
+ attr_accessor :db
5
+ end
6
+
7
+ class OgSession < Wee::Session
8
+ def awake
9
+ application.db.get_connection
10
+ end
11
+
12
+ def sleep
13
+ application.db.put_connection
14
+ end
15
+ end
16
+
17
+ class OgScaffolder < Wee::Component
18
+ def initialize(domain_class)
19
+ super()
20
+ @domain_class = domain_class
21
+ @attributes = domain_class.__props.map {|a| a.name}.reject {|a| a == 'oid'}
22
+ end
23
+
24
+ def delete(obj)
25
+ call Wee::MessageBox.new('Really delete?'), :confirm_delete, obj
26
+ end
27
+
28
+ def confirm_delete(confirmed, obj)
29
+ if confirmed
30
+ @objs.delete(obj)
31
+ obj.delete!
32
+ end
33
+ end
34
+
35
+ def edit(obj)
36
+ @edit = obj
37
+ end
38
+
39
+ def save(obj)
40
+ obj.save!
41
+ @edit = nil
42
+ end
43
+
44
+ def cancel
45
+ @objs.delete(@edit) if @edit and @edit.oid.nil?
46
+ @edit = nil
47
+ end
48
+
49
+ def refresh
50
+ @objs = @domain_class.all || []
51
+ end
52
+
53
+ def create
54
+ @objs << (@edit = @domain_class.new)
55
+ end
56
+
57
+ def render
58
+ refresh if @objs.nil?
59
+
60
+ r.h1 "#{ @domain_class } List"
61
+ r.anchor.callback { refresh }.with("Refresh")
62
+
63
+ r.form do
64
+ r.table.border(1).with {
65
+
66
+ r.table_row.with {
67
+ @attributes.each {|a|
68
+ r.table_header.with {
69
+ r.bold(a.capitalize)
70
+ }
71
+ }
72
+ r.table_header.with(" ")
73
+ }
74
+
75
+ @objs.each {|o|
76
+ r.table_row.with do
77
+ if @edit == o
78
+
79
+ @attributes.each { |attr|
80
+ r.table_data.with { r.text_input.callback {|v| o.send(attr+"=",v) }.value(o.send(attr)) }
81
+ }
82
+
83
+ r.table_data.with {
84
+ r.submit_button.callback { save(o) }.value("Save")
85
+ r.space
86
+ r.submit_button.callback { cancel() }.value("Cancel")
87
+ r.space
88
+ r.anchor.callback { delete(o) }.with("Delete")
89
+ }
90
+
91
+ else
92
+
93
+ @attributes.each { |attr|
94
+ r.table_data(o.send(attr))
95
+ }
96
+
97
+ r.table_data.with {
98
+ r.anchor.callback { edit(o) }.with("Edit")
99
+ r.space
100
+ r.anchor.callback { delete(o) }.with("Delete")
101
+ }
102
+
103
+ end
104
+ end
105
+ }
106
+ }
107
+ end
108
+
109
+ r.anchor.callback { create() }.with("Add new #{ @domain_class }")
110
+
111
+ end
112
+
113
+ end
114
+
@@ -47,47 +47,151 @@ class Brush::GenericEncodedTextBrush < Brush
47
47
  end
48
48
  end
49
49
 
50
+ module Brush::ToCallback
51
+ private
52
+
53
+ def to_callback(symbol, args, block)
54
+ raise ArgumentError if symbol and block
55
+ if symbol
56
+ Wee::LiteralMethodCallback.new(@canvas.current_component, symbol, *args)
57
+ else
58
+ raise ArgumentError if not args.empty?
59
+ block
60
+ end
61
+ end
62
+ end
63
+
50
64
  class Brush::GenericTagBrush < Brush
51
- def initialize(tag)
52
- super()
53
- @tag = tag
54
- @attributes = Hash.new
65
+
66
+ def self.bool_attr(*attrs)
67
+ attrs.each { |a|
68
+ class_eval "
69
+ def #{ a }(bool=true)
70
+ if bool
71
+ @attributes['#{ a }'] = nil
72
+ else
73
+ @attributes.delete('#{ a }')
74
+ end
75
+ self
76
+ end
77
+ "
78
+ }
79
+ end
80
+
81
+ def self.html_attr(*attrs)
82
+ attrs.each { |a|
83
+ class_eval "
84
+ def #{ a }(value)
85
+ html_attr('#{ a }', value)
86
+ end
87
+ "
88
+ }
55
89
  end
56
90
 
57
- def type(t)
58
- @attributes["type"] = t
91
+ private
92
+
93
+ def html_attr(attr, value)
94
+ if value.nil?
95
+ @attributes.delete(attr)
96
+ else
97
+ @attributes[attr] = value.to_s
98
+ end
59
99
  self
60
100
  end
61
101
 
102
+ public
103
+
104
+ def method_missing(id, attr)
105
+ html_attr(id.to_s, attr)
106
+ end
107
+
108
+ def initialize(tag, is_single_tag=false)
109
+ super()
110
+ @tag, @is_single_tag = tag, is_single_tag
111
+ @attributes = Hash.new
112
+ end
113
+
114
+ html_attr :type, :id
115
+
62
116
  def css_class(c)
63
- @attributes["class"] = c
64
- self
117
+ html_attr("class", c)
65
118
  end
66
119
 
67
- def id(x)
68
- @attributes["id"] = x
69
- self
120
+ include Brush::ToCallback
121
+
122
+ def onclick_callback(symbol=nil, *args, &block)
123
+ raise ArgumentError if symbol and block
124
+ url = @canvas.url_for_callback(to_callback(symbol, args, block))
125
+ onclick("javascript: document.location.href='#{ url }';")
70
126
  end
71
127
 
72
- def method_missing(m, arg)
73
- @attributes[m.to_s] = arg.to_s
74
- self
128
+ # This method construct the css-class attribute by looking up the property
129
+ # from the current component.
130
+
131
+ def css_class_for(c)
132
+ prop = 'css.' + c
133
+ val = @canvas.current_component.lookup_property(prop)
134
+ raise "no property found for: <#{ prop }>" if val.nil?
135
+ css_class(val)
75
136
  end
76
137
 
77
138
  def with(text=nil, &block)
78
139
  doc = @canvas.document
79
- doc.start_tag(@tag, @attributes)
80
- if text
81
- doc.text(text)
82
- super(text, &block)
140
+ if @is_single_tag
141
+ raise ArgumentError if text or block
142
+ doc.single_tag(@tag, @attributes)
143
+ @closed = true
83
144
  else
84
- super(&block)
145
+ doc.start_tag(@tag, @attributes)
146
+ if text
147
+ doc.text(text)
148
+ super(text, &block)
149
+ else
150
+ super(&block)
151
+ end
152
+ doc.end_tag(@tag)
85
153
  end
86
- doc.end_tag(@tag)
87
154
  nil
88
155
  end
89
156
  end
90
157
 
158
+ class Brush::GenericSingleTagBrush < Brush::GenericTagBrush
159
+ def initialize(tag)
160
+ super(tag, true)
161
+ end
162
+ end
163
+
164
+ class Brush::ImageTag < Brush::GenericSingleTagBrush
165
+ html_attr :src
166
+
167
+ # This method construct the src attribute by looking up the property from the
168
+ # current component.
169
+
170
+ def src_for(s)
171
+ prop = "img." + s
172
+ val = @canvas.current_component.lookup_property(prop)
173
+ raise "no property found for: <#{ prop }>" if val.nil?
174
+ src(val)
175
+ end
176
+
177
+ def initialize
178
+ super("img")
179
+ end
180
+
181
+ def with
182
+ super
183
+ end
184
+ end
185
+
186
+ class Brush::JavascriptTag < Brush::GenericTagBrush
187
+ html_attr :src, :type
188
+
189
+ def initialize
190
+ super("script")
191
+ type("text/javascript")
192
+ end
193
+ end
194
+
91
195
  class Brush::TableTag < Brush::GenericTagBrush
92
196
  def initialize
93
197
  super('table')
@@ -100,19 +204,34 @@ class Brush::TableRowTag < Brush::GenericTagBrush
100
204
  end
101
205
 
102
206
  def align_top
103
- @attributes['align'] = 'top'
104
- self
207
+ html_attr('align', 'top')
105
208
  end
106
209
 
107
- def columns(*cols)
210
+ def columns(*cols, &block)
108
211
  with {
109
- cols.each {|col| @canvas.table_data(col) }
212
+ cols.each {|col|
213
+ @canvas.table_data.with {
214
+ if block
215
+ block.call(col)
216
+ else
217
+ @canvas.text(col)
218
+ end
219
+ }
220
+ }
110
221
  }
111
222
  end
112
223
 
113
- def headings(*headers)
224
+ def headings(*headers, &block)
114
225
  with {
115
- headers.each {|head| @canvas.table_heading(head) }
226
+ headers.each {|header|
227
+ @canvas.table_header.with {
228
+ if block
229
+ block.call(header)
230
+ else
231
+ @canvas.text(header)
232
+ end
233
+ }
234
+ }
116
235
  }
117
236
  end
118
237
 
@@ -130,52 +249,52 @@ class Brush::TableRowTag < Brush::GenericTagBrush
130
249
  end
131
250
 
132
251
 
133
- class Brush::InputTag < Brush::GenericTagBrush
252
+ class Brush::InputTag < Brush::GenericSingleTagBrush
134
253
  def initialize
135
254
  super('input')
136
255
  end
137
256
 
138
- %w(type name value size maxlength checked src).each do |meth|
139
- eval %[
140
- def #{ meth }(arg)
141
- @attributes['#{ meth }'] = arg
142
- self
143
- end
144
- ]
145
- end
257
+ html_attr :type, :name, :value, :size, :maxlength, :src
258
+ bool_attr :checked, :disabled, :readonly
146
259
 
147
260
  def with
148
261
  super
149
262
  end
150
263
  end
151
264
 
265
+
152
266
  module Brush::InputCallbackMixin
153
267
  public
154
268
 
155
- def callback(symbol=nil, &block)
269
+ def callback(symbol=nil, *args, &block)
156
270
  raise ArgumentError if symbol and block
157
- block = @canvas.current_component.method(symbol) unless block
158
- name(@canvas.register_callback(:input, &block))
271
+ name(@canvas.register_callback(:input, to_callback(symbol, args, block)))
159
272
  end
273
+
274
+ include Brush::ToCallback
160
275
  end
161
276
 
162
277
  module Brush::ActionCallbackMixin
163
278
  public
164
279
 
165
- def callback(symbol=nil, &block)
280
+ def callback(symbol=nil, *args, &block)
166
281
  raise ArgumentError if symbol and block
167
- block = @canvas.current_component.method(symbol) unless block
168
- name(@canvas.register_callback(:action, &block))
282
+ name(@canvas.register_callback(:action, to_callback(symbol, args, block)))
169
283
  end
284
+
285
+ include Brush::ToCallback
170
286
  end
171
287
 
172
288
  # The callback id is listed in the URL (not as a form-data field)
173
289
  module Brush::ActionURLCallbackMixin
174
290
  public
175
291
 
176
- def callback(symbol=nil, &block)
177
- __set_url(@canvas.url_for_callback(symbol, &block))
292
+ def callback(symbol=nil, *args, &block)
293
+ raise ArgumentError if symbol and block
294
+ __set_url(@canvas.url_for_callback(to_callback(symbol, args, block)))
178
295
  end
296
+
297
+ include Brush::ToCallback
179
298
  end
180
299
 
181
300
  class Brush::TextAreaTag < Brush::GenericTagBrush
@@ -185,24 +304,8 @@ class Brush::TextAreaTag < Brush::GenericTagBrush
185
304
  super('textarea')
186
305
  end
187
306
 
188
- %w(name rows cols tabindex accesskey onfocus onblur onselect onchange).each do |meth|
189
- eval %[
190
- def #{ meth }(arg)
191
- @attributes['#{ meth }'] = arg
192
- self
193
- end
194
- ]
195
- end
196
-
197
- def disabled
198
- @attributes['disabled'] = nil
199
- self
200
- end
201
-
202
- def readonly
203
- @attributes['readonly'] = nil
204
- self
205
- end
307
+ html_attr :name, :rows, :cols, :tabindex, :accesskey, :onfocus, :onblur, :onselect, :onchange
308
+ bool_attr :disabled, :readonly
206
309
 
207
310
  def with(*args, &block)
208
311
  super
@@ -214,14 +317,7 @@ class Brush::SelectOptionTag < Brush::GenericTagBrush
214
317
  super('option')
215
318
  end
216
319
 
217
- def selected(bool=true)
218
- if bool
219
- @attributes['selected'] = nil
220
- else
221
- @attributes.delete('selected')
222
- end
223
- self
224
- end
320
+ bool_attr :selected
225
321
  end
226
322
 
227
323
  class Brush::SelectListTag < Brush::GenericTagBrush
@@ -241,17 +337,13 @@ class Brush::SelectListTag < Brush::GenericTagBrush
241
337
  ]
242
338
  end
243
339
 
244
- def multiple
245
- @multiple = true
246
- @attributes['multiple'] = nil
247
- self
248
- end
340
+ bool_attr :multiple
249
341
 
250
342
  alias __old_callback callback
251
343
  private :__old_callback
252
344
  def callback(symbol=nil, &block)
253
345
  raise ArgumentError if symbol and block
254
- block = @canvas.current_component.method(symbol) unless block
346
+ block = Wee::LiteralMethodCallback.new(@canvas.current_component, symbol) unless block
255
347
 
256
348
  @callback = block
257
349
  self
@@ -268,7 +360,9 @@ class Brush::SelectListTag < Brush::GenericTagBrush
268
360
  raise "invalid index in select list" if idx < 0 or idx > @items.size
269
361
  @items[idx]
270
362
  }
271
- raise "choosen more than one element from a non-multiple select list" if not @multiple and choosen.size > 1
363
+ if choosen.size > 1 and not @attributes.has_key?('multiple')
364
+ raise "choosen more than one element from a non-multiple select list"
365
+ end
272
366
  @callback.call(choosen)
273
367
  }
274
368
  end
@@ -277,7 +371,6 @@ class Brush::SelectListTag < Brush::GenericTagBrush
277
371
  @items.each_index do |i|
278
372
  @canvas.option.value(i).selected(@selected.include?(@items[i])).with(@labels[i])
279
373
  end
280
- @canvas.text("")
281
374
  end
282
375
  end
283
376
  end
@@ -291,7 +384,7 @@ class Brush::TextInputTag < Brush::InputTag
291
384
  end
292
385
  end
293
386
 
294
- class Wee::Brush::FileUploadTag < Wee::Brush::InputTag
387
+ class Brush::FileUploadTag < Brush::InputTag
295
388
  include Brush::InputCallbackMixin
296
389
 
297
390
  def initialize
@@ -318,7 +411,7 @@ end
318
411
  # #value method. Note that it's neccessary to parse the passed form-fields and
319
412
  # generate a "name" fields in the request, to make this image-button work.
320
413
 
321
- class Wee::Brush::ImageButtonTag < Wee::Brush::InputTag
414
+ class Brush::ImageButtonTag < Brush::InputTag
322
415
  include Brush::ActionCallbackMixin
323
416
 
324
417
  def initialize
@@ -338,8 +431,7 @@ class Brush::TableDataTag < Brush::GenericTagBrush
338
431
  end
339
432
 
340
433
  def align_top
341
- @attributes['align'] = 'top'
342
- self
434
+ html_attr('align', 'top')
343
435
  end
344
436
  end
345
437
 
@@ -358,15 +450,7 @@ class Brush::FormTag < Brush::GenericTagBrush
358
450
  @attributes['method'] = 'POST'
359
451
  end
360
452
 
361
- def action(href)
362
- @attributes['action'] = href
363
- self
364
- end
365
-
366
- def enctype(typ)
367
- @attributes['enctype'] = typ
368
- self
369
- end
453
+ html_attr :action, :enctype
370
454
 
371
455
  alias __set_url action
372
456
 
@@ -387,19 +471,16 @@ class Brush::AnchorTag < Brush::GenericTagBrush
387
471
  super('a')
388
472
  end
389
473
 
390
- def url(href)
391
- @attributes['href'] = href
392
- self
393
- end
394
- alias href url
395
-
474
+ html_attr :href, :title
475
+ alias url href
476
+ alias tooltip title
396
477
  alias __set_url url
397
478
  end
398
479
 
399
480
 
400
481
  class Brush::Page < Brush
401
- def title(str)
402
- @title = str
482
+ def title(t)
483
+ @title = t
403
484
  self
404
485
  end
405
486