wee 0.3.1 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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'