wee 0.4.0 → 0.5.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 (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