nitro 0.20.0 → 0.21.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 (115) hide show
  1. data/CHANGELOG +752 -543
  2. data/INSTALL +38 -38
  3. data/README +264 -225
  4. data/Rakefile +48 -49
  5. data/bin/nitro +3 -3
  6. data/bin/nitrogen +6 -6
  7. data/doc/AUTHORS +10 -10
  8. data/doc/CHANGELOG.1 +1939 -1939
  9. data/doc/CHANGELOG.2 +954 -954
  10. data/doc/LICENSE +3 -3
  11. data/doc/MIGRATION +28 -0
  12. data/doc/RELEASES +814 -643
  13. data/doc/config.txt +5 -5
  14. data/install.rb +7 -17
  15. data/lib/nitro.rb +38 -9
  16. data/lib/nitro/adapter/cgi.rb +311 -312
  17. data/lib/nitro/adapter/fastcgi.rb +18 -25
  18. data/lib/nitro/adapter/webrick.rb +128 -137
  19. data/lib/nitro/adapter/wee.rb +51 -0
  20. data/lib/nitro/caching.rb +20 -20
  21. data/lib/nitro/caching/actions.rb +43 -43
  22. data/lib/nitro/caching/fragments.rb +46 -46
  23. data/lib/nitro/caching/invalidation.rb +11 -11
  24. data/lib/nitro/caching/output.rb +65 -65
  25. data/lib/nitro/caching/stores.rb +67 -67
  26. data/lib/nitro/compiler.rb +262 -0
  27. data/lib/nitro/compiler/elements.rb +0 -0
  28. data/lib/nitro/compiler/errors.rb +65 -0
  29. data/lib/nitro/compiler/localization.rb +25 -0
  30. data/lib/nitro/compiler/markup.rb +19 -0
  31. data/lib/nitro/compiler/shaders.rb +206 -0
  32. data/lib/nitro/compiler/squeeze.rb +20 -0
  33. data/lib/nitro/compiler/xslt.rb +61 -0
  34. data/lib/nitro/context.rb +87 -88
  35. data/lib/nitro/controller.rb +151 -158
  36. data/lib/nitro/cookie.rb +34 -34
  37. data/lib/nitro/dispatcher.rb +195 -186
  38. data/lib/nitro/element.rb +132 -126
  39. data/lib/nitro/element/java_script.rb +6 -6
  40. data/lib/nitro/flash.rb +66 -66
  41. data/lib/nitro/mail.rb +192 -192
  42. data/lib/nitro/mixin/buffer.rb +66 -0
  43. data/lib/nitro/mixin/debug.rb +16 -16
  44. data/lib/nitro/mixin/form.rb +88 -0
  45. data/lib/nitro/mixin/helper.rb +2 -2
  46. data/lib/nitro/mixin/javascript.rb +108 -108
  47. data/lib/nitro/mixin/markup.rb +144 -0
  48. data/lib/nitro/mixin/pager.rb +202 -202
  49. data/lib/nitro/mixin/rss.rb +67 -0
  50. data/lib/nitro/mixin/table.rb +63 -0
  51. data/lib/nitro/mixin/xhtml.rb +75 -0
  52. data/lib/nitro/mixin/xml.rb +124 -0
  53. data/lib/nitro/render.rb +183 -359
  54. data/lib/nitro/request.rb +140 -140
  55. data/lib/nitro/response.rb +27 -27
  56. data/lib/nitro/routing.rb +21 -21
  57. data/lib/nitro/scaffold.rb +124 -118
  58. data/lib/nitro/server.rb +117 -80
  59. data/lib/nitro/server/runner.rb +341 -0
  60. data/lib/nitro/service.rb +12 -12
  61. data/lib/nitro/service/xmlrpc.rb +22 -22
  62. data/lib/nitro/session.rb +122 -120
  63. data/lib/nitro/session/drb.rb +9 -9
  64. data/lib/nitro/session/drbserver.rb +34 -34
  65. data/lib/nitro/template.rb +171 -155
  66. data/lib/nitro/testing/assertions.rb +90 -90
  67. data/lib/nitro/testing/context.rb +16 -16
  68. data/lib/nitro/testing/testcase.rb +34 -34
  69. data/proto/conf/lhttpd.conf +9 -9
  70. data/proto/public/error.xhtml +75 -75
  71. data/proto/public/index.xhtml +18 -18
  72. data/proto/public/js/behaviour.js +65 -65
  73. data/proto/public/js/controls.js +1 -1
  74. data/proto/public/js/prototype.js +3 -3
  75. data/proto/public/settings.xhtml +61 -61
  76. data/proto/run.rb +1 -5
  77. data/test/nitro/adapter/raw_post1.bin +0 -0
  78. data/test/nitro/adapter/tc_cgi.rb +57 -57
  79. data/test/nitro/adapter/tc_webrick.rb +4 -4
  80. data/test/nitro/mixin/tc_pager.rb +25 -25
  81. data/test/nitro/mixin/tc_rss.rb +24 -0
  82. data/test/nitro/mixin/tc_table.rb +31 -0
  83. data/test/nitro/mixin/tc_xhtml.rb +13 -0
  84. data/test/nitro/tc_caching.rb +10 -10
  85. data/test/nitro/tc_context.rb +8 -8
  86. data/test/nitro/tc_controller.rb +48 -48
  87. data/test/nitro/tc_cookie.rb +6 -6
  88. data/test/nitro/tc_dispatcher.rb +64 -64
  89. data/test/nitro/tc_element.rb +27 -27
  90. data/test/nitro/tc_flash.rb +31 -31
  91. data/test/nitro/tc_mail.rb +63 -63
  92. data/test/nitro/tc_server.rb +26 -26
  93. data/test/nitro/tc_session.rb +9 -9
  94. data/test/nitro/tc_template.rb +19 -19
  95. data/test/public/blog/list.xhtml +1 -1
  96. metadata +31 -37
  97. data/lib/nitro/buffering.rb +0 -45
  98. data/lib/nitro/builder/form.rb +0 -104
  99. data/lib/nitro/builder/rss.rb +0 -104
  100. data/lib/nitro/builder/table.rb +0 -80
  101. data/lib/nitro/builder/xhtml.rb +0 -132
  102. data/lib/nitro/builder/xml.rb +0 -131
  103. data/lib/nitro/conf.rb +0 -36
  104. data/lib/nitro/environment.rb +0 -21
  105. data/lib/nitro/errors.rb +0 -69
  106. data/lib/nitro/localization.rb +0 -153
  107. data/lib/nitro/markup.rb +0 -147
  108. data/lib/nitro/output.rb +0 -24
  109. data/lib/nitro/runner.rb +0 -348
  110. data/lib/nitro/shaders.rb +0 -206
  111. data/test/nitro/builder/tc_rss.rb +0 -23
  112. data/test/nitro/builder/tc_table.rb +0 -30
  113. data/test/nitro/builder/tc_xhtml.rb +0 -39
  114. data/test/nitro/builder/tc_xml.rb +0 -56
  115. data/test/nitro/tc_localization.rb +0 -49
@@ -0,0 +1,63 @@
1
+ module Nitro
2
+
3
+ # The TableBuilder is a helper class that automates the creation
4
+ # of tables from collections of objects. The resulting html
5
+ # can be styled using css.
6
+ #
7
+ # === Example
8
+ #
9
+ # <?r
10
+ # users = User.all.map { |u| [u.name, u.first_name, u.last_name, u.email] }
11
+ # header = ['Username', 'First name', 'Last name', 'Email']
12
+ # ?>
13
+ #
14
+ # <div class="custom-table-class">
15
+ # #{table(users, header)}
16
+ # </div>
17
+ #--
18
+ # TODO: sorting, thead/tbody/legend etc, verbose...
19
+ #++
20
+
21
+ module TableMixin
22
+
23
+ # [+options+]
24
+ # A hash of options.
25
+ #
26
+ # :id = id of the component.
27
+ # :headers = an array of the header values
28
+ # :values = an array of arrays.
29
+
30
+ def table(options)
31
+ c = options
32
+
33
+ str = '<table'
34
+ str << %| id="#{c[:id]}"| if c[:id]
35
+ str << '><tr>'
36
+
37
+ for h in c[:headers]
38
+ str << %|<th>#{h}</th>|
39
+ end
40
+
41
+ str << "</tr>"
42
+
43
+ for row in c[:values]
44
+ str << "<tr>"
45
+
46
+ for v in row
47
+ str << %|<td>#{v}</td>|
48
+ end
49
+
50
+ str << "</tr>"
51
+ end
52
+
53
+ str << "</table>"
54
+
55
+ return str
56
+ end
57
+ alias_method :build_table, :table
58
+
59
+ end
60
+
61
+ end
62
+
63
+ # * George Moschovitis <gm@navel.gr>
@@ -0,0 +1,75 @@
1
+ module Nitro
2
+
3
+ # A helper mixin for programmatically building XHTML
4
+ # blocks.
5
+
6
+ module XhtmlMixin
7
+
8
+ # Render select options. The parameter is a hash of options.
9
+ #
10
+ # [+labels+]
11
+ # The option labels.
12
+ #
13
+ # [+values+]
14
+ # The corresponding values.
15
+ #
16
+ # [+selected+]
17
+ # The value of the selected option.
18
+ #
19
+ # === Examples
20
+ #
21
+ # labels = ['Male', 'Female']
22
+ # o.select(:name => 'sex') {
23
+ # o.options(:labels => labels, :selected => 1)
24
+ # }
25
+ #
26
+ # or
27
+ #
28
+ # #{options :labels => labels, :values => [..], :selected => 1}
29
+ # #{build :options, :labels => labels, :values => [..], :selected => 1}
30
+
31
+ def options(options = {})
32
+ if labels = options[:labels]
33
+ str = ''
34
+
35
+ unless values = options[:values]
36
+ values = (0...labels.size).to_a
37
+ end
38
+
39
+ selected = (options[:selected] || -1).to_i
40
+
41
+ labels.each_with_index do |label, idx|
42
+ value = values[idx]
43
+ if value == selected
44
+ str << %|<option value="#{value}" selected="1">#{label}</option>|
45
+ else
46
+ str << %|<option value="#{value}">#{label}</option>|
47
+ end
48
+ end
49
+
50
+ return str
51
+ else
52
+ raise ArgumentError.new('No labels provided')
53
+ end
54
+ end
55
+
56
+ # Render a submit input.
57
+
58
+ def submit(options = nil)
59
+ str = ''
60
+
61
+ if options
62
+ opts = options.collect { |k, v| %[#{k}="#{v}"] }.join(' ')
63
+ str << %[<input type="submit" #{opts} />]
64
+ else
65
+ str << %|<input type="submit" />|
66
+ end
67
+
68
+ return str
69
+ end
70
+
71
+ end
72
+
73
+ end
74
+
75
+ # * George Moschovitis <gm@navel.gr>
@@ -0,0 +1,124 @@
1
+ module Nitro
2
+
3
+ # A helper mixin for programmatically building XML
4
+ # blocks.
5
+
6
+ module XmlMixin
7
+
8
+ def method_missing(tag, *args, &block)
9
+ self.class.module_eval <<-"end_eval", __FILE__, __LINE__
10
+ def #{tag}(*args)
11
+ attrs = args.last.is_a?(Hash) ? args.pop : nil
12
+
13
+ if block_given?
14
+ start_tag!('#{tag}', attrs)
15
+ yield
16
+ end_tag!('#{tag}')
17
+ elsif (!args.empty?)
18
+ start_tag!('#{tag}', attrs)
19
+ self << args.first
20
+ end_tag!('#{tag}')
21
+ else
22
+ start_tag!('#{tag}', attrs, false)
23
+ self << ' />'
24
+ end
25
+ end
26
+ end_eval
27
+
28
+ self.send(tag, *args, &block)
29
+ end
30
+
31
+ # Emit the start (opening) tag of an element.
32
+
33
+ def start_tag!(tag, attributes = nil, close = true)
34
+ unless attributes
35
+ if close
36
+ self << "<#{tag}>"
37
+ else
38
+ self << "<#{tag}"
39
+ end
40
+ else
41
+ self << "<#{tag}"
42
+ for name, value in attributes
43
+ if value
44
+ self << %| #{name}="#{value}"|
45
+ else
46
+ self << %| #{name}="1"|
47
+ end
48
+ end
49
+ self << ">" if close
50
+ end
51
+
52
+ return self
53
+ end
54
+
55
+ # Emit the end (closing) tag of an element.
56
+
57
+ def end_tag!(tag)
58
+ self << "</#{tag}>"
59
+
60
+ return self
61
+ end
62
+
63
+ # Emit a text string.
64
+
65
+ def text!(str)
66
+ self << str
67
+
68
+ return self
69
+ end
70
+ alias_method :print, :text!
71
+
72
+ # Emit a comment.
73
+
74
+ def comment!(str)
75
+ self << "<!-- #{str} -->"
76
+
77
+ return self
78
+ end
79
+
80
+ # Emit a processing instruction.
81
+
82
+ def processing_instruction!(name, attributes = nil)
83
+ unless attributes
84
+ self << "<?#{name} ?>"
85
+ else
86
+ self << "<?#{name} "
87
+ attributes.each do |a, v|
88
+ self << %[#{a}="#{v}" ]
89
+ end
90
+ self << "?>"
91
+ end
92
+ end
93
+ alias_method :pi!, :processing_instruction!
94
+
95
+ end
96
+
97
+ # A class that encapsulats the XML generation
98
+ # functionality. Utilizes duck typing to redirect
99
+ # output to a target buffer.
100
+
101
+ class XmlBuilder
102
+ include XmlMixin
103
+
104
+ # The target receives the generated xml,
105
+ # should respond_to :<<
106
+
107
+ attr_accessor :target
108
+
109
+ def initialize(target = '')
110
+ @target = target
111
+ end
112
+
113
+ def << (str)
114
+ @target << str
115
+ end
116
+
117
+ def to_s
118
+ @target.to_s
119
+ end
120
+ end
121
+
122
+ end
123
+
124
+ # * George Moschovitis <gm@navel.gr>
data/lib/nitro/render.rb CHANGED
@@ -1,3 +1,4 @@
1
+ require 'singleton'
1
2
  require 'sync'
2
3
 
3
4
  require 'facet/string/blank%3F'
@@ -6,388 +7,211 @@ require 'glue/attribute'
6
7
  require 'glue/misc'
7
8
  require 'glue/object'
8
9
  require 'glue/settings'
10
+ require 'glue/builder'
11
+ require 'glue/builder/xml'
9
12
 
10
- require 'nitro/shaders'
11
- require 'nitro/buffering'
12
- require 'nitro/errors'
13
+ require 'nitro/mixin/xhtml'
14
+ require 'nitro/mixin/form'
15
+ require 'nitro/mixin/table'
16
+ require 'nitro/mixin/buffer'
13
17
 
14
18
  module Nitro
15
19
 
16
- # Raise this exception to stop the current action.
20
+ # Raise or Throw this exception to stop the current action.
17
21
  # Typically called to skip the template.
18
22
 
19
23
  class ActionExit < Exception; end
20
-
21
- # Raise this exception to stop rendering altogether.
24
+
25
+ # Raise or Thorw this exception to stop rendering altogether.
22
26
  # Typically called by redirects.
23
27
 
24
28
  class RenderExit < Exception; end
25
29
 
26
- # Rendering utility methods
27
-
28
- module Rendering
29
-
30
- @@sync = Sync.new
31
-
32
- # The default template root
33
-
34
- setting :default_template_root, :default => 'public', :doc => 'The default template root'
35
-
36
- # The default template name (no extension).
37
-
38
- setting :default_template, :default => 'index', :doc => 'The default template name (no extension).'
39
-
40
- # The shader used for transforming templates.
41
- # The default shader is very simple, here is a
42
- # typical example of a production shader pipeline:
43
- #
44
- # <tt>
45
- # Rendering.shader =
46
- # XSLTShader.new("xsl/style.xsl",
47
- # RubyShader.new(
48
- # CompressShader.new
49
- # )
50
- # )
51
- # </tt>
52
-
53
- mattr_accessor :shader; @@shader = RubyShader.new
54
- # setting :shader, :default => RubyShader.new, :doc => 'The shader used for transforming templates'
55
-
56
- # If set to :full, reloads all controllers. Useful in
57
- # development.
58
-
59
- setting :reload, :default => false, :doc => 'If set to :full reloads everything, useful in development'
60
-
61
- # Given the action try find the matching template.
62
- # Can search for xhtml or xml templates.
63
- # Returns nil if no template file is found.
64
-
65
- def self.template_for_action(template_root, action, ext = :xhtml)
66
- # attempt to find a template of the form
67
- # template_root/action.xhtml
68
-
69
- path = "#{template_root}/#{action.gsub(/__/, '/')}.#{ext}".squeeze('/')
70
-
71
- unless File.exist?(path)
72
- # attempt to find a template of the form
73
- # template_root/action/index.xhtml
74
-
75
- path = "#{template_root}/#{action.gsub(/__/, '/')}/#{Rendering.default_template}.#{ext}".squeeze('/')
76
-
77
- unless File.exist?(path)
78
- # No template found!
79
- path = nil
80
- end
81
- end
82
-
83
- return path
84
- end
85
-
86
- # Transform a template to ruby rendering code.
87
-
88
- def self.transform_template(path, shader)
89
- Logger.debug "Transforming '#{path}'" if $DBG
90
-
91
- text = File.read(path)
92
- hash, text = shader.process(path, text)
93
-
94
- return text
95
- end
96
-
97
- # Compile a controller action.
98
-
99
- def self.compile_action(klass, action)
100
- @@sync.synchronize do
101
- Aspects.include_advice_modules(klass)
102
-
103
- action = action.to_s.gsub(/_action$/, '')
104
-
105
- # This is not a controller action.
106
-
107
- return false unless action
108
-
109
- Logger.debug "Compiling action '#{klass.template_root}/#{action}'" if $DBG
110
-
111
- valid = false
112
-
113
- code = %{
114
- def #{action}_action
115
- @parent_action_name = @action_name
116
- @action_name = '#{action}'
117
- }
118
-
119
- # Inject the pre advices.
120
-
121
- code << Aspects.gen_advice_code(action, klass.advices, :pre)
122
-
123
- # Call the action
124
-
125
- if klass.action_methods.include?(action)
126
- valid = true
127
-
128
- # Annotated parameters.
129
-
130
- if meta = klass.action_metadata[action.intern]
131
- params = meta.params.keys
132
- params = params.collect { |p| "@#{p} = @context['#{p}']" }
133
- code << "#{params.join(';')}"
134
- end
135
-
136
- # Try to resolve action parameters.
137
-
138
- param_count = klass.instance_method(action.intern).arity
139
-
140
- # gmosx, FIXME: REIMPLEMENT THIS!!!!
141
-
142
- if param_count > 0
143
- code << %{
144
- params = []
145
- qs = context.query_string.split(/[&;]/)
146
-
147
- #{param_count}.times do |i|
148
- params << qs.shift.split(/=/).last
149
- end
150
-
151
- unless :stop == #{action}(*params)
152
- }
153
- else
154
- code << %{
155
- unless :stop == #{action}
156
- }
157
- end
158
- end
159
-
160
- # Try to call the template method if it exists. It is a nice
161
- # practice to put output related code in this method instead
162
- # of the main action so that this method can be overloaded
163
- # separately.
164
- #
165
- # If no template method exists, try to convert an external
166
- # template file into a template method. It is an even better
167
- # practice to place the output related code in an external
168
- # template file.
169
-
170
- # Take :view metadata into account.
171
-
172
- view = nil
173
- if md = klass.action_metadata[action.intern]
174
- view = md[:view]
175
- end
176
- view ||= action
177
-
178
- cklass = klass
179
-
180
- while cklass.respond_to?(:template_root)
181
- if template = template_for_action(cklass.template_root, view.to_s)
182
- valid = true
183
- code << %{
184
- #{action}_template;
185
- }
186
- break
187
- end
188
-
189
- # don't search in parent template roots if an
190
- # action is defined.
191
-
192
- break if valid
193
-
194
- cklass = cklass.superclass
195
- end
196
-
197
- # raise "Invalid action '#{action}' for '#{klass}'!" unless valid
198
- return false unless valid
199
-
200
- # Inject the post advices.
201
-
202
- code << Aspects.gen_advice_code(action, klass.advices, :post)
203
-
204
- if klass.action_methods.include?(action)
205
- code << %{
206
- end
207
- }
208
- end
209
-
210
- code << %{
211
- @action_name = @parent_action_name
212
- redirect_referer if @out.empty?
213
- end
214
- }
215
-
216
- # First compile the action method.
217
-
218
- # begin
219
- klass.class_eval(code)
220
- # rescue SyntaxError => e
221
- # raise ActionCompileError.new(code, action, e)
222
- # end
223
-
224
- # Try to compile the template (if exists).
225
-
226
- if template
227
- code = %{
228
- def #{action}_template
229
- #{transform_template(template, Rendering.shader)}
230
- end
231
- }
232
-
233
- begin
234
- klass.class_eval(code, template)
235
- rescue SyntaxError => e
236
- raise TemplateCompileError.new(code, template, e)
237
- end
238
- end
239
- end
240
-
241
- return true
242
- end
30
+ # The output buffer.
31
+ #--
32
+ # TODO: Implement a FAST string (maybe in C)
33
+ #++
243
34
 
35
+ class OutputBuffer < String
244
36
  end
245
37
 
246
- # The rendering mixin.
38
+ # The rendering mixin. This module is typically included in
39
+ # published objects and/or controllers to provide rendering
40
+ # functionality.
247
41
  #--
248
42
  # TODO: handle template_root here instead of the
249
43
  # controller.
250
44
  #++
251
45
 
252
46
  module Render
253
-
254
- # The output buffer. The output of a script/action is
255
- # accumulated in this buffer.
256
-
257
- attr_accessor :out
258
-
259
- # A nice alias
260
-
261
- alias_method :body, :out
262
-
263
- # The context.
264
-
265
- attr_accessor :context
266
- alias_method :ctx, :context
267
- alias_method :ctx=, :context=
268
-
269
- # Aliases for context.
270
-
271
- attr_accessor :request, :response
272
-
273
- # An array holding the rendering errors for this
274
- # request.
275
-
276
- attr_accessor :rendering_errors
277
-
278
- # The name of the currently executing action.
279
-
280
- attr_accessor :action_name
281
-
282
- # The name of the current controller.
283
-
284
- attr_accessor :controller_name
285
-
286
- # Initialize the render.
287
- #
288
- # [+context+]
289
- # A parent render/controller acts as the context.
290
-
291
- def initialize(context, base = nil)
292
- @request = @response = @context = context
293
- @controller_name = base
294
- @out = context.out
295
- end
296
-
297
- # Renders the action denoted by path. The path
298
- # is resolved by the dispatcher to get the correct
299
- # controller.
300
-
301
- def render(path)
302
- Logger.debug "Rendering '#{path}'." if $DBG
303
-
304
- klass, action, base = @context.dispatcher.dispatch(path, @context)
305
-
306
- # FIXME:
307
- @context.content_type = klass.instance_variable_get('@content_type') || 'text/html'
308
-
309
- raise 'No controller for action' unless klass
310
-
311
- if self.class == klass
312
- self.send(action)
313
- else
314
- klass.new(self, base).send(action)
315
- end
316
-
317
- rescue RenderExit => e
318
-
319
- # Just stop rendering.
320
- # For example called by redirects.
321
-
322
- rescue Exception, StandardError => e
323
- log_error(e, path)
324
-
325
- # More fault tolerant, only flags the erroneous box with
326
- # error not the full page.
327
-
328
- @out << '(error)'
329
- end
47
+ include BufferMixin
48
+
49
+ # If true, auto redirect to referer on empty buffer.
50
+
51
+ setting :redirect_on_empty, :default => false, :doc => 'If true, auto redirect to referer on empty buffer'
52
+
53
+ # The output buffer. The output of a script/action is
54
+ # accumulated in this buffer.
55
+
56
+ attr_accessor :out
57
+
58
+ alias_method :body, :out
59
+
60
+ # The context.
61
+
62
+ attr_accessor :context
63
+ alias_method :ctx, :context
64
+ alias_method :ctx=, :context=
65
+
66
+ # Aliases for context.
67
+
68
+ attr_accessor :request, :response
69
+
70
+ # An array holding the rendering errors for this
71
+ # request.
72
+
73
+ attr_accessor :rendering_errors
74
+
75
+ # The name of the currently executing action.
76
+
77
+ attr_accessor :action_name
78
+
79
+ # The name of the current controller.
80
+
81
+ attr_accessor :controller_name
82
+
83
+ # Initialize the render.
84
+ #
85
+ # [+context+]
86
+ # A parent render/controller acts as the context.
87
+
88
+ def initialize(context, base = nil)
89
+ @request = @response = @context = context
90
+ @controller_name = base
91
+ @out = context.out
92
+ end
93
+
94
+ # Renders the action denoted by path. The path
95
+ # is resolved by the dispatcher to get the correct
96
+ # controller.
97
+
98
+ def render(path)
99
+ Logger.debug "Rendering '#{path}'." if $DBG
100
+
101
+ klass, action, base = @context.dispatcher.dispatch(path, @context)
102
+
103
+ # FIXME:
104
+ @context.content_type = klass.instance_variable_get('@content_type') || 'text/html'
105
+
106
+ raise 'No controller for action' unless klass
107
+
108
+ if self.class == klass
109
+ self.send(action)
110
+ else
111
+ klass.new(self, base).send(action)
112
+ end
113
+
114
+ rescue RenderExit => e
115
+
116
+ # Just stop rendering.
117
+ # For example called by redirects.
118
+
119
+ rescue Exception, StandardError => e
120
+ log_error(e, path)
121
+
122
+ # More fault tolerant, only flags the erroneous box with
123
+ # error not the full page.
124
+
125
+ @out << '(error)'
126
+ end
330
127
 
331
128
  private
332
129
 
333
- # Send a redirect response.
334
-
335
- def redirect(url, status = 303)
336
- url = url.to_s
337
- url = "#{@context.host_url}/#{url.gsub(/^\//, '')}" unless url =~ /http/
338
-
339
- @context.status = status
340
- @context.out = "<html><a href=\"#{url}\">#{url}</a>.</html>\n"
341
- @context.response_headers['location'] = url
342
-
343
- raise RenderExit
344
- end
345
-
346
- # Redirect to the referer of this method.
347
-
348
- def redirect_referer(postfix = nil, status = 303)
349
- redirect("#{@context.referer}#{postfix}", status)
350
- end
351
-
352
- # Log a rendering error.
353
-
354
- def log_error(error, path)
355
- @rendering_errors ||= []
356
- @rendering_errors << [error, path]
357
-
358
- # gmosx: Hmm perhaps this should not be logged
359
- # to avoid DOS attacks.
360
-
361
- Logger.error "Error while handling '#{path}'."
362
- Logger.error pp_exception(error)
363
- end
364
-
365
- # Convenience method to lookup the session.
366
-
367
- def session
368
- @context.session
369
- end
370
-
371
- # Convenience method to access the output buffer.
372
-
373
- def o
374
- @out
375
- end
376
-
377
- # Add some text to the output buffer.
378
-
379
- def render_text(text)
380
- @out << text
381
- end
382
- alias_method :print, :render_text
383
-
384
- #--
385
- # FIXME: do something better to stop the redirect.
386
- #++
387
-
388
- def render_nothing
389
- @out = ' '
390
- end
130
+ # Send a redirect response.
131
+
132
+ def redirect(url, status = 303)
133
+ url = url.to_s
134
+ url = "#{@context.host_url}/#{url.gsub(/^\//, '')}" unless url =~ /http/
135
+
136
+ @context.status = status
137
+ @context.out = "<html><a href=\"#{url}\">#{url}</a>.</html>\n"
138
+ @context.response_headers['location'] = url
139
+
140
+ raise RenderExit
141
+ end
142
+
143
+ # Redirect to the referer of this method.
144
+
145
+ def redirect_referer(postfix = nil, status = 303)
146
+ redirect("#{@context.referer}#{postfix}", status)
147
+ end
148
+
149
+ # Log a rendering error.
150
+
151
+ def log_error(error, path)
152
+ @rendering_errors ||= []
153
+ @rendering_errors << [error, path]
154
+
155
+ # gmosx: Hmm perhaps this should not be logged
156
+ # to avoid DOS attacks.
157
+
158
+ Logger.error "Error while handling '#{path}'."
159
+ Logger.error pp_exception(error)
160
+ end
161
+
162
+ # Convenience method to lookup the session.
163
+
164
+ def session
165
+ @context.session
166
+ end
167
+
168
+ # Add some text to the output buffer.
169
+
170
+ def render_text(text)
171
+ @out << text
172
+ end
173
+ alias_method :print, :render_text
174
+
175
+ # Access the programmatic renderer (builder).
176
+
177
+ def build(&block)
178
+ if block.arity == 1
179
+ yield XmlBuilder.new(@out)
180
+ else
181
+ XmlBuilder.new(@out).instance_eval(&block)
182
+ end
183
+ end
184
+
185
+ # Return a programmatic renderer that targets the
186
+ # output buffer.
187
+
188
+ def builder
189
+ XmlBuilder.new(@out)
190
+ end
191
+
192
+ # A Helper class to access rendering mixins. Useful to avoid
193
+ # poluting the Render with utility methods.
194
+ #--
195
+ # TODO: find a less confusing name.
196
+ #++
197
+
198
+ class Emitter
199
+ include Singleton
200
+ include XhtmlMixin
201
+ include FormMixin
202
+ include TableMixin
203
+ end
204
+
205
+ # A helper to access the utilities emitter:
206
+ #
207
+ # #{emit :form, entity}
208
+ # #{emit :options, :labels => [..], :values => [..], :selected => 1}
209
+ #
210
+ # Useful to avoid poluting the render with mixin methods.
211
+
212
+ def emit(meth, *options)
213
+ Emitter.instance.send(meth, *options)
214
+ end
391
215
 
392
216
  end
393
217