wee 0.3.1 → 0.4.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 (92) hide show
  1. data/README +0 -1
  2. data/Rakefile +5 -0
  3. data/TODO +6 -0
  4. data/doc/rdoc/classes/Enumerable.html +6 -6
  5. data/doc/rdoc/classes/Wee/AnswerDecoration.html +10 -10
  6. data/doc/rdoc/classes/Wee/Application.html +110 -52
  7. data/doc/rdoc/classes/Wee/Brush/ActionCallbackMixin.html +8 -15
  8. data/doc/rdoc/classes/Wee/Brush/ActionURLCallbackMixin.html +8 -19
  9. data/doc/rdoc/classes/Wee/Brush/AnchorTag.html +22 -22
  10. data/doc/rdoc/classes/Wee/Brush/FileUploadTag.html +156 -0
  11. data/doc/rdoc/classes/Wee/Brush/FormTag.html +50 -25
  12. data/doc/rdoc/classes/Wee/Brush/GenericEncodedTextBrush.html +12 -12
  13. data/doc/rdoc/classes/Wee/Brush/GenericTagBrush.html +36 -36
  14. data/doc/rdoc/classes/Wee/Brush/GenericTextBrush.html +12 -12
  15. data/doc/rdoc/classes/Wee/Brush/ImageButtonTag.html +16 -16
  16. data/doc/rdoc/classes/Wee/Brush/InputCallbackMixin.html +8 -15
  17. data/doc/rdoc/classes/Wee/Brush/InputTag.html +12 -12
  18. data/doc/rdoc/classes/Wee/Brush/Page.html +14 -14
  19. data/doc/rdoc/classes/Wee/Brush/SelectListTag.html +28 -28
  20. data/doc/rdoc/classes/Wee/Brush/SelectOptionTag.html +14 -14
  21. data/doc/rdoc/classes/Wee/Brush/SubmitButtonTag.html +7 -7
  22. data/doc/rdoc/classes/Wee/Brush/TableDataTag.html +14 -14
  23. data/doc/rdoc/classes/Wee/Brush/TableHeaderTag.html +7 -7
  24. data/doc/rdoc/classes/Wee/Brush/TableRowTag.html +36 -36
  25. data/doc/rdoc/classes/Wee/Brush/TableTag.html +6 -6
  26. data/doc/rdoc/classes/Wee/Brush/TextAreaTag.html +28 -28
  27. data/doc/rdoc/classes/Wee/Brush/TextInputTag.html +7 -7
  28. data/doc/rdoc/classes/Wee/Brush.html +19 -19
  29. data/doc/rdoc/classes/Wee/CallbackRegistry.html +33 -33
  30. data/doc/rdoc/classes/Wee/CallbackStream.html +18 -18
  31. data/doc/rdoc/classes/Wee/Canvas.html +24 -24
  32. data/doc/rdoc/classes/Wee/Component.html +163 -138
  33. data/doc/rdoc/classes/Wee/Decoration.html +72 -41
  34. data/doc/rdoc/classes/Wee/Delegate.html +31 -31
  35. data/doc/rdoc/classes/Wee/ErrorResponse.html +12 -12
  36. data/doc/rdoc/classes/Wee/GenericResponse.html +6 -6
  37. data/doc/rdoc/classes/Wee/Helper.html +148 -0
  38. data/doc/rdoc/classes/Wee/HtmlCanvas.html +267 -166
  39. data/doc/rdoc/classes/Wee/HtmlWriter.html +40 -40
  40. data/doc/rdoc/classes/Wee/LiteralMethodCallback.html +12 -12
  41. data/doc/rdoc/classes/Wee/PageDecoration.html +199 -0
  42. data/doc/rdoc/classes/Wee/Presenter.html +69 -69
  43. data/doc/rdoc/classes/Wee/RedirectResponse.html +6 -6
  44. data/doc/rdoc/classes/Wee/RefreshResponse.html +6 -6
  45. data/doc/rdoc/classes/Wee/Request.html +18 -18
  46. data/doc/rdoc/classes/Wee/RequestHandler.html +32 -31
  47. data/doc/rdoc/classes/Wee/Response.html +24 -24
  48. data/doc/rdoc/classes/Wee/Session.html +83 -64
  49. data/doc/rdoc/classes/Wee/SimpleIdGenerator.html +18 -18
  50. data/doc/rdoc/classes/Wee/Snapshot.html +19 -19
  51. data/doc/rdoc/classes/Wee/Utils/LRUCache.html +6 -6
  52. data/doc/rdoc/classes/Wee/ValueHolder.html +18 -18
  53. data/doc/rdoc/classes/Wee/WEBrickAdaptor.html +66 -62
  54. data/doc/rdoc/classes/Wee.html +63 -2
  55. data/doc/rdoc/created.rid +1 -1
  56. data/doc/rdoc/files/README.html +6 -9
  57. data/doc/rdoc/files/lib/wee/adaptors/webrick_rb.html +1 -1
  58. data/doc/rdoc/files/lib/wee/application_rb.html +1 -1
  59. data/doc/rdoc/files/lib/wee/components/page_decoration_rb.html +101 -0
  60. data/doc/rdoc/files/lib/wee/components_rb.html +108 -0
  61. data/doc/rdoc/files/lib/wee/core/callback_rb.html +1 -1
  62. data/doc/rdoc/files/lib/wee/core/component_rb.html +1 -1
  63. data/doc/rdoc/files/lib/wee/core/decoration_rb.html +1 -1
  64. data/doc/rdoc/files/lib/wee/core_rb.html +3 -3
  65. data/doc/rdoc/files/lib/wee/renderer/html/brushes_rb.html +1 -1
  66. data/doc/rdoc/files/lib/wee/renderer/html/canvas_rb.html +1 -1
  67. data/doc/rdoc/files/lib/wee/requesthandler_rb.html +1 -1
  68. data/doc/rdoc/files/lib/wee/session_rb.html +2 -1
  69. data/doc/rdoc/files/lib/wee/utils/autoreload_rb.html +101 -0
  70. data/doc/rdoc/files/lib/wee/utils/helper_rb.html +108 -0
  71. data/doc/rdoc/files/lib/wee_rb.html +2 -1
  72. data/doc/rdoc/fr_class_index.html +3 -1
  73. data/doc/rdoc/fr_file_index.html +4 -0
  74. data/doc/rdoc/fr_method_index.html +203 -191
  75. data/examples/example.rb +2 -16
  76. data/examples/test.rb +15 -18
  77. data/lib/wee/adaptors/webrick.rb +3 -1
  78. data/lib/wee/application.rb +55 -19
  79. data/lib/wee/components/page_decoration.rb +14 -0
  80. data/lib/wee/components.rb +1 -0
  81. data/lib/wee/core/callback.rb +1 -1
  82. data/lib/wee/core/component.rb +27 -3
  83. data/lib/wee/core/decoration.rb +9 -0
  84. data/lib/wee/renderer/html/brushes.rb +21 -23
  85. data/lib/wee/renderer/html/canvas.rb +25 -4
  86. data/lib/wee/requesthandler.rb +1 -0
  87. data/lib/wee/session.rb +21 -1
  88. data/lib/wee/utils/autoreload.rb +27 -0
  89. data/lib/wee/utils/helper.rb +15 -0
  90. data/lib/wee.rb +2 -1
  91. metadata +203 -191
  92. data/test/components/page_decoration.rb +0 -7
@@ -15,6 +15,7 @@ require 'webrick'
15
15
  # Wee::WEBrickAdaptor.
16
16
  # register('/app' => application).
17
17
  # register('/cnt' => application2).
18
+ # mount('/', WEBrick::HTTPServlet::FileHandler, '.').
18
19
  # start(:Port => 2000)
19
20
  #
20
21
 
@@ -48,7 +49,8 @@ class Wee::WEBrickAdaptor < WEBrick::HTTPServlet::AbstractServlet
48
49
  self
49
50
  end
50
51
 
51
- def mount(*args, &block)
52
+ # Convenience method
53
+ def self.mount(*args, &block)
52
54
  @mounts ||= []
53
55
  @mounts << [args, block]
54
56
  self
@@ -39,49 +39,85 @@ class Wee::Application
39
39
  raise ArgumentError, "No default request handler specified"
40
40
  end
41
41
  @mutex = Mutex.new
42
+
43
+ # start request-handler collecting thread
44
+ # run once every minute
45
+ @gc_thread = Thread.new {
46
+ sleep 60
47
+ @mutex.synchronize { garbage_collect_handlers }
48
+ }
42
49
  end
43
50
 
44
- # TODO: we have to use mutexes here
45
- # NOTE that id_generator must be thread-safe!
46
- # TODO: test for max_request_handlers
47
-
48
51
  def handle_request(context)
49
- @mutex.synchronize do
50
- request_handler_id = context.request.request_handler_id
51
- request_handler = @request_handlers[request_handler_id]
52
+ request_handler_id = context.request.request_handler_id
53
+ request_handler = @mutex.synchronize { @request_handlers[request_handler_id] }
54
+
55
+ if request_handler_id.nil?
56
+
57
+ # No id was given -> check whether the maximum number of request-handlers
58
+ # limit is reached. if not, create new id and handler
59
+
60
+ @mutex.synchronize {
61
+ if @max_request_handlers != nil and @request_handlers.size >= @max_request_handlers
62
+ # limit reached -> remove non-alive handlers...
63
+ garbage_collect_handlers()
52
64
 
53
- if request_handler_id.nil?
54
- # No id was given -> create new id and handler
55
- request_handler_id = unique_request_handler_id()
65
+ # ...and test again
66
+ if @request_handlers.size >= @max_request_handlers
67
+ # TODO: show a custom error message
68
+ raise "maximum number of request-handlers reached"
69
+ end
70
+ end
71
+
72
+ request_handler_id = unique_request_handler_id()
56
73
  request_handler = @default_request_handler.call
57
74
  request_handler.id = request_handler_id
58
75
  request_handler.application = self
59
76
  @request_handlers[request_handler_id] = request_handler
77
+ }
78
+
79
+ context.response = Wee::RedirectResponse.new(context.request.build_url(request_handler_id))
80
+ return
81
+
82
+ elsif request_handler.nil?
83
+
84
+ # A false request handler id was given. This might indicate that a
85
+ # request handler has expired.
60
86
 
61
- context.response = Wee::RedirectResponse.new(context.request.build_url(request_handler_id))
62
- return
87
+ request_handler_expired(context)
88
+ return
63
89
 
64
- elsif request_handler.nil?
65
- # A false request handler id was given. This might indicate that a
66
- # request handler has expired.
67
- request_handler_expired(context)
68
- return
69
- end
90
+ elsif !request_handler.alive?
91
+
92
+ # The request_handler is not alive anymore.
93
+
94
+ request_handler_expired(context)
95
+ return
70
96
 
71
- request_handler.handle_request(context)
72
97
  end
98
+
99
+ request_handler.handle_request(context)
100
+
73
101
  rescue => exn
74
102
  context.response = Wee::ErrorResponse.new(exn)
75
103
  end
76
104
 
77
105
  private
78
106
 
107
+ # MUST be called while holding @mutex
108
+
79
109
  def unique_request_handler_id
80
110
  id = @id_generator.next.to_s
81
111
  raise "failed to create unique request handler id" if @request_handlers.include?(id)
82
112
  return id
83
113
  end
84
114
 
115
+ # MUST be called while holding @mutex
116
+
117
+ def garbage_collect_handlers
118
+ @request_handlers.delete_if {|id,rh| !rh.alive? }
119
+ end
120
+
85
121
  def request_handler_expired(context)
86
122
  context.response = Wee::RefreshResponse.new("Invalid or expired request handler!", context.request.application_path)
87
123
  end
@@ -0,0 +1,14 @@
1
+ class Wee::PageDecoration < Wee::Decoration
2
+ def initialize(title='')
3
+ @title = title
4
+ super()
5
+ end
6
+
7
+ def global?() true end
8
+
9
+ def do_render(rendering_context)
10
+ with_renderer_for(rendering_context) do
11
+ r.page.title(@title).with { super(rendering_context) }
12
+ end
13
+ end
14
+ end
@@ -0,0 +1 @@
1
+ require 'wee/components/page_decoration'
@@ -50,7 +50,7 @@ class Wee::CallbackRegistry
50
50
  if c = @callbacks[type]
51
51
  c
52
52
  else
53
- raise
53
+ []
54
54
  end
55
55
  end
56
56
 
@@ -146,11 +146,35 @@ class Wee::Component < Wee::Presenter
146
146
  end
147
147
  end
148
148
 
149
- # Adds decoration +d+ in front of the decoration chain.
149
+ # Adds decoration +d+ to the decoration chain.
150
+ #
151
+ # A global decoration is added in front of the decoration chain, a local
152
+ # decoration is added in front of all other local decorations but after all
153
+ # global decorations.
150
154
 
151
155
  def add_decoration(d)
152
- d.owner = self.decoration
153
- self.decoration = d
156
+ if d.global?
157
+ d.owner = self.decoration
158
+ self.decoration = d
159
+ else
160
+ last_global = nil
161
+ each_decoration {|i|
162
+ if i.global?
163
+ last_global = i
164
+ else
165
+ break
166
+ end
167
+ }
168
+ if last_global.nil?
169
+ # no global decorations specified -> add in front
170
+ d.owner = self.decoration
171
+ self.decoration = d
172
+ else
173
+ # add after last_global
174
+ d.owner = last_global.owner
175
+ last_global.owner = d
176
+ end
177
+ end
154
178
  end
155
179
 
156
180
  # Remove decoration +d+ from the decoration chain.
@@ -33,6 +33,15 @@ class Wee::Decoration < Wee::Presenter
33
33
 
34
34
  attr_accessor :owner
35
35
 
36
+ # Is this decoration a global or a local one? By default all decorations are
37
+ # local unless this method is overwritten.
38
+ #
39
+ # A global decoration is added in front of the decoration chain, a local
40
+ # decoration is added in front of all other local decorations but after all
41
+ # global decorations.
42
+
43
+ def global?() false end
44
+
36
45
  # Forwards method call to the next decoration in the chain.
37
46
 
38
47
  def process_callbacks(callback_stream)
@@ -149,51 +149,32 @@ class Brush::InputTag < Brush::GenericTagBrush
149
149
  end
150
150
  end
151
151
 
152
- module Brush::CallbackMixin
153
- private
154
-
155
- def register_callback(type, &block)
156
- raise ArgumentError, "no callback block given" if block.nil?
157
- @canvas.rendering_context.callbacks.register_for(@canvas.current_component, type, &block)
158
- end
159
- end
160
-
161
152
  module Brush::InputCallbackMixin
162
- include Brush::CallbackMixin
163
-
164
153
  public
165
154
 
166
155
  def callback(symbol=nil, &block)
167
156
  raise ArgumentError if symbol and block
168
157
  block = @canvas.current_component.method(symbol) unless block
169
- name(register_callback(:input, &block))
158
+ name(@canvas.register_callback(:input, &block))
170
159
  end
171
160
  end
172
161
 
173
162
  module Brush::ActionCallbackMixin
174
- include Brush::CallbackMixin
175
-
176
163
  public
177
164
 
178
165
  def callback(symbol=nil, &block)
179
166
  raise ArgumentError if symbol and block
180
167
  block = @canvas.current_component.method(symbol) unless block
181
- name(register_callback(:action, &block))
168
+ name(@canvas.register_callback(:action, &block))
182
169
  end
183
170
  end
184
171
 
185
172
  # The callback id is listed in the URL (not as a form-data field)
186
173
  module Brush::ActionURLCallbackMixin
187
- include Brush::CallbackMixin
188
-
189
174
  public
190
175
 
191
176
  def callback(symbol=nil, &block)
192
- raise ArgumentError if symbol and block
193
- block = @canvas.current_component.method(symbol) unless block
194
- req = @canvas.rendering_context.request
195
- url = req.build_url(req.request_handler_id, req.page_id, register_callback(:action, &block))
196
- __set_url(url)
177
+ __set_url(@canvas.url_for_callback(symbol, &block))
197
178
  end
198
179
  end
199
180
 
@@ -310,6 +291,17 @@ class Brush::TextInputTag < Brush::InputTag
310
291
  end
311
292
  end
312
293
 
294
+ class Wee::Brush::FileUploadTag < Wee::Brush::InputTag
295
+ include Brush::InputCallbackMixin
296
+
297
+ def initialize
298
+ super
299
+ type('file')
300
+ end
301
+ end
302
+
303
+
304
+
313
305
  class Brush::SubmitButtonTag < Brush::InputTag
314
306
  include Brush::ActionCallbackMixin
315
307
 
@@ -327,7 +319,7 @@ end
327
319
  # generate a "name" fields in the request, to make this image-button work.
328
320
 
329
321
  class Wee::Brush::ImageButtonTag < Wee::Brush::InputTag
330
- include Wee::Brush::ActionCallbackMixin
322
+ include Brush::ActionCallbackMixin
331
323
 
332
324
  def initialize
333
325
  super
@@ -339,6 +331,7 @@ class Wee::Brush::ImageButtonTag < Wee::Brush::InputTag
339
331
  end
340
332
  end
341
333
 
334
+
342
335
  class Brush::TableDataTag < Brush::GenericTagBrush
343
336
  def initialize
344
337
  super('td')
@@ -370,6 +363,11 @@ class Brush::FormTag < Brush::GenericTagBrush
370
363
  self
371
364
  end
372
365
 
366
+ def enctype(typ)
367
+ @attributes['enctype'] = typ
368
+ self
369
+ end
370
+
373
371
  alias __set_url action
374
372
 
375
373
  def with(*args, &block)
@@ -42,14 +42,23 @@ class HtmlCanvas < Canvas
42
42
  @document = rendering_context.document
43
43
  end
44
44
 
45
- def bold(*args, &block)
46
- handle(Brush::GenericTagBrush.new("b"), *args, &block)
47
- end
48
-
49
45
  def method_missing(id, *args, &block)
50
46
  handle(Brush::GenericTagBrush.new(id.to_s), *args, &block)
51
47
  end
52
48
 
49
+ def url_for_callback(symbol=nil, &block)
50
+ raise ArgumentError if symbol and block
51
+ block = self.current_component.method(symbol) unless block
52
+ req = self.rendering_context.request
53
+ url = req.build_url(req.request_handler_id, req.page_id, register_callback(:action, &block))
54
+ return url
55
+ end
56
+
57
+ def register_callback(type, &block)
58
+ raise ArgumentError, "no callback block given" if block.nil?
59
+ self.rendering_context.callbacks.register_for(self.current_component, type, &block)
60
+ end
61
+
53
62
  def table(*args, &block)
54
63
  handle(Brush::TableTag.new, *args, &block)
55
64
  end
@@ -98,6 +107,10 @@ class HtmlCanvas < Canvas
98
107
  handle(Wee::Brush::ImageButtonTag.new, *args, &block)
99
108
  end
100
109
 
110
+ def file_upload(*args, &block)
111
+ handle(Wee::Brush::FileUploadTag.new, *args, &block)
112
+ end
113
+
101
114
  def page(*args, &block)
102
115
  handle(Brush::Page.new, *args, &block)
103
116
  end
@@ -110,6 +123,14 @@ class HtmlCanvas < Canvas
110
123
  set_brush(Brush::GenericTextBrush.new("&nbsp;"*n))
111
124
  end
112
125
 
126
+ def bold(*args, &block)
127
+ handle(Brush::GenericTagBrush.new("b"), *args, &block)
128
+ end
129
+
130
+ def paragraph
131
+ set_brush(Brush::GenericTagBrush.new("p"))
132
+ end
133
+
113
134
  def break
114
135
  set_brush(Brush::GenericTagBrush.new("br"))
115
136
  end
@@ -70,6 +70,7 @@ class Wee::RequestHandler
70
70
 
71
71
  def initialize
72
72
  @last_access = @creation_time = Time.now
73
+ @expire_after = 30*60 # The default is 30 minutes of inactivity
73
74
  @request_count = 0
74
75
  @running = true
75
76
  end
data/lib/wee/session.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  require 'wee/page'
2
2
  require 'thread'
3
+ require 'timeout'
3
4
 
4
5
  class Wee::Session < Wee::RequestHandler
5
6
  attr_accessor :root_component, :page_store
@@ -51,7 +52,25 @@ class Wee::Session < Wee::RequestHandler
51
52
  Thread.new {
52
53
  Thread.current[:wee_session] = self
53
54
  loop {
54
- @context = @in_queue.pop
55
+ @context = nil
56
+
57
+ # get a request, check whether this session is alive after every 5
58
+ # seconds.
59
+ while @context.nil?
60
+ begin
61
+ Timeout.timeout(5) {
62
+ @context = @in_queue.pop
63
+ }
64
+ rescue Timeout::Error
65
+ break unless alive?
66
+ end
67
+ end
68
+
69
+ # abort thread if no longer alive
70
+ break if not alive?
71
+
72
+ raise "invalid request" if @context.nil?
73
+
55
74
  begin
56
75
  process_request
57
76
  rescue Exception => exn
@@ -59,6 +78,7 @@ class Wee::Session < Wee::RequestHandler
59
78
  end
60
79
  @out_queue.push(@context)
61
80
  }
81
+ p "session loop terminated" if $DEBUG
62
82
  }
63
83
  end
64
84
 
@@ -0,0 +1,27 @@
1
+ module Wee
2
+ module_function
3
+
4
+ def autoreload(check_interval=10)
5
+ Thread.new(Time.now) {|start_time|
6
+ file_mtime = {}
7
+ loop do
8
+ sleep check_interval
9
+ $LOADED_FEATURES.each do |feature|
10
+ $LOAD_PATH.each do |lp|
11
+ file = File.join(lp, feature)
12
+ if (File.exists?(file) and
13
+ File.stat(file).mtime > (file_mtime[file] || start_time))
14
+ file_mtime[file] = File.stat(file).mtime
15
+ STDERR.puts "reload #{ file }"
16
+ begin
17
+ load(file)
18
+ rescue Exception => e
19
+ STDERR.puts e.inspect
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ }
26
+ end
27
+ end
@@ -0,0 +1,15 @@
1
+ require 'wee/utils/cache'
2
+
3
+ module Wee::Helper
4
+ def self.app_for(component, page_cache_capacity=10, id_seed=rand(1_000_000))
5
+ Wee::Application.new {|app|
6
+ app.default_request_handler {
7
+ Wee::Session.new {|sess|
8
+ sess.root_component = component.new
9
+ sess.page_store = Wee::Utils::LRUCache.new(page_cache_capacity)
10
+ }
11
+ }
12
+ app.id_generator = Wee::SimpleIdGenerator.new(id_seed)
13
+ }
14
+ end
15
+ end
data/lib/wee.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  module Wee
2
- Version = "0.3.1"
2
+ Version = "0.4.0"
3
3
  end
4
4
 
5
5
  require 'wee/core'
@@ -11,6 +11,7 @@ require 'wee/request'
11
11
  require 'wee/response'
12
12
  require 'wee/session'
13
13
 
14
+ require 'wee/components'
14
15
  require 'wee/snapshot_ext'
15
16
 
16
17
  require 'wee/renderer/html/writer'